diff --git a/CMakeLists.txt b/CMakeLists.txt index d28b83b..2ce133a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,16 +1,24 @@ cmake_minimum_required(VERSION 3.0) +#set (CMAKE_VERBOSE_MAKEFILE "1") + set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/") +set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) + +#set (CMAKE_C_COMPILER /usr/bin/clang) +#set (CMAKE_CXX_COMPILER /usr/bin/clang) + project(QAsm) -set(CIDER "0") +set(CIDER "1") -#set(CMAKE_BUILD_TYPE DEBUG) set(APPVERSION "4.0.9") set(LIBRARY_NAME pal) set(FIND_LIBRARY_USE_LIB64_PATHS TRUE) -include(./lib${LIBRARY_NAME}/cmake/CMakeHeader.txt) + +#message("root: ${PROJECT_ROOT}") +#include(./lib${LIBRARY_NAME}/cmake/CMakeHeader.txt) set ( PROJECT_NAME "qasm" ) @@ -23,7 +31,7 @@ set(SOURCE ) #find_package(OpenSSL REQUIRED) -find_package( Poco REQUIRED Foundation Util XML JSON Net ) +find_package( Poco REQUIRED Foundation Util JSON) if ( ${CIDER} ) find_package( ZLIB ) @@ -32,18 +40,21 @@ endif ( ${CIDER} ) include_directories(BEFORE ${PROJECT_ROOT} ${PROJECT_ROOT}/lib${LIBRARY_NAME}/include/${LIBRARY_NAME} + #${PROJECT_ROOT}/libpal/pal/include #${OPENSSL_INCLUDE_DIR} ${Poco_INCLUDE_DIRS} ) -include(${PROJECT_ROOT}/lib${LIBRARY_NAME}/cmake/CMakeApp.txt) +#include(${PROJECT_ROOT}/lib${LIBRARY_NAME}/cmake/CMakeApp.txt) set (CIDERLIBS "" ) if ( ${CIDER} ) add_definitions(-DCIDERPRESS) -include_directories(AFTER ${PROJECT_ROOT}/ciderpress/diskimg) -add_subdirectory(${PROJECT_ROOT}/ciderpress/nufxlib) -add_subdirectory(${PROJECT_ROOT}/ciderpress/diskimg) +include_directories(AFTER ${PROJECT_ROOT}/diskimg) +add_subdirectory(${PROJECT_ROOT}/libhfs) +add_subdirectory(${PROJECT_ROOT}/nufxlib) +add_subdirectory(${PROJECT_ROOT}/diskimg) + find_library(DISKIMG_LIB libnufx_static.a ${PROJECT_ROOT}/build ) find_library(HFS_LIB libnufx_static.a ${PROJECT_ROOT}/build ) @@ -51,7 +62,7 @@ find_library(NUFX_LIB libnufx_static.a ${PROJECT_ROOT}/build ) set (CIDERLIBS diskimg_static hfs_static nufx_static ${ZLIB_LIBRARIES}) endif ( ${CIDER} ) -add_subdirectory(${PROJECT_ROOT}/lib${LIBRARY_NAME}) +add_subdirectory(${PROJECT_ROOT}/libpal) add_executable( ${PROJECT_NAME} ${SOURCE}) @@ -63,7 +74,7 @@ ${CIDERLIBS} ${Poco_LIBRARIES} ) -include(./lib${LIBRARY_NAME}/cmake/CMakeCommands.txt) +#include(./lib${LIBRARY_NAME}/cmake/CMakeCommands.txt) diff --git a/Makefile b/Makefile index 992c873..8762073 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -export USE_CLANG=1 +export USE_CLANG=0 ifeq ($(USE_CLANG),1) export CXX=/usr/bin/clang++ @@ -31,6 +31,14 @@ debug: -cd ./build && cmake -DCMAKE_BUILD_TYPE=DEBUG .. && $(MAKE) $S + +hfs: + -mkdir -p ./libhfs/build + cd ./libhfs/build && cmake .. && $(MAKE) + +nufx: + cd ./nufxlib && $(MAKE) clean && ./configure && $(MAKE) + distclean: -rm -rf ./build -rm -rf ./qasmout @@ -39,6 +47,8 @@ distclean: clean: -rm -rf ./build -rm -rf ./testout + -rm -rf ./libhfs/build ./nufxlib/build ./diskimg/build ./libpal/build + depend: -cd ./build && $(MAKE) depend diff --git a/asm.cpp b/asm.cpp index 83758bb..aaa5f5a 100644 --- a/asm.cpp +++ b/asm.cpp @@ -4,6 +4,7 @@ #include "psuedo.h" #include #include +#include #define CLASS MerlinLine @@ -11,522 +12,524 @@ CLASS::CLASS() { - clear(); + clear(); } CLASS::CLASS(std::string line) { - clear(); - set(line); + clear(); + set(line); } void CLASS::setError(uint32_t ecode) { - errorcode = ecode; + errorcode = ecode; } void CLASS::print(uint32_t lineno) { - uint32_t l, i, savpcol, pcol; - bool commentprinted = false; - static bool checked = false; - static bool nc1 = false; - bool nc = false; - uint8_t commentcol = tabs[2]; + uint32_t l, i, savpcol, pcol; + bool commentprinted = false; + static bool checked = false; + static bool nc1 = false; + bool nc = false; + uint8_t commentcol = tabs[2]; - uint32_t b = 4; // how many bytes show on the first line + uint32_t b = 4; // how many bytes show on the first line - if (datafillct > 0) - { - l = datafillct; - } - else - { - l = outbytect; - } - if (l > b) - { - l = b; - } - if (errorcode > 0) - { - if (merlinerrors) - { - //printf("errorcode=%d\n",errorcode); - printf("\n%s in line: %d", errStrings[errorcode].c_str(), lineno + 1); - if (errorText != "") - { - printf(" (%s)", errorText.c_str()); - } - printf("\n"); - } - flags &= (~FLAG_NOLINEPRINT); - } + if (datafillct > 0) + { + l = datafillct; + } + else + { + l = outbytect; + } + if (l > b) + { + l = b; + } + if (errorcode > 0) + { + if (merlinerrors) + { + //printf("errorcode=%d\n",errorcode); + printf("\n%s in line: %d", errStrings[errorcode].c_str(), lineno + 1); + if (errorText != "") + { + printf(" (%s)", errorText.c_str()); + } + printf("\n"); + } + flags &= (~FLAG_NOLINEPRINT); + } - if (flags & FLAG_NOLINEPRINT) - { - return; - } - if (!checked) - { - nc1 = getBool("option.nocolor", false); - checked = true; - } - else - { - nc = nc1; - } + if (flags & FLAG_NOLINEPRINT) + { + return; + } + if (!checked) + { + nc1 = getBool("option.nocolor", false); + checked = true; + } + else + { + nc = nc1; + } - //if ((!isatty(STDOUT_FILENO)) || (merlinerrors)) - if ((!isatty(STDOUT_FILENO)) || (0)) - { - nc = true; - } + //if ((!isatty(STDOUT_FILENO)) || (merlinerrors)) + if ((!isatty(STDOUT_FILENO)) || (0)) + { + nc = true; + } - if (!nc) - { - if (errorcode > 0) - { - if (errorcode >= errFatal) - { - SetColor(CL_WHITE | CL_BOLD | BG_RED); - } - else - { - SetColor(CL_YELLOW | CL_BOLD | BG_NORMAL); - } - } - else - { - SetColor(CL_WHITE | CL_BOLD | BG_NORMAL); - } - } - bool empty = false; - if ((printlable == "") && (opcode == "") && (printoperand == "")) - { - empty = true; - } + if (!nc) + { + if (errorcode > 0) + { + if (errorcode >= errFatal) + { + SetColor(CL_WHITE | CL_BOLD | BG_RED); + } + else + { + SetColor(CL_YELLOW | CL_BOLD | BG_NORMAL); + } + } + else + { + SetColor(CL_WHITE | CL_BOLD | BG_NORMAL); + } + } + bool empty = false; + if ((printlable == "") && (opcode == "") && (printoperand == "")) + { + empty = true; + } - pcol = 0; + pcol = 0; - bool saddr = flags & FLAG_FORCEADDRPRINT; - saddr = (outbytect > 0) ? true : saddr; - saddr = (printlable != "") ? true : saddr; + bool saddr = flags & FLAG_FORCEADDRPRINT; + saddr = (outbytect > 0) ? true : saddr; + saddr = (printlable != "") ? true : saddr; - if (saddr) - { - pcol += printf("%02X/%04X:", (startpc >> 16), startpc & 0xFFFF); - } - else - { - pcol += printf(" "); - } + if (saddr) + { + pcol += printf("%02X/%04X:", (startpc >> 16), startpc & 0xFFFF); + } + else + { + pcol += printf(" "); + } - for (i = 0; i < l; i++) - { - uint8_t a = datafillbyte; - if (datafillct == 0) - { - a = outbytes[i]; - } + for (i = 0; i < l; i++) + { + uint8_t a = datafillbyte; + if (datafillct == 0) + { + a = outbytes[i]; + } - pcol += printf("%02X ", a); - } - for (i = l; i < b; i++) - { - pcol += printf(" "); - } + pcol += printf("%02X ", a); + } + for (i = l; i < b; i++) + { + pcol += printf(" "); + } - pcol += printf("%6d ", lineno + 1); + pcol += printf("%6d ", lineno + 1); - if (showmx) - { - if ((outbytect + datafillct) > 0) - { - pcol += printf("%%%c%c ", linemx & 02 ? '1' : '0', linemx & 01 ? '1' : '0'); - } - else - { - pcol += printf(" "); - } - } + if (showmx) + { + if ((outbytect + datafillct) > 0) + { + pcol += printf("%%%c%c ", linemx & 02 ? '1' : '0', linemx & 01 ? '1' : '0'); + } + else + { + pcol += printf(" "); + } + } - if (isDebug() > 1) - { - pcol += printf("%02X ", addressmode & 0xFF); - } + if (isDebug() > 1) + { + pcol += printf("%02X ", addressmode & 0xFF); + } - savpcol = pcol; // this is how many bytes are in the side margin - pcol = 0; // reset pcol here because this is where source code starts + savpcol = pcol; // this is how many bytes are in the side margin + pcol = 0; // reset pcol here because this is where source code starts - if (empty) - { - if (comment.length() > 0) - { - if (comment[0] == ';') - { - while (pcol < commentcol) - { - pcol += printf(" "); - } - } - //else - { - int comct = 0; - for (uint32_t cc = 0; cc < comment.length(); cc++) - { - pcol += printf("%c", comment[cc]); - comct++; - if ((comment[cc] <= ' ') && (pcol >= (commentcol + savpcol + 20))) - { - printf("\n"); - pcol = 0; - while (pcol < (commentcol + savpcol)) - { - pcol += printf(" "); - } - } - } - //pcol += printf("%s", comment.c_str()); + if (empty) + { + if (comment.length() > 0) + { + if (comment[0] == ';') + { + while (pcol < commentcol) + { + pcol += printf(" "); + } + } + //else + { + int comct = 0; + for (uint32_t cc = 0; cc < comment.length(); cc++) + { + pcol += printf("%c", comment[cc]); + comct++; + if ((comment[cc] <= ' ') && (pcol >= (commentcol + savpcol + 20))) + { + printf("\n"); + pcol = 0; + while (pcol < (commentcol + savpcol)) + { + pcol += printf(" "); + } + } + } + //pcol += printf("%s", comment.c_str()); - } - commentprinted = true; - } - } - else - { - pcol += printf("%s ", printlable.c_str()); - while (pcol < tabs[0]) - { - pcol += printf(" "); - } - pcol += printf("%s ", opcode.c_str()); - while (pcol < tabs[1]) - { - pcol += printf(" "); - } - if (isDebug() > 1) - { - pcol += printf("%s ", operand.c_str()); - } - else - { - if (printoperand.length() > 0) - { - pcol += printf("%s ", printoperand.c_str()); - } - else - { - pcol += printf("%s ", operand.c_str()); - } - } - //pcol += printf("%-12s %-8s %-10s ", printlable.c_str(), opcode.c_str(), operand.c_str()); - } - if ((errorcode > 0) && (!merlinerrors)) - { - while (pcol < commentcol) - { - pcol += printf(" "); - } - pcol += printf(":[Error] %s", errStrings[errorcode].c_str()); - if (errorText.length() > 0) - { - pcol += printf(" (%s)", errorText.c_str()); - } - } - else if (!commentprinted) - { - while (pcol < commentcol) - { - pcol += printf(" "); - } - pcol += printf("%s", comment.c_str()); - } - //printf("\n"); + } + commentprinted = true; + } + } + else + { + pcol += printf("%s ", printlable.c_str()); + while (pcol < tabs[0]) + { + pcol += printf(" "); + } + pcol += printf("%s ", opcode.c_str()); + while (pcol < tabs[1]) + { + pcol += printf(" "); + } + if (isDebug() > 1) + { + pcol += printf("%s ", operand.c_str()); + } + else + { + if (printoperand.length() > 0) + { + pcol += printf("%s ", printoperand.c_str()); - if ((!nc) && (errorcode > 0)) - { - SetColor(CL_NORMAL | BG_NORMAL); - } + } + else + { + pcol += printf("%s ", operand.c_str()); + } + } + //pcol += printf("%-12s %-8s %-10s ", printlable.c_str(), opcode.c_str(), operand.c_str()); + } + if ((errorcode > 0) && (!merlinerrors)) + { + while (pcol < commentcol) + { + pcol += printf(" "); + } + pcol += printf(":[Error] %s", errStrings[errorcode].c_str()); + if (errorText.length() > 0) + { + pcol += printf(" (%s)", errorText.c_str()); + } + } + else if (!commentprinted) + { + while (pcol < commentcol) + { + pcol += printf(" "); + } + pcol += printf("%s", comment.c_str()); + } + //printf("\n"); - uint32_t obc = datafillct; - if (obc == 0) - { - obc = outbytect; - } + if ((!nc) && (errorcode > 0)) + { + SetColor(CL_NORMAL | BG_NORMAL); + } - uint32_t ct = 1; - if ((obc > b) && ((truncdata & 0x01) == 0)) - { - ct = 0; - uint8_t db; - uint32_t t = b; - char *s = (char *)" "; + uint32_t obc = datafillct; + if (obc == 0) + { + obc = outbytect; + } - b = 8; + uint32_t ct = 1; + if ((obc > b) && ((truncdata & 0x01) == 0)) + { + ct = 0; + uint8_t db; + uint32_t t = b; + char *s = (char *)" "; - //printf("t=%d ct=%d\n",t,outbytect); - printf("\n"); - while (t < obc) - { - db = datafillbyte; - if (datafillct == 0) - { - db = outbytes[t]; - } - if (ct == 0) - { - printf("%s", s); - } + b = 8; - printf("%02X ", db); - t++; - ct++; - if (ct >= b) - { - printf("\n"); - ct = 0; - } - } - } - if (ct > 0) - { - printf("\n"); - } + //printf("t=%d ct=%d\n",t,outbytect); + printf("\n"); + while (t < obc) + { + db = datafillbyte; + if (datafillct == 0) + { + db = outbytes[t]; + } + if (ct == 0) + { + printf("%s", s); + } + + printf("%02X ", db); + t++; + ct++; + if (ct >= b) + { + printf("\n"); + ct = 0; + } + } + } + if (ct > 0) + { + printf("\n"); + } } void CLASS::clear() { - syntax = SYNTAX_MERLIN; - wholetext = ""; - lable = ""; - printlable = ""; - opcode = ""; - opcodelower = ""; - operand = ""; - printoperand = ""; - comment = ""; - operand_expr = ""; - operand_expr2 = ""; - addrtext = ""; - merlinerrors = false; - linemx = 0; - bytect = 0; - opflags = 0; - pass0bytect = 0; - startpc = 0; - errorcode = 0; - errorText = ""; - outbytect = 0; - datafillct = 0; - datafillbyte = 0; - lineno = 0; - outbytes.clear(); - addressmode = 0; - expr_value = 0; - eval_result = 0; - flags = 0; - outbytes.clear(); + syntax = SYNTAX_MERLIN; + wholetext = ""; + lable = ""; + printlable = ""; + opcode = ""; + opcodelower = ""; + operand = ""; + printoperand = ""; + comment = ""; + operand_expr = ""; + operand_expr2 = ""; + addrtext = ""; + merlinerrors = false; + linemx = 0; + bytect = 0; + opflags = 0; + pass0bytect = 0; + startpc = 0; + errorcode = 0; + errorText = ""; + outbytect = 0; + datafillct = 0; + datafillbyte = 0; + lineno = 0; + outbytes.clear(); + addressmode = 0; + expr_value = 0; + eval_result = 0; + flags = 0; + outbytes.clear(); } std::string operEx[] = { - "^(\\S*)(#?)([<>\\^|]?)([\"\'])(.*)(\\4)([\\S]*)", // catches the normal delims - "^(\\s*)([!-~])([!-~]*?)([^;]*)\\2(\\S*)", // catches the unusual delims - "^(\\s*)(\\S+)", // captures everything else - "" + "^(\\S*)(#?)([<>\\^|]?)([\"\'])(.*)(\\4)([\\S]*)", // catches the normal delims + "^(\\s*)([!-~])([!-~]*?)([^;]*)\\2(\\S*)", // catches the unusual delims + "^(\\s*)(\\S+)", // captures everything else + "" }; std::string commentEx = "^(\\s*)((;|\\/{2}))+(.*)"; void CLASS::set(std::string line) { - int state = 0; - int l = (int)line.length(); - int i = 0; - int x; - char c, delim; - bool isascii; - std::string opupper, s; - std::string restofline; - std::string tline = line; - clear(); + int state = 0; + int l = (int)line.length(); + int i = 0; + int x; + char c; + //char delim; + //bool isascii; + std::string opupper, s; + std::string restofline; + std::string tline = line; + clear(); - wholetext = line; - isascii = false; - delim = 0; - while (i < l) - { - c = tline[i++]; - switch (state) - { - case 7: - if (c >= ' ') - { - comment += c; - } - else - { - i = l; - } - break; - case 0: // start of line state - if ((c == ';') || (c == '*') || (c == '/')) - { - comment += c; - state = 7; - } - else if (c > ' ') - { - lable += c; - state = 1; - } - else - { - state = 2; - }; - break; - case 1: // read in entire lable until whitespace - if (c > ' ') - { - lable += c; - } - else - { - state = 2; - } - break; - case 2: // read whitespace between label and opcode - if (c == ';') - { - comment += c; - state = 7; - } - else if (((c == '*') || (c == '/')) && (lable.length() == 0)) - { - comment += c; - state = 7; - } - else if (c > ' ') - { - opcode += c; - state = 3; - } - break; - case 3: - { - if (c > ' ') - { - opcode += c; - } - else - { - i--; - state = 4; - } - } - break; - case 4: // read whitespace between opcode and operand - { - std::vector strs; - std::string s; + wholetext = line; + // isascii = false; + //delim = 0; + while (i < l) + { + c = tline[i++]; + switch (state) + { + case 7: + if (c >= ' ') + { + comment += c; + } + else + { + i = l; + } + break; + case 0: // start of line state + if ((c == ';') || (c == '*') || (c == '/')) + { + comment += c; + state = 7; + } + else if (c > ' ') + { + lable += c; + state = 1; + } + else + { + state = 2; + }; + break; + case 1: // read in entire lable until whitespace + if (c > ' ') + { + lable += c; + } + else + { + state = 2; + } + break; + case 2: // read whitespace between label and opcode + if (c == ';') + { + comment += c; + state = 7; + } + else if (((c == '*') || (c == '/')) && (lable.length() == 0)) + { + comment += c; + state = 7; + } + else if (c > ' ') + { + opcode += c; + state = 3; + } + break; + case 3: + { + if (c > ' ') + { + opcode += c; + } + else + { + i--; + state = 4; + } + } + break; + case 4: // read whitespace between opcode and operand + { + std::vector strs; + std::string s; - Poco::RegularExpression comEx(commentEx, 0, true); - restofline = Poco::trim(tline.substr(i, tline.length())) + " "; - //printf("ROL: |%s|\n",restofline.c_str()); + Poco::RegularExpression comEx(commentEx, 0, true); + restofline = Poco::trim(tline.substr(i, tline.length())) + " "; + //printf("ROL: |%s|\n",restofline.c_str()); - if (restofline == "") - { - i = l; - break; - } - strs.clear(); - x = 0; - try - { - x = comEx.split(restofline, strs, 0); - } - catch (Poco::Exception &e) - { - x = 0; - if (isDebug() > 3) - { - cout << e.displayText() << endl; - } - } - if (x > 0) - { - // if the comment detector above is true, then the rest of line is comment; - operand = ""; - comment = strs[0]; - //printf("comment=%s\n", comment.c_str()); - i = l; - break; - } + if (restofline == "") + { + i = l; + break; + } + strs.clear(); + x = 0; + try + { + x = comEx.split(restofline, strs, 0); + } + catch (Poco::Exception &e) + { + x = 0; + if (isDebug() > 3) + { + cout << e.displayText() << endl; + } + } + if (x > 0) + { + // if the comment detector above is true, then the rest of line is comment; + operand = ""; + comment = strs[0]; + //printf("comment=%s\n", comment.c_str()); + i = l; + break; + } - int ct = 0; - int x = 0; - bool match = false; - s = operEx[ct]; - while (s != "") - { - RegularExpression regex(s, 0, true); - strs.clear(); - x = 0; - try - { - x = regex.split(restofline, strs, 0); - } - catch (Poco::Exception &e) - { - x = 0; - if (isDebug() > 3) - { - cout << e.displayText() << endl; - } - } - if (x > 0) - { - //printf("%d regex %d match |%s|\n", ct, x, restofline.c_str()); - operand = strs[0]; - //printf("which=%d operand=|%s|\n",ct,operand.c_str()); - i = (int)operand.length(); - restofline = restofline.substr(i, restofline.length()); - comment = Poco::trim(restofline); - match = true; - break; - } - ct++; - s = operEx[ct]; - } - i = l; - if (!match) - { - // if you are here, there probably isn't an operand and/or comment after opcode - } - } - break; - } - } - printlable = lable; - x = (int)lable.length(); - if (x > 1) - { - // M32 syntax allows a colon after lable, and it is not part of the lable - if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) - { - while ((x > 1) && (lable[x - 1] == ':')) - { - lable = lable.substr(0, x - 1); - x--; - } - //printf("linelable: |%s|\n", lable.c_str()); - } - } + int ct = 0; + int x = 0; + bool match = false; + s = operEx[ct]; + while (s != "") + { + RegularExpression regex(s, 0, true); + strs.clear(); + x = 0; + try + { + x = regex.split(restofline, strs, 0); + } + catch (Poco::Exception &e) + { + x = 0; + if (isDebug() > 3) + { + cout << e.displayText() << endl; + } + } + if (x > 0) + { + //printf("%d regex %d match |%s|\n", ct, x, restofline.c_str()); + operand = strs[0]; + //printf("which=%d operand=|%s|\n",ct,operand.c_str()); + i = (int)operand.length(); + restofline = restofline.substr(i, restofline.length()); + comment = Poco::trim(restofline); + match = true; + break; + } + ct++; + s = operEx[ct]; + } + i = l; + if (!match) + { + // if you are here, there probably isn't an operand and/or comment after opcode + } + } + break; + } + } + printlable = lable; + x = (int)lable.length(); + if (x > 1) + { + // M32 syntax allows a colon after lable, and it is not part of the lable + if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + while ((x > 1) && (lable[x - 1] == ':')) + { + lable = lable.substr(0, x - 1); + x--; + } + //printf("linelable: |%s|\n", lable.c_str()); + } + } - opcodelower = Poco::toLower(opcode); + opcodelower = Poco::toLower(opcode); } #undef CLASS @@ -534,19 +537,19 @@ void CLASS::set(std::string line) CLASS::CLASS() { - int x; - errorct = 0; + int x; + errorct = 0; - win_columns = -1; - win_rows = -1; - struct winsize w; - x = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); - if (x == 0) - { - win_columns = w.ws_col; - win_rows = w.ws_row; - } - //printf("cols=%d rows=%d\n",win_columns,win_rows); + win_columns = -1; + win_rows = -1; + struct winsize w; + x = ioctl(STDOUT_FILENO, TIOCGWINSZ, &w); + if (x == 0) + { + win_columns = w.ws_col; + win_rows = w.ws_row; + } + //printf("cols=%d rows=%d\n",win_columns,win_rows); } @@ -556,73 +559,73 @@ CLASS::~CLASS() void CLASS::errorOut(uint16_t code) { - printf("error: %d\n", code); + printf("error: %d\n", code); } void CLASS::init(void) { - int ts, tabpos; - std::string s; + int ts, tabpos; + std::string s; - filenames.clear(); - starttime = GetTickCount(); - initialdir = Poco::Path::current(); - syntax = SYNTAX_MERLIN; - filecount = 0; + filenames.clear(); + starttime = GetTickCount(); + initialdir = Poco::Path::current(); + syntax = SYNTAX_MERLIN; + filecount = 0; - s = getConfig("option.syntax", "merlin16"); - s = Poco::toUpper(Poco::trim(s)); - if ((s == "MERLIN") || (s == "MERLIN16")) - { - syntax = SYNTAX_MERLIN; - } - else if (s == "MERLIN32") - { - syntax = SYNTAX_MERLIN32; - } - else if (s == "QASM") - { - syntax = SYNTAX_QASM; - } + s = getConfig("option.syntax", "merlin16"); + s = Poco::toUpper(Poco::trim(s)); + if ((s == "MERLIN") || (s == "MERLIN16")) + { + syntax = SYNTAX_MERLIN; + } + else if (s == "MERLIN32") + { + syntax = SYNTAX_MERLIN32; + } + else if (s == "QASM") + { + syntax = SYNTAX_QASM; + } - std::string tabstr = getConfig("reformat.tabs", "8,16,32"); - tabstr = Poco::trim(tabstr); + std::string tabstr = getConfig("reformat.tabs", "8,16,32"); + tabstr = Poco::trim(tabstr); - memset(tabs, 0x00, sizeof(tabs)); + memset(tabs, 0x00, sizeof(tabs)); - Poco::StringTokenizer t(tabstr, ",;", 0); - tabpos = 0; - for (auto itr = t.begin(); itr != t.end(); ++itr) - { - s = Poco::trim(*itr); - try - { - ts = Poco::NumberParser::parse(s); - } - catch (...) - { - ts = 0; - } - if ((ts >= 0) && (ts < 240)) - { - tabs[tabpos++] = ts; - } - } + Poco::StringTokenizer t(tabstr, ",;", 0); + tabpos = 0; + for (auto itr = t.begin(); itr != t.end(); ++itr) + { + s = Poco::trim(*itr); + try + { + ts = Poco::NumberParser::parse(s); + } + catch (...) + { + ts = 0; + } + if ((ts >= 0) && (ts < 240)) + { + tabs[tabpos++] = ts; + } + } } void CLASS::complete(void) { - uint64_t n = GetTickCount(); - //if (isDebug()) - { - //cout << "Processing Time: " << n - starttime << "ms" << endl; - uint64_t x = n - starttime; - uint32_t x1 = x & 0xFFFFFFFF; - printf("Elapsed time: %u ms\n", x1); + uint64_t n = GetTickCount(); + //if (isDebug()) + { + //cout << "Processing Time: " << n - starttime << "ms" << endl; + uint64_t x = n - starttime; + uint32_t x1 = x & 0xFFFFFFFF; + printf("Elapsed time: %u ms\n", x1); - } + } } void CLASS::process(void) @@ -631,286 +634,286 @@ void CLASS::process(void) } int CLASS::doline(int lineno, std::string line) { - UNUSED(lineno); - UNUSED(line); + UNUSED(lineno); + UNUSED(line); - int res = -1; + int res = -1; - return (res); + return (res); } std::string CLASS::processFilename(std::string fn, std::string curDir, int level) { - std::string res = fn; - std::string s, s1; - Path p = Poco::Path(fn); + std::string res = fn; + std::string s, s1; + Path p = Poco::Path(fn); - try - { - int n = p.depth(); - //LOG_DEBUG << "n=" << n << " " << fn << endl; - if (n == 0) - { - res = curDir + fn; - } - if (n > 0) - { - std::string d1 = p[0]; - uint32_t v = 100; - try - { - v = Poco::NumberParser::parseUnsigned(d1); - } - catch (...) - { - v = 99; - } - if (v < 10) - { - Poco::Path p1 = p.popFrontDirectory(); - s = p1.toString(); - s1 = "global.path" + Poco::NumberFormatter::format(v); - switch (v) - { - case 0: - s = initialdir + s; - break; - default: - s = getConfig(s1, ".") + "/" + s; - if (level < 5) - { - s = processFilename(s, curDir, level + 1); - } - break; - } - p = s; - p.makeAbsolute(); - } - res = p.toString(); - } - } - catch (Poco::Exception &e) - { - if (isDebug() > 2) - { - cout << "exception: " << e.displayText() << endl; - } - } - catch (std::exception &e) - { - if (isDebug() > 2) - { - cout << e.what() << endl; - } - } + try + { + int n = p.depth(); + //LOG_DEBUG << "n=" << n << " " << fn << endl; + if (n == 0) + { + res = curDir + fn; + } + if (n > 0) + { + std::string d1 = p[0]; + uint32_t v = 100; + try + { + v = Poco::NumberParser::parseUnsigned(d1); + } + catch (...) + { + v = 99; + } + if (v < 10) + { + Poco::Path p1 = p.popFrontDirectory(); + s = p1.toString(); + s1 = "global.path" + Poco::NumberFormatter::format(v); + switch (v) + { + case 0: + s = initialdir + s; + break; + default: + s = getConfig(s1, ".") + "/" + s; + if (level < 5) + { + s = processFilename(s, curDir, level + 1); + } + break; + } + p = s; + p.makeAbsolute(); + } + res = p.toString(); + } + } + catch (Poco::Exception &e) + { + if (isDebug() > 2) + { + cout << "exception: " << e.displayText() << endl; + } + } + catch (std::exception &e) + { + if (isDebug() > 2) + { + cout << e.what() << endl; + } + } - p = res; - p.makeAbsolute(); - res = p.toString(); + p = res; + p.makeAbsolute(); + res = p.toString(); - char buff[PATH_MAX + 1]; - memset(buff, 0x00, sizeof(buff)); - char *rp = realpath(res.c_str(), buff); - if (rp != NULL) - { - //printf("realpath: %s\n", buff); - res = rp; - } - p = res; - p.makeAbsolute(); - res = p.toString(); + char buff[PATH_MAX + 1]; + memset(buff, 0x00, sizeof(buff)); + char *rp = realpath(res.c_str(), buff); + if (rp != NULL) + { + //printf("realpath: %s\n", buff); + res = rp; + } + p = res; + p.makeAbsolute(); + res = p.toString(); - //LOG_DEBUG << "convert: |" << res << "|" << endl; + //LOG_DEBUG << "convert: |" << res << "|" << endl; - return (res); + return (res); } int CLASS::processfile(std::string p, std::string &newfilename) { - //Poco::File fn(p); - int c; - int res = -1; - uint32_t linect; - bool done, valid; - std::string currentdir; - std::string p1; - std::string line, op; + //Poco::File fn(p); + int c; + int res = -1; + uint32_t linect; + bool done, valid; + std::string currentdir; + std::string p1; + std::string line, op; - linect = 0; - done = false; + linect = 0; + done = false; - p = Poco::trim(p); - currentdir = Poco::Path::current(); + p = Poco::trim(p); + currentdir = Poco::Path::current(); - if (filecount == 0) - { - initialdir = currentdir; - //printf("initialdir=%s\n",initialdir.c_str()); - } + if (filecount == 0) + { + initialdir = currentdir; + //printf("initialdir=%s\n",initialdir.c_str()); + } - //printf("currentdir=%s initialdir=%s\n", currentdir.c_str(), initialdir.c_str()); - //LOG_DEBUG << "initial file name: " << p << endl; - p = processFilename(p, (filecount == 0) ? currentdir : currentdir, 0); + //printf("currentdir=%s initialdir=%s\n", currentdir.c_str(), initialdir.c_str()); + //LOG_DEBUG << "initial file name: " << p << endl; + p = processFilename(p, (filecount == 0) ? currentdir : currentdir, 0); - //LOG_DEBUG << "Converted filename: " << p << endl; + //LOG_DEBUG << "Converted filename: " << p << endl; - Poco::Path tp(p); - Poco::Path path = tp.makeAbsolute(); - Poco::Path parent = path.parent(); - std::string dir = parent.toString(); + Poco::Path tp(p); + Poco::Path path = tp.makeAbsolute(); + Poco::Path parent = path.parent(); + std::string dir = parent.toString(); - try - { + try + { - if (filecount == 0) - { - // is this the first file in the compilation, or a PUT/USE? - // if first, change CWD to location of file - //LOG_DEBUG << "Changing directory to: " << dir << endl; - if (chdir(dir.c_str())) {} // change directory to where the file is - } + if (filecount == 0) + { + // is this the first file in the compilation, or a PUT/USE? + // if first, change CWD to location of file + //LOG_DEBUG << "Changing directory to: " << dir << endl; + if (chdir(dir.c_str())) {} // change directory to where the file is + } - p1 = path.toString(); + p1 = path.toString(); - newfilename = p1; - //LOG_DEBUG << "initial file name: " << p1 << endl; + newfilename = p1; + //LOG_DEBUG << "initial file name: " << p1 << endl; - valid = true; - Poco::File fn(p1); - if (!fn.exists()) - { - fn = Poco::File(p1 + ".s"); - if (!fn.exists()) - { - fn = Poco::File(p1 + ".S"); - if (!fn.exists()) - { - fn = Poco::File(p1 + ".mac"); - if (!fn.exists()) - { - fn = Poco::File(p1); - valid = false; - } - } - } - } - p1 = fn.path(); - //LOG_DEBUG << "File name: " << p1 << endl; + valid = true; + Poco::File fn(p1); + if (!fn.exists()) + { + fn = Poco::File(p1 + ".s"); + if (!fn.exists()) + { + fn = Poco::File(p1 + ".S"); + if (!fn.exists()) + { + fn = Poco::File(p1 + ".mac"); + if (!fn.exists()) + { + fn = Poco::File(p1); + valid = false; + } + } + } + } + p1 = fn.path(); + //LOG_DEBUG << "File name: " << p1 << endl; - int ecode = -3; - valid = false; - if (fn.exists()) - { - ecode = -2; - valid = true; - //LOG_DEBUG << "File exists: " << p1 << endl; - if (fn.isLink()) - { - //LOG_DEBUG << "File is a link: " << p1 << endl; - } - if ((fn.isDirectory()) || (!fn.canRead())) - { - LOG_DEBUG << "File is a directory: " << p1 << endl; - valid = false; - } - } - else - { - printf("file does not exist |%s|\n", p1.c_str()); - } + int ecode = -3; + valid = false; + if (fn.exists()) + { + ecode = -2; + valid = true; + //LOG_DEBUG << "File exists: " << p1 << endl; + if (fn.isLink()) + { + //LOG_DEBUG << "File is a link: " << p1 << endl; + } + if ((fn.isDirectory()) || (!fn.canRead())) + { + LOG_DEBUG << "File is a directory: " << p1 << endl; + valid = false; + } + } + else + { + printf("file does not exist |%s|\n", p1.c_str()); + } - newfilename = p1; - if (!valid) - { - //fprintf(stderr, "Unable to access file: %s\n", p1.c_str()); + newfilename = p1; + if (!valid) + { + //fprintf(stderr, "Unable to access file: %s\n", p1.c_str()); - errorct = 1; - return (ecode); - } + errorct = 1; + return (ecode); + } - if (valid) - { + if (valid) + { - if (filecount == 0) - { - } - else - { - for (auto itr = filenames.begin(); itr != filenames.end(); ++itr) - { - if (*itr == p1) - { - return (-9); - } - } - } + if (filecount == 0) + { + } + else + { + for (auto itr = filenames.begin(); itr != filenames.end(); ++itr) + { + if (*itr == p1) + { + return (-9); + } + } + } - filecount++; - filenames.push_back(p1); + filecount++; + filenames.push_back(p1); - std::ifstream f(p1); - if (f.is_open()) - { - //printf("file is open\n"); - line = ""; + std::ifstream f(p1); + if (f.is_open()) + { + //printf("file is open\n"); + line = ""; - while ((!done) && (f.good()) && (!f.eof())) - { - c = f.get(); - if (c == 0x8D) // merlin line ending - { - c = 0x0A; // convert to linux - } - if (c == 0x8A) // possible merlin line ending - { - c = 0x00; // ignore - } - c &= 0x7F; - int x; - switch (c) - { - case 0x0D: - break; - case 0x09: - line += " "; - break; - case 0x0A: - linect++; - x = doline(linect, line); - if (x < 0) - { - done = true; - } - line = ""; - break; - default: - if ((c >= ' ') && (c < 0x7F)) - { - line += c; - } - else - { - //printf("garbage %08X\n",c); - } - break; - } - } - if ( (f.eof())) - { - res = 0; - } - } - } - else - { - fprintf(stderr, "File <%s> does not exist.\n\n", p.c_str()); - } - } - catch (...) - { + while ((!done) && (f.good()) && (!f.eof())) + { + c = f.get(); + if (c == 0x8D) // merlin line ending + { + c = 0x0A; // convert to linux + } + if (c == 0x8A) // possible merlin line ending + { + c = 0x00; // ignore + } + c &= 0x7F; + int x; + switch (c) + { + case 0x0D: + break; + case 0x09: + line += " "; + break; + case 0x0A: + linect++; + x = doline(linect, line); + if (x < 0) + { + done = true; + } + line = ""; + break; + default: + if ((c >= ' ') && (c < 0x7F)) + { + line += c; + } + else + { + //printf("garbage %08X\n",c); + } + break; + } + } + if ( (f.eof())) + { + res = 0; + } + } + } + else + { + fprintf(stderr, "File <%s> does not exist.\n\n", p.c_str()); + } + } + catch (...) + { - } - return (res); + } + return (res); } #undef CLASS @@ -923,90 +926,138 @@ CLASS::~CLASS() } void CLASS::init(void) { - TFileProcessor::init(); - std::string s; - lines.clear(); + TFileProcessor::init(); + std::string s; + lines.clear(); - syntax = SYNTAX_MERLIN; + syntax = SYNTAX_MERLIN; } int CLASS::doline(int lineno, std::string line) { - UNUSED(lineno); + UNUSED(lineno); - MerlinLine l(line); - lines.push_back(l); - return 0; + MerlinLine l(line); + lines.push_back(l); + return 0; } void CLASS::process(void) { - uint32_t ct = (uint32_t)lines.size(); + char buff[128*1024]; + uint32_t ct = (uint32_t)lines.size(); - uint32_t len, t, pos; + uint32_t len, tlen,t, pos,i; - for (uint32_t lineno = 0; lineno < ct; lineno++) - { - MerlinLine &line = lines.at(lineno); + tlen=0; + for (uint32_t lineno = 0; lineno < ct; lineno++) + { + MerlinLine &line = lines.at(lineno); + len=0; + pos = 0; + if ((line.lable.length() == 0) + && (line.opcode.length() == 0) + && (line.operand.length() == 0)) + { + if (line.comment.length() > 0) + { + char c = line.comment[0]; + if ((c == '*') || (c == '/')) + { + sprintf(&buff[len+tlen],"%s", line.comment.c_str()); + } + else + { + t = tabs[2]; + while (len < t) + { + len += sprintf(&buff[len+tlen]," "); + } + sprintf(&buff[len+tlen],"%s", line.comment.c_str()); + } + } + sprintf(&buff[len+tlen],"\r"); + } + else + { + t = tabs[pos++]; + len = sprintf(&buff[len+tlen],"%s ", line.printlable.c_str()); + while (len < t) + { + len += sprintf(&buff[len+tlen]," "); + } - pos = 0; - len = 0; - if ((line.lable.length() == 0) - && (line.opcode.length() == 0) - && (line.operand.length() == 0)) - { - if (line.comment.length() > 0) - { - char c = line.comment[0]; - if ((c == '*') || (c == '/')) - { - printf("%s", line.comment.c_str()); - } - else - { - t = tabs[2]; - while (len < t) - { - len += printf(" "); - } - printf("%s", line.comment.c_str()); - } - } - printf("\n"); - } + t = tabs[pos++]; + len += sprintf(&buff[len+tlen],"%s ", line.opcode.c_str()); + while (len < t) + { + len += sprintf(&buff[len+tlen]," "); + } + + t = tabs[pos++]; + len += sprintf(&buff[len+tlen],"%s ", line.operand.c_str()); + while (len < t) + { + len += sprintf(&buff[len+tlen]," "); + } + + t = tabs[pos++]; + len += sprintf(&buff[len+tlen],"%s", line.comment.c_str()); + while (len < t) + { + len += sprintf(&buff[len+tlen]," "); + } + if (syntax==SYNTAX_MERLIN) + len += sprintf(&buff[len+tlen],"\r"); + else + len += sprintf(&buff[len+tlen],"\n"); + } + tlen+=len; + } + //printf("tlen=%d\n",tlen); + if (tlen>0) + { + int tct=0; + for (i=0; i p(Poco::toUpper(op), sym); + sym.name = op; + sym.opcode = opcode; + sym.namelc = Poco::toLower(op); + sym.stype = flags; + sym.value = 0; + sym.cb = cb; + std::pair p(Poco::toUpper(op), sym); - opcodes.insert(p); + opcodes.insert(p); } TSymbol * CLASS::addSymbol(std::string symname, uint32_t val, bool replace) { - TSymbol *res = NULL; - TSymbol *fnd = NULL; + TSymbol *res = NULL; + TSymbol *fnd = NULL; - std::string sym = symname; - if (!casesen) - { - sym = Poco::toUpper(sym); - } + std::string sym = symname; + if (!casesen) + { + sym = Poco::toUpper(sym); + } - //printf("addSymbol: |%s|\n",sym.c_str()); - if (sym.length() > 0) - { - TSymbol s; - s.name = sym; - s.opcode = 0; - s.namelc = Poco::toLower(sym); - s.stype = 0; - s.value = val; - s.used = false; - s.cb = NULL; - std::pair p(sym, s); + //printf("addSymbol: |%s|\n",sym.c_str()); + if (sym.length() > 0) + { + TSymbol s; + s.name = sym; + s.opcode = 0; + s.namelc = Poco::toLower(sym); + s.stype = 0; + s.value = val; + s.used = false; + s.cb = NULL; + std::pair p(sym, s); - if (sym[0] == ':') - { - //local symbol - if (currentsym == NULL) - { - goto out; - } - else - { - fnd = findSymbol(sym); - if ((fnd != NULL) && (!replace)) - { - goto out; - } + if (sym[0] == ':') + { + //local symbol + if (currentsym == NULL) + { + goto out; + } + else + { + fnd = findSymbol(sym); + if ((fnd != NULL) && (!replace)) + { + goto out; + } - if (fnd != NULL) - { - fnd->value = val; - res = fnd; - goto out; - } - if (currentsym != NULL) - { - currentsym->locals.insert(p); - } - res = findSymbol(sym); - goto out; - } - } - else - { - fnd = findSymbol(sym); + if (fnd != NULL) + { + fnd->value = val; + res = fnd; + goto out; + } + if (currentsym != NULL) + { + currentsym->locals.insert(p); + } + res = findSymbol(sym); + goto out; + } + } + else + { + fnd = findSymbol(sym); - if ((fnd != NULL) && (!replace)) - { - goto out; - } + if ((fnd != NULL) && (!replace)) + { + goto out; + } - if (fnd != NULL) - { - //printf("replacing symbol: %s %08X\n",sym.c_str(),val); - fnd->value = val; - res = fnd; - goto out; - } + if (fnd != NULL) + { + //printf("replacing symbol: %s %08X\n",sym.c_str(),val); + fnd->value = val; + res = fnd; + goto out; + } - symbols.insert(p); - res = findSymbol(sym); - } - } + symbols.insert(p); + res = findSymbol(sym); + } + } out: - return (res); + return (res); } TMacro * CLASS::findMacro(std::string symname) { - TMacro *res = NULL; + TMacro *res = NULL; - std::string sym = symname; - if (!casesen) - { - sym = Poco::toUpper(sym); - } - if (symname.length() > 0) - { - //printf("finding: %s\n",symname.c_str()); - auto itr = macros.find(sym); - if (itr != macros.end()) - { - res = &itr->second; - } - } - return (res); + std::string sym = symname; + if (!casesen) + { + sym = Poco::toUpper(sym); + } + if (symname.length() > 0) + { + //printf("finding: %s\n",symname.c_str()); + auto itr = macros.find(sym); + if (itr != macros.end()) + { + res = &itr->second; + } + } + return (res); } TSymbol * CLASS::findSymbol(std::string symname) { - TSymbol *res = NULL; + TSymbol *res = NULL; - std::string sym = symname; - if (!casesen) - { - sym = Poco::toUpper(sym); - } - if (symname.length() > 0) - { - if (symname[0] == ':') - { - if (currentsym == NULL) - { - goto out; - } - else - { - auto itr = currentsym->locals.find(sym); - if (itr != currentsym->locals.end()) - { - res = &itr->second; - goto out; - } - } - } - else - { - //printf("finding: %s\n",symname.c_str()); - auto itr = symbols.find(sym); - if (itr != symbols.end()) - { - //printf("Found: %s 0x%08X\n",itr->second.name.c_str(),itr->second.value); - res = &itr->second; - goto out; - } - } - } + std::string sym = symname; + if (!casesen) + { + sym = Poco::toUpper(sym); + } + if (symname.length() > 0) + { + if (symname[0] == ':') + { + if (currentsym == NULL) + { + goto out; + } + else + { + auto itr = currentsym->locals.find(sym); + if (itr != currentsym->locals.end()) + { + res = &itr->second; + goto out; + } + } + } + else + { + //printf("finding: %s\n",symname.c_str()); + auto itr = symbols.find(sym); + if (itr != symbols.end()) + { + //printf("Found: %s 0x%08X\n",itr->second.name.c_str(),itr->second.value); + res = &itr->second; + goto out; + } + } + } out: - return (res); + return (res); } TSymbol * CLASS::addVariable(std::string symname, std::string val, TVariable &vars, bool replace) { - TSymbol *res = NULL; - TSymbol *fnd = NULL; + TSymbol *res = NULL; + TSymbol *fnd = NULL; - std::string sym = symname; - if (!casesen) - { - sym = Poco::toUpper(sym); - } + std::string sym = symname; + if (!casesen) + { + sym = Poco::toUpper(sym); + } - //printf("addvariable\n"); - fnd = findVariable(sym, vars); + //printf("addvariable\n"); + fnd = findVariable(sym, vars); - if ((fnd != NULL) && (!replace)) - { - return (NULL); // it is a duplicate - } + if ((fnd != NULL) && (!replace)) + { + return (NULL); // it is a duplicate + } - if (fnd != NULL) - { - //printf("replacing symbol: %s %08X\n",sym.c_str(),val); - fnd->var_text = val; - return (fnd); - } + if (fnd != NULL) + { + printf("replacing symbol: %s %s\n",sym.c_str(),val.c_str()); + fnd->var_text = val; + return (fnd); + } - TSymbol s; - s.name = sym; - s.opcode = 0; - s.namelc = Poco::toLower(sym); - s.stype = 0; - s.value = 0; - s.var_text = val; - s.used = false; - s.cb = NULL; + TSymbol s; + s.name = sym; + s.opcode = 0; + s.namelc = Poco::toLower(sym); + s.stype = 0; + s.value = 0; + s.var_text = val; + s.used = false; + s.cb = NULL; - //printf("addvariable: %s %s\n", s.name.c_str(), s.text.c_str()); + printf("addvariable: %s %s\n", s.name.c_str(), s.var_text.c_str()); - std::pair p(sym, s); - vars.vars.insert(p); - res = findVariable(sym, vars); - return (res); + std::pair p(sym, s); + vars.vars.insert(p); + res = findVariable(sym, vars); + return (res); } TSymbol * CLASS::findVariable(std::string symname, TVariable &vars) { - TSymbol *res = NULL; + TSymbol *res = NULL; - if (!casesen) - { - symname = Poco::toUpper(symname); - } + if (!casesen) + { + symname = Poco::toUpper(symname); + } - if ((expand_macrostack.size() > 0) && (vars.id != expand_macro.variables.id)) - { - res = findVariable(symname, expand_macro.variables); - if (res != NULL) - { - return (res); - } - } + if ((expand_macrostack.size() > 0) && (vars.id != expand_macro.variables.id)) + { + res = findVariable(symname, expand_macro.variables); + if (res != NULL) + { + return (res); + } + } - //printf("finding: %s\n",symname.c_str()); - auto itr = vars.vars.find(symname); - if (itr != vars.vars.end()) - { - //printf("Found: %s 0x%08X\n",itr->second.name.c_str(),itr->second.value); - res = &itr->second; + //printf("finding: %s\n",symname.c_str()); + auto itr = vars.vars.find(symname); + if (itr != vars.vars.end()) + { + //printf("Found: %s 0x%08X\n",itr->second.name.c_str(),itr->second.value); + res = &itr->second; - return (res); - } - return (res); + return (res); + } + return (res); } void CLASS::showVariables(TVariable &vars) { - if (vars.vars.size() > 0) - { - printf("\nVariables:\n"); + if (vars.vars.size() > 0) + { + printf("\nVariables:\n"); - for (auto itr = vars.vars.begin(); itr != vars.vars.end(); ++itr) - { - printf("%-16s %s\n", itr->first.c_str(), itr->second.var_text.c_str()); - } - printf("\n"); - } + for (auto itr = vars.vars.begin(); itr != vars.vars.end(); ++itr) + { + printf("%-16s %s\n", itr->first.c_str(), itr->second.var_text.c_str()); + } + printf("\n"); + } } // set alpha to true to print table sorted by name or // false to print by value; void CLASS::showSymbolTable(bool alpha) { - if (symbols.size() > 0) - { - std::map alphamap; - std::map nummap; + if (symbols.size() > 0) + { + std::map alphamap; + std::map nummap; - int columns = getInt("asm.symcolumns", 3); - int column = columns; + int columns = getInt("asm.symcolumns", 3); + int column = columns; - for (auto itr = symbols.begin(); itr != symbols.end(); itr++) - { - TSymbol ptr = itr->second; - alphamap.insert(pair(ptr.name, ptr.value)); - nummap.insert(pair(ptr.value, ptr.name)); - } + for (auto itr = symbols.begin(); itr != symbols.end(); itr++) + { + TSymbol ptr = itr->second; + alphamap.insert(pair(ptr.name, ptr.value)); + nummap.insert(pair(ptr.value, ptr.name)); + } - if (alpha) - { - printf("\n\nSymbol table sorted alphabetically:\n\n"); + if (alpha) + { + printf("\n\nSymbol table sorted alphabetically:\n\n"); - for (auto itr = alphamap.begin(); itr != alphamap.end(); ++itr) - { - printf("%-16s 0x%08X ", itr->first.c_str(), itr->second); - if ( !--column ) - { - printf("\n"); - column = columns; - } - } - } - else - { - printf("\n\nSymbol table sorted numerically:\n\n"); - for (auto itr = nummap.begin(); itr != nummap.end(); ++itr) - { - printf("0x%08X %-16s ", itr->first, itr->second.c_str()); - if ( !--column ) - { - printf("\n"); - column = columns; - } - } - } - if (column > 0) - { - printf("\n"); - } - } + for (auto itr = alphamap.begin(); itr != alphamap.end(); ++itr) + { + printf("%-16s 0x%08X ", itr->first.c_str(), itr->second); + if ( !--column ) + { + printf("\n"); + column = columns; + } + } + } + else + { + printf("\n\nSymbol table sorted numerically:\n\n"); + for (auto itr = nummap.begin(); itr != nummap.end(); ++itr) + { + printf("0x%08X %-16s ", itr->first, itr->second.c_str()); + if ( !--column ) + { + printf("\n"); + column = columns; + } + } + } + if (column > 0) + { + printf("\n"); + } + } } // set alpha to true to print table sorted by name or // false to print by value; void CLASS::showMacros(bool alpha) { - if (macros.size() > 0) - { - std::map alphamap; + if (macros.size() > 0) + { + std::map alphamap; - int columns = getInt("asm.symcolumns", 3); - int column = columns; + int columns = getInt("asm.symcolumns", 3); + int column = columns; - for (auto itr = macros.begin(); itr != macros.end(); itr++) - { - TMacro ptr = itr->second; - alphamap.insert(pair(ptr.name, 0)); - } + for (auto itr = macros.begin(); itr != macros.end(); itr++) + { + TMacro ptr = itr->second; + alphamap.insert(pair(ptr.name, 0)); + } - if (alpha) - { - printf("\n\nmacros sorted alphabetically:\n\n"); + if (alpha) + { + printf("\n\nmacros sorted alphabetically:\n\n"); - for (auto itr = alphamap.begin(); itr != alphamap.end(); ++itr) - { - printf("%-16s 0x%08X ", itr->first.c_str(), itr->second); - if ( !--column ) - { - printf("\n"); - column = columns; - } - } - } - if (column > 0) - { - printf("\n"); - } - } + for (auto itr = alphamap.begin(); itr != alphamap.end(); ++itr) + { + printf("%-16s 0x%08X ", itr->first.c_str(), itr->second); + if ( !--column ) + { + printf("\n"); + column = columns; + } + } + } + if (column > 0) + { + printf("\n"); + } + } } int CLASS::callOpCode(std::string op, MerlinLine &line) { - int res = -1; - char c; - std::string s; + int res = -1; + char c; + std::string s; - // 'op' is always lowercase here + // 'op' is always lowercase here // during MACRO definition no opcodes are called (except for MAC, EOM, <<) - if (macrostack.size() > 0) - { - // if something on the macro stack, then a macro is being defined - if (!((op == "mac") || (op == "eom") || (op == "<<<"))) - { - return 0; - } - } + if (macrostack.size() > 0) + { + // if something on the macro stack, then a macro is being defined + if (!((op == "mac") || (op == "eom") || (op == "<<<"))) + { + return 0; + } + } - if (op.length() == 4) // check for 4 digit 'L' opcodes - { - c = op[3] & 0x7F; - if ((c >= 'a') && (c <= 'z')) - { - c = c - 0x20; - } - switch (c) - { - case 'L': - op = op.substr(0, 3); - line.flags |= FLAG_FORCELONG; // 3 byte address - break; - default: // any char but 'L' as in Merlin 16+ - s = Poco::toUpper(op); - if ((s == "ELSE") || (s == "DEND")) - { - break; - } - if (c != 'D') - { - op = op.substr(0, 3); - line.flags |= FLAG_FORCEABS; // 2 byte address - } - break; - case 'Z': - op = op.substr(0, 3); - line.flags |= FLAG_FORCEDP; // one byte address - break; - } - } + if (op.length() == 4) // check for 4 digit 'L' opcodes + { + c = op[3] & 0x7F; + if ((c >= 'a') && (c <= 'z')) + { + c = c - 0x20; + } + switch (c) + { + case 'L': + op = op.substr(0, 3); + line.flags |= FLAG_FORCELONG; // 3 byte address + break; + default: // any char but 'L' as in Merlin 16+ + s = Poco::toUpper(op); + if ((s == "ELSE") || (s == "DEND")) + { + break; + } + if (c != 'D') + { + op = op.substr(0, 3); + line.flags |= FLAG_FORCEABS; // 2 byte address + } + break; + case 'Z': + op = op.substr(0, 3); + line.flags |= FLAG_FORCEDP; // one byte address + break; + } + } - if (line.addressmode == syn_imm) - { - //printf("immediate mode\n"); - switch (line.expr_shift) - { - case '<': - //line.expr_value &= 0xFF; - break; - case '>': - line.expr_value >>= 8; - //line.expr_value &= 0xFFFF; - break; - case '^': - line.expr_value = (line.expr_value >> 16); - //line.expr_value = (line.expr_value >> 16) & 0xFFFF; - break; - case '|': - if (syntax == SYNTAX_MERLIN) - { - line.setError(errBadLabel); - line.expr_value = 0; - } - break; - } - } - else - { - switch (line.expr_shift) - { - case '<': - line.flags |= FLAG_DP; - break; - case '>': + if (line.addressmode == syn_imm) + { + //printf("immediate mode\n"); + switch (line.expr_shift) + { + case '<': + //line.expr_value &= 0xFF; + break; + case '>': + line.expr_value >>= 8; + //line.expr_value &= 0xFFFF; + break; + case '^': + line.expr_value = (line.expr_value >> 16); + //line.expr_value = (line.expr_value >> 16) & 0xFFFF; + break; + case '|': + if (syntax == SYNTAX_MERLIN) + { + line.setError(errBadLabel); + line.expr_value = 0; + } + break; + } + } + else + { + switch (line.expr_shift) + { + case '<': + line.flags |= FLAG_DP; + break; + case '>': #if 0 - if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) - { - // bug in M32 or not, do what it does - line.flags |= FLAG_FORCEABS; - } - else + if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + // bug in M32 or not, do what it does + line.flags |= FLAG_FORCEABS; + } + else #endif - { - line.flags |= FLAG_FORCELONG; - } - break; - case '|': - line.flags |= FLAG_FORCEABS; - break; - case '^': - //line.flags |= FLAG_FORCELONG; - break; - } - } - if (line.expr_value >= 0x100) - { - line.flags |= FLAG_FORCEABS; - } + { + line.flags |= FLAG_FORCELONG; + } + break; + case '|': + line.flags |= FLAG_FORCEABS; + break; + case '^': + //line.flags |= FLAG_FORCELONG; + break; + } + } + if (line.expr_value >= 0x100) + { + line.flags |= FLAG_FORCEABS; + } - auto itr = opcodes.find(Poco::toUpper(op)); - if (itr != opcodes.end()) - { - TSymbol s = itr->second; - if (s.cb != NULL) - { - res = s.cb(line, s); - if (res == -1) - { - res = -2; - } - } - } - else - { - line.setError(errBadOpcode); - } - return (res); + auto itr = opcodes.find(Poco::toUpper(op)); + if (itr != opcodes.end()) + { + TSymbol s = itr->second; + if (s.cb != NULL) + { + res = s.cb(line, s); + if (res == -1) + { + res = -2; + } + } + } + else + { + line.setError(errBadOpcode); + } + return (res); } typedef struct { - std::string regEx; - uint16_t addrMode; - std::string text; - //std::string expression; + std::string regEx; + uint16_t addrMode; + std::string text; + //std::string expression; } TaddrMode; // these are the regular expressions that determine the addressing mode @@ -1512,19 +1563,19 @@ typedef struct const TaddrMode addrRegEx[] = { - { "^(?'expr'.+)\\,[s,S]{1}$", syn_s, "e,s"}, // expr,s - {"^[(]{1}(?'expr'.+)[,]{1}[(S|s)]{1}[)]{1}[,]{1}[(Y|y)]{1}$", syn_sy, "(e,s),y"}, // (expr,s),y - {"^#{1}(.+)$", syn_imm, "immediate"}, //#expr,#^expr,#|expr,#expr - {"^[(]{1}(?'expr'.+)[,]{1}[x,X]{1}\\)$", syn_diix, "(e,x)"}, // (expr,x) - {"^[(]{1}(?'expr'.+)[\\)]{1}[\\,][(Y|y]{1}$", syn_diiy, "(e),y"}, //(expr),y - {"^[(]{1}(?'expr'.+)[\\)]{1}$", syn_di, "(e)"}, // (expr) - {"^\\[{1}(?'expr'.+)\\]{1}[,]{1}[(Y|y)]{1}$", syn_iyl, "[e],y"}, // [expr],y - {"^\\[(?'expr'.+)\\]$", syn_dil, "[e]"}, // [expr] - {"^(?'expr'.+)[,]{1}[(X|x)]{1}$", syn_absx, "e,x"}, // expr,x - {"^(?'expr'.+)[,]{1}[(Y|y)]{1}$", syn_absy, "e,y"}, // expr,y - {"^(?'expr'.+)[,]{1}(?'expr2'.+)$", syn_bm, "block"}, // block move expr,expr1 - {"^(?'expr'.+)$", syn_abs, "absolute"}, // expr (MUST BE LAST) - {"", 0, ""} + { "^(?'expr'.+)\\,[s,S]{1}$", syn_s, "e,s"}, // expr,s + {"^[(]{1}(?'expr'.+)[,]{1}[(S|s)]{1}[)]{1}[,]{1}[(Y|y)]{1}$", syn_sy, "(e,s),y"}, // (expr,s),y + {"^#{1}(.+)$", syn_imm, "immediate"}, //#expr,#^expr,#|expr,#expr + {"^[(]{1}(?'expr'.+)[,]{1}[x,X]{1}\\)$", syn_diix, "(e,x)"}, // (expr,x) + {"^[(]{1}(?'expr'.+)[\\)]{1}[\\,][(Y|y]{1}$", syn_diiy, "(e),y"}, //(expr),y + {"^[(]{1}(?'expr'.+)[\\)]{1}$", syn_di, "(e)"}, // (expr) + {"^\\[{1}(?'expr'.+)\\]{1}[,]{1}[(Y|y)]{1}$", syn_iyl, "[e],y"}, // [expr],y + {"^\\[(?'expr'.+)\\]$", syn_dil, "[e]"}, // [expr] + {"^(?'expr'.+)[,]{1}[(X|x)]{1}$", syn_absx, "e,x"}, // expr,x + {"^(?'expr'.+)[,]{1}[(Y|y)]{1}$", syn_absy, "e,y"}, // expr,y + {"^(?'expr'.+)[,]{1}(?'expr2'.+)$", syn_bm, "block"}, // block move expr,expr1 + {"^(?'expr'.+)$", syn_abs, "absolute"}, // expr (MUST BE LAST) + {"", 0, ""} }; // one or more of any character except ][,(); @@ -1542,922 +1593,924 @@ const std::string varExpression = "([]]{1}[:0-9A-Z_a-z]{1}[0-9A-Z_a-z]*)"; // 0x02 = 65816 uint8_t opCodeCompatibility[256] = { - 0x00, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, - 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x02, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02 + 0x00, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x01, 0x00, 0x00, 0x02, + 0x01, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x01, 0x00, 0x01, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x02, 0x02, 0x00, 0x00, 0x02 }; void CLASS::init(void) { - uint8_t b = opCodeCompatibility[0]; - if (b) - { - } - TFileProcessor::init(); - lines.clear(); + uint8_t b = opCodeCompatibility[0]; + if (b) + { + } + TFileProcessor::init(); + lines.clear(); - insertOpcodes(); + insertOpcodes(); } void CLASS::initpass(void) { - std::string s; + std::string s; - casesen = getBool("asm.casesen", true); - listing = getBool("asm.lst", true); - showmx = getBool("asm.showmx", false); - merlinerrors = getBool("asm.merlinerrors", true); + casesen = getBool("asm.casesen", true); + listing = getBool("asm.lst", true); + showmx = getBool("asm.showmx", false); + merlinerrors = getBool("asm.merlinerrors", true); - trackrep = getBool("asm.trackrep", false); - if (syntax == SYNTAX_MERLIN32) - { - trackrep = true; // can't turn this off in M32 - } - else if (syntax == SYNTAX_MERLIN) - { - trackrep = false; // can't turn this ON in M16 - } - else if (syntax == SYNTAX_QASM) - { - // we will allow this to be settable default off - trackrep = false; - trackrep = getBool("asm.trackrep", trackrep); + trackrep = getBool("asm.trackrep", false); + if (syntax == SYNTAX_MERLIN32) + { + trackrep = true; // can't turn this off in M32 + } + else if (syntax == SYNTAX_MERLIN) + { + trackrep = false; // can't turn this ON in M16 + } + else if (syntax == SYNTAX_QASM) + { + // we will allow this to be settable default off + trackrep = false; + trackrep = getBool("asm.trackrep", trackrep); - } - //merlincompat = getBool("asm.merlincompatible", true); - allowdup = getBool("asm.allowduplicate", true); + } + //merlincompat = getBool("asm.merlincompatible", true); + allowdup = getBool("asm.allowduplicate", true); - skiplist = false; + skiplist = false; - PC.origin = 0x8000; - PC.currentpc = PC.origin; - PC.totalbytes = 0; - PC.orgsave = PC.origin; + PC.origin = 0x8000; + PC.currentpc = PC.origin; + PC.totalbytes = 0; + PC.orgsave = PC.origin; - s = getConfig("asm.cpu", "M65816"); - s = Poco::trim(Poco::toUpper(s)); + s = getConfig("asm.cpu", "M65816"); + s = Poco::trim(Poco::toUpper(s)); - cpumode = MODE_65816; + cpumode = MODE_65816; - if (s == "M65816") - { - cpumode = MODE_65816; - mx = 0x00; - } - else if (s == "M65C02") - { - cpumode = MODE_65C02; - mx = 0x03; - } - else if (s == "M6502") - { - cpumode = MODE_6502; - mx = 0x03; - } - else - { - printf("Unknown CPU type in .ini\n"); - mx = 0x00; - } - mx = getInt("asm.startmx", mx);; + if (s == "M65816") + { + cpumode = MODE_65816; + mx = 0x00; + } + else if (s == "M65C02") + { + cpumode = MODE_65C02; + mx = 0x03; + } + else if (s == "M6502") + { + cpumode = MODE_6502; + mx = 0x03; + } + else + { + printf("Unknown CPU type in .ini\n"); + mx = 0x00; + } + mx = getInt("asm.startmx", mx);; - savepath = getConfig("option.objfile", ""); + savepath = getConfig("option.objfile", ""); - lastcarry = false; - relocatable = false; - currentsym = NULL; - if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) - { - // M32 allows locals that don't have a global above. this is the catchall for that - currentsym = &topSymbol; // this is the default symbol for :locals without a global above; - } - currentsymstr = ""; - lineno = 0; - errorct = 0; - passcomplete = false; - dumstartaddr = 0; - dumstart = 0; - truncdata = 0; - variables.vars.clear(); // clear the variables for each pass + lastcarry = false; + relocatable = false; + currentsym = NULL; + if ((syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + // M32 allows locals that don't have a global above. this is the catchall for that + currentsym = &topSymbol; // this is the default symbol for :locals without a global above; + } + currentsymstr = ""; + lineno = 0; + errorct = 0; + passcomplete = false; + dumstartaddr = 0; + dumstart = 0; + truncdata = 0; + variables.vars.clear(); // clear the variables for each pass - while (!macrostack.empty()) - { - macrostack.pop(); - } - while (!expand_macrostack.empty()) - { - expand_macrostack.pop(); - } - while (!LUPstack.empty()) - { - LUPstack.pop(); - } - while (!DOstack.empty()) - { - DOstack.pop(); - } - while (!LSTstack.empty()) - { - LSTstack.pop(); - } - while (!PCstack.empty()) - { - PCstack.pop(); - } - currentmacro.clear(); - expand_macro.clear(); - curLUP.clear(); - curDO.clear(); + while (!macrostack.empty()) + { + macrostack.pop(); + } + while (!expand_macrostack.empty()) + { + expand_macrostack.pop(); + } + while (!LUPstack.empty()) + { + LUPstack.pop(); + } + while (!DOstack.empty()) + { + DOstack.pop(); + } + while (!LSTstack.empty()) + { + LSTstack.pop(); + } + while (!PCstack.empty()) + { + PCstack.pop(); + } + currentmacro.clear(); + expand_macro.clear(); + curLUP.clear(); + curDO.clear(); } void CLASS::complete(void) { - if (savepath != "") - { - if (errorct == 0) - { - std::string currentdir = Poco::Path::current(); + if (savepath != "") + { + if (errorct == 0) + { + std::string currentdir = Poco::Path::current(); - savepath = processFilename(savepath, currentdir, 0); - printf("saving to file: %s\n", savepath.c_str()); + savepath = processFilename(savepath, currentdir, 0); + printf("saving to file: %s\n", savepath.c_str()); - std::ofstream f(savepath); + std::ofstream f(savepath); - uint32_t lineno = 0; - uint32_t l = (uint32_t)lines.size(); - while (lineno < l) - { - MerlinLine &line = lines.at(lineno++); - if ((line.outbytect > 0) && ((line.flags & FLAG_INDUM) == 0)) - { - for (uint32_t i = 0; i < line.outbytect; i++) - { - f.put(line.outbytes[i]); - } - } - if ((line.datafillct > 0) && ((line.flags & FLAG_INDUM) == 0)) - { - for (uint32_t i = 0; i < line.datafillct; i++) - { - f.put(line.datafillbyte & 0xFF); - } + uint32_t lineno = 0; + uint32_t l = (uint32_t)lines.size(); + while (lineno < l) + { + MerlinLine &line = lines.at(lineno++); + if ((line.outbytect > 0) && ((line.flags & FLAG_INDUM) == 0)) + { + for (uint32_t i = 0; i < line.outbytect; i++) + { + f.put(line.outbytes[i]); + } + } + if ((line.datafillct > 0) && ((line.flags & FLAG_INDUM) == 0)) + { + for (uint32_t i = 0; i < line.datafillct; i++) + { + f.put(line.datafillbyte & 0xFF); + } - } - } - } - else - { - printf("\nErrors in assembly. Output not SAVED.\n\n"); - } - } + } + } + } + else + { + printf("\nErrors in assembly. Output not SAVED.\n\n"); + } + } - printf("\n\nEnd qASM assembly, %d bytes, %u errors, %lu lines, %lu symbols.\n", PC.totalbytes, errorct, lines.size(), symbols.size()); + printf("\n\nEnd qASM assembly, %d bytes, %u errors, %lu lines, %lu symbols.\n", PC.totalbytes, errorct, lines.size(), symbols.size()); - TFileProcessor::complete(); + TFileProcessor::complete(); - if (listing) - { - showSymbolTable(true); - showSymbolTable(false); - showVariables(variables); - showMacros(true); - } + if (listing) + { + showSymbolTable(true); + showSymbolTable(false); + showVariables(variables); + showMacros(true); + } } int CLASS::evaluate(MerlinLine &line, std::string expr, int64_t &value) { - int res = -1; - int64_t result = 0; + int res = -1; + int64_t result = 0; - if (expr.length() > 0) - { + if (expr.length() > 0) + { - TEvaluator eval(*this); - line.eval_result = 0; + TEvaluator eval(*this); + line.eval_result = 0; - res = eval.evaluate(expr, result, line.expr_shift); - if (res != 0) - { - if (isDebug() > 2) - { - int c = SetColor(CL_RED); - uint32_t rr = result & 0xFFFFFFFF; - printf("eval Error=%d %08X |%s|\n", res, rr, eval.badsymbol.c_str()); - SetColor(c); - } - } - if (res == 0) - { - uint64_t v1 = (uint64_t) result; - value = result; - if ((listing) && (pass > 0) && (isDebug() > 2)) - { - uint32_t rr = v1 & 0xFFFFFFFF; - printf("EV1=%08X '%c'\n", rr, line.expr_shift); - } - if (v1 >= 0x10000) - { - line.flags |= FLAG_BIGNUM; - } - if (v1 < 0x100) - { - line.flags |= FLAG_DP; - } - } - } - else - { - value = 0; - res = 0; - } - if (isDebug() >= 3) - { - uint32_t rr = value & 0xFFFFFFFF; - printf("Eval Result: %08X (status=%d)\n", rr, res); - } - return (res); + res = eval.evaluate(expr, result, line.expr_shift); + if (res != 0) + { + if (isDebug() > 2) + { + int c = SetColor(CL_RED); + uint32_t rr = result & 0xFFFFFFFF; + printf("eval Error=%d %08X |%s|\n", res, rr, eval.badsymbol.c_str()); + SetColor(c); + } + } + if (res == 0) + { + uint64_t v1 = (uint64_t) result; + value = result; + if ((listing) && (pass > 0) && (isDebug() > 2)) + { + uint32_t rr = v1 & 0xFFFFFFFF; + printf("EV1=%08X '%c'\n", rr, line.expr_shift); + } + if (v1 >= 0x10000) + { + line.flags |= FLAG_BIGNUM; + } + if (v1 < 0x100) + { + line.flags |= FLAG_DP; + } + } + } + else + { + value = 0; + res = 0; + } + if (isDebug() >= 3) + { + uint32_t rr = value & 0xFFFFFFFF; + printf("Eval Result: %08X (status=%d)\n", rr, res); + } + return (res); } int CLASS::getAddrMode(MerlinLine & line) { - int res = -1; - uint16_t mode = syn_none; - int idx, x; - std::string s, oper; - std::vector groups; + int res = -1; + uint16_t mode = syn_none; + int idx, x; + std::string s, oper; + std::vector groups; - oper = line.operand; + oper = line.operand; - if ((line.opcode.length() == 0) || (line.operand.length() == 0)) - { - return (syn_implied); - } + if ((line.opcode.length() == 0) || (line.operand.length() == 0)) + { + return (syn_implied); + } - idx = 0; - RegularExpression valEx(valExpression, 0, true); + idx = 0; + RegularExpression valEx(valExpression, 0, true); - while (mode == syn_none) - { - s = addrRegEx[idx].regEx; - if (s == "") - { - mode = syn_err; - } - else - { - RegularExpression regex(s, 0, true); - groups.clear(); - x = 0; - try - { - x = regex.split(oper, 0, groups, 0); - } - catch (...) - { - x = 0; - } - if (x > 0) - { - mode = addrRegEx[idx].addrMode; - line.addrtext = addrRegEx[idx].text; - //cout << "mode: " << line.addrtext << endl; - int ct = 0; - for (uint32_t i = 0; i < groups.size(); i++) - { - s = groups[i]; - //printf("ct=%zu idx=%d group: |%s|\n", groups.size(), i, s.c_str()); + while (mode == syn_none) + { + s = addrRegEx[idx].regEx; + if (s == "") + { + mode = syn_err; + } + else + { + RegularExpression regex(s, 0, true); + groups.clear(); + x = 0; + try + { + x = regex.split(oper, 0, groups, 0); + } + catch (...) + { + x = 0; + } + if (x > 0) + { + mode = addrRegEx[idx].addrMode; + line.addrtext = addrRegEx[idx].text; + //cout << "mode: " << line.addrtext << endl; + int ct = 0; + for (uint32_t i = 0; i < groups.size(); i++) + { + s = groups[i]; + //printf("ct=%zu idx=%d group: |%s|\n", groups.size(), i, s.c_str()); - if (s != "") - { - if ((s != "^") && (s != "<") && (s != ">") && (s != "|")) - { - bool v = true; - if (mode == syn_abs) - { - if (i > 0) - { - v = valEx.match(s, 0, 0); - if (v) - { - if (pass == 0) - { - // can only check on pass 0, because if the A" - // symbol is defined later, we will generate different - // bytes on the next pass + if (s != "") + { + if ((s != "^") && (s != "<") && (s != ">") && (s != "|")) + { + bool v = true; + if (mode == syn_abs) + { + if (i > 0) + { + v = valEx.match(s, 0, 0); + if (v) + { + if (pass == 0) + { + // can only check on pass 0, because if the A" + // symbol is defined later, we will generate different + // bytes on the next pass - if ((line.syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) - { - if (Poco::toUpper(oper) == "A") // check the whole operand, not just the expression - { - TSymbol *sym = findSymbol("A"); - if (sym == NULL) - { - line.flags |= FLAG_FORCEIMPLIED; - mode = syn_implied; // if the label hasn't been defined yet, assume Immediate addressing - goto out; - } - } - } - } - else if (line.flags & FLAG_FORCEIMPLIED) - { - mode = syn_implied; - goto out; - } - } - } - } - if (!v) - { - //printf("invalid expression |%s|\n", s.c_str()); - mode = syn_none; - } - else if (ct == 1) - { - line.operand_expr = s; - } - else if (ct == 2) - { - line.operand_expr2 = s; - } - ct++; - //printf("line expression=|%s|\n", s.c_str()); - } - else - { - // SGQ need to set a flag for a shift and process it after eval - } - } - } - } - } - idx++; - } + if ((line.syntax & SYNTAX_MERLIN32) == SYNTAX_MERLIN32) + { + if (Poco::toUpper(oper) == "A") // check the whole operand, not just the expression + { + TSymbol *sym = findSymbol("A"); + if (sym == NULL) + { + line.flags |= FLAG_FORCEIMPLIED; + mode = syn_implied; // if the label hasn't been defined yet, assume Immediate addressing + goto out; + } + } + } + } + else if (line.flags & FLAG_FORCEIMPLIED) + { + mode = syn_implied; + goto out; + } + } + } + } + if (!v) + { + //printf("invalid expression |%s|\n", s.c_str()); + mode = syn_none; + } + else if (ct == 1) + { + line.operand_expr = s; + } + else if (ct == 2) + { + line.operand_expr2 = s; + } + ct++; + //printf("line expression=|%s|\n", s.c_str()); + } + else + { + // SGQ need to set a flag for a shift and process it after eval + } + } + } + } + } + idx++; + } out: - if (mode == syn_none) - { - mode = syn_err; - } - res = mode; - //printf("syn_mode=%d\n", mode); - return (res); + if (mode == syn_none) + { + mode = syn_err; + } + res = mode; + //printf("syn_mode=%d\n", mode); + return (res); } int CLASS::parseOperand(MerlinLine & line) { - int res = -1; + int res = -1; - line.operand_expr = ""; - int m = getAddrMode(line); - if (m >= 0) - { - res = m; - } - else - { - //errorOut(errBadAddressMode); - } - return (res); + line.operand_expr = ""; + int m = getAddrMode(line); + if (m >= 0) + { + res = m; + } + else + { + //errorOut(errBadAddressMode); + } + return (res); } int CLASS::substituteVariables(MerlinLine & line, std::string &outop) { - int res = 0; - int x; - std::string::size_type offset, slen; - std::string oper = line.operand; - std::string s; - std::string operin; - TSymbol *sym; - uint32_t len, off, ct; + int res = 0; + int x; + std::string::size_type offset, slen; + std::string oper = line.operand; + std::string s; + std::string operin; + TSymbol *sym; + uint32_t len, off, ct; - bool done = false; - operin = oper; - ct = 0; + bool done = false; + operin = oper; + ct = 0; restart: - while (!done) - { + while (!done) + { - slen = oper.length(); - if (slen > 0) - { - std::vector groups; + slen = oper.length(); + if (slen > 0) + { + std::vector groups; - offset = 0; - RegularExpression varEx(varExpression, 0, true); - Poco::RegularExpression::MatchVec mVec; + offset = 0; + RegularExpression varEx(varExpression, 0, true); + Poco::RegularExpression::MatchVec mVec; - //printf("|%s|%s|\n", varExpression.c_str(), oper.c_str()); - groups.clear(); - while (offset < slen) - { - try - { - varEx.match(oper, offset, mVec, 0); - } - catch (...) - { - offset = slen; - } + //printf("|%s|%s|\n", varExpression.c_str(), oper.c_str()); + groups.clear(); + while (offset < slen) + { + try + { + varEx.match(oper, offset, mVec, 0); + } + catch (...) + { + offset = slen; + } - x = (int)mVec.size(); - if (x > 0) - { - res = 0; - off = (uint32_t)mVec[0].offset; - len = (uint32_t)mVec[0].length; - s = oper.substr(off, len); - slen = s.length(); - sym = NULL; - if (expand_macrostack.size() > 0) - { - sym = findVariable(s, expand_macro.variables); - } - if (sym == NULL) - { - sym = findVariable(s, variables); - } - if (sym != NULL) - { - //printf("match |%s|\n",sym->var_text.c_str()); + x = (int)mVec.size(); + if (x > 0) + { + res = 0; + off = (uint32_t)mVec[0].offset; + len = (uint32_t)mVec[0].length; + s = oper.substr(off, len); + slen = s.length(); + sym = NULL; + if (expand_macrostack.size() > 0) + { + sym = findVariable(s, expand_macro.variables); + } + if (sym == NULL) + { + sym = findVariable(s, variables); + } + if (sym != NULL) + { + //printf("match |%s|\n",sym->var_text.c_str()); - if (sym->var_text != "") - { - oper = oper.replace(off, len, sym->var_text); - slen = oper.length(); - ct++; - if (pass > 0) - { - //printf("%d |%s|\n", ct, s.c_str()); - } - goto restart; - } - } - else - { - done = true; - } - offset += len; - } - else - { - offset = slen; - done = true; - } - } + if (sym->var_text != "") + { + oper = oper.replace(off, len, sym->var_text); + slen = oper.length(); + ct++; + if (pass > 0) + { + //printf("%d |%s|\n", ct, s.c_str()); + } + goto restart; + } + } + else + { + done = true; + } + offset += len; + } + else + { + offset = slen; + done = true; + } + } - } - else - { - done = true; - } - } + } + else + { + done = true; + } + } //printf("inoper=|%s| outoper=|%s|\n",operin.c_str(),oper.c_str()); - if (ct > 0) - { - outop = oper; - res = ct; - } - return (res); + if (ct > 0) + { + outop = oper; + res = ct; + } + return (res); } bool CLASS::doOFF(void) { - bool res = curDO.doskip; - std::stack tmpstack; - TDOstruct doitem; + bool res = curDO.doskip; + std::stack tmpstack; + TDOstruct doitem; - uint32_t ct = (uint32_t)DOstack.size(); - if (ct > 0) - { - tmpstack = DOstack; - } - while (ct > 0) - { - doitem = tmpstack.top(); - tmpstack.pop(); - if (doitem.doskip) - { - res = true; - } - ct--; - } - //printf("DOOFF: %d\n",res); - return (res); + uint32_t ct = (uint32_t)DOstack.size(); + if (ct > 0) + { + tmpstack = DOstack; + } + while (ct > 0) + { + doitem = tmpstack.top(); + tmpstack.pop(); + if (doitem.doskip) + { + res = true; + } + ct--; + } + //printf("DOOFF: %d\n",res); + return (res); } // this function determines if code generation is turned off (IF,DO,LUP,MAC, etc bool CLASS::codeSkipped(void) { - bool res = false; + bool res = false; - res = (curLUP.lupskip) ? true : res; - res = doOFF() ? true : res; - res = currentmacro.running ? true : res; + res = (curLUP.lupskip) ? true : res; + res = doOFF() ? true : res; + res = currentmacro.running ? true : res; - //printf("codeskip: %d\n",res); + //printf("codeskip: %d\n",res); - return (res); + return (res); } void CLASS::process(void) { #if 0 - uint32_t ct = lines.size(); - for (uint32_t lineno = 0; lineno < ct; lineno++) - { - //MerlinLine &line = lines.at(lineno); - //printf("|%s| |%s| |%s| |%s|\n", line.lable.c_str() - // , line.opcode.c_str(), line.operand.c_str(), line.comment.c_str()); - } + uint32_t ct = lines.size(); + for (uint32_t lineno = 0; lineno < ct; lineno++) + { + //MerlinLine &line = lines.at(lineno); + //printf("|%s| |%s| |%s| |%s|\n", line.lable.c_str() + // , line.opcode.c_str(), line.operand.c_str(), line.comment.c_str()); + } #else - uint32_t l; - int x;; - char c; - char buff[256]; - MerlinLine errLine; - std::string op, realop, operand, ls; + uint32_t l; + int x;; + char c; + char buff[256]; + MerlinLine errLine; + std::string op, realop, operand, ls; - pass = 0; - while (pass < 2) - { - initpass(); + pass = 0; + while (pass < 2) + { + initpass(); - l = (uint32_t)lines.size(); - bool passdone = false; - while ((!passdone) && (!passcomplete)) - { + l = (uint32_t)lines.size(); + bool passdone = false; + while ((!passdone) && (!passcomplete)) + { - MerlinLine *ml = NULL; - bool srcline = true; - if (expand_macro.running) - { - srcline = false; - if (expand_macro.currentline >= expand_macro.len) - { - // macro is complete - lineno = expand_macro.sourceline + 1; - if (expand_macrostack.size() > 0) - { - expand_macro = expand_macrostack.top(); - expand_macrostack.pop(); - } - else - { - expand_macro.clear(); - } - srcline = true; - } - else - { - ml = &expand_macro.lines[expand_macro.currentline]; - lineno = expand_macro.sourceline; - expand_macro.currentline++; - } - } - if (srcline) - { - if (lineno >= l) - { - passdone = true; - goto passout; - } - else - { - ml = &lines[lineno]; - } - } + MerlinLine *ml = NULL; + bool srcline = true; + if (expand_macro.running) + { + srcline = false; + if (expand_macro.currentline >= expand_macro.len) + { + // macro is complete + lineno = expand_macro.sourceline + 1; + if (expand_macrostack.size() > 0) + { + expand_macro = expand_macrostack.top(); + expand_macrostack.pop(); + } + else + { + expand_macro.clear(); + } + srcline = true; + } + else + { + ml = &expand_macro.lines[expand_macro.currentline]; + lineno = expand_macro.sourceline; + expand_macro.currentline++; + } + } + if (srcline) + { + if (lineno >= l) + { + passdone = true; + goto passout; + } + else + { + ml = &lines[lineno]; + } + } - MerlinLine &line = *ml; + MerlinLine &line = *ml; - //printf("lineno=%u %s\n", lineno, line.wholetext.c_str()); + //printf("lineno=%u %s\n", lineno, line.wholetext.c_str()); - line.eval_result = 0; - line.lineno = lineno + 1; - line.truncdata = truncdata; - memcpy(line.tabs, tabs, sizeof(tabs)); - //printf("lineno: %d %d |%s|\n",lineno,l,line.operand.c_str()); + line.eval_result = 0; + line.lineno = lineno + 1; + line.truncdata = truncdata; + memcpy(line.tabs, tabs, sizeof(tabs)); + //printf("lineno: %d %d |%s|\n",lineno,l,line.operand.c_str()); - op = Poco::toLower(line.opcode); - realop = line.opcode; - operand = Poco::toLower(line.operand); - line.startpc = PC.currentpc; - line.linemx = mx; - line.bytect = 0; - line.showmx = showmx; - line.syntax = syntax; - line.merlinerrors = merlinerrors; + op = Poco::toLower(line.opcode); + realop = line.opcode; + operand = Poco::toLower(line.operand); + line.startpc = PC.currentpc; + line.linemx = mx; + line.bytect = 0; + line.showmx = showmx; + line.syntax = syntax; + line.merlinerrors = merlinerrors; - if ((line.lable != "") && (op != "mac")) - { - std::string lable = Poco::trim(line.lable); - TSymbol *sym = NULL; - bool dupsym = false; - c = line.lable[0]; - switch (c) - { - case ']': - sprintf(buff, "$%X", PC.currentpc); - ls = buff; - sym = addVariable(line.lable, ls, variables, true); - //if (sym == NULL) { dupsym = true; } - break; + if ((line.lable != "") && (op != "mac")) + { + std::string lable = Poco::trim(line.lable); + TSymbol *sym = NULL; + bool dupsym = false; + c = line.lable[0]; + switch (c) + { + case ']': + sprintf(buff, "$%X", PC.currentpc); + ls = buff; + sym = addVariable(line.lable, ls, variables, true); + //if (sym == NULL) { dupsym = true; } + break; - case ':': - default: - if (pass == 0) - { - sym = addSymbol(line.lable, PC.currentpc, false); - if (sym == NULL) - { - dupsym = true; - line.setError(errDupSymbol); - } - } - if (c != ':') - { - currentsym = findSymbol(line.lable); - currentsymstr = line.lable; - } - break; - } - if (dupsym) - { - line.setError(errDupSymbol); - } - } - std::string outop; - line.printoperand = line.operand; + case ':': + default: + if (pass == 0) + { + sym = addSymbol(line.lable, PC.currentpc, false); + if (sym == NULL) + { + dupsym = true; + line.setError(errDupSymbol); + } + } + if (c != ':') + { + currentsym = findSymbol(line.lable); + currentsymstr = line.lable; + } + break; + } + if (dupsym) + { + line.setError(errDupSymbol); + } + } + std::string outop; + line.printoperand = line.operand; + //printf("\nprintoperand: |%s|\n",line.printoperand.c_str()); + //printf("\noperand: |%s|\n",operand.c_str()); - x = 0; - if (macrostack.size() == 0) - { - x = substituteVariables(line, outop); - } - if (x > 0) - { - line.printoperand = outop; - line.operand = outop; - } - x = parseOperand(line); - if (x >= 0) - { - line.addressmode = x; - } + x = 0; + if (macrostack.size() == 0) + { + x = substituteVariables(line, outop); + } + if (x > 0) + { + line.printoperand = outop; + line.operand = outop; + } + x = parseOperand(line); + if (x >= 0) + { + line.addressmode = x; + } - int64_t value = -1; - x = evaluate(line, line.operand_expr, value); + int64_t value = -1; + x = evaluate(line, line.operand_expr, value); - line.eval_result = x; - if (x == 0) - { - value &= 0xFFFFFFFF; - line.expr_value = (uint32_t)value; - } - else - { - line.expr_value = 0; - } + line.eval_result = x; + if (x == 0) + { + value &= 0xFFFFFFFF; + line.expr_value = (uint32_t)value; + } + else + { + line.expr_value = 0; + } - x = 0; - if (op.length() > 0) - { - bool skipop = false; - if (doOFF()) - { - skipop = true; - if ((op == "fin") || (op == "else") || (op == "do") || (op == "if")) - { - skipop = false; - } - } - if (!skipop) - { - TMacro *mac = NULL; - bool inoperand = false; - if (macrostack.size() == 0) - { - mac = findMacro(realop); - if (mac == NULL) - { - if (op == ">>>") // specal merlin way of calling a macro - { - Poco::StringTokenizer tok(operand, ", ", Poco::StringTokenizer::TOK_TRIM | - Poco::StringTokenizer::TOK_IGNORE_EMPTY); - std::string s = ""; - if (tok.count() > 0) - { - s = tok[0]; - } - mac = findMacro(s); - inoperand = true; - } - } - } - if (mac == NULL) - { - x = callOpCode(op, line); - } - if (mac != NULL) - { - expand_macrostack.push(expand_macro); - expand_macro = *mac; + x = 0; + if (op.length() > 0) + { + bool skipop = false; + if (doOFF()) + { + skipop = true; + if ((op == "fin") || (op == "else") || (op == "do") || (op == "if")) + { + skipop = false; + } + } + if (!skipop) + { + TMacro *mac = NULL; + bool inoperand = false; + if (macrostack.size() == 0) + { + mac = findMacro(realop); + if (mac == NULL) + { + if (op == ">>>") // specal merlin way of calling a macro + { + Poco::StringTokenizer tok(operand, ", ", Poco::StringTokenizer::TOK_TRIM | + Poco::StringTokenizer::TOK_IGNORE_EMPTY); + std::string s = ""; + if (tok.count() > 0) + { + s = tok[0]; + } + mac = findMacro(s); + inoperand = true; + } + } + } + if (mac == NULL) + { + x = callOpCode(op, line); + } + if (mac != NULL) + { + expand_macrostack.push(expand_macro); + expand_macro = *mac; - expand_macro.lines.clear(); - //printf("mac start=%u end=%u\n", expand_macro.start, expand_macro.end); - for (uint32_t lc = expand_macro.start; lc < expand_macro.end; lc++) - { - //printf("pushing %s\n", lines[lc].wholetext.c_str()); - MerlinLine nl(lines[lc].wholetext); // create a new clean line (without errors,data) - expand_macro.lines.push_back(nl); - } - expand_macro.running = true; - expand_macro.sourceline = lineno; - expand_macro.variables.vars.clear(); - // set the variables for the macro here SGQ + expand_macro.lines.clear(); + //printf("mac start=%u end=%u\n", expand_macro.start, expand_macro.end); + for (uint32_t lc = expand_macro.start; lc < expand_macro.end; lc++) + { + //printf("pushing %s\n", lines[lc].wholetext.c_str()); + MerlinLine nl(lines[lc].wholetext); // create a new clean line (without errors,data) + expand_macro.lines.push_back(nl); + } + expand_macro.running = true; + expand_macro.sourceline = lineno; + expand_macro.variables.vars.clear(); + // set the variables for the macro here SGQ - std::string parms = line.operand; - if (inoperand) - { - Poco::StringTokenizer tok(parms, ", ", Poco::StringTokenizer::TOK_TRIM | - Poco::StringTokenizer::TOK_IGNORE_EMPTY); - parms = ""; - if (tok.count() > 1) - { - parms = tok[1]; - } - } - Poco::StringTokenizer tok(parms, ",;", Poco::StringTokenizer::TOK_TRIM | - Poco::StringTokenizer::TOK_IGNORE_EMPTY); + std::string parms = line.operand; + if (inoperand) + { + Poco::StringTokenizer tok(parms, ", ", Poco::StringTokenizer::TOK_TRIM | + Poco::StringTokenizer::TOK_IGNORE_EMPTY); + parms = ""; + if (tok.count() > 1) + { + parms = tok[1]; + } + } + Poco::StringTokenizer tok(parms, ",;", Poco::StringTokenizer::TOK_TRIM | + Poco::StringTokenizer::TOK_IGNORE_EMPTY); - uint32_t ct = 0; - for (auto itr = tok.begin(); itr != tok.end(); ++itr) - { - //evaluate each of these strings, check for errors on pass 2 - std::string expr = *itr; - std::string v = "]" + Poco::NumberFormatter::format(ct + 1); - //printf("var: %s %s\n", v.c_str(), expr.c_str()); - addVariable(v, expr, expand_macro.variables, true); - ct++; - } - x = 0; - expand_macro.currentline = 0; - } - } - } + uint32_t ct = 0; + for (auto itr = tok.begin(); itr != tok.end(); ++itr) + { + //evaluate each of these strings, check for errors on pass 2 + std::string expr = *itr; + std::string v = "]" + Poco::NumberFormatter::format(ct + 1); + //printf("var: %s %s\n", v.c_str(), expr.c_str()); + addVariable(v, expr, expand_macro.variables, true); + ct++; + } + x = 0; + expand_macro.currentline = 0; + } + } + } - if ((x > 0) && (codeSkipped())) // has a psuedo-op turned off code generation? (LUP, IF, etc) - { - x = 0; - line.outbytect = 0; - } + if ((x > 0) && (codeSkipped())) // has a psuedo-op turned off code generation? (LUP, IF, etc) + { + x = 0; + line.outbytect = 0; + } - if (x > 0) - { - if (!PCstack.empty()) // are we inside a DUM section? - { - line.flags |= FLAG_INDUM; - } - if ((line.eval_result != 0) && (pass > 0)) - { - line.setError(errBadOperand); - line.errorText = line.operand_expr; - } - line.bytect = x; - PC.currentpc += x; - PC.totalbytes += x; - } - if (pass == 0) - { - line.pass0bytect = line.bytect; - } + if (x > 0) + { + if (!PCstack.empty()) // are we inside a DUM section? + { + line.flags |= FLAG_INDUM; + } + if ((line.eval_result != 0) && (pass > 0)) + { + line.setError(errBadOperand); + line.errorText = line.operand_expr; + } + line.bytect = x; + PC.currentpc += x; + PC.totalbytes += x; + } + if (pass == 0) + { + line.pass0bytect = line.bytect; + } - if (dumstart > 0) // starting a dummy section - { - PCstack.push(PC); - PC.origin = dumstartaddr; - PC.currentpc = PC.origin; - dumstart = 0; - dumstartaddr = 0; - } - if (dumstart < 0) - { - PC = PCstack.top(); - PCstack.pop(); - dumstart = 0; - dumstartaddr = 0; - } + if (dumstart > 0) // starting a dummy section + { + PCstack.push(PC); + PC.origin = dumstartaddr; + PC.currentpc = PC.origin; + dumstart = 0; + dumstartaddr = 0; + } + if (dumstart < 0) + { + PC = PCstack.top(); + PCstack.pop(); + dumstart = 0; + dumstartaddr = 0; + } - if (pass == 1) - { - if ((line.pass0bytect != line.bytect) && (line.errorcode == 0)) - { - if (expand_macrostack.size() == 0) // if macro expanding, you can't make this check - { - line.setError(errBadByteCount); - } - } + if (pass == 1) + { + if ((line.pass0bytect != line.bytect) && (line.errorcode == 0)) + { + if (expand_macrostack.size() == 0) // if macro expanding, you can't make this check + { + line.setError(errBadByteCount); + } + } - if (line.errorcode != 0) - { - errorct++; - } - if (((!skiplist) && (listing) && (pass == 1)) || (line.errorcode != 0)) - { - line.print(lineno); - } - skiplist = false; - } - lineno++; - } + if (line.errorcode != 0) + { + errorct++; + } + if (((!skiplist) && (listing) && (pass == 1)) || (line.errorcode != 0)) + { + line.print(lineno); + } + skiplist = false; + } + lineno++; + } passout: - // end of file reached here, do some final checks + // end of file reached here, do some final checks #if 0 - if (LUPstack.size() > 0) - { - errLine.clear(); - errLine.setError(errUnexpectedEOF); - errLine.print(lineno); - pass = 2; - } + if (LUPstack.size() > 0) + { + errLine.clear(); + errLine.setError(errUnexpectedEOF); + errLine.print(lineno); + pass = 2; + } #endif - pass++; - } + pass++; + } #endif } int CLASS::doline(int lineno, std::string line) { - int res = 0; - int x; - std::string op; + int res = 0; + int x; + std::string op; - UNUSED(lineno); + UNUSED(lineno); - MerlinLine l(line); + MerlinLine l(line); - op = Poco::toLower(l.opcode); - if (op == "merlin") - { - syntax = SYNTAX_MERLIN; - } - else if (op == "orca") - { - syntax = SYNTAX_ORCA; - } - l.syntax = syntax; - lines.push_back(l); + op = Poco::toLower(l.opcode); + if (op == "merlin") + { + syntax = SYNTAX_MERLIN; + } + else if (op == "orca") + { + syntax = SYNTAX_ORCA; + } + l.syntax = syntax; + lines.push_back(l); - if ((op == "use") || (op == "put")) - { - std::string fn; - x = processfile(l.operand, fn); - //printf("processfile : %d\n",x); - if (x < 0) - { - switch (x) - { - case -9: - l.setError(errDuplicateFile); - break; - case -3: - l.setError(errFileNotFound); - break; - case -2: - l.setError(errFileNoAccess); - break; - default: - l.setError(errFileNotFound); - break; - } - l.operand = fn; - l.print(0); - errorct++; - res = -1; - } - } + if ((op == "use") || (op == "put")) + { + std::string fn; + x = processfile(l.operand, fn); + //printf("processfile : %d\n",x); + if (x < 0) + { + switch (x) + { + case -9: + l.setError(errDuplicateFile); + break; + case -3: + l.setError(errFileNotFound); + break; + case -2: + l.setError(errFileNoAccess); + break; + default: + l.setError(errFileNotFound); + break; + } + l.operand = fn; + l.print(0); + errorct++; + res = -1; + } + } - return (res); + return (res); } #undef CLASS @@ -2474,7 +2527,7 @@ CLASS::~CLASS() void CLASS::init(void) { - TFileProcessor::init(); + TFileProcessor::init(); } void CLASS::process(void) @@ -2487,12 +2540,12 @@ void CLASS::complete(void) int CLASS::doline(int lineno, std::string line) { - UNUSED(lineno); - UNUSED(line); + UNUSED(lineno); + UNUSED(line); - int res = 0; + int res = 0; - return (res); + return (res); } #undef CLASS diff --git a/ciderpress/.gitignore b/ciderpress/.gitignore deleted file mode 100644 index ca86d52..0000000 --- a/ciderpress/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# build product output directories (top-level and component) -Debug -Release - -# precompiled headers -ipch - -# binaries generated by Visual Studio -CP.sdf -CP.opensdf -CP.v12.suo -*.aps -.vs/ - -# installer binary -DIST/Setup*.exe - -# linux binaries -*.o -diskimg/libdiskimg.a -diskimg/libhfs/libhfs.a - -# ctags -tags - -# VIM -*~ -*.swp diff --git a/ciderpress/CP.sln b/ciderpress/CP.sln deleted file mode 100644 index 35d6367..0000000 --- a/ciderpress/CP.sln +++ /dev/null @@ -1,72 +0,0 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "app", "app\app.vcxproj", "{B023611B-7086-46E1-847B-3B21C4732384}" - ProjectSection(ProjectDependencies) = postProject - {B66109F4-217B-43C0-86AA-EB55657E5AC0} = {B66109F4-217B-43C0-86AA-EB55657E5AC0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "diskimg", "diskimg\diskimg.vcxproj", "{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}" - ProjectSection(ProjectDependencies) = postProject - {B66109F4-217B-43C0-86AA-EB55657E5AC0} = {B66109F4-217B-43C0-86AA-EB55657E5AC0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdc", "mdc\mdc.vcxproj", "{7DF41D71-C8DC-48AA-B372-4613210310A4}" - ProjectSection(ProjectDependencies) = postProject - {B66109F4-217B-43C0-86AA-EB55657E5AC0} = {B66109F4-217B-43C0-86AA-EB55657E5AC0} - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util", "util\util.vcxproj", "{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhfs", "diskimg\libhfs\libhfs.vcxproj", "{0FA742E9-8C07-43DD-AFF8-CE31FAF70821}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reformat", "reformat\reformat.vcxproj", "{18BCF397-397E-460C-A1DC-3E26798966E4}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "zlib\zlib.vcxproj", "{B66109F4-217B-43C0-86AA-EB55657E5AC0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nufxlib", "nufxlib\nufxlib.vcxproj", "{C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {B023611B-7086-46E1-847B-3B21C4732384}.Debug|Win32.ActiveCfg = Debug|Win32 - {B023611B-7086-46E1-847B-3B21C4732384}.Debug|Win32.Build.0 = Debug|Win32 - {B023611B-7086-46E1-847B-3B21C4732384}.Release|Win32.ActiveCfg = Release|Win32 - {B023611B-7086-46E1-847B-3B21C4732384}.Release|Win32.Build.0 = Release|Win32 - {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Debug|Win32.ActiveCfg = Debug|Win32 - {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Debug|Win32.Build.0 = Debug|Win32 - {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Release|Win32.ActiveCfg = Release|Win32 - {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Release|Win32.Build.0 = Release|Win32 - {7DF41D71-C8DC-48AA-B372-4613210310A4}.Debug|Win32.ActiveCfg = Debug|Win32 - {7DF41D71-C8DC-48AA-B372-4613210310A4}.Debug|Win32.Build.0 = Debug|Win32 - {7DF41D71-C8DC-48AA-B372-4613210310A4}.Release|Win32.ActiveCfg = Release|Win32 - {7DF41D71-C8DC-48AA-B372-4613210310A4}.Release|Win32.Build.0 = Release|Win32 - {04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Debug|Win32.ActiveCfg = Debug|Win32 - {04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Debug|Win32.Build.0 = Debug|Win32 - {04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Release|Win32.ActiveCfg = Release|Win32 - {04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Release|Win32.Build.0 = Release|Win32 - {0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Debug|Win32.ActiveCfg = Debug|Win32 - {0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Debug|Win32.Build.0 = Debug|Win32 - {0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Release|Win32.ActiveCfg = Release|Win32 - {0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Release|Win32.Build.0 = Release|Win32 - {18BCF397-397E-460C-A1DC-3E26798966E4}.Debug|Win32.ActiveCfg = Debug|Win32 - {18BCF397-397E-460C-A1DC-3E26798966E4}.Debug|Win32.Build.0 = Debug|Win32 - {18BCF397-397E-460C-A1DC-3E26798966E4}.Release|Win32.ActiveCfg = Release|Win32 - {18BCF397-397E-460C-A1DC-3E26798966E4}.Release|Win32.Build.0 = Release|Win32 - {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Debug|Win32.ActiveCfg = Debug|Win32 - {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Debug|Win32.Build.0 = Debug|Win32 - {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Release|Win32.ActiveCfg = Release|Win32 - {B66109F4-217B-43C0-86AA-EB55657E5AC0}.Release|Win32.Build.0 = Release|Win32 - {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Debug|Win32.ActiveCfg = Debug|Win32 - {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Debug|Win32.Build.0 = Debug|Win32 - {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Release|Win32.ActiveCfg = Release|Win32 - {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/ciderpress/DIST/License.txt b/ciderpress/DIST/License.txt deleted file mode 100644 index 0094241..0000000 --- a/ciderpress/DIST/License.txt +++ /dev/null @@ -1,38 +0,0 @@ -End-User License Agreement for CiderPress -Copyright (C) 2017, CiderPress project authors -All rights reserved. - -AGREEMENT. After reading this agreement carefully, if you ("Customer") do -not agree to all of the terms of this agreement, you may not use -CiderPress ("Software"). Your use of this Software indicates your -acceptance of this license agreement and warranty. All updates to the -Software shall be considered part of the Software and subject to the terms -of this Agreement. Changes to this Agreement may accompany updates to the -Software, in which case by installing such update Customer accepts the -terms of the Agreement as changed. The Agreement is not otherwise subject -to addition, amendment, modification, or exception unless in writing -signed by an officer of both Customer and FaddenSoft, LLC ("FaddenSoft"). - -1. LICENSE. This is free software, distributed under the terms of the -BSD License. See "LICENSE.txt" for details. - -2. LIMITED WARRANTY. THE SOFTWARE IS PROVIDED AS IS AND FADDENSOFT -DISCLAIMS ALL WARRANTIES RELATING TO THIS SOFTWARE, WHETHER EXPRESSED OR -IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. - -3. LIMITATION ON DAMAGES. NEITHER FADDENSOFT NOR ANYONE INVOLVED IN THE -CREATION, PRODUCTION, OR DELIVERY OF THIS SOFTWARE SHALL BE LIABLE FOR ANY -INDIRECT, CONSEQUENTIAL, OR INCIDENTAL DAMAGES ARISING OUT OF THE USE OR -INABILITY TO USE SUCH SOFTWARE EVEN IF FADDENSOFT HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES OR CLAIMS. IN NO EVENT SHALL FADDENSOFT'S -LIABILITY FOR ANY DAMAGES EXCEED THE PRICE PAID FOR THE LICENSE TO USE THE -SOFTWARE, REGARDLESS OF THE FORM OF CLAIM. THE PERSON USING THE SOFTWARE -BEARS ALL RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE. - -4. GOVERNING LAW AND GENERAL PROVISIONS. This Agreement will be governed -by the laws of the State of California, U.S.A. If any part of this -Agreement is found void and unenforceable, it will not affect the validity -of the balance of the Agreement, which shall remain valid and enforceable -according to its terms. This Agreement shall automatically terminate upon -failure by Customer to comply with its terms. diff --git a/ciderpress/DIST/NList.Data.TXT b/ciderpress/DIST/NList.Data.TXT deleted file mode 100644 index b4efab5..0000000 --- a/ciderpress/DIST/NList.Data.TXT +++ /dev/null @@ -1,2516 +0,0 @@ -fff9 | NLIST Data File: Last mod 17-Oct-93 DAL (Loma Prieta + 4) -fffa | Based on Apple IIgs System Disk 6.0.1+UserTool#1,2 -fffb | Dave Lyons -fffc | dlyons@apple.com -0040 P8:ALLOC_INTERRUPT(2:IntNum/1,CodePtr) -0041 P8:DEALLOC_INTERRUPT(1:IntNum/1) -0042 P8:ATLK:AppleTalk(Async/1,Cmd/1,Result,...) -0043 P8:ATLK:SpecialOpenFork(4or84:pn,ioBuff,Ref/1,Mode/1) -0044 P8:ATLK:ByteRangeLock(4:Ref/1,Flag/1,Off/3,Len/3) -0065 P8:QUIT(4:Type/1,Path,zz/1,zz) -0080 P8:READ_BLOCK(3:Unit/1,Buff,BlkNum) -0081 P8:WRITE_BLOCK(3:Unit/1,Buff,BlkNum) -0082 P8:GET_TIME() -00C0 P8:CREATE(7:pn,acc/1,type/1,aux,stt/1,cD,cT) -00C1 P8:DESTROY(1:pn) -00C2 P8:RENAME(2:pn1,pn2) -00C3 P8:SetFileInfo(7:pn,a/1,t/1,aux,nul/3,mD,mT) -00C4 P8:GetFileInfo(10:pn,a/1,t/1,x,s/1,b,mDTcDT) -00C5 P8:ONLINE(2:UnitNum/1,Buff) -00C6 P8:SET_PREFIX(1:pn) -00C7 P8:GET_PREFIX(1:Buff) -00C8 P8:OPEN(3:pn,ioBuff,Ref/1) -00C9 P8:NEWLINE(3:Ref/1,Mask/1,Char/1) -00CA P8:READ(4:Ref/1,Where,reqCount,xfrCount) -00CB P8:WRITE(4:Ref/1,Where,reqCount,xfrCount) -00CC P8:CLOSE(1:Ref/1) -00CD P8:FLUSH(1:Ref/1) -00CE P8:SET_MARK(2:Ref/1,Position/3) -00CF P8:GET_MARK(2:Ref/1,Position/3) -00D0 P8:SET_EOF(2:Ref/1,Position/3) -00D1 P8:GET_EOF(2:Ref/1,Position/3) -00D2 P8:SET_BUF(2:Ref/1,ioBuff) -00D3 P8:GET_BUF(2:Ref/1,ioBuff) -* ProDOS 16 / GS/OS -0001 P16:CREATE(@Path,Acc,Typ,Aux/4,StT,CrD,CrT) -0002 P16:DESTROY(@Path) -0004 P16:CHANGE_PATH(@Path1,@Path2) -0005 P16:SET_FILE_INFO(@P,a,t,xt/4,z,cD,cT,mD,mT) -0006 P16:GET_FILE_INFO(@P,a,t,xt/4,s,cDT,mDT,b/4) -0008 P16:VOLUME(@DevN,@VolN,Blks/4,FreeBlks/4,fsID) -0009 P16:SET_PREFIX(Pfx#,@Prefix) -000A P16:GET_PREFIX(Pfx#,@Buff) -000B P16:CLEAR_BACKUP_BIT(@Path) -0010 P16:OPEN(Ref,@Path,xxx/4) -0011 P16:NEWLINE(Ref,Mask,Char) -0012 P16:READ(Ref,@Where,Count/4,xfCount/4) -0013 P16:WRITE(Ref,@Where,Count/4,xfCount/4) -0014 P16:CLOSE(Ref) -0015 P16:FLUSH(Ref) -0016 P16:SET_MARK(Ref,Pos/4) -0017 P16:GET_MARK(Ref,Pos/4) -0018 P16:SET_EOF(Ref,EOF/4) -0019 P16:GET_EOF(Ref,EOF/4) -001A P16:SET_LEVEL(Level) -001B P16:GET_LEVEL(Level) -001C P16:GET_DIR_ENTRY(Ref#,z,Bs,Dis,@Bf,dEnt/36) -0020 P16:GET_DEV_NUM(@DevName,Dev#) -0021 P16:GET_LAST_DEV(Dev#) -0022 P16:READ_BLOCK(Dev#,@Where,Blk#/4) -0023 P16:WRITE_BLOCK(Dev#,@Where,Blk#/4) -0024 P16:FORMAT(@DevName,@VolName,fsID) -0025 P16:ERASE_DISK(@DevName,@VolName,fsID) -0027 P16:GET_NAME(@Buff) -0028 P16:GET_BOOT_VOL(@Buff) -0029 P16:QUIT(@Path,Flags) -002A P16:GET_VERSION(Version) -002C P16:D_INFO(Dev#,@DevName) -0031 P16:ALLOC_INTERRUPT(Int#,@Code) -0032 P16:DEALLOCATE_INTERRUPT(Int#) -0101 Shell:Get_LInfo (...) -0102 Shell:Set_LInfo (...) -0103 Shell:Get_Lang(Lang) -0104 Shell:Set_Lang(Lang) -0105 Shell:Error(Error) -0106 Shell:Set_Variable(@VarName,Val/4) -0107 Shell:Version(Vers/4) -0108 Shell:Read_Indexed(@VarName,Val/4,Index) -0109 Shell:Init_Wildcard(@File,Flags) -010A Shell:Next_Wildcard(@NextFile) -010B Shell:Read_Variable(@VarName,Value/4) -010C Shell:ChangeVector(res,vec,@proc,@old) -010D Shell:Execute(Flag,@CmdStr) -010E Shell:FastFile(act,ind,flg,H,L/4,@n,...) -010F Shell:Direction(Dev,Direct) -0110 Shell:Redirect(Dev,ApndFlg,@File) -0113 Shell:Stop(StopFlag) -0114 Shell:ExpandDevices(@name) -0115 Shell:UnsetVariable(@var) -0116 Shell:Export(@var,flags) -0117 Shell:PopVariables() -0118 Shell:PushVariables() -0119 Shell:SetStopFlag(stopFlag) -011A Shell:ConsoleOut(Char) -011B Shell:SetIODevices(OutT,@out,ErrT,@err,InT,@in) -011C Shell:GetIODevices(OutT,@out,ErrT,@err,InT,@in) -011D Shell:GetCommand(idx,restart,rsv,cmd,name/16) -2001 GS/OS:Create(1-7:@P,Acc,Typ,Aux/4,Stg,EOF/4,rEOF/4) -2002 GS/OS:Destroy(1:@P) -2003 GS/OS:OSShutdown(1:Flags) -2004 GS/OS:ChangePath(2-3:@P1,@P2,TrustMeFlag) -2005 GS/OS:SetFileInfo(2-12:@P,A,T,X/4,,c/8,m/8,@Opt,,,,) -2006 GS/OS:GetFileInfo(2-12:@P,A,T,X/4,S,c/8,m/8,@Opt,EOF/4,B/4,rEOF/4,rB/4) -2007 GS/OS:JudgeName(3-6:fileSysID,Descr,@Rules,MaxLen,@Path,Result) -2008 GS/OS:Volume(2-8:@DevN,@vnOut,blks/4,free/4,fSys,BlkSz,char,devID) -2009 GS/OS:SetPrefix(1-2:pfxNum,@Pfx) -200A GS/OS:GetPrefix(2:pfxNum,@Pfx) -200B GS/OS:ClearBackup(1:@P) -200C GS/OS:SetSysPrefs(1:prefs) -200D GS/OS:Null(0:) -200E GS/OS:ExpandPath(2-3:@InPath,@OutPath,UpcaseFlg) -200F GS/OS:GetSysPrefs(1:prefs) -2010 GS/OS:Open(2-15:ref,@P,Acc,fork,gotAcc,+GET_FILE_INFO) -2011 GS/OS:NewLine(4:ref,ANDmask,NumChars,@NLtable) -2012 GS/OS:Read(4-5:ref,@buff,count/4,xfer/4,cacheFlg) -2013 GS/OS:Write(4-5:ref,@buff,count/4,xfer/4,cacheFlg) -2014 GS/OS:Close(1:ref) -2015 GS/OS:Flush(1-2:ref,flags) -2016 GS/OS:SetMark(3:ref,base,displ/4) -2017 GS/OS:GetMark(2:ref,pos/4) -2018 GS/OS:SetEOF(3:ref,base,displ/4) -2019 GS/OS:GetEOF(2:ref,eof/4) -201A GS/OS:SetLevel(1-2:level,levelMode) -201B GS/OS:GetLevel(1-2:level,levelMode) -201C GS/OS:GetDirEntry(5-17:rf,fl,bs,ds,@n,n,T,EOF/4,b/4,c/8,m/8,A,X/4,FS,@o,resEOF/4,resBk/4) -201D GS/OS:BeginSession(0:) -201E GS/OS:EndSession(0:) -201F GS/OS:SessionStatus(1:status) -2020 GS/OS:GetDevNumber(2:@DevN,devnum) -2024 GS/OS:Format(1-6:@DevN,@VolN,gotFS,wantFS,flags,realVolName) -2025 GS/OS:EraseDisk(1-6:@DevN,@VolN,gotFS,wantFS,flags,realVolName) -2026 GS/OS:ResetCache(0:) -2027 GS/OS:GetName(1:@n) -2028 GS/OS:GetBootvol(1:@n) -2029 GS/OS:Quit(0-2:@P,flags) -202A GS/OS:GetVersion(1:version) -202B GS/OS:GetFSTInfo(2-7:n,fs,@n,ver,attr,bSz,mxV/4,mxF/4) -202C GS/OS:DInfo(2-10:n,@n,chr,B/4,sl,unit,ver,dTyp,@hd,@nx) -202D GS/OS:DStatus(5:n,statusReq,@statList,count/4,xfer/4) -202E GS/OS:DControl(5:n,code,@ctlList,count/4,xfer/4) -202F GS/OS:DRead(6:n,@bf,count/4,blk/4,blkSz,xfer/4) -2030 GS/OS:DWrite(6:n,@bf,count/4,blk/4,blkSz,xfer/4) -2031 GS/OS:BindInt(3:IntNum,VecRefNum,@handler) -2032 GS/OS:UnbindInt(1:IntNum) -2033 GS/OS:FSTSpecific(2+...) -2034 GS/OS:AddNotifyProc(1:@proc) -2035 GS/OS:DelNotifyProc(1:@proc) -2036 GS/OS:DRename(2:n,@newName) -2037 GS/OS:GetStdRefNum(2:pfxNum,refNum) -2038 GS/OS:GetRefNum(2-6:@path,ref,acc,res,case,disp) -2039 GS/OS:GetRefInfo(2-5:ref,acc,@path,resNum,level) -203A GS/OS:SetStdRefNum(2:pfxNum,refNum) -* System tools -0000 === System Tools === -0001 === tool locator === -0101 TLBootInit() -0201 TLStartUp() -0301 TLShutDown() -0401 TLVersion():Vers -0501 TLReset() -0601 TLStatus():ActFlg -0901 GetTSPtr(SysFlg,TS#):@FPT -0A01 SetTSPtr(SysFlg,TS#,@FPT) -0B01 GetFuncPtr(SysFlg,Func):@Func -0C01 GetWAP(SysFlg,TS#):@WAP -0D01 SetWAP(SysFlg,TS#,@WAP) -0E01 LoadTools(@ToolTable) -0F01 LoadOneTool(TS#,MinVers) -1001 UnloadOneTool(TS#) -1101 TLMountVolume(X,Y,@L1,@L2,@B1,@B2):Btn# -1201 TLTextMountVolume(@L1,@L2,@B1,@B2):Btn# -1301 SaveTextState():StateH -1401 RestoreTextState(StateH) -1501 MessageCenter(Action,Type,MsgH) -1601 SetDefaultTPT() -1701 MessageByName(CreateF,@inpRec):Created,Type -1801 StartUpTools(MemID,ssDesc,ssRef/4):ssRef/4 -1901 ShutDownTools(ssDesc,ssRef/4) -1A01 GetMsgHandle(Flags,MsgRef/4):H -1B01 AcceptRequests(@NameStr,UserID,@ReqProc) -1C01 SendRequest(ReqCode,How,Target/4,@In,@Out) -0002 === memory manager === -0102 MMBootInit() -0202 MMStartUp():MemID -0302 MMShutDown(MemID) -0402 MMVersion():Vers -0502 MMReset() -0602 MMStatus():ActFlg -0902 NewHandle(Size/4,MemID,Attr,@loc):H -0A02 ReAllocHandle(Size/4,MemID,Attr,@loc,H) -0B02 RestoreHandle(H) -0C02 AddToOOMQueue(@header) -0D02 RemoveFromOOMQueue(@header) -1002 DisposeHandle(H) -1102 DisposeAll(MemID) -1202 PurgeHandle(H) -1302 PurgeAll(MemID) -1802 GetHandleSize(H):Size/4 -1902 SetHandleSize(Size/4,H) -1A02 FindHandle(@byte):H -1B02 FreeMem():FreeBytes/4 -1C02 MaxBlock():Size/4 -1D02 TotalMem():Size/4 -1E02 CheckHandle(H) -1F02 CompactMem() -2002 HLock(H) -2102 HLockAll(MemID) -2202 HUnlock(H) -2302 HUnlockAll(MemID) -2402 SetPurge(PrgLvl,H) -2502 SetPurgeAll(PrgLvl,MemID) -2802 PtrToHand(@Src,DestH,Count/4) -2902 HandToPtr(SrcH,@Dest,Count/4) -2A02 HandToHand(SrcH,DestH,Count/4) -2B02 BlockMove(@Source,@Dest,Count/4) -2F02 RealFreeMem():Size/4 -3002 SetHandleID(newMemID,theH):oldMemID -0003 === misc tools === -0103 MTBootInit() -0203 MTStartUp() -0303 MTShutDown() -0403 MTVersion():Vers -0503 MTReset() -0603 MTStatus():ActFlg -0903 WriteBRam(@Buff) -0A03 ReadBRam(@Buff) -0B03 WriteBParam(Data,Parm#) -0C03 ReadBParam(Parm#):Data -0D03 ReadTimeHex():WkDay,Mn&Dy,Yr&Hr,Mn&Sec -0E03 WriteTimeHex(Mn&Dy,Yr&Hr,Mn&Sec) -0F03 ReadAsciiTime(@Buff) -1003 SetVector(Vec#,@x) -1103 GetVector(Vec#):@x -1203 SetHeartBeat(@Task) -1303 DelHeartBeat(@Task) -1403 ClrHeartBeat() -1503 SysFailMgr(Code,@Msg) -1603 GetAddr(Ref#):@Parm -1703 ReadMouse():X,Y,Stat&Mode -1803 InitMouse(Slot) -1903 SetMouse(Mode) -1A03 HomeMouse() -1B03 ClearMouse() -1C03 ClampMouse(Xmn,Xmx,Ymn,Ymx) -1D03 GetMouseClamp():Xmn,Xmx,Ymn,Ymx -1E03 PosMouse(X,Y) -1F03 ServeMouse():IntStat -2003 GetNewID(Kind):MemID -2103 DeleteID(MemID) -2203 StatusID(MemID) -2303 IntSource(Ref#) -2403 FWEntry(A,X,Y,Address):P,A,X,Y -2503 GetTick():Ticks/4 -2603 PackBytes(@StartPtr,@Sz,@OutBf,OutSz):Size -2703 UnPackBytes(@Buff,BfSz,@StartPtr,@Sz):Size -2803 Munger(@Dst,@DstL,@t,tL,@Rpl,RplL,@Pad):N -2903 GetIRQEnable():IntStat -2A03 SetAbsClamp(Xmn,Xmx,Ymn,Ymx) -2B03 GetAbsClamp():Xmn,Xmx,Ymn,Ymx -2C03 SysBeep() -2E03 AddToQueue(@newTask,@queueHeader) -2F03 DeleteFromQueue(@task,@queueHeader) -3003 SetInterruptState(@stateRec,NumBytes) -3103 GetInterruptState(@stateRec,NumBytes) -3203 GetIntStateRecSize():Size -3303 ReadMouse2():xPos,yPos,StatMode -3403 GetCodeResConverter():@proc -3503 GetROMResource(???,???/4):???H -3603 ReleaseROMResource(???,???/4) -3703 ConvSeconds(convVerb,Secs/4,@Date):SecondsOut/4 -3803 SysBeep2(beepKind) -3903 VersionString(flags,Version/4,@Buffer) -3A03 WaitUntil(WaitFromTime,DelayTime):NewTime -3B03 StringToText(flags,@String,StrLen,@Buffer):ResFlags,PrntLen -3C03 ShowBootInfo(@String,@Icon) -3D03 ScanDevices():DevNum -3E03 AlertMessage(@Table,MsgNum,@Subs):Button -3F03 DoSysPrefs(bitsToClear,bitsToSet):SysPrefs -0004 === QuickDraw II === -0104 QDBootInit() -0204 QDStartUp(DirPg,MastSCB,MaxWid,MemID) -0304 QDShutDown() -0404 QDVersion():Vers -0504 QDReset() -0604 QDStatus():ActFlg -0904 GetAddress(what):@Table -0A04 GrafOn() -0B04 GrafOff() -0C04 GetStandardSCB():SCB -0D04 InitColorTable(@Table) -0E04 SetColorTable(Tab#,@SrcTab) -0F04 GetColorTable(Tab#,@DestTbl) -1004 SetColorEntry(Tab#,Ent#,NewCol) -1104 GetColorEntry(Tab#,Ent#):Color -1204 SetSCB(Line#,SCB) -1304 GetSCB(Line#):SCB -1404 SetAllSCBs(SCB) -1504 ClearScreen(Color) -1604 SetMasterSCB(SCB) -1704 GetMasterSCB():SCB -1804 OpenPort(@Port) -1904 InitPort(@Port) -1A04 ClosePort(@Port) -1B04 SetPort(@Port) -1C04 GetPort():@Port -1D04 SetPortLoc(@LocInfo) -1E04 GetPortLoc(@LocInfo) -1F04 SetPortRect(@Rect) -2004 GetPortRect(@Rect) -2104 SetPortSize(w,h) -2204 MovePortTo(h,v) -2304 SetOrigin(h,v) -2404 SetClip(RgnH) -2504 GetClip(RgnH) -2604 ClipRect(@Rect) -2704 HidePen() -2804 ShowPen() -2904 GetPen(@Pt) -2A04 SetPenState(@PenSt) -2B04 GetPenState(@PenSt) -2C04 SetPenSize(w,h) -2D04 GetPenSize(@Pt) -2E04 SetPenMode(Mode) -2F04 GetPenMode():Mode -3004 SetPenPat(@Patt) -3104 GetPenPat(@Patt) -3204 SetPenMask(@Mask) -3304 GetPenMask(@Mask) -3404 SetBackPat(@Patt) -3504 GetBackPat(@Patt) -3604 PenNormal() -3704 SetSolidPenPat(Color) -3804 SetSolidBackPat(Color) -3904 SolidPattern(Color,@Patt) -3A04 MoveTo(h,v) -3B04 Move(dh,dv) -3C04 LineTo(h,v) -3D04 Line(dh,dv) -3E04 SetPicSave(Val/4) -3F04 GetPicSave():Val/4 -4004 SetRgnSave(Val/4) -4104 GetRgnSave():Val/4 -4204 SetPolySave(Val/4) -4304 GetPolySave():Val/4 -4404 SetGrafProcs(@GrafProcs) -4504 GetGrafProcs():@GrafProcs -4604 SetUserField(Val/4) -4704 GetUserField():Val/4 -4804 SetSysField(Val/4) -4904 GetSysField():Val/4 -4A04 SetRect(@Rect,left,top,right,bot) -4B04 OffsetRect(@Rect,dh,dv) -4C04 InsetRect(@Rect,dh,dv) -4D04 SectRect(@R1,@R2,@DstR):nonEmptyF -4E04 UnionRect(@Rect1,@Rect2,@UnionRect) -4F04 PtInRect(@Pt,@Rect):Flag -5004 Pt2Rect(@Pt1,@Pt2,@Rect) -5104 EqualRect(@Rect1,@Rect2):Flag -5204 NotEmptyRect(@Rect):Flag -5304 FrameRect(@Rect) -5404 PaintRect(@Rect) -5504 EraseRect(@Rect) -5604 InvertRect(@Rect) -5704 FillRect(@Rect,@Patt) -5804 FrameOval(@Rect) -5904 PaintOval(@Rect) -5A04 EraseOval(@Rect) -5B04 InvertOval(@Rect) -5C04 FillOval(@Rect,@Patt) -5D04 FrameRRect(@Rect,OvalW,OvalHt) -5E04 PaintRRect(@Rect,OvalW,OvalHt) -5F04 EraseRRect(@Rect,OvalW,OvalHt) -6004 InvertRRect(@Rect,OvalW,OvalHt) -6104 FillRRect(@Rect,OvalW,OvalHt,@Patt) -6204 FrameArc(@Rect,Ang1,ArcAng) -6304 PaintArc(@Rect,Ang1,ArcAng) -6404 EraseArc(@Rect,Ang1,ArcAng) -6504 InvertArc(@Rect,Ang1,ArcAng) -6604 FillArc(@Rect,Ang1,ArcAng,@Patt) -6704 NewRgn():RgnH -6804 DisposeRgn(RgnH) -6904 CopyRgn(SrcRgnH,DestRgnH) -6A04 SetEmptyRgn(RgnH) -6B04 SetRectRgn(RgnH,left,top,right,bot) -6C04 RectRgn(RgnH,@Rect) -6D04 OpenRgn() -6E04 CloseRgn(RgnH) -6F04 OffsetRgn(RgnH,dh,dv) -7004 InsetRgn(RgnH,dh,dv) -7104 SectRgn(Rgn1H,Rgn2H,DstRgnH) -7204 UnionRgn(Rgn1H,Rgn2H,UnionRgnH) -7304 DiffRgn(Rgn1H,Rgn2H,DstRgnH) -7404 XorRgn(Rgn1H,Rgn2H,DstRgnH) -7504 PtInRgn(@Pt,RgnH):Flag -7604 RectInRgn(@Rect,RgnH):Flag -7704 EqualRgn(Rgn1H,Rgn2H):Flag -7804 EmptyRgn(RgnH):Flag -7904 FrameRgn(RgnH) -7A04 PaintRgn(RgnH) -7B04 EraseRgn(RgnH) -7C04 InvertRgn(RgnH) -7D04 FillRgn(RgnH,@Patt) -7E04 ScrollRect(@Rect,dh,dv,UpdtRgnH) -7F04 PaintPixels(@ppParms) -8004 AddPt(@SrcPt,@DestPt) -8104 SubPt(@SrcPt,@DstPt) -8204 SetPt(@Pt,h,v) -8304 EqualPt(@Pt1,@Pt2):Flag -8404 LocalToGlobal(@Pt) -8504 GlobalToLocal(@Pt) -8604 Random():N -8704 SetRandSeed(Seed/4) -8804 GetPixel(Hor,Vert):Pixel -8904 ScalePt(@Pt,@SrcRect,@DstRect) -8A04 MapPt(@Pt,@SrcRect,@DstRect) -8B04 MapRect(@Rect,@SrcRect,@DstRect) -8C04 MapRgn(MapRgnH,@SrcRect,@DstRect) -8D04 SetStdProcs(@StdProcRec) -8E04 SetCursor(@Curs) -8F04 GetCursorAdr():@Curs -9004 HideCursor() -9104 ShowCursor() -9204 ObscureCursor() -9304 SetMouseLoc ??? -9404 SetFont(FontH) -9504 GetFont():FontH -9604 GetFontInfo(@InfoRec) -9704 GetFontGlobals(@FGRec) -9804 SetFontFlags(Flags) -9904 GetFontFlags():Flags -9A04 SetTextFace(TextF) -9B04 GetTextFace():TextF -9C04 SetTextMode(TextM) -9D04 GetTextMode():TextM -9E04 SetSpaceExtra(SpEx/4f) -9F04 GetSpaceExtra():SpEx/4f -A004 SetForeColor(Color) -A104 GetForeColor():Color -A204 SetBackColor(BackCol) -A304 GetBackColor():BackCol -A404 DrawChar(Char) -A504 DrawString(@Str) -A604 DrawCString(@cStr) -A704 DrawText(@Text,Len) -A804 CharWidth(Char):Width -A904 StringWidth(@Str):Width -AA04 CStringWidth(@cStr):Width -AB04 TextWidth(@Text,Len):Width -AC04 CharBounds(Char,@Rect) -AD04 StringBounds(@Str,@Rect) -AE04 CStringBounds(@cStr,@Rect) -AF04 TextBounds(@Text,Len,@Rect) -B004 SetArcRot(ArcRot) -B104 GetArcRot():ArcRot -B204 SetSysFont(FontH) -B304 GetSysFont():FontH -B404 SetVisRgn(RgnH) -B504 GetVisRgn(RgnH) -B604 SetIntUse(Flag) -B704 OpenPicture(@FrameRect):PicH -B804 PicComment(Kind,DataSz,DataH) -B904 ClosePicture() -BA04 DrawPicture(PicH,@DstRect) -BB04 KillPicture(PicH) -BC04 FramePoly(PolyH) -BD04 PaintPoly(PolyH) -BE04 ErasePoly(PolyH) -BF04 InvertPoly(PolyH) -C004 FillPoly(PolyH,@Patt) -C104 OpenPoly():PolyH -C204 ClosePoly() -C304 KillPoly(PolyH) -C404 OffsetPoly(PolyH,dh,dv) -C504 MapPoly(PolyH,@SrcRect,@DstRect) -C604 SetClipHandle(RgnH) -C704 GetClipHandle():RgnH -C804 SetVisHandle(RgnH) -C904 GetVisHandle():RgnH -CA04 InitCursor() -CB04 SetBufDims(MaxW,MaxFontHt,MaxFBRext) -CC04 ForceBufDims(MaxW,MaxFontHt,MaxFBRext) -CD04 SaveBufDims(@SizeInfo) -CE04 RestoreBufDims(@SizeInfo) -CF04 GetFGSize():FGSize -D004 SetFontID(FontID/4) -D104 GetFontID():FontID/4 -D204 SetTextSize(TextSz) -D304 GetTextSize():TextSz -D404 SetCharExtra(ChEx/4f) -D504 GetCharExtra():ChEx/4f -D604 PPToPort(@SrcLoc,@SrcRect,X,Y,Mode) -D704 InflateTextBuffer(NewW,NewHt) -D804 GetRomFont(@Rec) -D904 GetFontLore(@Rec,RecSize):Size -DA04 Get640Colors():@PattTable -DB04 Set640Color(color) -0005 === desk manager === -0105 DeskBootInit() -0205 DeskStartUp() -0305 DeskShutDown() -0405 DeskVersion():Vers -0505 DeskReset() -0605 DeskStatus():ActFlg -0905 SaveScrn() -0A05 RestScrn() -0B05 SaveAll() -0C05 RestAll() -0E05 InstallNDA(ndaH) -0F05 InstallCDA(cdaH) -1105 ChooseCDA() -1305 SetDAStrPtr(AltDispH,@StrTbl) -1405 GetDAStrPtr():@StrTbl -1505 OpenNDA(ItemID):Ref# -1605 CloseNDA(Ref#) -1705 SystemClick(@EvRec,@Wind,fwRes) -1805 SystemEdit(eType):Flag -1905 SystemTask() -1A05 SystemEvent(Mods,Where/4,When/4,Msg/4,What):F -1B05 GetNumNDAs():N -1C05 CloseNDAbyWinPtr(@Wind) -1D05 CloseAllNDAs() -1E05 FixAppleMenu(MenuID) -1F05 AddToRunQ(@taskHeader) -2005 RemoveFromRunQ(@taskHeader) -2105 RemoveCDA(cdaH) -2205 RemoveNDA(ndaH) -2305 GetDeskAccInfo(flags,daRef/4,BufSize,@Buffer) -2405 CallDeskAcc(flags,daRef/4,Action,Data/4):Result -2505 GetDeskGlobal(selector):Value/4 -0006 === event manager === -0106 EMBootInit() -0206 EMStartUp(DirPg,qSz,Xmn,Xmx,Ymn,Ymx,MemID) -0306 EMShutDown() -0406 EMVersion():Vers -0506 EMReset() -0606 EMStatus():ActFlg -0906 DoWindows():DirPg -0A06 GetNextEvent(evMask,@EvRec):Flag -0B06 EventAvail(evMask,@EvRec):Flag -0C06 GetMouse(@Pt) -0D06 Button(Btn#):DownFlg -0E06 StillDown(Btn#):Flag -0F06 WaitMouseUp(Btn#):Flag -1006 TickCount():Ticks/4 -1106 GetDblTime():Ticks/4 -1206 GetCaretTime():Ticks/4 -1306 SetSwitch() -1406 PostEvent(code,Msg/4):Flag -1506 FlushEvents(evMask,StopMask):F -1606 GetOSEvent(evMask,@EvRec):Flag -1706 OSEventAvail(evMask,@EvRec):Flag -1806 SetEventMask(evMask) -1906 FakeMouse(ChFlg,Mods,X,Y,BtnStat) -1A06 SetAutoKeyLimit(NewLimit) -1B06 GetKeyTranslation():kTransID -1C06 SetKeyTranslation(kTransID) -0007 === scheduler === -0107 SchBootInit() -0207 SchStartUp() -0307 SchShutDown() -0407 SchVersion():Vers -0507 SchReset() -0607 SchStatus():ActFlg -0907 SchAddTask(@Task):Flag -0A07 SchFlush() -0008 === sound manager === -0108 SoundBootInit() -0208 SoundStartUp(DirPg) -0308 SoundShutDown() -0408 SoundVersion():Vers -0508 SoundReset() -0608 SoundToolStatus():ActFlg -0908 WriteRamBlock(@Src,DOCStart,Count) -0A08 ReadRamBlock(@Dest,DOCStart,Count) -0B08 GetTableAddress():@JumpTbl -0C08 GetSoundVolume(Gen#):Vol -0D08 SetSoundVolume(Vol,Gen#) -0E08 FFStartSound(GenN&mode,@Parms) -0F08 FFStopSound(GenMask) -1008 FFSoundStatus():ActFlg -1108 FFGeneratorStatus(Gen#):Stat -1208 SetSoundMIRQV(@IntHandler) -1308 SetUserSoundIRQV(@NewIRQ):@OldIRQ -1408 FFSoundDoneStatus(Gen#):Stat -1508 FFSetUpSound(ChannelGen,@Parms) -1608 FFStartPlaying(GenWord) -1708 SetDocReg(@DocRegParms) -1808 ReadDocReg(@DocRegParms) -0009 === desktop bus === -0109 ADBBootInit() -0209 ADBStartUp() -0309 ADBShutDown() -0409 ADBVersion():Vers -0509 ADBReset() -0609 ADBStatus():ActFlg -0909 SendInfo(NumB,@Data,Cmd) -0A09 ReadKeyMicroData(NumB,@Data,Cmd) -0B09 ReadKeyMicroMemory(@DataOut,@DataIn,Cmd) -0C09 [resynch--don't call] -0D09 AsyncADBReceive(@CompVec,Cmd) -0E09 SyncADBReceive(InputWrd,@CompVec,Cmd) -0F09 AbsOn() -1009 AbsOff() -1109 RdAbs():Flag -1209 SetAbsScale(@DataOut) -1309 GetAbsScale(@DataIn) -1409 SRQPoll(@CompVec,ADBreg) -1509 SRQRemove(ADBreg) -1609 ClearSRQTable() -FF09 [OBSOLETE: Use 09FF] -000A === SANE === -010A SANEBootInit() -020A SANEStartUp(DirPg) -030A SANEShutDown() -040A SANEVersion():Vers -050A SANEReset() -060A SANEStatus():ActFlg -090A FPNum (...) -0A0A DecStrNum (...) -0B0A ElemNum (...) -FF0A [OBSOLETE: USE $0AFF] -000B === integer math === -010B IMBootInit() -020B IMStartUp() -030B IMShutDown() -040B IMVersion():Vers -050B IMReset() -060B IMStatus():ActFlg -090B Multiply(A,B):Prod/4 -0A0B SDivide(Num,Den):Rem,Quot -0B0B UDivide(Num,Den):Rem,Quot -0C0B LongMul(A/4,B/4):Prod/8 -0D0B LongDivide(Num/4,Denom/4):Rem/4,Quot/4 -0E0B FixRatio(Numer,Denom):fxRatio/4 -0F0B FixMul(fx1/4,fx2/4):fxProd/4 -100B FracMul(fr1/4,fr2/4):frRes/4 -110B FixDiv(Quot/4,Divisor/4):fxRes/4 -120B FracDiv(Quot/4,Divisor/4):frRes/4 -130B FixRound(fxVal/4):Int -140B FracSqrt(frVal/4):frRes/4 -150B FracCos(fxAngle/4):frRes/4 -160B FracSin(fxAngle/4):frRes/4 -170B FixATan2(In1/4,In2/4):fxArcTan/4 -180B HiWord(Long/4):Int -190B LoWord(Long/4):Int -1A0B Long2Fix(Long/4):fxRes/4 -1B0B Fix2Long(Fix/4):Long/4 -1C0B Fix2Frac(fxVal/4):Frac/4 -1D0B Frac2Fix(frVal/4):fxRes/4 -1E0B Fix2X(Fix/4,@Extended) -1F0B Frac2X(frVal/4,@Extended) -200B X2Fix(@Extended):fxRes/4 -210B X2Frac(@Extended):frRes/4 -220B Int2Hex(Int,@Str,Len) -230B Long2Hex(Long/4,@Str,Len) -240B Hex2Int(@Str,Len):Int -250B Hex2Long(@Str,Len):Long/4 -260B Int2Dec(Int,@Str,Len,SgnFlg) -270B Long2Dec(Long/4,@Str,Len,SgnFlg) -280B Dec2Int(@Str,Len,SgnFlg):Int -290B Dec2Long(@Str,Len,SgnFlg):Long/4 -2A0B HexIt(Int):Hex/4 -000C === text tools === -010C TextBootInit() -020C TextStartUp() -030C TextShutDown() -040C TextVersion():Vers -050C TextReset() -060C TextStatus():ActFlg -090C SetInGlobals(ANDmsk,ORmsk) -0A0C SetOutGlobals(ANDmsk,ORmsk) -0B0C SetErrGlobals(ANDmsk,ORmsk) -0C0C GetInGlobals():ANDmsk,ORmsk -0D0C GetOutGlobals():ANDmsk,ORmsk -0E0C GetErrGlobals():ANDmsk,ORmsk -0F0C SetInputDevice(Type,@drvr|Slot/4) -100C SetOutputDevice(Type,@drvr|Slot/4) -110C SetErrorDevice(Type,@drvr|Slot/4) -120C GetInputDevice():Type,@drvr|Slot/4 -130C GetOutputDevice():Type,@drvr|Slot/4 -140C GetErrorDevice():Type,@drvr|Slot/4 -150C InitTextDev(dev) -160C CtlTextDev(dev,code) -170C StatusTextDev(dev,request) -180C WriteChar(Char) -190C ErrWriteChar(Char) -1A0C WriteLine(@Str) -1B0C ErrWriteLine(@Str) -1C0C WriteString(@Str) -1D0C ErrWriteString(@Str) -1E0C TextWriteBlock(@Text,Offset,Len) -1F0C ErrWriteBlock(@Text,Offset,Len) -200C WriteCString(@cStr) -210C ErrWriteCString(@cStr) -220C ReadChar(EchoFlg):Char -230C TextReadBlock(@Buff,Offset,Size,EchoFlg) -240C ReadLine(@Buff,Max,EOLch,EchoFlg):Count -000D === reserved === -000E === window manager === -010E WindBootInit() -020E WindStartUp(MemID) -030E WindShutDown() -040E WindVersion():Vers -050E WindReset() -060E WindStatus():ActFlg -090E NewWindow(@Parms):@Wind -0A0E CheckUpdate(@EvRec):Flag -0B0E CloseWindow(@Wind) -0C0E Desktop(Oper,param/4):result/4 -0D0E SetWTitle(@Title,@Wind) -0E0E GetWTitle(@Wind):@Title -0F0E SetFrameColor(@NewColTbl,@Wind) -100E GetFrameColor(@Table,@Wind) -110E SelectWindow(@Wind) -120E HideWindow(@Wind) -130E ShowWindow(@Wind) -140E SendBehind(@BehindWho,@Wind) -150E FrontWindow():@Wind -160E SetInfoDraw(@Proc,@Wind) -170E FindWindow(@WindVar,X,Y):Where -180E TrackGoAway(X,Y,@Wind):Flag -190E MoveWindow(X,Y,@Wind) -1A0E DragWindow(Grid,X,Y,Grace,@bRect,@Wind) -1B0E GrowWindow(mnW,mnH,X,Y,@Wind):nSize/4 -1C0E SizeWindow(w,h,@Wind) -1D0E TaskMaster(evMask,@TaskRec):Code -1E0E BeginUpdate(@Wind) -1F0E EndUpdate(@Wind) -200E GetWMgrPort():@Port -210E PinRect(X,Y,@Rect):Point/4 -220E HiliteWindow(Flag,@Wind) -230E ShowHide(Flag,@Wind) -240E BringToFront(@Wind) -250E WindNewRes() -260E TrackZoom(X,Y,@Wind):Flag -270E ZoomWindow(@Wind) -280E SetWRefCon(Refcon/4,@Wind) -290E GetWRefCon(@Wind):Refcon/4 -2A0E GetNextWindow(@Wind):@Wind -2B0E GetWKind(@Wind):Flag -2C0E GetWFrame(@Wind):Frame -2D0E SetWFrame(Frame,@Wind) -2E0E GetStructRgn(@Wind):StructRgnH -2F0E GetContentRgn(@Wind):ContRgnH -300E GetUpdateRgn(@Wind):UpdateRgnH -310E GetDefProc(@Wind):@Proc -320E SetDefProc(@Proc,@Wind) -330E GetWControls(@Wind):CtrlH -340E SetOriginMask(Mask,@Wind) -350E GetInfoRefCon(@Wind):Refcon/4 -360E SetInfoRefCon(Val/4,@Wind) -370E GetZoomRect(@Wind):@zRect -380E SetZoomRect(@zRect,@Wind) -390E RefreshDesktop(@Rect) -3A0E InvalRect(@Rect) -3B0E InvalRgn(RgnH) -3C0E ValidRect(@Rect) -3D0E ValidRgn(RgnH) -3E0E GetContentOrigin(@Wind):Origin/4 -3F0E SetContentOrigin(X,Y,@Wind) -400E GetDataSize(@Wind):DataSize/4 -410E SetDataSize(w,h,@Wind) -420E GetMaxGrow(@Wind):MaxGrow/4 -430E SetMaxGrow(maxWidth,maxHeight,@Wind) -440E GetScroll(@Wind):Scroll/4 -450E SetScroll(h,v,@Wind) -460E GetPage(@Wind):Page/4 -470E SetPage(h,v,@Wind) -480E GetContentDraw(@Wind):@Proc -490E SetContentDraw(@Proc,@Wind) -4A0E GetInfoDraw(@Wind):@Proc -4B0E SetSysWindow(@Wind) -4C0E GetSysWFlag(@Wind):Flag -4D0E StartDrawing(@Wind) -4E0E SetWindowIcons(NewFontH):OldFontH -4F0E GetRectInfo(@InfoRect,@Wind) -500E StartInfoDrawing(@iRect,@Wind) -510E EndInfoDrawing() -520E GetFirstWindow():@Wind -530E WindDragRect(@a,@P,X,Y,@R,@lR,@sR,F):M/4 -540E Private01():@func [GetDragRectPtr] -550E DrawInfoBar(@Wind) -560E WindowGlobal(Flags):Flags -570E SetContentOrigin2(ScrollFlag,X,Y,@Wind) -580E GetWindowMgrGlobals():@Globals -590E AlertWindow(AlertDesc,@SubArray,AlertRef/4):Btn -5A0E StartFrameDrawing(@Wind) -5B0E EndFrameDrawing() -5C0E ResizeWindow(hidden,@ContRect,@Wind) -5D0E TaskMasterContent -5E0E TaskMasterKey -5F0E TaskMasterDA(evMask,@bigTaskRec):taskCode -600E CompileText(subType,@subs,@text,size):H -610E NewWindow2(@T,RC/4,@draw,@def,pDesc,pRef/4,rType):@W -620E ErrorWindow(subType,@subs,ErrNum):Button -630E GetAuxWindInfo(@Wind):@Info -640E DoModalWindow(@Event,@Update,@EvHook,@Beep,Flags):Result/4 -650E MWGetCtlPart():Part -660E MWSetMenuProc(@NewMenuProc):@OldMenuProc -670E MWStdDrawProc() -680E MWSetUpEditMenu() -690E FindCursorCtl(@CtrlH,x,y,@Wind):PartCode -6A0E ResizeInfoBar(flags,newHeight,@Wind) -6B0E HandleDiskInsert(flags,devNum):resFlags,resDevNum -6C0E UpdateWindow(flags,@Wind) -000F === menu manager === -010F MenuBootInit() -020F MenuStartUp(MemID,DirPg) -030F MenuShutDown() -040F MenuVersion():Vers -050F MenuReset() -060F MenuStatus():ActFlg -090F MenuKey(@TaskRec,BarH) -0A0F GetMenuBar():BarH -0B0F MenuRefresh(@RedrawProc) -0C0F FlashMenuBar() -0D0F InsertMenu(MenuH,AfterWhat) -0E0F DeleteMenu(MenuID) -0F0F InsertMItem(@Item,AfterItem,MenuID) -100F DeleteMItem(ItemID) -110F GetSysBar():BarH -120F SetSysBar(BarH) -130F FixMenuBar():Height -140F CountMItems(MenuID):N -150F NewMenuBar(@Wind):BarH -160F GetMHandle(MenuID):MenuH -170F SetBarColors(BarCol,InvCol,OutCol) -180F GetBarColors():Colors/4 -190F SetMTitleStart(hStart) -1A0F GetMTitleStart():hStart -1B0F GetMenuMgrPort():@Port -1C0F CalcMenuSize(w,h,MenuID) -1D0F SetMTitleWidth(w,MenuID) -1E0F GetMTitleWidth(MenuID):TitleWidth -1F0F SetMenuFlag(Flags,MenuID) -200F GetMenuFlag(MenuID):Flags -210F SetMenuTitle(@Title,MenuID) -220F GetMenuTitle(MenuID):@Title -230F MenuGlobal(Flags):Flags -240F SetMItem(@Str,ItemID) -250F GetMItem(ItemID):@ItemName -260F SetMItemFlag(Flags,ItemID) -270F GetMItemFlag(ItemID):Flag -280F SetMItemBlink(Count) -290F MenuNewRes() -2A0F DrawMenuBar() -2B0F MenuSelect(@TaskRec,BarH) -2C0F HiliteMenu(Flag,MenuID) -2D0F NewMenu(@MenuStr):MenuH -2E0F DisposeMenu(MenuH) -2F0F InitPalette() -300F EnableMItem(ItemID) -310F DisableMItem(ItemID) -320F CheckMItem(Flag,ItemID) -330F SetMItemMark(MarkCh,ItemID) -340F GetMItemMark(ItemID):MarkChar -350F SetMItemStyle(TextStyle,ItemID) -360F GetMItemStyle(ItemID):TextStyle -370F SetMenuID(New,Old) -380F SetMItemID(New,Old) -390F SetMenuBar(BarH) -3A0F SetMItemName(@Str,ItemID) -3B0F GetPopUpDefProc():@proc -3C0F PopUpMenuSelect(SelID,left,top,flag,MenuH):id -3D0F [DrawPopUp(SelID,Flag,right,bottom,left,top,MenuH)] -3E0F NewMenu2(RefDesc,Ref/4):MenuH -3F0F InsertMItem2(RefDesc,Ref/4,After,MenuID) -400F SetMenuTitle2(RefDesc,TitleRef/4,MenuID) -410F SetMItem2(RefDesc,Ref/4,Item) -420F SetMItemName2(RefDesc,Ref/4,Item) -430F NewMenuBar2(RefDesc,Ref/4,@Wind):BarH -450F HideMenuBar() -460F ShowMenuBar() -470F SetMItemIcon(IconDesc,IconRef/4,ItemID) -480F GetMItemIcon(ItemID):IconRef/4 -490F SetMItemStruct(Desc,StructRef/4,ItemID) -4A0F GetMItemStruct(ItemID):ItemStruct/4 -4B0F RemoveMItemStruct(ItemID) -4C0F GetMItemFlag2(ItemID):ItemFlag2 -4D0F SetMItemFlag2(newValue,ItemID) -4F0F GetMItemBlink():Count -500F InsertPathMItems(flags,@Path,devnum,MenuID,AfterID,StartID,@Results) -0010 === control manager === -0110 CtlBootInit() -0210 CtlStartUp(MemID,DirPg) -0310 CtlShutDown() -0410 CtlVersion():Vers -0510 CtlReset() -0610 CtlStatus():ActFlg -0910 NewControl(@W,@R,@T,F,V,P1,P2,@p,r/4,@C):cH -0A10 DisposeControl(CtrlH) -0B10 KillControls(@Wind) -0C10 SetCtlTitle(@Title,CtrlH) -0D10 GetCtlTitle(CtrlH):@Title -0E10 HideControl(CtrlH) -0F10 ShowControl(CtrlH) -1010 DrawControls(@Wind) -1110 HiliteControl(Flag,CtrlH) -1210 CtlNewRes() -1310 FindControl(@CtrlHVar,X,Y,@Wind):Part -1410 TestControl(X,Y,CtrlH):Part -1510 TrackControl(X,Y,@ActProc,CtrlH):Part -1610 MoveControl(X,Y,CtrlH) -1710 DragControl(X,Y,@LimR,@slR,Axis,CtrlH) -1810 SetCtlIcons(FontH):OldFontH -1910 SetCtlValue(Val,CtrlH) -1A10 GetCtlValue(CtrlH):Val -1B10 SetCtlParams(P2,P1,CtrlH) -1C10 GetCtlParams(CtrlH):P1,P2 -1D10 DragRect(@acPr,@P,X,Y,@drR,@l,@slR,F):M/4 -1E10 GrowSize():Size/4 -1F10 GetCtlDpage():DirPg -2010 SetCtlAction(@ActProc,CtrlH) -2110 GetCtlAction(CtrlH):Action/4 -2210 SetCtlRefCon(Refcon/4,CtrlH) -2310 GetCtlRefCon(CtrlH):Refcon/4 -2410 EraseControl(CtrlH) -2510 DrawOneCtl(CtrlH) -2610 FindTargetCtl():CtrlH -2710 MakeNextCtlTarget():CtrlH -2810 MakeThisCtlTarget(CtrlH) -2910 SendEventToCtl(TgtOnly,@Wind,@eTask):Accepted -2A10 GetCtlID(CtrlH):CtlID/4 -2B10 SetCtlID(CtlID/4,CtrlH) -2C10 CallCtlDefProc(CtrlH,Msg,Param/4):Result/4 -2D10 NotifyCtls(Mask,Msg,Param/4,@Wind) -2E10 GetCtlMoreFlags(CtrlH):Flags -2F10 SetCtlMoreFlags(Flags,CtrlH) -3010 GetCtlHandleFromID(@Wind,CtlID/4):CtrlH -3110 NewControl2(@Wind,InKind,InRef/4):CtrlH -3210 CMLoadResource(rType,rID/4):resH -3310 CMReleaseResource(rType,rID/4) -3410 SetCtlParamPtr(@SubArray) -3510 GetCtlParamPtr():@SubArray -3710 InvalCtls(@Wind) -3810 [reserved] -3910 FindRadioButton(@Wind,FamilyNum):WhichRadio -3A10 SetLETextByID(@Wind,leID/4,@PString) -3B10 GetLETextByID(@Wind,leID/4,@PString) -3C10 SetCtlValueByID(Value,@Wind,CtlID/4) -3D10 GetCtlValueByID(@Wind,CtlID/4):Value -3E10 InvalOneCtlByID(@Wind,CtlID/4) -3F10 HiliteCtlByID(Hilite,@Wind,CtlID/4) -0011 === loader === -0111 LoaderBootInit() -0211 LoaderStartUp() -0311 LoaderShutDown() -0411 LoaderVersion():Vers -0511 LoaderReset() -0611 LoaderStatus():ActFlg -0911 InitialLoad(MemID,@path,F):dpsSz,dps,@l,MemID -0A11 Restart(MemID):dpsSz,dps,@loc,MemID -0B11 LoadSegNum(MemID,file#,seg#):@loc -0C11 UnloadSegNum(MemID,file#,seg#) -0D11 LoadSegName(MemID,@path,@segn):@loc,MemID,file#,sg# -0E11 UnloadSeg(@loc):seg#,file#,MemID -0F11 GetLoadSegInfo(MemID,file#,seg#,@buff) -1011 GetUserID(@Pathname):MemID -1111 LGetPathname(MemID,file#):@path -1211 UserShutDown(MemID,qFlag):MemID -1311 RenamePathname(@path1,@path2) -2011 InitialLoad2(MemID,@in,F,Type):dpsSz,dps,@l,MemID -2111 GetUserID2(@path):MemID -2211 LGetPathname2(MemID,file#):@path -0012 === QuickDraw Aux === -0112 QDAuxBootInit() -0212 QDAuxStartUp() -0312 QDAuxShutDown() -0412 QDAuxVersion():Vers -0512 QDAuxReset() -0612 QDAuxStatus():ActFlg -0912 CopyPixels(@sLoc,@dLoc,@sRect,@dRct,M,MskH) -0A12 WaitCursor() -0B12 DrawIcon(@Icon,Mode,X,Y) -0C12 SpecialRect(@Rect,FrameColor,FillColor) -0D12 SeedFill(@sLoc,@sR,@dLoc,@dR,X,Y,Mode,@Patt,@Leak) -0E12 CalcMask(@sLoc,@sR,@dLoc,@dR,Mode,@Patt,@Leak) -0F12 GetSysIcon(flags,value,aux/4):@Icon -1012 PixelMap2Rgn(@srcLoc,bitsPerPix,colorMask):RgnH -1312 IBeamCursor() -1412 WhooshRect(flags/4,@smallRect,@bigRect) -1512 DrawStringWidth(Flags,Ref/4,Width) -1612 UseColorTable(tableNum,@Table,Flags):ColorInfoH -1712 RestoreColorTable(ColorInfoH,Flags) -0013 === print manager === -0113 PMBootInit() -0213 PMStartUp(MemID,DirPg) -0313 PMShutDown() -0413 PMVersion():Vers -0513 PMReset() -0613 PMStatus():ActFlg -0913 PrDefault(PrRecH) -0A13 PrValidate(PrRecH):Flag -0B13 PrStlDialog(PrRecH):Flag -0C13 PrJobDialog(PrRecH):Flag -0D13 PrPixelMap(@LocInfo,@SrcRect,colorFlag) -0E13 PrOpenDoc(PrRecH,@Port):@Port -0F13 PrCloseDoc(@Port) -1013 PrOpenPage(@Port,@Frame) -1113 PrClosePage(@Port) -1213 PrPicFile(PrRecH,@Port,@StatRec) -1313 PrControl [obsolete] -1413 PrError():Error -1513 PrSetError(Error) -1613 PrChoosePrinter():DrvFlag -1813 PrGetPrinterSpecs():Type,Characteristics -1913 PrDevPrChanged(@PrinterName) -1A13 PrDevStartup(@PrinterName,@ZoneName) -1B13 PrDevShutDown() -1C13 PrDevOpen(@compProc,Reserved/4) -1D13 PrDevRead(@buffer,reqCount):xferCount -1E13 PrDevWrite(@compProc,@buff,bufLen) -1F13 PrDevClose() -2013 PrDevStatus(@statBuff) -2113 PrDevAsyncRead(@compPr,bufLen,@buff):xferCount -2213 PrDevWriteBackground(@compProc,bufLen,@buff) -2313 PrDriverVer():Vers -2413 PrPortVer():Vers -2513 PrGetZoneName():@ZoneName -2813 PrGetPrinterDvrName():@Name -2913 PrGetPortDvrName():@Name -2A13 PrGetUserName():@Name -2B13 PrGetNetworkName():@Name -3013 PrDevIsItSafe():safeFlag -3113 GetZoneList [obsolete?] -3213 GetMyZone [obsolete?] -3313 GetPrinterList [obsolete?] -3413 PMUnloadDriver(whichDriver) -3513 PMLoadDriver(whichDriver) -3613 PrGetDocName():@pStr -3713 PrSetDocName(@pStr) -3813 PrGetPgOrientation(PrRecH):Orientation -0014 === line edit === -0114 LEBootInit() -0214 LEStartUp(MemID,DirPg) -0314 LEShutDown() -0414 LEVersion():Vers -0514 LEReset() -0614 LEStatus():ActFlg -0914 LENew(@DstRect,@ViewRect,MaxL):leH -0A14 LEDispose(leH) -0B14 LESetText(@Text,Len,leH) -0C14 LEIdle(leH) -0D14 LEClick(@EvRec,leH) -0E14 LESetSelect(Start,End,leH) -0F14 LEActivate(leH) -1014 LEDeactivate(leH) -1114 LEKey(Key,Mods,leH) -1214 LECut(leH) -1314 LECopy(leH) -1414 LEPaste(leH) -1514 LEDelete(leH) -1614 LEInsert(@Text,Len,leH) -1714 LEUpdate(leH) -1814 LETextBox(@Text,Len,@Rect,Just) -1914 LEFromScrap() -1A14 LEToScrap() -1B14 LEScrapHandle():ScrapH -1C14 LEGetScrapLen():Len -1D14 LESetScrapLen(NewL) -1E14 LESetHilite(@HiliteProc,leH) -1F14 LESetCaret(@CaretProc,leH) -2014 LETextBox2(@Text,Len,@Rect,Just) -2114 LESetJust(Just,leH) -2214 LEGetTextHand(leH):TextH -2314 LEGetTextLen(leH):TxtLen -2414 GetLEDefProc():@proc -2514 LEClassifyKey(@Event):Flag -0015 === dialog manager === -0115 DialogBootInit() -0215 DialogStartUp(MemID) -0315 DialogShutDown() -0415 DialogVersion():Vers -0515 DialogReset() -0615 DialogStatus():ActFlg -0915 ErrorSound(@SoundProc) -0A15 NewModalDialog(@bR,vis,refcon/4):@Dlog -0B15 NewModelessDialog(@R,@T,@b,fr,rf/4,@zR):@D -0C15 CloseDialog(@Dlog) -0D15 NewDItem(@Dlog,dItem,@R,ty,Des/4,V,F,@Col) -0E15 RemoveDItem(@Dlog,dItem) -0F15 ModalDialog(@FilterProc):Hit -1015 IsDialogEvent(@EvRec):Flag -1115 DialogSelect(@EvRec,@Dlog,@Hit):Flag -1215 DlgCut(@Dlog) -1315 DlgCopy(@Dlog) -1415 DlgPaste(@Dlog) -1515 DlgDelete(@Dlog) -1615 DrawDialog(@Dlog) -1715 Alert(@AlertTmpl,@FiltProc):Hit -1815 StopAlert(@AlertTmpl,@FiltProc):Hit -1915 NoteAlert(@AlertTmpl,@FiltProc):Hit -1A15 CautionAlert(@AlertTmpl,@FiltProc):Hit -1B15 ParamText(@P0,@P1,@P2,@P3) -1C15 SetDAFont(FontH) -1E15 GetControlDItem(@Dlog,dItem):CtrlH -1F15 GetIText(@Dlog,dItem,@Str) -2015 SetIText(@Dlog,dItem,@Str) -2115 SelectIText(@Dlog,dItem,start,end) -2215 HideDItem(@Dlog,dItem) -2315 ShowDItem(@Dlog,dItem) -2415 FindDItem(@Dlog,Point/4):Hit -2515 UpdateDialog(@Dlog,UpdtRgnH) -2615 GetDItemType(@Dlog,dItem):type -2715 SetDItemType(type,@Dlog,dItem) -2815 GetDItemBox(@Dlog,dItem,@Rect) -2915 SetDItemBox(@Dlog,dItem,@Rect) -2A15 GetFirstDItem(@Dlog):dItem -2B15 GetNextDItem(@Dlog,dItem):dItem -2C15 ModalDialog2(@FilterProc):HitInfo/4 -2E15 GetDItemValue(@Dlog,dItem):Val -2F15 SetDItemValue(val,@Dlog,dItem) -3215 GetNewModalDialog(@DlogTmpl):@Dlog -3315 GetNewDItem(@Dlog,@ItemTmpl) -3415 GetAlertStage():Stage -3515 ResetAlertStage() -3615 DefaultFilter(@Dlog,@EvRec,@Hit):Flag -3715 GetDefButton(@Dlog):dItem -3815 SetDefButton(BtnID,@Dlog) -3915 DisableDItem(@Dlog,dItem) -3A15 EnableDItem(@Dlog,dItem) -0016 === scrap manager === -0116 ScrapBootInit() -0216 ScrapStartUp() -0316 ScrapShutDown() -0416 ScrapVersion():Vers -0516 ScrapReset() -0616 ScrapStatus():ActFlg -0916 UnloadScrap() -0A16 LoadScrap() -0B16 ZeroScrap() -0C16 PutScrap(Count/4,Type,@Src) -0D16 GetScrap(DestH,Type) -0E16 GetScrapHandle(Type):ScrapH -0F16 GetScrapSize(Type):Size/4 -1016 GetScrapPath():@Pathname -1116 SetScrapPath(@Pathname) -1216 GetScrapCount():Count -1316 GetScrapState():State -1416 GetIndScrap(Index,@buffer) -1516 ShowClipboard(flags,@rect):@Wind -0017 === standard file === -0117 SFBootInit() -0217 SFStartUp(MemID,DirPg) -0317 SFShutDown() -0417 SFVersion():Vers -0517 SFReset() -0617 SFStatus():ActFlg -0917 SFGetFile(X,Y,@Prmpt,@FPrc,@tL,@Reply) -0A17 SFPutFile(X,Y,@Prompt,@DfltName,mxL,@Reply) -0B17 SFPGetFile(X,Y,@P,@FPrc,@tL,@dTmp,@dHk,@Rp) -0C17 SFPPutFile(X,Y,@P,@Df,mxL,@dTmpl,@dHk,@Rply) -0D17 SFAllCaps(Flag) -0E17 SFGetFile2(X,Y,prDesc,prRef/4,@fProc,@tList,@rep) -0F17 SFPutFile2(X,Y,prDesc,prRef/4,nmDesc,nmRef/4,@rep) -1017 SFPGetFile2(X,Y,@draw,prD,prRf/4,@fP,@tL,@d,@hk,@rep) -1117 SFPPutFile2(X,Y,@draw,prD,prRf/4,nmD,nmRf/4,@d,@hk,@rep) -1217 SFShowInvisible(InvisState):OldState -1317 SFReScan(@filterProc,@typeList) -1417 SFMultiGet2(X,Y,prDesc,prRef/4,@fP,@tL,@rep) -1517 SFPMultiGet2(X,Y,@draw,prD,prRf/4,@fP,@tL,@d,@hk,@rep) -0019 === note synthesizer === -0119 NSBootInit() -0219 NSStartUp(Rate,@UpdProc) -0319 NSShutDown() -0419 NSVersion():Vers -0519 NSReset() -0619 NSStatus():ActFlg -0919 AllocGen(Priority):Gen# -0A19 DeallocGen(Gen#) -0B19 NoteOn(Gen#,Semitone,Vol,@Instr) -0C19 NoteOff(Gen#,Semitone) -0D19 AllNotesOff() -0E19 NSSetUpdateRate(NewRate):OldRate -0F19 NSSetUserUpdateRtn(@New):@Old -001A === note sequencer === -011A SeqBootInit() -021A SeqStartUp(DirPg,Mode,Rate,Incr) -031A SeqShutDown() -041A SeqVersion():Vers -051A SeqReset() -061A SeqStatus():ActFlg -091A SetIncr(Increment) -0A1A ClearIncr():OldIncr -0B1A GetTimer():Tick -0C1A GetLoc():Phrase,Patt,Level -0D1A SeqAllNotesOff() -0E1A SetTrkInfo(Priority,InstIndex,TrkNum) -0F1A StartSeq(@ErrRtn,@CompRtn,SeqH) -101A StepSeq() -111A StopSeq(NextFlag) -121A SetInstTable(TableH) -131A StartInts() -141A StopInts() -151A StartSeqRel(@errHndlr,@CompRtn,SeqH) -001B === font manager === -011B FMBootInit() -021B FMStartUp(MemID,DirPg) -031B FMShutDown() -041B FMVersion():Vers -051B FMReset() -061B FMStatus():ActFlg -091B CountFamilies(FamSpecs):Count -0A1B FindFamily(Specs,Pos,@Name):FamNum -0B1B GetFamInfo(FamNum,@Name):FamStats -0C1B GetFamNum(@Name):FamNum -0D1B AddFamily(FamNum,@Name) -0E1B InstallFont(ID/4,Scale) -0F1B SetPurgeStat(FontID/4,PrgStat) -101B CountFonts(ID/4,Specs):N -111B FindFontStats(ID/4,Specs,Pos,@FStatRec) -121B LoadFont(ID/4,Specs,Pos,@FStatRec) -131B LoadSysFont() -141B AddFontVar(FontH,NewSpecs) -151B FixFontMenu(MenuID,StartID,FamSpecs) -161B ChooseFont(CurrID/4,Famspecs):NewID/4 -171B ItemID2FamNum(ItemID):FamNum -181B FMSetSysFont(FontID/4) -191B FMGetSysFID():SysID/4 -1A1B FMGetCurFID():CurID/4 -1B1B FamNum2ItemID(FamNum):ItemID -1C1B InstallWithStats(ID/4,Scale,@ResultRec) -001C === List Manager === -011C ListBootInit() -021C ListStartUp() -031C ListShutDown() -041C ListVersion():Vers -051C ListReset() -061C ListStatus():ActFlg -091C CreateList(@Wind,@ListRec):CtrlH -0A1C SortList(@CompareProc,@ListRec) -0B1C NextMember(@Member,@ListRec):@NxtMemVal -0C1C DrawMember(@Member,@ListRec) -0D1C SelectMember(@Member,@ListRec) -0E1C GetListDefProc():@Proc -0F1C ResetMember(@ListRec):NxtMemVal/4 -101C NewList(@Member,@ListRec) -111C DrawMember2(itemNum,CtrlH) -121C NextMember2(itemNum,CtrlH):itemNum -131C ResetMember2(CtrlH):itemNum -141C SelectMember2(itemNum,CtrlH) -151C SortList2(@CompareProc,CtrlH) -161C NewList2(@draw,start,ref/4,refKind,size,CtrlH) -171C ListKey(flags,@EventRec,CtrlH) -181C CompareStrings(flags,@String1,@String2):Order -001D === Audio Compression/Expansion === -011D ACEBootInit() -021D ACEStartUp(DirPg) -031D ACEShutDown() -041D ACEVersion():Vers -051D ACEReset() -061D ACEStatus():ActFlg -071D ACEInfo(Code):Value/4 -091D ACECompress(SrcH,SrcOff/4,DestH,DestOff/4,Blks,Method) -0A1D ACEExpand(SrcH,SrcOff/4,DestH,DestOff/4,Blks,Method) -0B1D ACECompBegin() -0C1D ACEExpBegin() -0D1D GetACEExpState(@Buffer) -0E1D SetACEExpState(@Buffer) -001E === Resource Manager === -011E ResourceBootInit() -021E ResourceStartUp(MemID) -031E ResourceShutDown() -041E ResourceVersion():Vers -051E ResourceReset() -061E ResourceStatus():ActFlag -091E CreateResourceFile(aux/4,fType,Access,@n) -0A1E OpenResourceFile(reqAcc,@mapAddr,@n):fileID -0B1E CloseResourceFile(fileID) -0C1E AddResource(H,Attr,rType,rID/4) -0D1E UpdateResourcefile(fileID) -0E1E LoadResource(rType,rID/4):H -0F1E RemoveResource(rType,rID/4) -101E MarkResourceChange(changeFlag,rType,rID/4) -111E SetCurResourceFile(fileID) -121E GetCurResourceFile():fileID -131E SetCurResourceApp(MemID) -141E GetCurResourceApp():MemID -151E HomeResourceFile(rType,rID/4):fileID -161E WriteResource(rType,rID/4) -171E ReleaseResource(PurgeLevel,rType,rID/4) -181E DetachResource(rType,rID/4) -191E UniqueResourceID(IDrange,rType):rID/4 -1A1E SetResourceID(newID/4,rType,oldID/4) -1B1E GetResourceAttr(rType,rID/4):Attr -1C1E SetResourceAttr(rAttr,rType,rID/4) -1D1E GetResourceSize(rType,rID/4):Size/4 -1E1E MatchResourceHandle(@buffer,H) -1F1E GetOpenFileRefNum(fileID):RefNum -201E CountTypes():Num -211E GetIndType(tIndex):rType -221E CountResources(rType):Num/4 -231E GetIndResource(rType,rIndex/4):rID/4 -241E SetResourceLoad(Flag):oldFlag -251E SetResourceFileDepth(Depth):oldDepth -261E GetMapHandle(fileID):MapH -271E LoadAbsResource(@loc,MaxSize/4,rType,rID/4):Size/4 -281E ResourceConverter(@proc,rType,logFlags) -291E LoadResource2(flag,@AttrBuff,rType,rID/4):H -2A1E RMFindNamedResource(rType,@name,@fileID):rID/4 -2B1E RMGetResourceName(rType,rID/4,@nameBuffer) -2C1E RMLoadNamedResource(rType,@name):H -2D1E RMSetResourceName(rType,rID/4,@name) -2E1E OpenResourceFileByID(reqAcc,userID):oldResApp -2F1E CompactResourceFile(flags,fileID) -0020 === MIDI === -0120 MidiBootInit() -0220 MidiStartUp(MemID,DirPg) -0320 MidiShutDown() -0420 MidiVersion():Vers -0520 MidiReset() -0620 MidiStatus():ActFlg -0920 MidiControl(Function,Argument/4) -0A20 MidiDevice(Function,@DriverInfo) -0B20 MidiClock(Function,Argument/4) -0C20 MidiInfo(Function):Info/4 -0D20 MidiReadPacket(@buff,size):Count -0E20 MidiWritePacket(@buff):Count -0021 === Video Overlay === -0121 VDBootInit() -0221 VDStartUp() -0321 VDShutDown() -0421 VDVersion():Vers -0521 VDReset() -0621 VDStatus():ActFlg -0921 VDInStatus(Selector):Status -0A21 VDInSetStd(InStandard) -0B21 VDInGetStd():InStandard -0C21 VDInConvAdj(Selector,AdjFunction) -0D21 VDKeyControl(Selector,KeyerCtrlVal) -0E21 VDKeyStatus(Selector):KeyerStatus -0F21 VDKeySetKCol(Red,Green,Blue) -1021 VDKeyGetKRCol():RedValue -1121 VDKeyGetKGCol():GreenValue -1221 VDKeyGetKBCol():BlueValue -1321 VDKeySetKDiss(KDissolve) -1421 VDKeyGetKDiss():KDissolve -1521 VDKeySetNKDiss(NKDissolve) -1621 VDKeyGetNKDiss():NKDissolve -1721 VDOutSetStd(OutStandard) -1821 VDOutGetStd():OutStandard -1921 VDOutControl(Selector,Value) -1A21 VDOutStatus(Selector):OutStatus -1B21 VDGetFeatures(Feature):Info -1C21 VDInControl(Selector,Value) -1D21 VDGGControl(Selector,Value) -1E21 VDGGStatus(Selector):Value -0022 === Text Edit === -0122 TEBootInit() -0222 TEStartUp(MemID,DirPg) -0322 TEShutDown() -0422 TEVersion():Vers -0522 TEReset() -0622 TEStatus():ActFlg -0922 TENew(@parms):teH -0A22 TEKill(teH) -0B22 TESetText(tDesc,tRef/4,Len/4,stDesc,stRef/4,teH) -0C22 TEGetText(bDesc,bRef/4,bLen/4,stDesc,stRef/4,teH):L/4 -0D22 TEGetTextInfo(@infoRec,parmCount,teH) -0E22 TEIdle(teH) -0F22 TEActivate(teH) -1022 TEDeactivate(teH) -1122 TEClick(@eventRec,teH) -1222 TEUpdate(teH) -1322 TEPaintText(@Port,startLn/4,@R,Flags,teH):NextLn/4 -1422 TEKey(@eventRec,teH) -1522 [not supported] -1622 TECut(teH) -1722 TECopy(teH) -1822 TEPaste(teH) -1922 TEClear(teH) -1A22 TEInsert(tDesc,tRef/4,tLen/4,stDesc,stRef/4,teH) -1B22 TEReplace(tDesc,tRef/4,tLen/4,stDesc,stRef/4,teH) -1C22 TEGetSelection(@selStart,@selEnd,teH) -1D22 TESetSelection(selStart/4,selEnd/4,teH) -1E22 TEGetSelectionStyle(@stRec,stH,teH):comFlag -1F22 TEStyleChange(flags,@stRec,teH) -2022 TEOffsetToPoint(offset/4,@vertPos,@horPos,teH) -2122 TEPointToOffset(vertPos/4,horPos/4,teH):textOffset/4 -2222 TEGetDefProc():@defProc -2322 TEGetRuler(rulerDesc,rulerRef/4,teH) -2422 TESetRuler(rulerDesc,rulerRef/4,teH) -2522 TEScroll(scrDesc,vertAmt/4,horAmt/4,teH):Offset/4 -2622 TEGetInternalProc():@proc -2722 TEGetLastError(clearFlag,teH):lastError -2822 TECompactRecord(teH) -0023 === MIDI Synth === -0123 MSBootInit() -0223 MSStartUp() -0323 MSShutDown() -0423 MSVersion():Vers -0523 MSReset() -0623 MSStatus():ActFlg -0923 SetBasicChannel(Channel) -0A23 SetMIDIMode(Mode) -0B23 PlayNote(Channel,NoteNum,KeyVel) -0C23 StopNote(Channel,NoteNum) -0D23 KillAllNotes() -0E23 SetRecTrack(TrackNum) -0F23 SetPlayTrack(TrackNum,State) -1023 TrackToChannel(TrackNum,ChannelNum) -1123 Locate(TimeStamp/4,@SeqBuff):@SeqItem -1223 SetVelComp(VelocityOffset) -1323 SetMIDIPort(EnabInput,EnabOutput) -1423 SetInstrument(@InstRec,InstNum) -1523 SeqPlayer(@SeqPlayerRec) -1623 SetTempo(Tempo) -1723 SetCallBack(@CallBackRec) -1823 SysExOut(@Msg,Delay,@MonRoutine) -1923 SetBeat(BeatDuration) -1A23 MIDIMessage(Dest,nBytes,Message,Byte1,Byte2) -1B23 LocateEnd(@seqBuffer):@End -1C23 Merge(@Buffer1,@Buffer2) -1D23 DeleteTrack(TrackNum,@Seq) -1E23 SetMetro(Volume,Freq,@Wave) -1F23 GetMSData():Reserved/4,@DirPage -2023 ConvertToTime(TkPerBt,BtPerMsr,BeatNum,MsrNum):Ticks/4 -2123 ConvertToMeasure(TkPerBt,BtPerMsr,Ticks/4):Ticks,Beat,Msr -2223 MSSuspend() -2323 MSResume() -2423 SetTuningTable(@Table) -2523 GetTuningTable(@Buffer) -2623 SetTrackOut(TrackNum,PathVal) -2723 InitMIDIDriver(Slot,Internal,UserID,@Driver) -2823 RemoveMIDIDriver() -0026 === Media Controller === -0126 MCBootInit() -0226 MCStartUp(MemID) -0326 MCShutDown() -0426 MCVersion():Vers -0526 MCReset() -0626 MCStatus():ActFlg -0926 MCGetErrorMsg(mcErrorNo,@PStringBuff) -0A26 MCLoadDriver(mcChannelNo) -0B26 MCUnLoadDriver(mcChannelNo) -0C26 MCTimeToBin(mcTimeValue/4):result/4 -0D26 MCBinToTime(mcBinVal/4):result/4 -0E26 MCGetTrackTitle(mcDiskID/4,mcTrackNo,@PStringBuff) -0F26 MCSetTrackTitle(mcDiskID/4,TrackNum,@title) -1026 MCGetProgram(mcDiskID/4,@resultBuff) -1126 MCSetProgram(mcDiskID/4,@mcProg) -1226 MCGetDiscTitle(mcDiskID/4,@PStringBuff) -1326 MCSetDiscTitle(mcDiskID/4,@title) -1426 MCDStartUp(mcChannelNo,@portName,userID) -1526 MCDShutDown(mcChannelNo) -1626 MCGetFeatures(mcChannelNo,mcFeatSel):result/4 -1726 MCPlay(mcChannelNo) -1826 MCPause(mcChannelNo) -1926 MCSendRawData(mcChannelNo,@mcNative) -1A26 MCGetStatus(mcChannelNo,mcStatusSel):result -1B26 MCControl(mcChannelNo,ctlCommand) -1C26 MCScan(mcChannelNo,mcDirection) -1D26 MCGetSpeeds(mcChannelNo,@PStringBuff) -1E26 MCSpeed(mcChannelNo,mcFPS) -1F26 MCStopAt(mcChannelNo,mcUnitType,mcStopLoc/4) -2026 MCJog(mcChannelNo,mcUnitType,mcNJog/4,mcJogRepeat) -2126 MCSearchTo(mcChannelNo,mcUnitType,searchLoc/4) -2226 MCSearchDone(mcChannelNo):result -2326 MCSearchWait(mcChannelNo) -2426 MCGetPosition(mcChannelNo,mcUnitType):result/4 -2526 MCSetAudio(mcChannelNo,mcAudioCtl) -2626 MCGetTimes(mcChannelNo,mctimesSel):result/4 -2726 MCGetDiscTOC(mcChannelNo,mcTrackNo):result/4 -2826 MCGetDiscID(mcChannelNo):result/4 -2926 MCGetNoTracks(mcChannelNo):result -2A26 MCRecord(mcChannelNo) -2B26 MCStop(mcChannelNo) -2C26 MCWaitRawData(mcChannelNo,@result,tickWait,termMask) -2D26 MCGetName(mcChannelNo,@PStringBuff) -2E26 MCSetVolume(mcChannelNo,mcLeftVol,mcRightVol) -0032 === Male Voice === -0132 MaleBootInit() -0232 MaleStartUp() -0332 MaleShutDown() -0432 MaleVersion():Vers -0532 MaleReset() -0632 MaleStatus():ActFlg -0932 MaleSpeak(Vol,Speed,Pitch,@PhonStr) -0033 === Female Voice === -0133 FemaleBootInit() -0233 FemaleStartUp() -0333 FemaleShutDown() -0433 FemaleVersion():Vers -0533 FemaleReset() -0633 FemaleStatus():ActFlg -0933 FemaleSpeak(Vol,Speed,Pitch,@PhonStr) -0034 === TML Speech Toolkit parser === -0134 SpeechBootInit() -0234 SpeechStartUp(MemID) -0334 SpeechShutDown() -0434 SpeechVersion():Vers -0534 SpeechReset() -0634 SpeechStatus():ActFlg -0934 Parse(@EnglStr,@PhonStr,Start) -0A34 DictInsert(@EnglStr,@PhonStr) -0B34 DictDelete(@EnglStr) -0C34 DictDump(@EnglStr,@PhonStr):@Str; -0D34 SetSayGlobals(Gend,Tone,Pitch,Spd,Vol) -0E34 DictInit(Flag) -0F34 Say(@EnglishStr) -1034 Activate... -0042 === Finder (error codes only) === -00FF === GSBug === -04FF DebugVersion():Vers -06FF DebugStatus():ActFlg -09FF DebugStr(@PStr) -0AFF SetMileStone(@PStr) -0BFF DebugSetHook(@hook) -0CFF DebugGetInfo(selector):Info/4 -0DFF DebugControl(data/4,extraData/4,operation,type) -0EFF DebugQuery(data/4,operation,type):Info/4 -* User tools -0000 === User Tools === -0001 === fakeModalDialog (DTS) === -0101 fmdBootInit() -0201 fmdStartUp() -0301 fmdShutDown() -0401 fmdVersion():Vers -0501 fmdReset() -0601 fmdStatus():ActFlg -0901 fakeModalDialog(@Event,@Update,@EvHook,@Beep,Flags):Result/4 -0A01 fmdSetMenuProc(@MenuProc) -0B01 fmdGetMenuProc():@MenuProc -0C01 fmdStdDrawProc() -0D01 fmdEditMenu() -0E01 fmdFindCursorCtl(@CtrlH,x,y,@Wind):PartCode -0F01 fmdLESetText(@Wind,leID/4,@PString) -1001 fmdLEGetText(@Wind,leID/4,@PString) -1101 fmdWhichRadio(@Wind,RadioID/4):WhichRadio -1201 fmdIBeamCursor() -1301 fmdInitIBeam() -1401 fmdSetIBeam(@Cursor) -1501 fmdGetIBeamAdr():@Cursor -1601 fmdGetCtlPart():Part -1701 fmdGetError():Error -0002 === PixelMap Tools (DTS) === -0102 pmapBootInit() -0202 pmapStartUp() -0302 pmapShutDown() -0402 pmapVersion():Vers -0502 pmapReset() -0602 pmapStatus():ActFlg -0902 pixelMap2Rgn(@srcLoc,bitsPerPix,colorMask):RgnH -0A02 newPort(@pmapPortInfo):@port -0B02 killPort(@pmapPortInfo) -* E1xxxx vectors -0000 System Tool dispatcher -0004 System Tool dispatcher, glue entry -0008 User Tool dispatcher -000C User Tool dispatcher, glue entry -0010 Interrupt mgr -0014 COP mgr -0018 Abort mgr -001C System Death mgr -0020 AppleTalk interrupt -0024 Serial interrupt -0028 Scanline interrupt -002C Sound interrupt -0030 VertBlank interrupt -0034 Mouse interrupt -0038 1/4 sec interrupt -003C Keyboard interrupt -0040 ADB Response byte int -0044 ADB SRQ int -0048 Desk Acc mgr -004C FlushBuffer handler -0050 KbdMicro interrupt -0054 1 sec interrupt -0058 External VGC int -005C other interrupt -0060 Cursor update -0064 IncBusy -0068 DecBusy -006C Bell vector -0070 Break vector -0074 Trace vector -0078 Step vector -007C [install ROMdisk] -0080 ToWriteBram -0084 ToReadBram -0088 ToWriteTime -008C ToReadTime -0090 ToCtrlPanel -0094 ToBramSetup -0098 ToPrintMsg8 -009C ToPrintMsg16 -00A0 Native Ctrl-Y -00A4 ToAltDispCDA -00A8 ProDOS 16 [inline parms] -00AC OS vector -00B0 GS/OS(@parms,call) [stack parms] -00B4 OS_P8_Switch -00B8 OS_Public_Flags -00BC OS_KIND (byte: 0=P8,1=P16) -00BD OS_BOOT (byte) -00BE OS_BUSY (bit 15=busy) -00C0 MsgPtr -0180 ToBusyStrip -0184 ToStrip -01B2 MidiInputPoll -0200 Memory Mover -0204 Set System Speed -0208 Slot Arbiter -0220 HyperCard IIgs callback -0224 WordForRTL -1004 ATLK: BASIC -1008 ATLK: Pascal -100C ATLK: RamGoComp -1010 ATLK: SoftReset -1014 ATLK: RamDispatch -1018 ATLK: RamForbid -101C ATLK: RamPermit -1020 ATLK: ProEntry -1022 ATLK: ProDOS -1026 ATLK: SerStatus -102A ATLK: SerWrite -102E ATLK: SerRead -103A init file hook -103E ATLK: PFI Vector -D600 ATLK: CmdTable -DA00 ATLK: TickCount -* E0xxxx vectors -1E04 QD:StdText -1E08 QD:StdLine -1E0C QD:StdRect -1E10 QD:StdRRect -1E14 QD:StdOval -1E18 QD:StdArc -1E1C QD:StdPoly -1E20 QD:StdRgn -1E24 QD:StdPixels -1E28 QD:StdComment -1E2C QD:StdTxMeas -1E30 QD:StdTxBnds -1E34 QD:StdGetPic -1E38 QD:StdPutPic -1E98 QD:ShieldCursor -1E9C QD:UnShieldCursor -* softswitches and F8 ROM routines -B000 Dvx: xgetparm_ch -B003 Dvx: xgetparm_n -B006 Dvx: xmess -B009 Dvx: xprint_ftype -B00C Dvx: xprint_access -B00F Dvx: xprdec_2 -B012 Dvx: xprdec_3 -B015 Dvx: xprdec_pad -B018 Dvx: xprint_path -B01B Dvx: xbuild_local -B01E Dvx: xprint_sd -B021 Dvx: xprint_drvr -B024 Dvx: xredirect -B027 Dvx: xpercent -B02A Dvx: xyesno -B02D Dvx: xgetln -B030 Dvx: xbell -B033 Dvx: xdowncase -B036 Dvx: xplural -B039 Dvx: xcheck_wait -B03C Dvx: xpr_date_ay -B03F Dvx: xpr_time_ay -B042 Dvx: xProDOS_err -B045 Dvx: xProDOS_er -B048 Dvx: xerr -B04B Dvx: xprdec_pady -B04E Dvx: xdir_setup -B051 Dvx: xdir_finish -B054 Dvx: xread1dir -B057 Dvx: xpmgr -B05A Dvx: xmmgr -B05D Dvx: xpoll_io -B060 Dvx: xprint_ver -B063 Dvx: xpush_level -B066 Dvx: xfman_open -B069 Dvx: xfman_read -B06C Dvx: xrdkey (v1.1+) -B06F Dvx: xdirty (v1.1+) -B072 Dvx: xgetnump (v1.1+) -B075 Dvx: xyesno2 (v1.2+) -B078 Dvx: xdir_setup2 (v1.23+) -B07B Dvx: xshell_info (v1.25+) -C000 r:KBD w:CLR80COL -C001 w:SET80COL -C002 w:RDMAINRAM -C003 w:RDCARDRAM -C004 w:WRMAINRAM -C005 w:WRCARDRAM -C006 w:SETSLOTCXROM -C007 w:SETINTCXROM -C008 w:SETSTDZP -C009 w:SETALTZP -C00A w:SETINTC3ROM -C00B w:SETSLOTC3ROM -C00C w:CLR80VID -C00D w:SET80VID -C00E w:CLRALTCHAR -C00F w:SETALTCHAR -C010 r:KBDSTRB -C011 r:RDLCBNK2 -C012 r:RDLCRAM -C013 r:RDRAMRD -C014 r:RDRAMWRT -C015 r:RDCXROM -C016 r:RDALTZP -C017 r:RDC3ROM -C018 r:RD80COL -C019 r:RDVBLBAR -C01A r:RDTEXT -C01B r:RDMIX -C01C r:RDPAGE2 -C01D r:RDHIRES -C01E r:ALTCHARSET -C01F r:RD80VID -C020 reserved [cassette] -C021 rw:MONOCOLOR -C022 rw:TBCOLOR -C023 rw:VGCINT -C024 r:MOUSEDATA -C025 r:KEYMODREG -C026 rw:DATAREG [key micro] -C027 rw:KMSTATUS -C028 rw:ROMBANK [IIc Plus] -C029 rw:NEWVIDEO -C02B rw:LANGSEL -C02C r:CHARROM -C02D rw:SLTROMSEL -C02E r:VERTCNT -C02F r:HORIZCNT -C030 rw:SPKR -C031 rw:DISKREG -C032 w:SCANINT -C033 rw:CLOCKDATA -C034 rw:CLOCKCTL [+border color] -C035 rw:SHADOW -C036 rw:CYAREG -C037 rw:DMAREG -C038 rw:SCCBREG -C039 rw:SCCAREG -C03A rw:SCCBDATA -C03B rw:SCCADATA -C03C rw:SOUNDCTL -C03D rw:SOUNDDATA -C03E rw:SOUNDADRL -C03F rw:SOUNDADRH -C040 reserved [C040 Strobe] -C041 *rw:INTEN -C044 *r:MMDELTAX -C045 *r:MMDELTAY -C046 w:DIAGTYPE r:INTFLAG -C047 w:CLRVBLINT -C048 w:CLRXYINT -C050 rw:TXTCLR -C051 rw:TXTSET -C052 rw:MIXCLR -C053 rw:MIXSET -C054 rw:TXTPAGE1 -C055 rw:TXTPAGE2 -C056 rw:LORES -C057 rw:HIRES -C058 rw:SETAN0 -C059 rw:CLRAN0 -C05A rw:SETAN1 -C05B rw:CLRAN1 -C05C rw:SETAN2 -C05D rw:CLRAN2 -C05E rw:SETAN3 -C05F rw:CLRAN3 -C060 r:BUTN3 -C061 r:BUTN0 -C062 r:BUTN1 -C063 r:BUTN2 -C064 r:PADDL0 -C065 r:PADDL1 -C066 r:PADDL2 -C067 r:PADDL3 -C068 rw:STATEREG -C06D *TESTREG -C06E *CLTRM -C06F *ENTM -C070 rw:PTRIG -C081 rw:ROMIN -C083 rw:LCBANK2 -C08B rw:LCBANK1 -C0E0 IWM:PH0 off -C0E1 IWM:PH0 on -C0E2 IWM:PH1 off -C0E3 IWM:PH1 on -C0E4 IWM:PH2 off -C0E5 IWM:PH2 on -C0E6 IWM:PH3 off -C0E7 IWM:PH3 on -C0E8 IWM:motor off -C0E9 IWM:motor on -C0EA IWM:drive 1 -C0EB IWM:drive 2 -C0EC IWM:Q6 OFF (Read) -C0ED IWM:Q6 ON (WP-sense) -C0EE IWM:Q7 OFF (WP-sense/Read) -C0EF IWM:Q7 ON (Write) -C311 ROM:AUXMOVE -C314 ROM:XFER -CFFF rw:CLRROM -F800 F8ROM:PLOT -F80E F8ROM:PLOT1 -F819 F8ROM:HLINE -F828 F8ROM:VLINE -F832 F8ROM:CLRSCR -F836 F8ROM:CLRTOP -F847 F8ROM:GBASCALC -F85F F8ROM:NXTCOL -F864 F8ROM:SETCOL -F871 F8ROM:SCRN -F88C F8ROM:INSDS1.2 -F88E F8ROM:INSDS2 -F890 F8ROM:GET816LEN -F8D0 F8ROM:INSTDSP -F940 F8ROM:PRNTYX -F941 F8ROM:PRNTAX -F944 F8ROM:PRNTX -F948 F8ROM:PRBLNK -F94A F8ROM:PRBL2 -F953 F8ROM:PCADJ -F962 F8ROM:TEXT2COPY -FA40 F8ROM:OLDIRQ -FA4C F8ROM:BREAK -FA59 F8ROM:OLDBRK -FA62 F8ROM:RESET -FAA6 F8ROM:PWRUP -FABA F8ROM:SLOOP -FAD7 F8ROM:REGDSP -FB19 F8ROM:RTBL -FB1E F8ROM:PREAD -FB21 F8ROM:PREAD4 -FB2F F8ROM:INIT -FB39 F8ROM:SETTXT -FB40 F8ROM:SETGR -FB4B F8ROM:SETWND -FB51 F8ROM:SETWND2 -FB5B F8ROM:TABV -FB60 F8ROM:APPLEII -FB6F F8ROM:SETPWRC -FB78 F8ROM:VIDWAIT -FB88 F8ROM:KBDWAIT -FBB3 F8ROM:VERSION -FBBF F8ROM:ZIDBYTE2 -FBC0 F8ROM:ZIDBYTE -FBC1 F8ROM:BASCALC -FBDD F8ROM:BELL1 -FBE2 F8ROM:BELL1.2 -FBE4 F8ROM:BELL2 -FBF0 F8ROM:STORADV -FBF4 F8ROM:ADVANCE -FBFD F8ROM:VIDOUT -FC10 F8ROM:BS -FC1A F8ROM:UP -FC22 F8ROM:VTAB -FC24 F8ROM:VTABZ -FC42 F8ROM:CLREOP -FC58 F8ROM:HOME -FC62 F8ROM:CR -FC66 F8ROM:LF -FC70 F8ROM:SCROLL -FC9C F8ROM:CLREOL -FC9E F8ROM:CLREOLZ -FCA8 F8ROM:WAIT -FCB4 F8ROM:NXTA4 -FCBA F8ROM:NXTA1 -FCC9 F8ROM:HEADR -FD0C F8ROM:RDKEY -FD10 F8ROM:FD10 -FD18 F8ROM:RDKEY1 -FD1B F8ROM:KEYIN -FD35 F8ROM:RDCHAR -FD67 F8ROM:GETLNZ -FD6A F8ROM:GETLN -FD6C F8ROM:GETLN0 -FD6F F8ROM:GETLN1 -FD8B F8ROM:CROUT1 -FD8E F8ROM:CROUT -FD92 F8ROM:PRA1 -FDDA F8ROM:PRBYTE -FDE3 F8ROM:PRHEX -FDED F8ROM:COUT -FDF0 F8ROM:COUT1 -FDF6 F8ROM:COUTZ -FE1F F8ROM:IDROUTINE -FE2C F8ROM:MOVE -FE5E F8ROM:LIST (not on GS) -FE80 F8ROM:SETINV -FE84 F8ROM:SETNORM -FE89 F8ROM:SETKBD -FE8B F8ROM:INPORT -FE93 F8ROM:SETVID -FE95 F8ROM:OUTPORT -FEB6 F8ROM:GO -FECD F8ROM:WRITE -FEFD F8ROM:READ -FF2D F8ROM:PRERR -FF3A F8ROM:BELL -FF3F F8ROM:RESTORE -FF4A F8ROM:SAVE -FF58 F8ROM:IORTS -FF59 F8ROM:OLDRST -FF65 F8ROM:MON -FF69 F8ROM:MONZ -FF6C F8ROM:MONZ2 -FF70 F8ROM:MONZ4 -FF8A F8ROM:DIG -FFA7 F8ROM:GETNUM -FFAD F8ROM:NXTCHR -FFBE F8ROM:TOSUB -FFC7 F8ROM:ZMODE -* 01xxxx vectors -FC00 SysSrv: DEV_DISPATCHER -FC04 SysSrv: CACHE_FIND_BLK -FC08 SysSrv: CACHE_ADD_BLK -FC14 SysSrv: CACHE_DEL_BLK -FC1C SysSrv: ALLOC_SEG -FC20 SysSrv: RELEASE_SEG -FC34 SysSrv: SWAP_OUT -FC38 SysSrv: DEREF -FC50 SysSrv: SET_SYS_SPEED -FC68 SysSrv: LOCK_MEM -FC6C SysSrv: UNLOCK_MEM -FC70 SysSrv: MOVE_INFO -FC88 SysSrv: SIGNAL -FC90 SysSrv: SET_DISK_SW -FCA4 SysSrv: SUP_DRVR_DISP -FCA8 SysSrv: INSTALL_DRIVER -FCBC SysSrv: DYN_SLOT_ARBITER -FCD8 SysSrv: UNBIND_INT_VEC -* Nifty List service calls -0000 NLServ: nlRecover -0001 NLServ: nlEnter -0002 NLServ: nlRemoveNL -0003 NLServ: nlGetInfo -0004 NLServ: nlInstallHook -0005 NLServ: nlRemoveHook -0006 NLServ: nlGetDirectory():@dir -0007 NLServ: nlNewSession(@callback):sessRef -0008 NLServ: nlKillSession(sessRef) -0009 NLServ: nlSetSession(sessRef):oldRef -000A NLServ: nlWelcome -0010 NLServ: nlGetFirstHandle -0011 NLServ: nlGetHandleInfo -0012 NLServ: nlLookup -0013 NLServ: nlIndLookup -0014 NLServ: nlGetProcName(@proc):@pString -0020 NLServ: nlScanHandles -0021 NLServ: nlDisasm1 -0022 NLServ: nlExecCmdLine -0023 NLServ: nlGetRange -0024 NLServ: nlGetAGlobal(ref):value -0025 NLServ: nlSetAGlobal(@(ref,value)) -0026 NLServ: nlAbortToCmd -0030 NLServ: nlWriteChar -0031 NLServ: nlShowChar -0032 NLServ: nlWriteStr -0033 NLServ: nlShowStr -0034 NLServ: nlWriteCStr -0035 NLServ: nlShowCStr -0036 NLServ: nlWriteText -0037 NLServ: nlShowText -0038 NLServ: nlWriteByte -0039 NLServ: nlWriteWord -003A NLServ: nlWritePtr -003B NLServ: nlWriteLong -003C NLServ: nlGetLn -003D NLServ: nlGetChar -003E NLServ: nlCheckKey -003F NLServ: nlCrout -0040 NLServ: nlSpout -0041 NLServ: nlPause -0042 NLServ: nlHandleInfo -0043 NLServ: nlWriteNoVoice(@cStr) -0044 NLServ: nlShowWString(@wStr) -0050 NLServ: nlChrGet -0051 NLServ: nlChrGot -0052 NLServ: nlEatBlanks() -0054 NLServ: nlEvalExpr(@exprBuff):exprLen -0060 NLServ: nlGetByte(@addr):byte -0061 NLServ: nlGetWord(@addr):word -0062 NLServ: nlGetLong(@addr):long -* resource type names -8001 rIcon -8002 rPicture -8003 rControlList -8004 rControlTemplate -8005 rC1InputString -8006 rPString -8007 rStringList -8008 rMenuBar -8009 rMenu -800A rMenuItem -800B rTextForLETextBox2 -800C rCtlDefProc -800D rCtlColorTbl -800E rWindParam1 -800F rWindParam2 -8010 rWindColor -8011 rTextBlock -8012 rStyleBlock -8013 rToolStartup -8014 rResName -8015 rAlertString -8016 rText -8017 rCodeResource -8018 rCDEVCode -8019 rCDEVFlags -801A rTwoRects -801B rFileType -801C rListRef -801D rCString -801E rXCMD -801F rXFCN -8020 rErrorString -8021 rKTransTable -8022 rWString -8023 rC1OutputString -8024 rSoundSample -8025 rTERuler -8026 rFSequence -8027 rCursor -8028 rItemStruct -8029 rVersion -802A rComment -802B rBundle -802C rFinderPath -802D rPaletteWindow -802E rTaggedStrings -802F rPatternList -C001 rRectList -C002 rPrintRecord -C003 rFont -* Error codes -0001 OS:bad call number / dispatcher:toolset not found -0002 function not found -0004 OS:bad parameter count -0007 GS/OS is busy -0010 GS/OS:device not found -0011 GS/OS:bad device number -0020 GS/OS:invalid driver request -0021 GS/OS:invalid driver control or status code -0022 GS/OS:bad call parameter -0023 GS/OS:character device not open -0024 GS/OS:character device already open -0025 OS:interrupt table full -0026 GS/OS:resources not available -0027 OS:I/O error -0028 OS:no device connected -0029 GS/OS:driver is busy -002B OS:disk write protected -002C GS/OS:invalid byte count -002D GS/OS:invalid block address -002E OS:disk switched -002F OS:no disk -0040 OS:bad pathname -0042 OS:max number of files already open -0043 OS:bad file reference number -0044 OS:directory not found -0045 OS:volume not found -0046 OS:file not found -0047 OS:duplicate filename -0048 OS:volume full -0049 OS:volume directory full -004A OS:incompatible file format -004B OS:unsupported storage type -004C OS:end of file encountered -004D OS:position out of range -004E OS:access not allowed -004F GS/OS:buffer too small -0050 OS:file is open -0051 OS:directory damaged -0052 OS:unknown volume type -0053 OS:parameter out of range -0054 GS/OS:out of memory -0055 P8:volume control block table full -0056 P8:bad buffer address -0057 OS:duplicate volume name -0058 GS/OS:not a block device -0059 GS/OS:file level out of range -005A OS:bad bitmap address (block # too large) -005B GS/OS:invalid pathnames for ChangePath -005C GS/OS:not an executable file -005D GS/OS:Operating System not supported -005F GS/OS:too many applications on stack -0060 GS/OS:data unavailable -0061 GS/OS:end of directory -0062 GS/OS:invalid FST call class -0063 GS/OS:file doesn't have a resource fork -0064 GS/OS:invalidFSTID -0065 GS/OS:invalid FST operation -0066 GS/OS:fstCaution -0067 GS/OS:devNameErr -0068 GS/OS:devListFull -0069 GS/OS:supListFull -006A GS/OS:fstError (generic) -0070 GS/OS:resExistsErr -0071 GS/OS:resAddErr -0088 network error -0110 toolVersionErr -0111 messNotFoundErr -0112 messageOvfl -0113 srqNameTooLong -0120 reqNotAccepted -0121 duplicateName -0122 invalidSendRequest -0201 memErr (couldn't allocate memory) -0202 emptyErr -0203 notEmptyErr -0204 lockErr -0205 purgeErr -0206 handleErr -0207 idErr -0208 attrErr -0301 badInputErr -0302 noDevParamErr -0303 taskInstlErr -0304 noSigTaskErr -0305 queueDmgdErr -0306 taskNtFdErr -0307 firmTaskErr -0308 hbQueueBadErr -0309 unCnctdDevErr -030B idTagNtAvlErr -034F mtBuffTooSmall -0381 invalidTag -0382 alreadyInQueue -0390 badTimeVerb -0391 badTimeData -0401 alreadyInitialized -0402 cannotReset -0403 notInitialized -0410 screenReserved -0411 badRect -0420 notEqualChunkiness -0430 rgnAlreadyOpen -0431 rgnNotOpen -0432 rgnScanOverflow -0433 rgnFull -0440 polyAlreadyOpen -0441 polyNotOpen -0442 polyTooBig -0450 badTableNum -0451 badColorNum -0452 badScanLine -0510 daNotFound -0511 notSysWindow -0520 deskBadSelector -0601 emDupStrtUpErr -0602 emResetErr -0603 emNotActErr -0604 emBadEvtCodeErr -0605 emBadBttnNoErr -0606 emQSiz2LrgErr -0607 emNoMemQueueErr -0681 emBadEvtQErr -0682 emBadQHndlErr -0810 noDOCFndErr -0811 docAddrRngErr -0812 noSAppInitErr -0813 invalGenNumErr -0814 synthModeErr -0815 genBusyErr -0817 mstrIRQNotAssgnErr -0818 sndAlreadyStrtErr -08FF uncleamedSntIntErr -0910 cmndIncomplete -0911 cantSync -0982 adbBusy -0983 devNotAtAddr -0984 srqListFull -0B01 imBadInptParam -0B02 imIllegalChar -0B03 imOverflow -0B04 imStrOverflow -0C01 badDevType -0C02 badDevNum -0C03 badMode -0C04 unDefHW -0C05 lostDev -0C06 lostFile -0C07 badTitle -0C08 noRoom -0C09 noDevice -0C0B dupFile -0C0C notClosed -0C0D notOpen -0C0E badFormat -0C0F ringBuffOFlo -0C10 writeProtected -0C40 devErr -0E01 paramLenErr -0E02 allocateErr -0E03 taskMaskErr -0E04 compileTooLarge -0E05 cantUpdateErr -0F01 menuStarted -0F02 menuItemNotFound -0F03 menuNoStruct -0F04 dupMenuID -1001 wmNotStartedUp -1002 cmNotInitialized -1003 noCtlInList -1004 noCtlError -1005 notExtendedCtlError -1006 noCtlTargetError -1007 notExtendedCtlError -1008 canNotBeTargetError -1009 noSuchIDError -100A tooFewParmsError -100B noCtlToBeTargetError -100C noFrontWindowError -1101 idNotFound / segment not found? -1102 OMF version error -1103 idPathnameErr -1104 idNotLoadFile -1105 idBusyErr -1107 idFilVersErr -1108 idUserIDErr -1109 idSequenceErr -110A idBadRecordErr -110B idForeignSegErr -1210 picEmpty -1211 picAlreadyOpen / badRectSize? -1212 pictureError / destModeError? -121F bad picture opcode -1221 badRect -1222 badMode -1230 badGetSysIconInput -1301 missingDriver -1302 portNotOn -1303 noPrintRecord -1304 badLaserPrep -1305 badLPFile -1306 papConnNotOpen -1307 papReadWriteErr -1308 ptrConnFailed -1309 badLoadParam -130A callNotSupported -1321 startUpAlreadyMade -1401 leDupStartUpErr -1402 leResetErr -1403 leNotActiveErr -1404 leScrapErr -150A badItemType -150B newItemFailed -150C itemNotFound -150D notModalDialog -1610 badScrapType -1701 badPromptDesc -1702 badOrigNameDesc -1704 badReplyNameDesc -1705 badReplyPathDesc -1706 badCall -17FF sfNotStarted -1901 nsAlreadyInit -1902 nsSndNotInit -1921 nsNotAvail -1922 nsBadGenNum -1923 nsNotInit -1924 nsGenAlreadyOn -1925 soundWrongVer -1A00 noRoomMidiErr -1A01 noCommandErr -1A02 noRoomErr -1A03 startedErr -1A04 noNoteErr -1A05 noStartErr -1A06 instBndsErr -1A07 nsWrongVer -1B01 fmDupStartUpErr -1B02 fmResetErr -1B03 fmNotActiveErr -1B04 fmFamNotFndErr -1B05 fmFontNtFndErr -1B06 fmFontMemErr -1B07 fmSysFontErr -1B08 fmBadFamNumErr -1B09 fmBadSizeErr -1B0A fmBadNameErr -1B0B fmMenuErr -1B0C fmScaleSizeErr -1B0D fmBadParmErr -1C02 listRejectEvent -1D01 aceIsActive -1D02 aceBadDP -1D03 aceNotActive -1D04 aceNoSuchParam -1D05 aceBadMethod -1D06 aceBadSrc -1D07 aceBadDest -1D08 aceDataOverlap -1E01 resForkUsed -1E02 resBadFormat -1E03 resNoConverter -1E04 resNoCurFile -1E05 resDupID -1E06 resNotFound -1E07 resFileNotFound -1E08 resBadAppID -1E09 resNoUniqueID -1E0A resIndexRange -1E0B resSysIsOpen -1E0C resHasChanged -1E0D resDiffConverter -1E0E resDiskFull -1E0F resInvalidShutDown -1E10 resNameNotFound -1E11 resBadNameVers -1E12 resDupStartUp -1E13 resInvalidTypeOrID -2000 miStartUpErr -2001 miPacketErr -2002 miArrayErr -2003 miFullbufErr -2004 miToolsErr -2005 miOutOffErr -2007 miNoBufErr -2008 miDriverErr -2009 miBadFreqErr -200A miClockErr -200B miConflictErr -200C miNoDevErr -2080 miDevNotAvail -2081 miDevSlotBusy -2082 miDevBusy -2083 miDevOverrun -2084 miDevNoConnect -2085 miDevReadErr -2086 miDevVersion -2087 miDevIntHndlr -2110 vdNoVideoDevice -2111 vdAlreadyStarted -2112 vdInvalidSelector -2113 vdInvalidParam -21FF vdUnImplemented -2201 teAlreadyStarted -2202 teNotStarted -2203 teInvalidHandle -2204 teInvalidDescriptor -2205 teInvalidFlag -2206 teInvalidPCount -2208 teBufferOverflow -2209 teInvalidLine -220B teInvalidParameter -220C teInvalidTextBox2 -220D teNeedsTools -2301 msAlreadyStarted -2302 msNotStarted -2303 msNoDPMem -2304 msNoMemBlock -2305 msNoMiscTool -2306 msNoSoundTool -2307 msGenInUse -2308 msBadPortNum -2309 msPortBusy -230A msParamRangeErr -230B msMsgQueueFull -230C msRecBufFull -230D msOutputDisabled -230E msMessageError -230F msOutputBufFull -2310 msDriverNotStarted -2311 msDriverAlreadySet -2380 msDevNotAvail -2381 msDevSlotBusy -2382 msDevBusy -2383 msDevOverrun -2384 msDevNoConnect -2385 msDevReadErr -2386 msDevVersion -2387 msDevIntHndlr -2601 mcUnimp -2602 mcBadSpeed -2603 mcBadUnitType -2604 mcTimeOutErr -2605 mcNotLoaded -2606 mcBadAudio -2607 mcDevRtnError -2608 mcUnrecStatus -2609 mcBadSelector -260A mcFunnyData -260B mcInvalidPort -260C mcOnlyOnce -260D mcNoResMgr -260E mcItemNotThere -260F mcWasShutDown -2610 mcWasStarted -2611 mcBadChannel -2612 mcInvalidParam -2613 mcCallNotSupported -4201 fErrBadInput -4202 fErrFailed -4203 fErrCancel -4204 fErrDimmed -4205 fErrBusy -4206 fErrNotPrudent -4207 fErrBadBundle -42FF fErrNotImp -FF01 debugUnImpErr -FF02 debugBadSelErr -FF03 debugDupBreakErr -FF04 debugBreakNotSetErr -FF05 debugTableFullErr -FF06 debugTableEmptyErr -FF07 debugBreaksInErr -* HyperCardIIgs callbacks -0001 HC:SendCardMessage(@Str) -0002 HC:EvalExpr(@Str):H -0003 HC:StringLength(@Str):Length/4 -0004 HC:StringMatch(@Pattern,@Target):@Ptr -0005 HC:SendHCMessage(@Msg) -0006 HC:ZeroBytes(@Ptr,Count/4) -0007 HC:PasToZero(@Str):StringH -0008 HC:ZeroToPas(@ZeroStr,@Str) -0009 HC:StrToLong(@Str31):Long/4 -000A HC:StrToNum(@Str31):Long/4 -000B HC:StrToBool(@Str31):Boolean -000C HC:StrToExt(@Str31):@Extended -000D HC:LongToStr(posNum/4):@Str31 -000E HC:NumToStr(Num/4):@Str31 -000F HC:NumToHex(Num/4,nDigits):@Str31 -0010 HC:BoolToStr(Boolean):@Str31 -0011 HC:ExtToStr(@Extended):@Str31 -0012 HC:GetGlobal(@GlobalName):ValueH -0013 HC:SetGlobal(@GlobalName,GlobValueH) -0014 HC:GetFieldByName(cardFieldFlag,@FieldName):H -0015 HC:GetFieldByNum(cardFieldFlag,fieldNum):H -0016 HC:GetFieldByID(cardFieldFlag,fieldID):H -0017 HC:SetFieldByName(cardFieldFlag,@fieldName,ValueH) -0018 HC:SetFieldByNum(cardFieldFlag,fieldNum,ValueH) -0019 HC:SetFieldByID(cardFieldFlag,fieldID,ValueH) -001A HC:StringEqual(@Str1,@Str2):Boolean -001B HC:ReturnToPas(@ZeroStr,@Str) -001C HC:ScanToReturn(@PtrToPtr) -001D HC:ScanToZero(@PtrToPtr) -001E HC:GSToPString(GStringH):@Str -001F HC:PToGSString(@Str):GStringH -0020 HC:CopyGSString(GStringH):GString2H -0021 HC:GSConcat(GString1H,GString2H):NewGStringH -0022 HC:GSStringEqual(GString1H,GString2H):Boolean -0023 HC:GSToZero(GStringH):ZeroH -0024 HC:ZeroToGS(ZeroH):GStringH -0025 HC:LoadNamedResource(whichType,@Name):H -0026 HC:FindNamedResource(Type,@Name,@File,@ID/4):Bool -0027 HC:SetResourceName(Type,ID/4,@Name) -0028 HC:GetResourceName(Type,ID/4):@Str -0029 HC:BeginXSound() -002A HC:EndXSound() -002B HC:GetMaskAndData(@MaskLocInfo,@DataLocInfo) -002C HC:ChangedMaskAndData(whatChanged) -002D HC:PointToStr(Point/4,@String) -002E HC:RectToStr(@Rect,@String) -002F HC:StrToPoint(@String,@Point) -0030 HC:StrToRect(@String,@Rect) -0031 HC:NewXWindow(@BoundsR,@Title,visFlg,windStyle):WindowPtr -0032 HC:SetXWIdleTime(@Window,Interval/4) -0033 HC:CloseXWindow(@Window) -0034 HC:HideHCPalettes() -0035 HC:ShowHCPalettes() -0036 HC:SetXWindowValue(@Window,Value/4) -0037 HC:GetXWindowValue(@Window):Value/4 -0038 HC:XWAllowReEntrancy(@Window,SysEvts,HCEvts) -* Request Codes -0001 systemSaysBeep -0002 systemSaysUnknownDisk -0003 srqGoAway -0004 srqGetrSoundSample -0005 srqSynchronize -0006 srqPlayrSoundSample -0008 systemSaysNewDeskMsg -000C systemSaysDoClipboard -000D systemSaysForceUndim -000E systemSaysEjectingDev -0010 srqOpenOrPrint -0011 srqQuit -0100 finderSaysHello -0101 finderSaysGoodbye -0102 finderSaysSelectionChanged -0103 finderSaysMItemSelected -0104 finderSaysBeforeOpen -0105 finderSaysOpenFailed -0106 finderSaysBeforeCopy -0107 finderSaysIdle -0108 finderSaysExtrasChosen -0109 finderSaysBeforeRename -010A finderSaysKeyHit -0502 systemSaysDeskStartUp -0503 systemSaysDeskShutDown -051E systemSaysFixedAppleMenu -0F01 systemSaysMenuKey -1201 systemSaysGetSysIcon -8000 tellFinderGetDebugInfo (or srqMountServer to EasyMount) -8001 askFinderAreYouThere -8002 tellFinderOpenWindow -8003 tellFinderCloseWindow -8004 tellFinderGetSelectedIcons -8005 tellFinderSetSelectedIcons -8006 tellFinderLaunchThis -8007 tellFinderShutDown -8008 tellFinderMItemSelected -800A tellFinderMatchFileToIcon -800B tellFinderAddBundle -800C tellFinderAboutChange -800D tellFinderCheckDatabase -800E tellFinderColorSelection -800F tellFinderAddToExtras -8011 askFinderIdleHowLong -8012 tellFinderGetWindowIcons -8013 tellFinderGetWindowInfo -8014 tellFinderRemoveFromExtras -8015 tellFinderSpecialPreferences -8200 srqConvertRelPitch -9000 cpOpenCDev -9001 cpOpenControlPanels -* diff --git a/ciderpress/DIST/ReadMe.txt b/ciderpress/DIST/ReadMe.txt deleted file mode 100644 index be4c7d6..0000000 --- a/ciderpress/DIST/ReadMe.txt +++ /dev/null @@ -1,11 +0,0 @@ -faddenSoft CiderPress(tm) - -CiderPress is a Windows utility for accessing Apple II archives and -disk images. A wide range of formats are supported, including -ShrinkIt (NuFX) archives and disk images with DOS, ProDOS, Pascal, -CP/M, and RDOS filesystems. - -This program used to be shareware, but is now free. - -If you have any questions, please visit the CiderPress -web site at http://a2ciderpress.com/. diff --git a/ciderpress/DIST/mfc120u.dll b/ciderpress/DIST/mfc120u.dll deleted file mode 100644 index 3e5c2be..0000000 Binary files a/ciderpress/DIST/mfc120u.dll and /dev/null differ diff --git a/ciderpress/DIST/msvcr120.dll b/ciderpress/DIST/msvcr120.dll deleted file mode 100644 index 8c36149..0000000 Binary files a/ciderpress/DIST/msvcr120.dll and /dev/null differ diff --git a/ciderpress/DIST/with-mdc.deploy b/ciderpress/DIST/with-mdc.deploy deleted file mode 100644 index 6fe85ac..0000000 --- a/ciderpress/DIST/with-mdc.deploy +++ /dev/null @@ -1,365 +0,0 @@ -DeployMaster Installation Script -17 -faddenSoft -http://www.faddensoft.com/ -CiderPress -http://a2ciderpress.com/ -4.0.3 -43081 -C:\DATA\faddenSoft\fs.ico -Copyright © 2017 CiderPress project authors. All rights reserved. -C:\Src\CiderPress\DIST\ReadMe.txt -C:\Src\CiderPress\DIST\License.txt -FALSE - - -%PROGRAMFILES%\faddenSoft\CiderPress -%COMMONFILES%\faddenSoft\ -%PROGRAMSMENU%\CiderPress - -1 -1 -You are attempting to run the installer for %APP% %VERSION%. This installer only works on %INSTALLER%-bit versions of Windows. You are running a %USER%-bit version of Windows. Please check the developer's website to see whether there is a separate %USER%-bit installer for %APP% that you can use on your version of Windows. -FALSE -FALSE -FALSE -FALSE -FALSE -TRUE -TRUE -TRUE -TRUE -TRUE -TRUE -TRUE -1 -You are attempting to run the installer for %APP% %VERSION%. This installer supports the following versions of Windows: %INSTALLER%. You are running %USER%. Please check the developer's website to see whether there is a separate installer for %APP% that you can use on %USER%. -TRUE -TRUE -1 --16777198 -MS Sans Serif -8 -FALSE -FALSE -FALSE -FALSE -FALSE -16711680 -0 - -TRUE -faddenSoft CiderPress 0.1 -1 -16777215 -Times New Roman -36 -TRUE -FALSE -FALSE -FALSE -TRUE -0 -Copyright © 2003 faddenSoft, LLC. All rights reserved. -1 -16777215 -Times New Roman -20 -TRUE -FALSE -FALSE -FALSE -TRUE -0 -English (Default) -FALSE -FALSE -FALSE -FALSE -TRUE -FALSE - -FALSE -0 -FALSE -0 -+ -CiderPress -0 -TRUE -FALSE -Main CiderPress application -+ -%APPCOMMONFOLDER% -1 -FALSE -%APPFOLDER% -1 -FALSE -+ -C:\Src\CiderPress\DIST\NList.Data.TXT -3 -1 -TRUE -TRUE -FALSE -C:\Src\ciderpress\Release\CiderPress.chm -3 -0 -TRUE -TRUE -FALSE -C:\Src\ciderpress\Release\CiderPress.exe -3 -0 -TRUE -TRUE -FALSE -FALSE -- -%APPMENU% -1 -FALSE -+ -C:\Src\ciderpress\Release\CiderPress.chm -4 -CiderPress Help -Help file for CiderPress - - -0 -0 -C:\Src\ciderpress\Release\CiderPress.exe -4 -CiderPress -The ultimate Apple II archive utility - - -0 -0 -- -%DESKTOP% -1 -FALSE -%SENDTO% -1 -FALSE -%STARTUP% -1 -FALSE -%SYSTEM% -1 -FALSE -%WINDOWS% -1 -FALSE -- -Common DLLs -0 -FALSE -FALSE -NuFX, zlib, and disk image access libraries. -+ -%APPCOMMONFOLDER% -1 -FALSE -%APPFOLDER% -1 -FALSE -+ -C:\Src\ciderpress\DIST\mfc120u.dll -3 -0 -TRUE -TRUE -FALSE -FALSE -C:\Src\ciderpress\DIST\msvcr120.dll -3 -0 -TRUE -TRUE -FALSE -FALSE -C:\Src\ciderpress\Release\diskimg5.dll -3 -0 -TRUE -TRUE -FALSE -FALSE -C:\Src\ciderpress\Release\nufxlib.dll -3 -0 -TRUE -TRUE -FALSE -FALSE -C:\Src\ciderpress\Release\zlib.dll -3 -0 -TRUE -TRUE -FALSE -FALSE -- -%APPMENU% -1 -FALSE -%DESKTOP% -1 -FALSE -%SENDTO% -1 -FALSE -%STARTUP% -1 -FALSE -%SYSTEM% -1 -FALSE -%WINDOWS% -1 -FALSE -- -MDC -0 -TRUE -FALSE -Multi-Disk Catalog utility. -+ -%APPCOMMONFOLDER% -1 -FALSE -%APPFOLDER% -1 -FALSE -+ -C:\Src\ciderpress\Release\mdc.exe -3 -0 -TRUE -TRUE -FALSE -FALSE -- -%APPMENU% -1 -FALSE -+ -C:\Src\ciderpress\Release\mdc.exe -4 -MDC -Multi-Disk Catalog for Apple II disk images - - -0 -0 -- -%DESKTOP% -1 -FALSE -%SENDTO% -1 -FALSE -%STARTUP% -1 -FALSE -%SYSTEM% -1 -FALSE -%WINDOWS% -1 -FALSE -- -- -1 -1 -0 -1 -1 -0 -2 -TRUE -CiderPress -+ -HKEY_CLASSES_ROOT -0 -FALSE -HKEY_CURRENT_USER -0 -FALSE -+ -Software -0 -FALSE -+ -faddenSoft -0 -FALSE -+ -CiderPress -0 -TRUE -- -- -- -HKEY_LOCAL_MACHINE -0 -FALSE -+ -Software -0 -FALSE -+ -faddenSoft -0 -FALSE -+ -CiderPress -0 -TRUE -- -- -- -- -0 -FALSE -FALSE -FALSE -FALSE -FALSE -FALSE -3 - -http://www.deploymaster.com/dotnetfx.html -0 -TRUE -FALSE -FALSE -TRUE -C:\Src\ciderpress\Release\CiderPress.exe - --install -C:\Src\ciderpress\Release\CiderPress.exe - --uninstall -TRUE -faddenSoft.CiderPress.4 - -TRUE -FALSE -36725 -0 -FALSE -36725 -0 -4095 - - -Setup403.exe - -FALSE - - -TRUE diff --git a/ciderpress/LICENSE.txt b/ciderpress/LICENSE.txt deleted file mode 100644 index e7adc74..0000000 --- a/ciderpress/LICENSE.txt +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2009, CiderPress project authors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of FaddenSoft, LLC nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY FaddenSoft, LLC ``AS IS'' AND ANY -EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL FaddenSoft, LLC BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/ciderpress/README-linux.md b/ciderpress/README-linux.md deleted file mode 100644 index f40578f..0000000 --- a/ciderpress/README-linux.md +++ /dev/null @@ -1,68 +0,0 @@ -CiderPress Linux Utilities -========================== - -CiderPress is a Windows app, but the code for accessing NuFX (ShrinkIt) -archives and disk images can be built as libraries on Linux and used -from applications. A few samples are provided. Most are really just -API demos, but they may come in handy. - -You need to build NufxLib, the diskimg library, and then the samples. - -Build Instructions ------------------- - -1. Configure and build NufxLib library - - cd nufxlib - ./configure - make - -2. build diskimg library - - cd ../diskimg - make - -3. build libhfs library - - cd libhfs - make - -4. build samples - - cd ../../linux - make - -The build system could use some work. - - -Sample Programs ---------------- - -The current sample programs are: - -`getfile disk-image filename` -- -Extract a file from a disk image The file is written to stdout. - -`makedisk {dos|prodos|pascal} size image-filename.po file1 ...` -- -Create a new disk image, with the specified size and format, and copy the -specified files onto it. The NON file type is used. - -`mdc file1 ...` -- -This is a Linux port of the MDC utility that ships with CiderPress. -It recursively scans all files and directories specified, displaying -the contents of any disk images it finds. - - -### Bonus Programs ### - -`iconv infile outfile` -- -Convert an image from one format to another. This was used for testing. - -`packddd infile outfile` -- -The DDD code was originally developed under Linux. This code is here -for historical reasons. - -`sstasm part1 part2` -- -The SST re-assembly code was originally developed under Linux. The code -is here for historical reasons. - diff --git a/ciderpress/README.md b/ciderpress/README.md deleted file mode 100644 index a63ab23..0000000 --- a/ciderpress/README.md +++ /dev/null @@ -1,240 +0,0 @@ -CiderPress -========== - -A Windows utility for managing Apple II file archives and disk images. -Visit the web site at http://a2ciderpress.com/. - -CiderPress was initially sold by faddenSoft, LLC as a shareware product, -starting in March 2003. In March 2007, the program was released as -open source under the BSD license. A "refresh" to modernize the code was -done in January 2015. - -Why Bother? ------------ - -Back in 2002 I decided it was time to learn how to write an application -for Microsoft Windows. I had been a professional software engineer for -many years -- including 2.5 years at Microsoft! -- but had never written -a Windows program more complex than "Hello, world!". - -I decided to write a Windows version of GS/ShrinkIt. I had already written -NufxLib, which handled all of the ShrinkIt stuff, so I could focus on -writing the Windows user interface code. - -Somewhere in the early stages of the project, it occurred to me that a -disk image isn't substantially different from a file archive. They're -both just collections of files laid out in a well-defined manner. The -decision to handle disk images as well as ShrinkIt archives seemed like -a simple improvement at the time. The rest is history. - -CiderPress has allowed me to explore a variety of interesting -technologies. It has five different ways of reading a block from physical -media, depending on your operating system and what sort of device you're -reading from. I was able to take what I learned from a digital signal -processing textbook and apply it to a real-world problem (decoding Apple -II cassette data). It is also my first Shareware product, not to mention -the initial product of my first small business venture (faddenSoft, LLC). - -I could have written other things. No doubt they would have made more -money. CiderPress is something that I find very useful, however, in the -pursuit of my Apple II hobby. - -Above all, this has been a labor of love. I have tried to get the details -right, because in the end it's the little things that mean the difference -between "good" and merely "good enough". - - -Source License --------------- - -The source code to CiderPress is available under the BSD license. See -the file [LICENSE.txt](LICENSE.txt) for details. - -CiderPress requires three other libraries, all of which are included as -source code: - -- NufxLib, also available under the BSD license. -- Zlib, available under the Zlib license. -- libhfs, available under the GPL license. - -The license allows you to do a great many things. For example, you could -take the source code to CiderPress, compile it, and sell it. I'm not sure -why anyone would buy it, but you're legally allowed to do so, as long as -you retain the appropriate copyright notice. - -If you retain libhfs, any changes you make to any part of CiderPress must -be made available, due to the "viral" nature of the GPL license. If this -is not acceptable, you can remove HFS disk image support from CiderPress -(look for "EXCISE_GPL_CODE" in DiskImg.h). - - -Building the Sources --------------------- - -The current version of CiderPress is targeted for Visual Studio 2013, -using the WinXP compatibility Platform Toolset to allow installation on -Windows XP systems. You should be able to select Debug or Release and -just build the entire thing. The project files have been updated so -that VS2015 Community Edition will accept them, but the new "universal CRT" -causes problems with WinXP, so the build files still require the older -set of tools. - -If you want to use the static analyzer, you will need to change the -Platform Toolset to straight Visual Studio 2013. - -A pre-compiled .CHM file, with the help text and pop-up messages, -is provided. The source files are all included, but generation of the -.CHM is not part of the build. If you want to update the help files, -you will need to download the HTML Help Workshop from Microsoft, and use -that to compile the help project in the app/Help directory. - -The installer binary is created with [DeployMaster](http://deploymaster.com/). - - -Building for Linux ------------------- - -The NuFX archive and disk image manipulation libraries can be used from -Linux. See the [Linux README](README-linux.md). - - -Source Notes ------------- - -Some notes on what you'll find in the various directories. - -#### Main Application #### - -This is highly Windows-centric. My goal was to learn how to write a -Windows application, so I made no pretense at portability. For better -or worse, I avoided the Visual Studio "wizards" for the dialogs. - -Much of the user interface text is in the resource file. Much is not, -especially when it comes to error messages. This will need to be addressed -if internationalization is attempted. - -It may be possible to convert this for use with wxWidgets, which uses an -MFC-like structure, and runs on Mac and Linux as well. The greatest barrier -to entry is probably the heavy reliance on the Rich Edit control. Despite -its bug-ridden history, the Rich Edit control allowed me to let Windows -deal with a lot of text formatting and image display stuff. - -#### MDC Application #### - -MDC (Multi-Disk Catalog) was written as a simple demonstration of the -value of having the DiskImg code in a DLL instead of meshed with the main -application. There's not much to it, and it hasn't changed substantially -since it was first written. - -#### DiskImg Library #### - -This library provides access to disk images. It automatically handles -a wide variety of formats. - -This library can be built under Linux or Windows. One of my key motivations -for making it work under Linux was the availability of "valgrind". Similar -tools for Windows usually very expensive or inferior (or both). - -An overview of the library can be found in the -[DiskImg README](diskimg/README.md). - -The library depends on NufxLib and zlib for access to compressed images. - -#### Reformat Library #### - -This is probably the most "fun" component of CiderPress. It converts -Apple II files to more easily accessible Windows equivalents. - -Start in Reformat.h and ReformatBase.h. There are two basic kinds of -reformatter: text and graphics. Everything else is a sub-class of one of -the two basic types. - -The general idea is to allow the reformatter to decide whether or -not it is capable of reformatting a file. To this end, the file type -information and file contents are presented to the "examine" function -of each reformatter in turn. The level of confidence is specified in a -range. If it's better than "no", it is presented to the user as an option, -ordered by the strength of its convictions. If chosen, the "process" -function is called to convert the data. - -Bear in mind that reformatters may be disabled from the preferences menu. -Also, when extracting files for easy access in Windows, the "best" -reformatter is employed by the extraction code. - -Most of the code should be portable, though some of it uses the MFC -CString class. This could probably be altered to use STL strings or plain. - -#### Util Library #### - -Miscellaneous utility functions. - -#### NufxLib and zlib #### - -These are source snapshots from [NufxLib](http://github.com/fadden/nulib2) -and [zlib](http://www.zlib.org). - -#### DIST #### - -Files used when making a distribution, notably: - -- the DeployMaster configuration file -- the license and README files that are included in the installer -- redistributable Windows runtime libraries (only needed on WinXP?) -- NiftyList data file - - -Future Trouble Spots --------------------- - -Microsoft generally does an excellent job of maintaining backward -compatibility, but as Windows and the build tools continue to evolve it is -likely that some things will break. The original version of CiderPress was -written to work on Win98, using tools of that era, and quite a bit of effort -in the 4.0 release was devoted to bringing CP into the modern era. - -In another 15 years things may be broken all over again. Some areas of -particular concern: - -1. File + folder selection. The dialog that allows you to select a combination -of files and folders is a customized version of the standard file dialog. -There is no standard dialog that works for this. The original version, based -on the old Win98-era file dialogs, worked fine at first but started to fail -in Vista. CiderPress v4.0 provided a new implementation, based on the -Win2K "explorer" dialog, which works well in WinXP and Win7/8. However, -WinVista introduced a new style, and those dialogs have a very different -structure (and don't work on WinXP). At some point it may be necessary -to replace the dialog again. - -2. Help files. CiderPress initially used the old WinHelp system. v4.0 -switched to the newer HtmlHelp, but judging by the level of support it would -seem that HtmlHelp is on its way out. The favored approach seems to be to -just pop open a web browser to a web site or a document on disk. The pop-up -help text, which currently comes out of a special section of the help file, -would instead use MFC tooltip features, with strings coming out of the -resource file. (This is probably more convenient and definitely more -flexible, so switching the pop-up help messages may happen sooner.) - -3. Unicode filenames. CiderPress cannot open most files with non-ASCII, -non-CP-1252 characters in their names (e.g. kanji). This is because the -NufxLib and DiskImg libraries use narrow strings for filenames. The libraries -are expected to build on Linux, so converting them is a bit of a pain. At -some point it may be necessary to support Unicode fully. v4.0 did a lot of -code reorganization to make this easier, as did NufxLib v3.0. - -4. Windows XP support. The default Visual Studio 2013 configuration creates -executables that do not work in Windows XP. CiderPress uses a compatibility -toolset and packs about 5MB of additional DLLs (mfc120u.dll, msvcr120.dll) in -the install package to keep things working. Visual Studio 2015 shipped with a -new "Universal CRT" that requires more effort and disk space. At some point -it may not be possible to support WinXP, or building for WinXP will prevent -something from working. The good news is that, for the current round of -tools, it's possible to build a single binary that works fully on WinXP and -later systems. - -5. Installer magic. Security improvements and changes like the Win8 "Metro" -launcher affect the way apps are installed and launched. So far the only -impact on CiderPress was to the file association handling (the stuff that -allows you to double-click a file and have CiderPress open it), but it's -likely that future OS changes will require matching app changes. The use -of DeployMaster is helpful here, as it has been kept up-to-date with changes -in Windows. diff --git a/ciderpress/app/ACUArchive.cpp b/ciderpress/app/ACUArchive.cpp deleted file mode 100644 index 44122fd..0000000 --- a/ciderpress/app/ACUArchive.cpp +++ /dev/null @@ -1,790 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * AppleLink Compression Utility file support. - * - * This was adapted from the Binary II support, which has a mixed history, - * so this is a little scrambled in spots. - */ -#include "stdafx.h" -#include "ACUArchive.h" -#include "NufxArchive.h" // uses NuError -#include "Preferences.h" -#include "Main.h" -#include "Squeeze.h" -#include - -/* -+00 2b Number of items in archive -+02 2b 0100 -+04 5b "fZink" -+09 11b 0136 0000 0000 0000 0000 dd - -+14 - -+00 1b ?? 00 -+01 1b Compression type, 00=none, 03=sq -+02 2b ?? 0000 0000 0000 0000 -+04 2b ?? 0a74 961f 7d85 af2c 2775 <- 0000 for dir (checksum?) -+06 2b ?? 0000 0000 0000 0000 -+08 2b ?? 0000 0000 0000 0000 -+0a 2b Storage size (in 512-byte blocks) -+0c 6b ?? 000000 000000 000000 000000 -+12 4b Length of file in this archive (compressed or uncompressed) -+16 2b ProDOS file permissions -+18 2b ProDOS file type -+1a 4b ProDOS aux type -+1e ?? 0000 -+20 1b ProDOS storage type (usually 02, 0d for dirs) -+21 ?? 00 -+22 ?? 0000 0000 -+26 4b Uncompressed file len -+2a 2b ProDOS date (create?) -+2c 2b ProDOS time -+2e 2b ProDOS date (mod?) -+30 2b ProDOS time -+32 2b Filename len -+34 2b ?? ac4a 2d02 for dir <- header checksum? -+36 FL Filename -+xx data start (dir has no data) -*/ - -/* - * =========================================================================== - * AcuEntry - * =========================================================================== - */ - -int AcuEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const -{ - NuError nerr; - ExpandBuffer expBuf; - char* dataBuf = NULL; - long len; - bool needAlloc = true; - int result = -1; - - ASSERT(fpArchive != NULL); - ASSERT(fpArchive->fFp != NULL); - - if (*ppText != NULL) - needAlloc = false; - - if (which != kDataThread) { - *pErrMsg = "No such fork"; - goto bail; - } - - len = (long) GetUncompressedLen(); - if (len == 0) { - if (needAlloc) { - *ppText = new char[1]; - **ppText = '\0'; - } - *pLength = 0; - result = IDOK; - goto bail; - } - - SET_PROGRESS_BEGIN(); - - errno = 0; - if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format(L"Unable to seek to offset %ld: %hs", - fOffset, strerror(errno)); - goto bail; - } - - if (GetSqueezed()) { - nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(), - &expBuf, false, 0); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); - goto bail; - } - - char* unsqBuf = NULL; - long unsqLen = 0; - expBuf.SeizeBuffer(&unsqBuf, &unsqLen); - LOGI("Unsqueezed %ld bytes to %d", - (unsigned long) GetCompressedLen(), unsqLen); - if (unsqLen == 0) { - // some bonehead squeezed a zero-length file - delete[] unsqBuf; - ASSERT(*ppText == NULL); - LOGI("Handling zero-length squeezed file!"); - if (needAlloc) { - *ppText = new char[1]; - **ppText = '\0'; - } - *pLength = 0; - } else { - if (needAlloc) { - /* just use the seized buffer */ - *ppText = unsqBuf; - *pLength = unsqLen; - } else { - if (*pLength < unsqLen) { - pErrMsg->Format(L"buf size %ld too short (%ld)", - *pLength, unsqLen); - delete[] unsqBuf; - goto bail; - } - - memcpy(*ppText, unsqBuf, unsqLen); - delete[] unsqBuf; - *pLength = unsqLen; - } - } - - } else { - if (needAlloc) { - dataBuf = new char[len]; - if (dataBuf == NULL) { - pErrMsg->Format(L"allocation of %ld bytes failed", len); - goto bail; - } - } else { - if (*pLength < (long) len) { - pErrMsg->Format(L"buf size %ld too short (%ld)", - *pLength, len); - goto bail; - } - dataBuf = *ppText; - } - if (fread(dataBuf, len, 1, fpArchive->fFp) != 1) { - pErrMsg->Format(L"File read failed: %hs", strerror(errno)); - goto bail; - } - - if (needAlloc) - *ppText = dataBuf; - *pLength = len; - } - - result = IDOK; - -bail: - if (result == IDOK) { - SET_PROGRESS_END(); - ASSERT(pErrMsg->IsEmpty()); - } else { - ASSERT(result == IDCANCEL || !pErrMsg->IsEmpty()); - if (needAlloc) { - delete[] dataBuf; - ASSERT(*ppText == NULL); - } - } - return result; -} - -int AcuEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const -{ - NuError nerr; - long len; - int result = -1; - - ASSERT(IDOK != -1 && IDCANCEL != -1); - if (which != kDataThread) { - *pErrMsg = L"No such fork"; - goto bail; - } - - len = (long) GetUncompressedLen(); - if (len == 0) { - LOGI("Empty fork"); - result = IDOK; - goto bail; - } - - errno = 0; - if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format(L"Unable to seek to offset %ld: %hs", - fOffset, strerror(errno)); - goto bail; - } - - SET_PROGRESS_BEGIN(); - - /* - * Generally speaking, anything in an ACU file is going to be small. - * - * To make life easy, we either unsqueeze the entire thing into a buffer - * and then write that, or we do a file-to-file copy of the specified - * number of bytes. - */ - if (GetSqueezed()) { - ExpandBuffer expBuf; - bool lastCR = false; - char* buf; - long uncLen; - - nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(), - &expBuf, false, 0); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); - goto bail; - } - - expBuf.SeizeBuffer(&buf, &uncLen); - LOGI("Unsqueezed %ld bytes to %d", len, uncLen); - - // some bonehead squeezed a zero-length file - if (uncLen == 0) { - ASSERT(buf == NULL); - LOGI("Handling zero-length squeezed file!"); - result = IDOK; - goto bail; - } - - int err = GenericEntry::WriteConvert(outfp, buf, uncLen, &conv, - &convHA, &lastCR); - if (err != 0) { - pErrMsg->Format(L"File write failed: %hs", strerror(err)); - delete[] buf; - goto bail; - } - - delete[] buf; - } else { - nerr = CopyData(outfp, conv, convHA, pErrMsg); - if (nerr != kNuErrNone) { - if (pErrMsg->IsEmpty()) { - pErrMsg->Format(L"Failed while copying data: %hs\n", - NuStrError(nerr)); - } - goto bail; - } - } - - result = IDOK; - -bail: - SET_PROGRESS_END(); - return result; -} - -NuError AcuEntry::CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, - CString* pMsg) const -{ - NuError nerr = kNuErrNone; - const int kChunkSize = 8192; - char buf[kChunkSize]; - bool lastCR = false; - long srcLen, dataRem; - - srcLen = (long) GetUncompressedLen(); - ASSERT(srcLen > 0); // empty files should've been caught earlier - - /* - * Loop until all data copied. - */ - dataRem = srcLen; - while (dataRem) { - int chunkLen; - - if (dataRem > kChunkSize) - chunkLen = kChunkSize; - else - chunkLen = dataRem; - - /* read a chunk from the source file */ - nerr = fpArchive->AcuRead(buf, chunkLen); - if (nerr != kNuErrNone) { - pMsg->Format(L"File read failed: %hs.", NuStrError(nerr)); - goto bail; - } - - /* write chunk to destination file */ - int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, - &convHA, &lastCR); - if (err != 0) { - pMsg->Format(L"File write failed: %hs.", strerror(err)); - nerr = kNuErrGeneric; - goto bail; - } - - dataRem -= chunkLen; - SET_PROGRESS_UPDATE(ComputePercent(srcLen - dataRem, srcLen)); - } - -bail: - return nerr; -} - - -NuError AcuEntry::TestEntry(CWnd* pMsgWnd) -{ - NuError nerr = kNuErrNone; - CString errMsg; - long len; - int result = -1; - - len = (long) GetUncompressedLen(); - if (len == 0) - goto bail; - - errno = 0; - if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - nerr = kNuErrGeneric; - errMsg.Format(L"Unable to seek to offset %ld: %hs\n", - fOffset, strerror(errno)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - if (GetSqueezed()) { - nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(), - NULL, false, 0); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unsqueeze failed: %hs.", NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - } else { - errno = 0; - if (fseek(fpArchive->fFp, fOffset + len, SEEK_SET) < 0) { - nerr = kNuErrGeneric; - errMsg.Format(L"Unable to seek to offset %ld (file truncated?): %hs\n", - fOffset, strerror(errno)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - } - - if (SET_PROGRESS_UPDATE(100) == IDCANCEL) - nerr = kNuErrAborted; - -bail: - return nerr; -} - - -/* - * =========================================================================== - * AcuArchive - * =========================================================================== - */ - -/*static*/ CString AcuArchive::AppInit(void) -{ - return L""; -} - -GenericArchive::OpenResult AcuArchive::Open(const WCHAR* filename, - bool readOnly, CString* pErrMsg) -{ - CString errMsg; - - //fIsReadOnly = true; // ignore "readOnly" - - errno = 0; - fFp = _wfopen(filename, L"rb"); - if (fFp == NULL) { - errMsg.Format(L"Unable to open %ls: %hs.", filename, strerror(errno)); - goto bail; - } - - { - CWaitCursor waitc; - int result; - - result = LoadContents(); - if (result < 0) { - errMsg.Format(L"The file is not an ACU archive."); - goto bail; - } else if (result > 0) { - errMsg.Format(L"Failed while reading data from ACU archive."); - goto bail; - } - } - - SetPathName(filename); - -bail: - *pErrMsg = errMsg; - if (!errMsg.IsEmpty()) - return kResultFailure; - else - return kResultSuccess; -} - -CString AcuArchive::New(const WCHAR* /*filename*/, const void* /*options*/) -{ - return L"Sorry, AppleLink Compression Utility files can't be created."; -} - - -long AcuArchive::GetCapability(Capability cap) -{ - switch (cap) { - case kCapCanTest: - return true; - break; - case kCapCanRenameFullPath: - return true; - break; - case kCapCanRecompress: - return true; - break; - case kCapCanEditComment: - return false; - break; - case kCapCanAddDisk: - return false; - break; - case kCapCanConvEOLOnAdd: - return false; - break; - case kCapCanCreateSubdir: - return false; - break; - case kCapCanRenameVolume: - return false; - break; - default: - ASSERT(false); - return -1; - break; - } -} - -int AcuArchive::LoadContents(void) -{ - NuError nerr; - int numEntries; - - ASSERT(fFp != NULL); - rewind(fFp); - - /* - * Read the master header. In an ACU file this holds the number of - * files and a bunch of stuff that doesn't seem to change. - */ - if (ReadMasterHeader(&numEntries) != 0) - return -1; - - while (numEntries) { - AcuFileEntry fileEntry; - - nerr = ReadFileHeader(&fileEntry); - if (nerr != kNuErrNone) - return 1; - - if (CreateEntry(&fileEntry) != 0) - return 1; - - /* if file isn't empty, seek past it */ - if (fileEntry.dataStorageLen) { - nerr = AcuSeek(fileEntry.dataStorageLen); - if (nerr != kNuErrNone) - return 1; - } - - numEntries--; - } - - return 0; -} - -CString AcuArchive::Reload(void) -{ - fReloadFlag = true; // tell everybody that cached data is invalid - - DeleteEntries(); - if (LoadContents() != 0) { - return L"Reload failed."; - } - - return ""; -} - -int AcuArchive::ReadMasterHeader(int* pNumEntries) -{ - AcuMasterHeader header; - unsigned char buf[kAcuMasterHeaderLen]; - NuError nerr; - - nerr = AcuRead(buf, kAcuMasterHeaderLen); - if (nerr != kNuErrNone) - return -1; - - header.fileCount = buf[0x00] | buf[0x01] << 8; - header.unknown1 = buf[0x02] | buf[0x03] << 8; - memcpy(header.fZink, &buf[0x04], 5); - header.fZink[5] = '\0'; - memcpy(header.unknown2, &buf[0x09], 11); - - if (header.fileCount == 0 || - header.unknown1 != 1 || - strcmp((char*) header.fZink, "fZink") != 0) - { - LOGW("Not an ACU archive"); - return -1; - } - - LOGD("Looks like an ACU archive with %d entries", header.fileCount); - - *pNumEntries = header.fileCount; - return 0; -} - -NuError AcuArchive::ReadFileHeader(AcuFileEntry* pEntry) -{ - NuError err = kNuErrNone; - unsigned char buf[kAcuEntryHeaderLen]; - - ASSERT(pEntry != NULL); - - err = AcuRead(buf, kAcuEntryHeaderLen); - if (err != kNuErrNone) - goto bail; - - // unknown at 00 - pEntry->compressionType = buf[0x01]; - // unknown at 02-03 - pEntry->dataChecksum = buf[0x04] | buf[0x05] << 8; // not sure - // unknown at 06-09 - pEntry->blockCount = buf[0x0a] | buf[0x0b] << 8; - // unknown at 0c-11 - pEntry->dataStorageLen = buf[0x12] | buf [0x13] << 8 | buf[0x14] << 16 | - buf[0x15] << 24; - pEntry->access = buf[0x16] | buf[0x17] << 8; - pEntry->fileType = buf[0x18] | buf[0x19] << 8; - pEntry->auxType = buf[0x1a] | buf[0x1b] << 8; - // unknown at 1e-1f - pEntry->storageType = buf[0x20]; - // unknown at 21-25 - pEntry->dataEof = buf[0x26] | buf[0x27] << 8 | buf[0x28] << 16 | - buf[0x29] << 24; - pEntry->prodosModDate = buf[0x2a] | buf[0x2b] << 8; - pEntry->prodosModTime = buf[0x2c] | buf[0x2d] << 8; - AcuConvertDateTime(pEntry->prodosModDate, pEntry->prodosModTime, - &pEntry->modWhen); - pEntry->prodosCreateDate = buf[0x2e] | buf[0x2f] << 8; - pEntry->prodosCreateTime = buf[0x30] | buf[0x31] << 8; - AcuConvertDateTime(pEntry->prodosCreateDate, pEntry->prodosCreateTime, - &pEntry->createWhen); - pEntry->fileNameLen = buf[0x32] | buf[0x33] << 8; - pEntry->headerChecksum = buf[0x34] | buf[0x35] << 8; // not sure - - /* read the filename */ - if (pEntry->fileNameLen > kAcuMaxFileName) { - LOGI("GLITCH: filename is too long (%d bytes)", - pEntry->fileNameLen); - err = kNuErrGeneric; - goto bail; - } - if (!pEntry->fileNameLen) { - LOGI("GLITCH: filename missing"); - err = kNuErrGeneric; - goto bail; - } - - /* don't know if this is possible or not */ - if (pEntry->storageType == 5) { - LOGI("HEY: EXTENDED FILE"); - } - - err = AcuRead(pEntry->fileName, pEntry->fileNameLen); - if (err != kNuErrNone) - goto bail; - pEntry->fileName[pEntry->fileNameLen] = '\0'; - - //DumpFileHeader(pEntry); - -bail: - return err; -} - -void AcuArchive::DumpFileHeader(const AcuFileEntry* pEntry) -{ - time_t createWhen, modWhen; - CString createStr, modStr; - - createWhen = NufxArchive::DateTimeToSeconds(&pEntry->createWhen); - modWhen = NufxArchive::DateTimeToSeconds(&pEntry->modWhen); - FormatDate(createWhen, &createStr); - FormatDate(modWhen, &modStr); - - LOGI(" Header for file '%hs':", pEntry->fileName); - LOGI(" dataStorageLen=%d eof=%d blockCount=%d checksum=0x%04x", - pEntry->dataStorageLen, pEntry->dataEof, pEntry->blockCount, - pEntry->dataChecksum); - LOGI(" fileType=0x%02x auxType=0x%04x storageType=0x%02x access=0x%04x", - pEntry->fileType, pEntry->auxType, pEntry->storageType, pEntry->access); - LOGI(" created %ls, modified %ls", - (LPCWSTR) createStr, (LPCWSTR) modStr); - LOGI(" fileNameLen=%d headerChecksum=0x%04x", - pEntry->fileNameLen, pEntry->headerChecksum); -} - -int AcuArchive::CreateEntry(const AcuFileEntry* pEntry) -{ - const int kAcuFssep = '/'; - NuError err = kNuErrNone; - AcuEntry* pNewEntry; - - /* - * Create the new entry. - */ - pNewEntry = new AcuEntry(this); - pNewEntry->SetPathNameMOR(pEntry->fileName); - pNewEntry->SetFssep(kAcuFssep); - pNewEntry->SetFileType(pEntry->fileType); - pNewEntry->SetAuxType(pEntry->auxType); - pNewEntry->SetAccess(pEntry->access); - pNewEntry->SetCreateWhen(NufxArchive::DateTimeToSeconds(&pEntry->createWhen)); - pNewEntry->SetModWhen(NufxArchive::DateTimeToSeconds(&pEntry->modWhen)); - - /* always ProDOS? */ - pNewEntry->SetSourceFS(DiskImg::kFormatProDOS); - - pNewEntry->SetHasDataFork(true); - pNewEntry->SetHasRsrcFork(false); // ? - if (IsDir(pEntry)) { - pNewEntry->SetRecordKind(GenericEntry::kRecordKindDirectory); - } else { - pNewEntry->SetRecordKind(GenericEntry::kRecordKindFile); - } - - pNewEntry->SetCompressedLen(pEntry->dataStorageLen); - pNewEntry->SetDataForkLen(pEntry->dataEof); - - if (pEntry->compressionType == kAcuCompNone) { - pNewEntry->SetFormatStr(L"Uncompr"); - } else if (pEntry->compressionType == kAcuCompSqueeze) { - pNewEntry->SetFormatStr(L"Squeeze"); - pNewEntry->SetSqueezed(true); - } else { - pNewEntry->SetFormatStr(L"(unknown)"); - pNewEntry->SetSqueezed(false); - } - - pNewEntry->SetOffset(ftell(fFp)); - - AddEntry(pNewEntry); - - return err; -} - - -/* - * =========================================================================== - * ACU functions - * =========================================================================== - */ - -bool AcuArchive::IsDir(const AcuFileEntry* pEntry) -{ - return (pEntry->storageType == 0x0d); -} - -NuError AcuArchive::AcuRead(void* buf, size_t nbyte) -{ - size_t result; - - ASSERT(buf != NULL); - ASSERT(nbyte > 0); - ASSERT(fFp != NULL); - - errno = 0; - result = fread(buf, 1, nbyte, fFp); - if (result != nbyte) - return errno ? (NuError)errno : kNuErrFileRead; - return kNuErrNone; -} - -NuError AcuArchive::AcuSeek(long offset) -{ - ASSERT(fFp != NULL); - ASSERT(offset > 0); - - /*DBUG(("--- seeking forward %ld bytes\n", offset));*/ - - if (fseek(fFp, offset, SEEK_CUR) < 0) - return kNuErrFileSeek; - - return kNuErrNone; -} - - -void AcuArchive::AcuConvertDateTime(uint16_t prodosDate, - uint16_t prodosTime, NuDateTime* pWhen) -{ - pWhen->second = 0; - pWhen->minute = prodosTime & 0x3f; - pWhen->hour = (prodosTime >> 8) & 0x1f; - pWhen->day = (prodosDate & 0x1f) -1; - pWhen->month = ((prodosDate >> 5) & 0x0f) -1; - pWhen->year = (prodosDate >> 9) & 0x7f; - if (pWhen->year < 40) - pWhen->year += 100; /* P8 uses 0-39 for 2000-2039 */ - pWhen->extra = 0; - pWhen->weekDay = 0; -} - - -/* - * =========================================================================== - * AcuArchive -- test files - * =========================================================================== - */ - -bool AcuArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - // TODO: this is essentially copy & paste from NufxArchive::TestSelection(). - // We can move the implementation to GenericArchive and just have an - // archive-specific TestEntry() function. - NuError nerr; - AcuEntry* pEntry; - CString errMsg; - bool retVal = false; - - ASSERT(fFp != NULL); - - LOGI("Testing %d entries", pSelSet->GetNumEntries()); - - SelectionEntry* pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = (AcuEntry*) pSelEntry->GetEntry(); - - LOGD(" Testing '%ls' (offset=%ld)", (LPCWSTR) pEntry->GetDisplayName(), - pEntry->GetOffset()); - - SET_PROGRESS_UPDATE2(0, pEntry->GetDisplayName(), NULL); - - nerr = pEntry->TestEntry(pMsgWnd); - if (nerr != kNuErrNone) { - if (nerr == kNuErrAborted) { - CString title; - CheckedLoadString(&title, IDS_MB_APP_NAME); - errMsg = L"Cancelled."; - pMsgWnd->MessageBox(errMsg, title, MB_OK); - } else { - errMsg.Format(L"Failed while testing '%ls': %hs.", - (LPCWSTR) pEntry->GetPathNameUNI(), NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - } - goto bail; - } - - pSelEntry = pSelSet->IterNext(); - } - - /* show success message */ - errMsg.Format(L"Tested %d file%ls, no errors found.", - pSelSet->GetNumEntries(), - pSelSet->GetNumEntries() == 1 ? L"" : L"s"); - pMsgWnd->MessageBox(errMsg); - retVal = true; - -bail: - SET_PROGRESS_END(); - return retVal; -} diff --git a/ciderpress/app/ACUArchive.h b/ciderpress/app/ACUArchive.h deleted file mode 100644 index 26e3142..0000000 --- a/ciderpress/app/ACUArchive.h +++ /dev/null @@ -1,297 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * AppleLink Compression Utility archive support (read-only). - */ -#ifndef APP_ACUARCHIVE_H -#define APP_ACUARCHIVE_H - -#include "GenericArchive.h" - - -class AcuArchive; - -/* - * One file in an ACU archive. - */ -class AcuEntry : public GenericEntry { -public: - AcuEntry(AcuArchive* pArchive) : - fpArchive(pArchive), fIsSqueezed(false), fOffset(-1) - {} - virtual ~AcuEntry(void) {} - - virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const override; - virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const override; - - // doesn't matter - virtual long GetSelectionSerial(void) const override { return -1; } - - virtual bool GetFeatureFlag(Feature feature) const override { - if (feature == kFeatureHasFullAccess || - feature == kFeatureCanChangeType || - feature == kFeatureHasInvisibleFlag) - { - return true; - } else { - return false; - } - } - - /* - * Test this entry by extracting it. - * - * If the file isn't compressed, just make sure the file is big enough. If - * it's squeezed, invoke the un-squeeze function with a "NULL" buffer pointer. - */ - NuError TestEntry(CWnd* pMsgWnd); - - bool GetSqueezed(void) const { return fIsSqueezed; } - void SetSqueezed(bool val) { fIsSqueezed = val; } - long GetOffset(void) const { return fOffset; } - void SetOffset(long offset) { fOffset = offset; } - -private: - /* - * Copy data from the seeked archive to outfp, possibly converting EOL along - * the way. - */ - NuError CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, - CString* pMsg) const; - - AcuArchive* fpArchive; // holds FILE* for archive - bool fIsSqueezed; - long fOffset; -}; - - -/* - * ACU archive definition. - */ -class AcuArchive : public GenericArchive { -public: - AcuArchive(void) : fFp(NULL) {} - virtual ~AcuArchive(void) { (void) Close(); } - - /* - * Perform one-time initialization. There really isn't any for us. - * - * Returns an error string on failure. - */ - static CString AppInit(void); - - /* - * Open an ACU archive. - * - * Returns an error string on failure, or "" on success. - */ - virtual OpenResult Open(const WCHAR* filename, bool readOnly, - CString* pErrMsg) override; - - /* - * Finish instantiating an AcuArchive object by creating a new archive. - * - * This isn't implemented, and will always return an error. - */ - virtual CString New(const WCHAR* filename, const void* options) override; - - virtual CString Flush(void) override { return L""; } - - virtual CString Reload(void) override; - virtual bool IsReadOnly(void) const override { return true; }; - virtual bool IsModified(void) const override { return false; } - virtual CString GetDescription() const override { return L"AppleLink ACU"; } - virtual bool BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual CString TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const override - { ASSERT(false); return L"!"; } - virtual CString TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, char newFssep) const override - { ASSERT(false); return L"!"; } - virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) override - { ASSERT(false); return false; } - virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) override - { ASSERT(false); return kXferFailed; } - virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry, - CString* pStr) override - { ASSERT(false); return false; } - virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry, - const CString& str) override - { ASSERT(false); return false; } - virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) override - { ASSERT(false); return false; } - virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) override - { ASSERT(false); return false; } - virtual void PreferencesChanged(void) override {} - virtual long GetCapability(Capability cap) override; - - friend class AcuEntry; - -private: - virtual CString Close(void) { - if (fFp != NULL) { - fclose(fFp); - fFp = NULL; - } - return L""; - } - virtual void XferPrepare(const XferFileOptions* pXferOpts) override - { ASSERT(false); } - virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override - { ASSERT(false); return L"!"; } - virtual void XferAbort(CWnd* pMsgWnd) override - { ASSERT(false); } - virtual void XferFinish(CWnd* pMsgWnd) override - { ASSERT(false); } - - virtual ArchiveKind GetArchiveKind(void) override { return kArchiveACU; } - virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) override - { ASSERT(false); return kNuErrGeneric; } - - enum { - kAcuMaxFileName = 256, // nice big number - - kAcuMasterHeaderLen = 20, - kAcuEntryHeaderLen = 54, - }; - - /* - * The header at the front of an ACU archive. - */ - struct AcuMasterHeader { - uint16_t fileCount; - uint16_t unknown1; // 0x01 00 -- might be "version 1?" - uint8_t fZink[6]; // "fZink", low ASCII - uint8_t unknown2[11]; // 0x01 36 00 00 00 00 00 00 00 00 dd - }; - - /* - * An entry in an ACU archive. Each archive is essentially a stream - * of files; only the "filesToFollow" value gives any indication that - * something else follows this entry. - * - * We read this from the archive and then unpack the interesting parts - * into GenericEntry fields in an AcuEntry. - */ - //struct AcuFileEntry; - //friend struct AcuFileEntry; - struct AcuFileEntry { - uint8_t compressionType; - uint16_t dataChecksum; // ?? - uint16_t blockCount; // total blocks req'd to hold file - uint32_t dataStorageLen; // length of data within archive - uint16_t access; - uint16_t fileType; - uint32_t auxType; - uint8_t storageType; - uint32_t dataEof; - uint16_t prodosModDate; - uint16_t prodosModTime; - NuDateTime modWhen; // computed from previous two fields - uint16_t prodosCreateDate; - uint16_t prodosCreateTime; - NuDateTime createWhen; // computed from previous two fields - uint16_t fileNameLen; - uint16_t headerChecksum; // ?? - char fileName[kAcuMaxFileName+1]; // ASCII - - // possibilities for mystery fields: - // - OS type (note ProDOS is $00) - // - forked file support - }; - - /* known compression types */ - enum CompressionType { - kAcuCompNone = 0, - kAcuCompSqueeze = 3, - }; - - /* - * Load the contents of the archive. - * - * Returns 0 on success, < 0 if this is not an ACU archive, or > 0 if - * this appears to be an ACU archive but it's damaged. - */ - int LoadContents(void); - - /* - * Read the archive header. The archive file is left seeked to the point - * at the end of the header. - * - * Returns 0 on success, -1 on failure. Sets *pNumEntries to the number of - * entries in the archive. - */ - int ReadMasterHeader(int* pNumEntries); - - /* - * Read and decode an AppleLink Compression Utility file entry header. - * This leaves the file seeked to the point immediately past the filename. - */ - NuError ReadFileHeader(AcuFileEntry* pEntry); - - /* - * Dump the contents of an AcuFileEntry struct. - */ - void DumpFileHeader(const AcuFileEntry* pEntry); - - /* - * Given an AcuFileEntry structure, add an appropriate entry to the list. - */ - int CreateEntry(const AcuFileEntry* pEntry); - - /* - * Test if this entry is a directory. - */ - bool IsDir(const AcuFileEntry* pEntry); - - /* - * Wrapper for fread(). Note the arguments resemble read(2) rather - * than fread(3S). - */ - NuError AcuRead(void* buf, size_t nbyte); - - /* - * Seek within an archive. Because we need to handle streaming archives, - * and don't need to special-case anything, we only allow relative - * forward seeks. - */ - NuError AcuSeek(long offset); - - /* - * Convert from ProDOS compact date format to the expanded DateTime format. - */ - void AcuConvertDateTime(uint16_t prodosDate, - uint16_t prodosTime, NuDateTime* pWhen); - - FILE* fFp; - //bool fIsReadOnly; -}; - -#endif /*APP_ACUARCHIVE_H*/ diff --git a/ciderpress/app/AboutDialog.cpp b/ciderpress/app/AboutDialog.cpp deleted file mode 100644 index 0fd806f..0000000 --- a/ciderpress/app/AboutDialog.cpp +++ /dev/null @@ -1,192 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of our About box. - */ -#include "stdafx.h" -#include "AboutDialog.h" -#include "EnterRegDialog.h" -#include "MyApp.h" -#include "resource.h" -#include "../nufxlib/NufxLib.h" -#include "../diskimg/DiskImg.h" -#include "../zlib/zlib.h" - - -BEGIN_MESSAGE_MAP(AboutDialog, CDialog) - ON_BN_CLICKED(IDC_ABOUT_CREDITS, OnAboutCredits) - //ON_BN_CLICKED(IDC_ABOUT_ENTER_REG, OnEnterReg) -END_MESSAGE_MAP() - -static const WCHAR kVersionExtra[] = -#ifdef _DEBUG - L" _DEBUG" -#else - L"" -#endif - ; - -BOOL AboutDialog::OnInitDialog(void) -{ - NuError nerr; - int32_t major, minor, bug; - CString newVersion, tmpStr; - CStatic* pStatic; - //CString versionFmt; - - /* CiderPress version string */ - pStatic = (CStatic*) GetDlgItem(IDC_CIDERPRESS_VERS_TEXT); - ASSERT(pStatic != NULL); - pStatic->GetWindowText(tmpStr); - newVersion.Format(tmpStr, - kAppMajorVersion, kAppMinorVersion, kAppBugVersion, - kAppDevString, kVersionExtra); - pStatic->SetWindowText(newVersion); - - /* grab the static text control with the NufxLib version info */ - pStatic = (CStatic*) GetDlgItem(IDC_NUFXLIB_VERS_TEXT); - ASSERT(pStatic != NULL); - nerr = NuGetVersion(&major, &minor, &bug, NULL, NULL); - ASSERT(nerr == kNuErrNone); - - pStatic->GetWindowText(tmpStr); - newVersion.Format(tmpStr, major, minor, bug); - pStatic->SetWindowText(newVersion); - - /* grab the static text control with the DiskImg version info */ - pStatic = (CStatic*) GetDlgItem(IDC_DISKIMG_VERS_TEXT); - ASSERT(pStatic != NULL); - DiskImgLib::Global::GetVersion(&major, &minor, &bug); - - pStatic->GetWindowText(tmpStr); - newVersion.Format(tmpStr, major, minor, bug); - pStatic->SetWindowText(newVersion); - - /* set the zlib version */ - pStatic = (CStatic*) GetDlgItem(IDC_ZLIB_VERS_TEXT); - ASSERT(pStatic != NULL); - pStatic->GetWindowText(tmpStr); - CString zlibVersionStr(zlibVersion()); - newVersion.Format(tmpStr, zlibVersionStr); - pStatic->SetWindowText(newVersion); - - /* and, finally, the ASPI version */ - pStatic = (CStatic*) GetDlgItem(IDC_ASPI_VERS_TEXT); - ASSERT(pStatic != NULL); - if (DiskImgLib::Global::GetHasASPI()) { - CString versionStr; - DWORD version = DiskImgLib::Global::GetASPIVersion(); - versionStr.Format(L"%d.%d.%d.%d", - version & 0x0ff, - (version >> 8) & 0xff, - (version >> 16) & 0xff, - (version >> 24) & 0xff); - pStatic->GetWindowText(tmpStr); - newVersion.Format(tmpStr, versionStr); - } else { - CheckedLoadString(&newVersion, IDS_ASPI_NOT_LOADED); - } - pStatic->SetWindowText(newVersion); - - //ShowRegistrationInfo(); - { - CWnd* pWnd = GetDlgItem(IDC_ABOUT_ENTER_REG); - if (pWnd != NULL) { - pWnd->EnableWindow(FALSE); - pWnd->ShowWindow(FALSE); - } - } - - return CDialog::OnInitDialog(); -} - -#if 0 -/* - * Set the appropriate fields in the dialog box. - * - * This is called during initialization and after new registration data is - * entered successfully. - */ -void AboutDialog::ShowRegistrationInfo(void) -{ - /* - * Pull out the registration info. We shouldn't need to do much in the - * way of validation, since it should have been validated either before - * the program finished initializing or before we wrote the values into - * the registry. It's always possible that somebody went and messed with - * the registry while we were running -- perhaps a different instance of - * CiderPress -- but that should be rare enough that we don't have to - * worry about the occasional ugliness. - */ - const int kDay = 24 * 60 * 60; - CString user, company, reg, versions, expire; - CWnd* pUserWnd; - CWnd* pCompanyWnd; - //CWnd* pExpireWnd; - - pUserWnd = GetDlgItem(IDC_REG_USER_NAME); - ASSERT(pUserWnd != NULL); - pCompanyWnd = GetDlgItem(IDC_REG_COMPANY_NAME); - ASSERT(pCompanyWnd != NULL); - //pExpireWnd = GetDlgItem(IDC_REG_EXPIRES); - //ASSERT(pExpireWnd != NULL); - - if (gMyApp.fRegistry.GetRegistration(&user, &company, ®, &versions, - &expire) == 0) - { - if (reg.IsEmpty()) { - /* not registered, show blank stuff */ - CString unreg; - unreg.LoadString(IDS_ABOUT_UNREGISTERED); - pUserWnd->SetWindowText(unreg); - pCompanyWnd->SetWindowText(""); - - /* show expire date */ - time_t expireWhen; - expireWhen = atol(expire); - if (expireWhen > 0) { - CString expireStr; - time_t now = time(NULL); - expireStr.Format(IDS_REG_EVAL_REM, - ((expireWhen - now) + kDay-1) / kDay); - /* leave pUserWnd and pCompanyWnd set to defaults */ - pCompanyWnd->SetWindowText(expireStr); - } else { - pCompanyWnd->SetWindowText(_T("Has already expired!")); - } - } else { - /* show registration info */ - pUserWnd->SetWindowText(user); - pCompanyWnd->SetWindowText(company); - //pExpireWnd->SetWindowText(""); - - /* remove "Enter Registration" button */ - CWnd* pWnd = GetDlgItem(IDC_ABOUT_ENTER_REG); - if (pWnd != NULL) { - pWnd->EnableWindow(FALSE); - } - } - } -} -#endif - -void AboutDialog::OnAboutCredits(void) -{ - MyApp::HandleHelp(this, HELP_TOPIC_CREDITS); -} - -#if 0 -/* - * User hit "enter registration" button. Bring up the appropriate dialog. - */ -void -AboutDialog::OnEnterReg(void) -{ - if (EnterRegDialog::GetRegInfo(this) == 0) { - ShowRegistrationInfo(); - } -} -#endif diff --git a/ciderpress/app/AboutDialog.h b/ciderpress/app/AboutDialog.h deleted file mode 100644 index 563f720..0000000 --- a/ciderpress/app/AboutDialog.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Class definition for About dialog. - */ -#ifndef APP_ABOUTDIALOG_H -#define APP_ABOUTDIALOG_H - -#include "resource.h" - -/* - * A simple dialog with an overridden initialization so we can tweak the - * controls slightly. - */ -class AboutDialog : public CDialog { -public: - AboutDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_ABOUTDLG, pParentWnd) - {} - -protected: - /* - * Update the static strings with DLL version numbers. - */ - virtual BOOL OnInitDialog(void) override; - - /* - * User hit the "Credits" button. - */ - afx_msg void OnAboutCredits(void); - - //afx_msg void OnEnterReg(void); - //void ShowRegistrationInfo(void); - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_ABOUTDIALOG_H*/ diff --git a/ciderpress/app/ActionProgressDialog.cpp b/ciderpress/app/ActionProgressDialog.cpp deleted file mode 100644 index be02fde..0000000 --- a/ciderpress/app/ActionProgressDialog.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ - /* - * Support for ActionProgressDialog class. - */ -#include "stdafx.h" -#include "ActionProgressDialog.h" -#include "AddFilesDialog.h" -#include "Main.h" - -BEGIN_MESSAGE_MAP(ActionProgressDialog, ProgressCancelDialog) - //ON_MESSAGE(WMU_START, OnStart) -END_MESSAGE_MAP() - -BOOL ActionProgressDialog::OnInitDialog(void) -{ - CDialog::OnInitDialog(); - - LOGI("Action is %d", fAction); - - CenterWindow(AfxGetMainWnd()); - - CWnd* pWnd; - - // clear the filename fields - pWnd = GetDlgItem(IDC_PROG_ARC_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(_T("-")); - pWnd = GetDlgItem(IDC_PROG_FILE_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(_T("-")); - - pWnd->SetFocus(); // get the focus off the Cancel button - - if (fAction == kActionExtract) { - /* defaults are correct */ - } else if (fAction == kActionRecompress) { - CString tmpStr; - pWnd = GetDlgItem(IDC_PROG_VERB); - ASSERT(pWnd != NULL); - CheckedLoadString(&tmpStr, IDS_NOW_EXPANDING); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_PROG_TOFROM); - ASSERT(pWnd != NULL); - CheckedLoadString(&tmpStr, IDS_NOW_COMPRESSING); - pWnd->SetWindowText(tmpStr); - } else if (fAction == kActionAdd || fAction == kActionAddDisk || - fAction == kActionConvFile || fAction == kActionConvDisk) - { - CString tmpStr; - pWnd = GetDlgItem(IDC_PROG_VERB); - ASSERT(pWnd != NULL); - CheckedLoadString(&tmpStr, IDS_NOW_ADDING); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_PROG_TOFROM); - ASSERT(pWnd != NULL); - CheckedLoadString(&tmpStr, IDS_ADDING_AS); - pWnd->SetWindowText(tmpStr); - } else if (fAction == kActionDelete) { - CString tmpStr; - pWnd = GetDlgItem(IDC_PROG_VERB); - ASSERT(pWnd != NULL); - CheckedLoadString(&tmpStr, IDS_NOW_DELETING); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_PROG_TOFROM); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_PROG_FILE_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(_T("")); - } else if (fAction == kActionTest) { - CString tmpStr; - pWnd = GetDlgItem(IDC_PROG_VERB); - ASSERT(pWnd != NULL); - CheckedLoadString(&tmpStr, IDS_NOW_TESTING); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_PROG_TOFROM); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_PROG_FILE_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(_T("")); - } else { - ASSERT(false); - } - - return FALSE; -} - -void ActionProgressDialog::SetArcName(const WCHAR* str) -{ - CString oldStr; - - CWnd* pWnd = GetDlgItem(IDC_PROG_ARC_NAME); - ASSERT(pWnd != NULL); - pWnd->GetWindowText(oldStr); - if (oldStr != str) - pWnd->SetWindowText(str); -} - -const CString ActionProgressDialog::GetFileName(void) -{ - CString str; - - CWnd* pWnd = GetDlgItem(IDC_PROG_FILE_NAME); - ASSERT(pWnd != NULL); - pWnd->GetWindowText(str); - - return str; -} - -void ActionProgressDialog::SetFileName(const WCHAR* str) -{ - CString oldStr; - - CWnd* pWnd = GetDlgItem(IDC_PROG_FILE_NAME); - ASSERT(pWnd != NULL); - pWnd->GetWindowText(oldStr); - if (oldStr != str) - pWnd->SetWindowText(str); -} - -int ActionProgressDialog::SetProgress(int perc) -{ - ASSERT(perc >= 0 && perc <= 100); - MainWindow* pMainWin = (MainWindow*)::AfxGetMainWnd(); - - /* solicit input */ - pMainWin->PeekAndPump(); - - return ProgressCancelDialog::SetProgress(perc * - (kProgressResolution/100)); -} diff --git a/ciderpress/app/ActionProgressDialog.h b/ciderpress/app/ActionProgressDialog.h deleted file mode 100644 index 028b9e9..0000000 --- a/ciderpress/app/ActionProgressDialog.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Show the progress of an action like "add" or "extract". - */ -#ifndef APP_ACTIONPROGRESSDIALOG_H -#define APP_ACTIONPROGRESSDIALOG_H - -#include "resource.h" - -/* - * Modeless dialog; must be allocated on the heap. - */ -class ActionProgressDialog : public ProgressCancelDialog { -public: - typedef enum { - kActionUnknown = 0, - kActionAdd, - kActionAddDisk, - kActionExtract, - kActionDelete, - kActionTest, - kActionRecompress, - kActionConvDisk, - kActionConvFile, - } Action; - - ActionProgressDialog(void) { - fAction = kActionUnknown; - //fpSelSet = NULL; - //fpOptionsDlg = NULL; - fCancel = false; - //fResult = 0; - } - virtual ~ActionProgressDialog(void) {} - - BOOL Create(Action action, CWnd* pParentWnd = NULL) { - fAction = action; - pParentWnd->EnableWindow(FALSE); - return ProgressCancelDialog::Create(&fCancel, IDD_ACTION_PROGRESS, - IDC_PROG_PROGRESS, pParentWnd); - } - void Cleanup(CWnd* pParentWnd) { - pParentWnd->EnableWindow(TRUE); - DestroyWindow(); - } - - /* - * Set the name of the file as it appears in the archive. - */ - void SetArcName(const WCHAR* str); - - /* - * Set the name of the file as it appears under Windows. - */ - void SetFileName(const WCHAR* str); - - /* - * Get the name of the file as it appears under Windows. - */ - const CString GetFileName(void); - - /* - * Update the progress meter. - * - * We take a percentage, but the underlying control uses 1000ths. - */ - int SetProgress(int perc); - -private: - /* - * Initialize the static text controls to say something reasonable. - */ - virtual BOOL OnInitDialog(void) override; - - Action fAction; - bool fCancel; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_ACTIONPROGRESSDIALOG_H*/ diff --git a/ciderpress/app/Actions.cpp b/ciderpress/app/Actions.cpp deleted file mode 100644 index 1bd1a0b..0000000 --- a/ciderpress/app/Actions.cpp +++ /dev/null @@ -1,2377 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * File actions. These are actually part of MainWindow, but for readability - * these are split into their own file. - */ -#include "stdafx.h" -#include "Main.h" -#include "ViewFilesDialog.h" -#include "ChooseDirDialog.h" -#include "AddFilesDialog.h" -#include "CreateSubdirDialog.h" -#include "ExtractOptionsDialog.h" -#include "UseSelectionDialog.h" -#include "RecompressOptionsDialog.h" -#include "ConvDiskOptionsDialog.h" -#include "ConvFileOptionsDialog.h" -#include "EditCommentDialog.h" -#include "EditPropsDialog.h" -#include "RenameVolumeDialog.h" -#include "ConfirmOverwriteDialog.h" -#include "ImageFormatDialog.h" -#include "FileNameConv.h" -#include "GenericArchive.h" -#include "NufxArchive.h" -#include "DiskArchive.h" -#include "ChooseAddTargetDialog.h" -#include "CassetteDialog.h" -#include "BasicImport.h" -#include "../diskimg/TwoImg.h" -#include - - -/* - * ========================================================================== - * View - * ========================================================================== - */ - -void MainWindow::OnActionsView(void) -{ - HandleView(); -} -void MainWindow::OnUpdateActionsView(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && - fpContentList->GetSelectedCount() > 0); -} - -void MainWindow::HandleView(void) -{ - ASSERT(fpContentList != NULL); - - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread | GenericEntry::kAllowDamaged | - GenericEntry::kAllowDirectory | GenericEntry::kAllowVolumeDir; - selSet.CreateFromSelection(fpContentList, threadMask); - selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - MessageBox(L"Nothing viewable found.", - L"No match", MB_OK | MB_ICONEXCLAMATION); - return; - } - - ViewFilesDialog vfd(this); - vfd.SetSelectionSet(&selSet); - vfd.SetTextTypeFace(fPreferences.GetPrefString(kPrViewTextTypeFace)); - vfd.SetTextPointSize(fPreferences.GetPrefLong(kPrViewTextPointSize)); - vfd.SetNoWrapText(fPreferences.GetPrefBool(kPrNoWrapText)); - vfd.DoModal(); - - // remember which font they used (sticky pref, not in registry) - fPreferences.SetPrefString(kPrViewTextTypeFace, vfd.GetTextTypeFace()); - fPreferences.SetPrefLong(kPrViewTextPointSize, vfd.GetTextPointSize()); - LOGI("Preferences: saving view font %d-point '%ls'", - fPreferences.GetPrefLong(kPrViewTextPointSize), - fPreferences.GetPrefString(kPrViewTextTypeFace)); -} - - -/* - * ========================================================================== - * Open as disk image - * ========================================================================== - */ - -void MainWindow::OnActionsOpenAsDisk(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpContentList->GetSelectedCount() == 1); - - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry->GetHasDiskImage()) - TmpExtractAndOpen(pEntry, GenericEntry::kDiskImageThread, kModeDiskImage); - else - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage); -} -void MainWindow::OnUpdateActionsOpenAsDisk(CCmdUI* pCmdUI) -{ - const int kMinLen = 512 * 7; - bool allow = false; - - if (fpContentList != NULL && fpContentList->GetSelectedCount() == 1) { - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry != NULL) { - if ((pEntry->GetHasDataFork() || pEntry->GetHasDiskImage()) && - pEntry->GetUncompressedLen() > kMinLen) - { - allow = true; - } - } - } - pCmdUI->Enable(allow); -} - - -/* - * ========================================================================== - * Add Files - * ========================================================================== - */ - -void MainWindow::OnActionsAddFiles(void) -{ - LOGI("Add files!"); - AddFilesDialog addFiles(this); - DiskImgLib::A2File* pTargetSubdir = NULL; - - /* - * Special handling for adding files to disk images. - */ - if (fpOpenArchive->GetArchiveKind() == GenericArchive::kArchiveDiskImage) - { - if (!ChooseAddTarget(&pTargetSubdir, &addFiles.fpTargetDiskFS)) - return; - } - - addFiles.fStoragePrefix = ""; - addFiles.fIncludeSubfolders = - fPreferences.GetPrefBool(kPrAddIncludeSubFolders); - addFiles.fStripFolderNames = - fPreferences.GetPrefBool(kPrAddStripFolderNames); - addFiles.fOverwriteExisting = - fPreferences.GetPrefBool(kPrAddOverwriteExisting); - addFiles.fTypePreservation = - fPreferences.GetPrefLong(kPrAddTypePreservation); - addFiles.fConvEOL = - fPreferences.GetPrefLong(kPrAddConvEOL); - - /* if they can't convert EOL when adding files, disable the option */ - if (!fpOpenArchive->GetCapability(GenericArchive::kCapCanConvEOLOnAdd)) { - addFiles.fConvEOL = AddFilesDialog::kConvEOLNone; - addFiles.fConvEOLEnable = false; - } - - /* - * Disable editing of the storage prefix field. Force pathname - * stripping to be on for non-hierarchical filesystems (i.e. everything - * but ProDOS and HFS). - */ - if (addFiles.fpTargetDiskFS != NULL) { - DiskImg::FSFormat format; - format = addFiles.fpTargetDiskFS->GetDiskImg()->GetFSFormat(); - - if (pTargetSubdir != NULL) { - ASSERT(!pTargetSubdir->IsVolumeDirectory()); - addFiles.fStoragePrefix = pTargetSubdir->GetPathName(); - } - - addFiles.fStripFolderNamesEnable = false; - addFiles.fStoragePrefixEnable = false; - switch (format) { - case DiskImg::kFormatProDOS: - case DiskImg::kFormatMacHFS: - addFiles.fStripFolderNamesEnable = true; - break; - default: - break; - } - } - if (!addFiles.fStripFolderNamesEnable) { - /* if we disabled it, we did so because it's mandatory */ - addFiles.fStripFolderNames = true; - } - - addFiles.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrAddFileFolder); - - LRESULT addResult = addFiles.DoModal(); - if (addResult == IDOK) { - fPreferences.SetPrefBool(kPrAddIncludeSubFolders, - addFiles.fIncludeSubfolders != 0); - if (addFiles.fStripFolderNamesEnable) { - // only update the pref if they had the ability to change it - fPreferences.SetPrefBool(kPrAddStripFolderNames, - addFiles.fStripFolderNames != 0); - } - fPreferences.SetPrefBool(kPrAddOverwriteExisting, - addFiles.fOverwriteExisting != 0); - fPreferences.SetPrefLong(kPrAddTypePreservation, - addFiles.fTypePreservation); - if (addFiles.fConvEOLEnable) - fPreferences.SetPrefLong(kPrAddConvEOL, - addFiles.fConvEOL); - - CString saveFolder = addFiles.GetDirectory(); - fPreferences.SetPrefString(kPrAddFileFolder, saveFolder); - - /* - * Set up a progress dialog and kick things off. - */ - bool result; - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionAdd, this); - - //fpContentList->Invalidate(); // don't allow redraws until done - result = fpOpenArchive->BulkAdd(fpActionProgress, &addFiles); - fpContentList->Reload(); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - if (result) - SuccessBeep(); - } else { - LOGI("SFD bailed with Cancel"); - } -} -void MainWindow::OnUpdateActionsAddFiles(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly()); -} - -bool MainWindow::ChooseAddTarget(DiskImgLib::A2File** ppTargetSubdir, - DiskImgLib::DiskFS** ppTargetDiskFS) -{ - ASSERT(ppTargetSubdir != NULL); - ASSERT(ppTargetDiskFS != NULL); - - *ppTargetSubdir = NULL; - *ppTargetDiskFS = NULL; - - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry != NULL && - (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory || - pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)) - { - /* - * They've selected a single subdirectory. Add the files there. - */ - DiskEntry* pDiskEntry = (DiskEntry*) pEntry; - *ppTargetSubdir = pDiskEntry->GetA2File(); - *ppTargetDiskFS = (*ppTargetSubdir)->GetDiskFS(); - } else { - /* - * Nothing selected, non-subdir selected, or multiple files - * selected. Whatever the case, pop up the choose target dialog. - * - * This works on DOS 3.3 and Pascal disks, because the absence - * of subdirectories means there's only one possible place to - * put the files. We could short-circuit this code for anything - * but ProDOS and HFS, but we have to be careful about embedded - * sub-volumes. - */ - DiskArchive* pDiskArchive = (DiskArchive*) fpOpenArchive; - - LOGD("Trying ChooseAddTarget"); - - ChooseAddTargetDialog targetDialog(this); - targetDialog.fpDiskFS = pDiskArchive->GetDiskFS(); - if (targetDialog.DoModal() != IDOK) - return false; - - *ppTargetSubdir = targetDialog.fpChosenSubdir; - *ppTargetDiskFS = targetDialog.fpChosenDiskFS; - - /* make sure the subdir is part of the diskfs */ - ASSERT(*ppTargetSubdir == NULL || - (*ppTargetSubdir)->GetDiskFS() == *ppTargetDiskFS); - } - - if (*ppTargetSubdir != NULL && (*ppTargetSubdir)->IsVolumeDirectory()) - *ppTargetSubdir = NULL; - - return true; -} - - -/* - * ========================================================================== - * Add Disks - * ========================================================================== - */ - -void MainWindow::OnActionsAddDisks(void) -{ - DIError dierr; - DiskImg img; - CString failed, errMsg; - CString openFilters, saveFolder; - CStringA pathNameA; - AddFilesDialog addOpts; - - LOGI("Add disks!"); - - CheckedLoadString(&failed, IDS_FAILED); - - openFilters = kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - dlg.m_ofn.lpstrTitle = L"Add Disk Image"; - - /* file is always opened read-only */ - dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrAddFileFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrAddFileFolder, saveFolder); - - /* open the image file and analyze it */ - pathNameA = dlg.GetPathName(); - dierr = img.OpenImage(pathNameA, PathProposal::kLocalFssep, true); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); - goto bail; - } - - if (img.AnalyzeImage() != kDIErrNone) { - errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", - (LPCWSTR) dlg.GetPathName()); - MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); - goto bail; - } - - - /* if requested (or necessary), verify the format */ - if (/*img.GetFSFormat() == DiskImg::kFormatUnknown ||*/ - img.GetSectorOrder() == DiskImg::kSectorOrderUnknown || - fPreferences.GetPrefBool(kPrQueryImageFormat)) - { - ImageFormatDialog imf; - - imf.InitializeValues(&img); - imf.fFileSource = dlg.GetPathName(); - imf.SetQueryDisplayFormat(false); - - LOGI(" On entry, sectord=%d format=%d", - imf.fSectorOrder, imf.fFSFormat); - if (imf.fFSFormat == DiskImg::kFormatUnknown) - imf.fFSFormat = DiskImg::kFormatGenericProDOSOrd; - - if (imf.DoModal() != IDOK) { - LOGI("User bailed on IMF dialog"); - goto bail; - } - - LOGI(" On exit, sectord=%d format=%d", - imf.fSectorOrder, imf.fFSFormat); - - if (imf.fSectorOrder != img.GetSectorOrder() || - imf.fFSFormat != img.GetFSFormat()) - { - LOGI("Initial values overridden, forcing img format"); - dierr = img.OverrideFormat(img.GetPhysicalFormat(), imf.fFSFormat, - imf.fSectorOrder); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to access disk image using selected" - L" parameters. Error: %hs.", - DiskImgLib::DIStrError(dierr)); - MessageBox(errMsg, failed, MB_OK | MB_ICONSTOP); - goto bail; - } - } - } - - /* - * We want to read from the image the way that a ProDOS application - * would, which means forcing the FSFormat to generic ProDOS ordering. - * This way, ProDOS disks load serially, and DOS 3.3 disks load with - * their sectors swapped around. - */ - dierr = img.OverrideFormat(img.GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, img.GetSectorOrder()); - if (dierr != kDIErrNone) { - errMsg.Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", - DiskImgLib::DIStrError(dierr)); - MessageBox(errMsg, failed, MB_OK | MB_ICONSTOP); - goto bail; - } - - /* - * Set up an AddFilesDialog, but don't actually use it as a dialog. - * Instead, we just configure the various options appropriately. - */ - ASSERT(dlg.m_ofn.nFileOffset > 0); - { - CString directory(dlg.m_ofn.lpstrFile, dlg.m_ofn.nFileOffset - 1); - CString file(dlg.m_ofn.lpstrFile + dlg.m_ofn.nFileOffset); - LOGD("Stuffing '%ls' '%ls'", (LPCWSTR) directory, (LPCWSTR) file); - addOpts.StuffSingleFilename(directory, file); - } - addOpts.fStoragePrefix = ""; - addOpts.fIncludeSubfolders = false; - addOpts.fStripFolderNames = false; - addOpts.fOverwriteExisting = false; - addOpts.fTypePreservation = AddFilesDialog::kPreserveTypes; - addOpts.fConvEOL = AddFilesDialog::kConvEOLNone; - addOpts.fConvEOLEnable = false; // no EOL conversion on disk images! - addOpts.fpDiskImg = &img; - - bool result; - - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionAddDisk, this); - - //fpContentList->Invalidate(); // don't allow updates until done - result = fpOpenArchive->AddDisk(fpActionProgress, &addOpts); - fpContentList->Reload(); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - - if (result) - SuccessBeep(); - -bail: - return; -} -void MainWindow::OnUpdateActionsAddDisks(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() && - fpOpenArchive->GetCapability(GenericArchive::kCapCanAddDisk)); -} - - -/* - * ========================================================================== - * Create Subdirectory - * ========================================================================== - */ - -void MainWindow::OnActionsCreateSubdir(void) -{ - CreateSubdirDialog csDialog; - - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - ASSERT(!fpOpenArchive->IsReadOnly()); - - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry == NULL) { - // can happen for no selection or multi-selection; should not be here - ASSERT(false); - return; - } - if (pEntry->GetRecordKind() != GenericEntry::kRecordKindDirectory && - pEntry->GetRecordKind() != GenericEntry::kRecordKindVolumeDir) - { - CString errMsg; - errMsg = "Please select a subdirectory."; - ShowFailureMsg(this, errMsg, IDS_MB_APP_NAME); - return; - } - - LOGI("Creating subdir in '%ls'", (LPCWSTR) pEntry->GetPathNameUNI()); - - csDialog.fBasePath = pEntry->GetPathNameUNI(); - csDialog.fpArchive = fpOpenArchive; - csDialog.fpParentEntry = pEntry; - csDialog.fNewName = "New.Subdir"; - if (csDialog.DoModal() != IDOK) - return; - - LOGI("Creating '%ls'", (LPCWSTR) csDialog.fNewName); - - fpOpenArchive->CreateSubdir(this, pEntry, csDialog.fNewName); - fpContentList->Reload(); -} -void MainWindow::OnUpdateActionsCreateSubdir(CCmdUI* pCmdUI) -{ - bool enable = fpContentList != NULL && !fpOpenArchive->IsReadOnly() && - fpContentList->GetSelectedCount() == 1 && - fpOpenArchive->GetCapability(GenericArchive::kCapCanCreateSubdir); - - if (enable) { - /* second-level check: make sure it's a subdir */ - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry == NULL) { - ASSERT(false); - return; - } - if (pEntry->GetRecordKind() != GenericEntry::kRecordKindDirectory && - pEntry->GetRecordKind() != GenericEntry::kRecordKindVolumeDir) - { - enable = false; - } - } - - pCmdUI->Enable(enable); -} - - -/* - * ========================================================================== - * Extract - * ========================================================================== - */ - -void MainWindow::OnActionsExtract(void) -{ - ASSERT(fpContentList != NULL); - - /* - * Ask the user about various options. - */ - ExtractOptionsDialog extOpts(fpContentList->GetSelectedCount(), this); - extOpts.fExtractPath = fPreferences.GetPrefString(kPrExtractFileFolder); - extOpts.fConvEOL = fPreferences.GetPrefLong(kPrExtractConvEOL); - extOpts.fConvHighASCII = fPreferences.GetPrefBool(kPrExtractConvHighASCII); - extOpts.fIncludeDataForks = fPreferences.GetPrefBool(kPrExtractIncludeData); - extOpts.fIncludeRsrcForks = fPreferences.GetPrefBool(kPrExtractIncludeRsrc); - extOpts.fIncludeDiskImages = fPreferences.GetPrefBool(kPrExtractIncludeDisk); - extOpts.fEnableReformat = fPreferences.GetPrefBool(kPrExtractEnableReformat); - extOpts.fDiskTo2MG = fPreferences.GetPrefBool(kPrExtractDiskTo2MG); - extOpts.fAddTypePreservation = fPreferences.GetPrefBool(kPrExtractAddTypePreservation); - extOpts.fAddExtension = fPreferences.GetPrefBool(kPrExtractAddExtension); - extOpts.fStripFolderNames = fPreferences.GetPrefBool(kPrExtractStripFolderNames); - extOpts.fOverwriteExisting = fPreferences.GetPrefBool(kPrExtractOverwriteExisting); - - if (fpContentList->GetSelectedCount() > 0) - extOpts.fFilesToExtract = ExtractOptionsDialog::kExtractSelection; - else - extOpts.fFilesToExtract = ExtractOptionsDialog::kExtractAll; - - if (extOpts.DoModal() != IDOK) - return; - - if (extOpts.fExtractPath.Right(1) != "\\") - extOpts.fExtractPath += "\\"; - - /* push preferences back out */ - fPreferences.SetPrefString(kPrExtractFileFolder, extOpts.fExtractPath); - fPreferences.SetPrefLong(kPrExtractConvEOL, extOpts.fConvEOL); - fPreferences.SetPrefBool(kPrExtractConvHighASCII, extOpts.fConvHighASCII != 0); - fPreferences.SetPrefBool(kPrExtractIncludeData, extOpts.fIncludeDataForks != 0); - fPreferences.SetPrefBool(kPrExtractIncludeRsrc, extOpts.fIncludeRsrcForks != 0); - fPreferences.SetPrefBool(kPrExtractIncludeDisk, extOpts.fIncludeDiskImages != 0); - fPreferences.SetPrefBool(kPrExtractEnableReformat, extOpts.fEnableReformat != 0); - fPreferences.SetPrefBool(kPrExtractDiskTo2MG, extOpts.fDiskTo2MG != 0); - fPreferences.SetPrefBool(kPrExtractAddTypePreservation, extOpts.fAddTypePreservation != 0); - fPreferences.SetPrefBool(kPrExtractAddExtension, extOpts.fAddExtension != 0); - fPreferences.SetPrefBool(kPrExtractStripFolderNames, extOpts.fStripFolderNames != 0); - fPreferences.SetPrefBool(kPrExtractOverwriteExisting, extOpts.fOverwriteExisting != 0); - - LOGI("Requested extract path is '%ls'", (LPCWSTR) extOpts.fExtractPath); - - /* - * Create a "selection set" of things to display. - */ - SelectionSet selSet; - int threadMask = 0; - if (extOpts.fIncludeDataForks) - threadMask |= GenericEntry::kDataThread; - if (extOpts.fIncludeRsrcForks) - threadMask |= GenericEntry::kRsrcThread; - if (extOpts.fIncludeDiskImages) - threadMask |= GenericEntry::kDiskImageThread; - - if (extOpts.fFilesToExtract == ExtractOptionsDialog::kExtractSelection) { - selSet.CreateFromSelection(fpContentList, threadMask); - } else { - selSet.CreateFromAll(fpContentList, threadMask); - } - //selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK | MB_ICONEXCLAMATION); - return; - } - - /* - * Set up the progress dialog then do the extraction. - */ - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionExtract, this); - DoBulkExtract(&selSet, &extOpts); - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; -} -void MainWindow::OnUpdateActionsExtract(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL); -} - -void MainWindow::DoBulkExtract(SelectionSet* pSelSet, - const ExtractOptionsDialog* pExtOpts) -{ - /* - * IMPORTANT: since the pActionProgress dialog has the foreground, it's - * vital that any MessageBox calls go through that. Otherwise the - * progress dialog message handler won't get disabled by MessageBox and - * we can end up permanently hiding the dialog. (Could also use - * ::MessageBox or ::AfxMessageBox instead.) - */ - ReformatHolder* pHolder = NULL; - bool overwriteExisting, ovwrForAll; - - ASSERT(pSelSet != NULL); - ASSERT(fpActionProgress != NULL); - - pSelSet->IterReset(); - - /* set up our "overwrite existing files" logic */ - overwriteExisting = ovwrForAll = (pExtOpts->fOverwriteExisting != FALSE); - - while (true) { - SelectionEntry* pSelEntry; - bool result; - - /* make sure we service events */ - PeekAndPump(); - - pSelEntry = pSelSet->IterNext(); - if (pSelEntry == NULL) { - SuccessBeep(); - break; // out of while (all done!) - } - - GenericEntry* pEntry = pSelEntry->GetEntry(); - if (pEntry->GetDamaged()) { - LOGI("Skipping '%ls' due to damage", (LPCWSTR) pEntry->GetPathNameUNI()); - continue; - } - - /* - * Extract all parts of the file -- including those we don't actually - * intend to extract to disk -- and hold them in the ReformatHolder. - * Some formats, e.g. GWP, need the resource fork to do formatting, - * so it's important to have the resource fork available even if - * we're just going to throw it away. - * - * The selection set should have screened out anything totally - * inappropriate, e.g. files with nothing but a resource fork don't - * make it into the set if we're not extracting resource forks. - * - * We only want to reformat files, not disk images, directories, - * volume dirs, etc. We have a reformatter for ProDOS directories, - * but (a) we don't explicitly extract subdirs, and (b) we'd really - * like directories to be directories so we can extract files into - * them. - */ - if (pExtOpts->ShouldTryReformat() && - (pEntry->GetRecordKind() == GenericEntry::kRecordKindFile || - pEntry->GetRecordKind() == GenericEntry::kRecordKindForkedFile)) - { - fpActionProgress->SetArcName(pEntry->GetDisplayName()); - fpActionProgress->SetFileName(L"-"); - SET_PROGRESS_BEGIN(); - - if (GetFileParts(pEntry, &pHolder) == 0) { - /* - * Use the prefs, but disable generic text conversion, so - * that we default to "raw". That way we will use the text - * conversion that the user has specified in the "extract" - * dialog. - * - * We might want to just disable any "always"-level - * reformatter, but that would require tweaking the reformat - * code to return "raw" when nothing applies. - */ - ConfigureReformatFromPreferences(pHolder); - pHolder->SetReformatAllowed(ReformatHolder::kReformatTextEOL_HA, - false); - - pHolder->SetSourceAttributes( - pEntry->GetFileType(), - pEntry->GetAuxType(), - ReformatterSourceFormat(pEntry->GetSourceFS()), - pEntry->GetFileNameExtensionMOR()); - pHolder->TestApplicability(); - } - } - - if (pExtOpts->fIncludeDataForks && pEntry->GetHasDataFork()) { - result = ExtractEntry(pEntry, GenericEntry::kDataThread, - pHolder, pExtOpts, &overwriteExisting, &ovwrForAll); - if (!result) - break; - } - if (pExtOpts->fIncludeRsrcForks && pEntry->GetHasRsrcFork()) { - result = ExtractEntry(pEntry, GenericEntry::kRsrcThread, - pHolder, pExtOpts, &overwriteExisting, &ovwrForAll); - if (!result) - break; - } - if (pExtOpts->fIncludeDiskImages && pEntry->GetHasDiskImage()) { - result = ExtractEntry(pEntry, GenericEntry::kDiskImageThread, - pHolder, pExtOpts, &overwriteExisting, &ovwrForAll); - if (!result) - break; - } - delete pHolder; - pHolder = NULL; - } - - // if they cancelled, delete the "stray" - delete pHolder; -} - -bool MainWindow::ExtractEntry(GenericEntry* pEntry, int thread, - ReformatHolder* pHolder, const ExtractOptionsDialog* pExtOpts, - bool* pOverwriteExisting, bool* pOvwrForAll) -{ - /* - * This first bit of setup is the same for every file. However, it's - * pretty quick, and it's easier to pass "pExtOpts" in than all of - * this stuff, so we just do it every time. - */ - GenericEntry::ConvertEOL convEOL; - GenericEntry::ConvertHighASCII convHA; - bool convTextByType = false; - - - /* translate the EOL conversion mode into GenericEntry terms */ - switch (pExtOpts->fConvEOL) { - case ExtractOptionsDialog::kConvEOLNone: - convEOL = GenericEntry::kConvertEOLOff; - break; - case ExtractOptionsDialog::kConvEOLType: - convEOL = GenericEntry::kConvertEOLOff; - convTextByType = true; - break; - case ExtractOptionsDialog::kConvEOLAuto: - convEOL = GenericEntry::kConvertEOLAuto; - break; - case ExtractOptionsDialog::kConvEOLAll: - convEOL = GenericEntry::kConvertEOLOn; - break; - default: - ASSERT(false); - convEOL = GenericEntry::kConvertEOLOff; - break; - } - if (pExtOpts->fConvHighASCII) - convHA = GenericEntry::kConvertHAAuto; - else - convHA = GenericEntry::kConvertHAOff; - - //LOGI(" DBE initial text conversion: eol=%d ha=%d", - // convEOL, convHA); - - - ReformatHolder holder; - CString outputPath; - CString failed, errMsg; - CheckedLoadString(&failed, IDS_FAILED); - bool writeFailed = false; - bool extractAs2MG = false; - char* reformatText = NULL; - MyDIBitmap* reformatDib = NULL; - - ASSERT(pEntry != NULL); - - /* - * If we're interested in extracting disk images as 2MG files, - * see if we want to handle this one that way. - * - * If they said "don't extract disk images", the images should - * have been culled from the selection set earlier. - */ - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) { - ASSERT(pExtOpts->fIncludeDiskImages); - if (pExtOpts->fDiskTo2MG) { - if (pEntry->GetUncompressedLen() > 0 && - (pEntry->GetUncompressedLen() % TwoImgHeader::kBlockSize) == 0) - { - extractAs2MG = true; - } else { - LOGI("Not extracting funky image '%ls' as 2MG (len=%I64d)", - (LPCWSTR) pEntry->GetPathNameUNI(), - pEntry->GetUncompressedLen()); - } - } - } - - /* - * Convert the archived pathname to something suitable for the - * local machine (i.e. Win32). - */ - PathProposal pathProp; - CString convName, convNameExtd, convNameExtdPlus; - pathProp.Init(pEntry); - pathProp.fThreadKind = thread; - if (pExtOpts->fStripFolderNames) - pathProp.fJunkPaths = true; - - pathProp.ArchiveToLocal(); - convName = pathProp.fLocalPathName; - - /* run it through again, this time with optional type preservation */ - if (pExtOpts->fAddTypePreservation) - pathProp.fPreservation = true; - pathProp.ArchiveToLocal(); - convNameExtd = pathProp.fLocalPathName; - - /* and a 3rd time, also taking additional extensions into account */ - if (pExtOpts->fAddExtension) - pathProp.fAddExtension = true; - pathProp.ArchiveToLocal(); - convNameExtdPlus = pathProp.fLocalPathName; - - /* - * Prepend the extraction dir to the local pathname. We also add - * the sub-volume name (if any), which should already be a valid - * Win32 directory name. We can't add it earlier because the fssep - * char might be '\0'. - */ - ASSERT(pExtOpts->fExtractPath.Right(1) == "\\"); - CString adjustedExtractPath(pExtOpts->fExtractPath); - if (!pExtOpts->fStripFolderNames && !pEntry->GetSubVolName().IsEmpty()) { - adjustedExtractPath += pEntry->GetSubVolName(); - adjustedExtractPath += "\\"; - } - outputPath = adjustedExtractPath; - outputPath += convNameExtdPlus; - - - ReformatOutput* pOutput = NULL; - - /* - * If requested, try to reformat this file. - */ - if (pHolder != NULL) { - ReformatHolder::ReformatPart part = ReformatHolder::kPartUnknown; - ReformatHolder::ReformatID id; - CString title; - //int result; - - switch (thread) { - case GenericEntry::kDataThread: - part = ReformatHolder::kPartData; - break; - case GenericEntry::kRsrcThread: - part = ReformatHolder::kPartRsrc; - break; - case GenericEntry::kDiskImageThread: - part = ReformatHolder::kPartData; - break; - case GenericEntry::kCommentThread: - default: - assert(false); - return false; - } - - fpActionProgress->SetFileName(_T("(reformatting)")); - id = pHolder->FindBest(part); - - { - CWaitCursor waitc; - pOutput = pHolder->Apply(part, id); - } - - if (pOutput != NULL) { - /* use output pathname without preservation */ - CString tmpPath; - bool goodReformat = true; - bool noChangePath = false; - - tmpPath = adjustedExtractPath; - tmpPath += convName; - - CString lastFour = tmpPath.Right(4); - - /* - * Tack on a file extension identifying the reformatted - * contents. If the filename already has the correct - * extension, don't tack it on again. - */ - switch (pOutput->GetOutputKind()) { - case ReformatOutput::kOutputText: - if (lastFour.CompareNoCase(L".txt") != 0) - tmpPath += L".txt"; - break; - case ReformatOutput::kOutputRTF: - if (lastFour.CompareNoCase(L".rtf") != 0) - tmpPath += L".rtf"; - break; - case ReformatOutput::kOutputCSV: - if (lastFour.CompareNoCase(L".csv") != 0) - tmpPath += L".csv"; - break; - case ReformatOutput::kOutputBitmap: - if (lastFour.CompareNoCase(L".bmp") != 0) - tmpPath += L".bmp"; - break; - case ReformatOutput::kOutputRaw: - noChangePath = true; - break; - default: - // kOutputErrorMsg, kOutputUnknown - goodReformat = false; - break; - } - - /* - * Issue #26: if "add type extension" is not checked, don't add - * the format converter extension. This is useful for Merlin - * source files where you want to do the text conversion but still - * want the file to end with ".S". - */ - if (!pExtOpts->fAddExtension) { - noChangePath = true; - } - - if (goodReformat) { - if (!noChangePath) - outputPath = tmpPath; - } else { - delete pOutput; - pOutput = NULL; - } - } - } - if (extractAs2MG) { - /* - * Reduce to base name and add 2IMG suffix. Would be nice to keep - * the non-extended file type preservation stuff, but right now we - * only expect unadorned sectors for that (and so does NuLib2). - */ - outputPath = adjustedExtractPath; - outputPath += convName; - outputPath += L".2mg"; - } - - /* update the display in case we renamed it */ - if (outputPath != fpActionProgress->GetFileName()) { - LOGI(" Renamed our output, from '%ls' to '%ls'", - (LPCWSTR) fpActionProgress->GetFileName(), (LPCWSTR) outputPath); - fpActionProgress->SetFileName(outputPath); - } - - /* - * Update the progress meter output filename, and reset the thermometer. - */ - fpActionProgress->SetArcName(pathProp.fStoredPathName); - fpActionProgress->SetFileName(outputPath); - LOGI("Extracting from '%ls' to '%ls'", - (LPCWSTR) pathProp.fStoredPathName, (LPCWSTR) outputPath); - SET_PROGRESS_BEGIN(); - - /* - * Open the output file. - * - * Returns IDCANCEL on failures as well as user cancellation. - */ - FILE* fp = NULL; - int result; - result = OpenOutputFile(&outputPath, pathProp, pEntry->GetModWhen(), - pOverwriteExisting, pOvwrForAll, &fp); - if (result == IDCANCEL) { - // no messagebox for this one - delete pOutput; - return false; - } - - - /* update the display in case they renamed the file */ - if (outputPath != fpActionProgress->GetFileName()) { - LOGI(" Detected rename, from '%ls' to '%ls'", - (LPCWSTR) fpActionProgress->GetFileName(), (LPCWSTR) outputPath); - fpActionProgress->SetFileName(outputPath); - } - - if (fp == NULL) { - /* looks like they elected to skip extraction of this file */ - delete pOutput; - return true; - } - - //EventPause(500); // DEBUG DEBUG - - - /* - * Handle "extract as 2MG" by writing a 2MG header to the start - * of the file before we hand off to the extraction function. - * - * NOTE: we're currently assuming that we're extracting an image - * in ProDOS sector order. This is a valid assumption so long as - * we're only pulling disk images out of ShrinkIt archives. - * - * We don't currently use the WriteFooter call here, because we're - * not adding comments. - */ - if (extractAs2MG) { - TwoImgHeader header; - header.InitHeader(TwoImgHeader::kImageFormatProDOS, - (long) pEntry->GetUncompressedLen(), - (long) (pEntry->GetUncompressedLen() / TwoImgHeader::kBlockSize)); - int err; - ASSERT(ftell(fp) == 0); - err = header.WriteHeader(fp); - if (err != 0) { - errMsg.Format(L"Unable to save 2MG file '%ls': %hs\n", - (LPCWSTR) outputPath, strerror(err)); - fpActionProgress->MessageBox(errMsg, failed, - MB_OK | MB_ICONERROR); - goto open_file_fail; - } - ASSERT(ftell(fp) == 64); // size of 2MG header - } - - - /* - * In some cases we want to override the automatic text detection. - * - * If we're in "auto" mode, force conversion on for DOS/RDOS text files. - * This is important when "convHA" is off, because in high-ASCII mode - * we might not recognize text files for what they are. We also - * consider 0x00 to be binary, which screws up random-access text. - * - * We don't want to do text conversion on disk images or resource - * forks, ever. Turn them off here. - */ - GenericEntry::ConvertEOL thisConv; - thisConv = convEOL; - if (thisConv == GenericEntry::kConvertEOLAuto) { - if (DiskImg::UsesDOSFileStructure(pEntry->GetSourceFS()) && - pEntry->GetFileType() == kFileTypeTXT) - { - LOGI("Switching EOLAuto to EOLOn for DOS text file"); - thisConv = GenericEntry::kConvertEOLOn; - } - } else if (convTextByType) { - /* force it on or off when in conv-by-type mode */ - if (pEntry->GetFileType() == kFileTypeTXT || - pEntry->GetFileType() == kFileTypeSRC) - { - LOGI("Enabling EOL conv for text file"); - thisConv = GenericEntry::kConvertEOLOn; - } else { - ASSERT(thisConv == GenericEntry::kConvertEOLOff); - } - } - if (thisConv != GenericEntry::kConvertEOLOff && - (thread == GenericEntry::kRsrcThread || - thread == GenericEntry::kDiskImageThread)) - { - LOGI("Disabling EOL conv for resource fork or disk image"); - thisConv = GenericEntry::kConvertEOLOff; - } - - - /* - * Extract the contents to the file. - * - * In some cases, notably when the file size exceeds the limit of - * the reformatter, we will be trying to reformat but won't have - * loaded the original data. In such cases we fall through to the - * normal extraction mode, because we threw out pOutput above when - * the result was kOutputErrorMsg. - * - * (Could also be due to extraction failure, e.g. bad CRC.) - */ - if (pOutput != NULL) { - /* - * We have the data in our buffer. Write it out. No need - * to tweak the progress updater, which already shows 100%. - * - * There are four possibilities: - * - Valid text/rtf/csv converted text. Write reformatted. - * - Valid bitmap converted. Write bitmap. - * - No reformatter found, type is "raw". Write raw. (Note - * this may be zero bytes long for an empty file.) - * - Error message encoded in result. Should not be here! - */ - if (pOutput->GetOutputKind() == ReformatOutput::kOutputText || - pOutput->GetOutputKind() == ReformatOutput::kOutputRTF || - pOutput->GetOutputKind() == ReformatOutput::kOutputCSV) - { - LOGI(" Writing text, RTF, CSV, or raw"); - ASSERT(pOutput->GetTextBuf() != NULL); - int err = 0; - if (fwrite(pOutput->GetTextBuf(), - pOutput->GetTextLen(), 1, fp) != 1) - err = errno; - if (err != 0) { - errMsg.Format(L"Unable to save reformatted file '%ls': %hs\n", - (LPCWSTR) outputPath, strerror(err)); - fpActionProgress->MessageBox(errMsg, failed, - MB_OK | MB_ICONERROR); - writeFailed = true; - } else { - SET_PROGRESS_UPDATE(100); - } - } else if (pOutput->GetOutputKind() == ReformatOutput::kOutputBitmap) { - LOGI(" Writing bitmap"); - ASSERT(pOutput->GetDIB() != NULL); - int err = pOutput->GetDIB()->WriteToFile(fp); - if (err != 0) { - errMsg.Format(L"Unable to save bitmap '%ls': %hs\n", - (LPCWSTR) outputPath, strerror(err)); - fpActionProgress->MessageBox(errMsg, failed, - MB_OK | MB_ICONERROR); - writeFailed = true; - } else { - SET_PROGRESS_UPDATE(100); - } - } else if (pOutput->GetOutputKind() == ReformatOutput::kOutputRaw) { - /* - * Send raw data through the text conversion configured in the - * extract files dialog. Any file for which no dedicated - * reformatter could be found ends up here. - * - * We could just send it through to the generic non-reformatter - * case, but that would require reading the file twice. - */ - LOGI(" Writing un-reformatted data (%ld bytes)", - pOutput->GetTextLen()); - ASSERT(pOutput->GetTextBuf() != NULL); - bool lastCR = false; - GenericEntry::ConvertHighASCII thisConvHA = convHA; - int err; - err = GenericEntry::WriteConvert(fp, pOutput->GetTextBuf(), - pOutput->GetTextLen(), &thisConv, &thisConvHA, &lastCR); - if (err != 0) { - errMsg.Format(L"Unable to write file '%ls': %hs\n", - (LPCWSTR) outputPath, strerror(err)); - fpActionProgress->MessageBox(errMsg, failed, - MB_OK | MB_ICONERROR); - writeFailed = true; - } else { - SET_PROGRESS_UPDATE(100); - } - - } else { - /* something failed, and we don't have the file */ - LOGI("How'd we get here?"); - ASSERT(false); - } - } else { - /* - * We don't have the data, probably because we aren't using file - * reformatters. Use the GenericEntry extraction routine to copy - * the data directly into the file. - * - * We also get here if the file has a length of zero. - */ - CString msg; - int result; - ASSERT(fpActionProgress != NULL); - LOGI("Extracting '%ls', requesting thisConv=%d, convHA=%d", - (LPCWSTR) outputPath, thisConv, convHA); - result = pEntry->ExtractThreadToFile(thread, fp, - thisConv, convHA, &msg); - if (result != IDOK) { - if (result == IDCANCEL) { - CString msg; - CheckedLoadString(&msg, IDS_OPERATION_CANCELLED); - fpActionProgress->MessageBox(msg, - L"CiderPress", MB_OK | MB_ICONEXCLAMATION); - } else { - LOGI(" FAILED on '%ls': %ls", - (LPCWSTR) outputPath, (LPCWSTR) msg); - errMsg.Format(L"Unable to extract file '%ls': %ls\n", - (LPCWSTR) outputPath, (LPCWSTR) msg); - fpActionProgress->MessageBox(errMsg, failed, - MB_OK | MB_ICONERROR); - } - writeFailed = true; - } - } - -open_file_fail: - delete pOutput; - - fclose(fp); - if (writeFailed) { - // clean up - ::DeleteFile(outputPath); - return false; - } - - /* - * Fix the modification date. - */ - PathName datePath(outputPath); - datePath.SetModWhen(pEntry->GetModWhen()); -// datePath.SetAccess(pEntry->GetAccess()); - - return true; -} - -int MainWindow::OpenOutputFile(CString* pOutputPath, const PathProposal& pathProp, - time_t arcFileModWhen, bool* pOverwriteExisting, bool* pOvwrForAll, - FILE** pFp) -{ - const int kUserCancel = -2; // must not conflict with errno values - CString failed; - CString msg; - int err = 0; - - CheckedLoadString(&failed, IDS_FAILED); - - *pFp = NULL; - -did_rename: - PathName path(*pOutputPath); - if (path.Exists()) { - if (*pOverwriteExisting) { -do_overwrite: - /* delete existing */ - LOGI(" Deleting existing '%ls'", (LPCWSTR) *pOutputPath); - if (::_wunlink(*pOutputPath) != 0) { - err = errno; - LOGI(" Failed deleting '%ls', err=%d", - (LPCWSTR)*pOutputPath, err); - if (err == ENOENT) { - /* user might have removed it while dialog was up */ - err = 0; - } else { - /* unable to delete, we'd better bail out */ - goto bail; - } - } - } else if (*pOvwrForAll) { - /* never overwrite */ - LOGI(" Skipping '%ls'", (LPCWSTR) *pOutputPath); - goto bail; - } else { - /* no firm policy, ask the user */ - ConfirmOverwriteDialog confOvwr; - PathName path(*pOutputPath); - - confOvwr.fExistingFile = *pOutputPath; - confOvwr.fExistingFileModWhen = path.GetModWhen(); - confOvwr.fNewFileSource = pathProp.fStoredPathName; - confOvwr.fNewFileModWhen = arcFileModWhen; - if (confOvwr.DoModal() == IDCANCEL) { - err = kUserCancel; - goto bail; - } - if (confOvwr.fResultRename) { - *pOutputPath = confOvwr.fExistingFile; - goto did_rename; - } - if (confOvwr.fResultApplyToAll) { - *pOvwrForAll = confOvwr.fResultApplyToAll; - *pOverwriteExisting = confOvwr.fResultOverwrite; - } - if (confOvwr.fResultOverwrite) - goto do_overwrite; - else - goto bail; - } - - } - - /* create the subdirectories, if necessary */ - err = path.CreatePathIFN(); - if (err != 0) - goto bail; - - *pFp = _wfopen(*pOutputPath, L"wb"); - if (*pFp == NULL) - err = errno ? errno : -1; - /* fall through with error */ - -bail: - /* if we failed, tell the user why */ - if (err == ENOTDIR) { - /* part of the output path exists, but isn't a directory */ - msg.Format(L"Unable to create folders for '%ls': part of the path " - L"already exists but is not a folder.\n", - (LPCWSTR) *pOutputPath); - fpActionProgress->MessageBox(msg, failed, MB_OK | MB_ICONERROR); - return IDCANCEL; - } else if (err == EINVAL) { - /* invalid argument; assume it's an invalid filename */ - msg.Format(L"Unable to create file '%ls': invalid filename.\n", - (LPCWSTR) *pOutputPath); - fpActionProgress->MessageBox(msg, failed, MB_OK | MB_ICONERROR); - return IDCANCEL; - } else if (err == kUserCancel) { - /* user elected to cancel */ - LOGI("Cancelling due to user request"); - return IDCANCEL; - } else if (err != 0) { - msg.Format(L"Unable to create file '%ls': %hs\n", - (LPCWSTR) *pOutputPath, strerror(err)); - fpActionProgress->MessageBox(msg, failed, MB_OK | MB_ICONERROR); - return IDCANCEL; - } - - return IDOK; -} - - -/* - * ========================================================================== - * Test - * ========================================================================== - */ - -void MainWindow::OnActionsTest(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - - /* - * Ask the user about various options. - */ - UseSelectionDialog selOpts(fpContentList->GetSelectedCount(), this); - selOpts.Setup(IDS_TEST_TITLE, IDS_TEST_OK, IDS_TEST_SELECTED_COUNT, - IDS_TEST_SELECTED_COUNTS_FMT, IDS_TEST_ALL_FILES); - if (fpContentList->GetSelectedCount() > 0) - selOpts.fFilesToAction = UseSelectionDialog::kActionSelection; - else - selOpts.fFilesToAction = UseSelectionDialog::kActionAll; - - if (selOpts.DoModal() != IDOK) { - LOGI("Test cancelled"); - return; - } - - /* - * Create a "selection set" of things to test. - * - * We don't currently test directories, because we don't currently - * allow testing anything that has a directory (NuFX doesn't store - * them explicitly). We could probably add them to the threadMask. - */ - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread; - - if (selOpts.fFilesToAction == UseSelectionDialog::kActionSelection) { - selSet.CreateFromSelection(fpContentList, threadMask); - } else { - selSet.CreateFromAll(fpContentList, threadMask); - } - //selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - /* should be impossible */ - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK|MB_ICONEXCLAMATION); - return; - } - - /* - * Set up the progress window. - */ - bool result; - - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionTest, this); - - result = fpOpenArchive->TestSelection(fpActionProgress, &selSet); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - //if (result) - // SuccessBeep(); -} -void -MainWindow::OnUpdateActionsTest(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && fpContentList->GetItemCount() > 0 - && fpOpenArchive->GetCapability(GenericArchive::kCapCanTest)); -} - - -/* - * ========================================================================== - * Delete - * ========================================================================== - */ - -void MainWindow::OnActionsDelete(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - ASSERT(!fpOpenArchive->IsReadOnly()); - - /* - * We handle deletions specially. If they have selected any - * subdirectories, we recursively select the files in those subdirs - * as well. We want to do it early so that the "#of files to delete" - * display accurately reflects what we're about to do. - */ - fpContentList->SelectSubdirContents(); - -#if 0 - /* - * Ask the user about various options. - */ - UseSelectionDialog delOpts(fpContentList->GetSelectedCount(), this); - delOpts.Setup(IDS_DEL_TITLE, IDS_DEL_OK, IDS_DEL_SELECTED_COUNT, - IDS_DEL_SELECTED_COUNTS_FMT, IDS_DEL_ALL_FILES); - if (fpContentList->GetSelectedCount() > 0) - delOpts.fFilesToAction = UseSelectionDialog::kActionSelection; - else - delOpts.fFilesToAction = UseSelectionDialog::kActionAll; - - if (delOpts.DoModal() != IDOK) { - LOGI("Delete cancelled"); - return; - } -#endif - - /* - * Create a "selection set" of things to delete. - * - * We can't delete volume directories, so they're not included. - */ - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread | - GenericEntry::kAllowDirectory /*| GenericEntry::kAllowVolumeDir*/; - -#if 0 - if (delOpts.fFilesToAction == UseSelectionDialog::kActionSelection) { - selSet.CreateFromSelection(fpContentList, threadMask); - } else { - CString appName; - UINT response; - appName.LoadString(IDS_MB_APP_NAME); - response = MessageBox("Are you sure you want to delete everything?", - appName, MB_OKCANCEL | MB_ICONEXCLAMATION); - if (response == IDCANCEL) - return; - selSet.CreateFromAll(fpContentList, threadMask); - } - //selSet.Dump(); -#endif - - selSet.CreateFromSelection(fpContentList, threadMask); - if (selSet.GetNumEntries() == 0) { - /* can happen if they selected volume dir only */ - MessageBox(L"Nothing to delete.", - L"No match", MB_OK | MB_ICONEXCLAMATION); - return; - } - - CString appName, msg; - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - msg.Format(L"Delete %d file%ls?", selSet.GetNumEntries(), - selSet.GetNumEntries() == 1 ? L"" : L"s"); - if (MessageBox(msg, appName, MB_OKCANCEL | MB_ICONQUESTION) != IDOK) - return; - - bool result; - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionDelete, this); - - //fpContentList->Invalidate(); // don't allow updates until done - result = fpOpenArchive->DeleteSelection(fpActionProgress, &selSet); - fpContentList->Reload(); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - - if (result) - SuccessBeep(); -} -void MainWindow::OnUpdateActionsDelete(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() - && fpContentList->GetSelectedCount() > 0); -} - - -/* - * ========================================================================== - * Rename - * ========================================================================== - */ - -void MainWindow::OnActionsRename(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - ASSERT(!fpOpenArchive->IsReadOnly()); - - /* - * Create a "selection set" of entries to rename. We always go by - * the selection, so there's no need to present a "all or some?" dialog. - * - * Renaming the volume dir is not done from here, so we don't include - * it in the set. We could theoretically allow renaming of "damaged" - * files, since most of the time the damage is in the file structure - * not the directory, but the disk will be read-only anyway so there's - * no point. - */ - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread | GenericEntry::kAllowDirectory; - - selSet.CreateFromSelection(fpContentList, threadMask); - //selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - /* should be impossible */ - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK | MB_ICONEXCLAMATION); - return; - } - - //fpContentList->Invalidate(); // this might be unnecessary - fpOpenArchive->RenameSelection(this, &selSet); - fpContentList->Reload(); - - // user interaction on each step, so skip the SuccessBeep -} -void MainWindow::OnUpdateActionsRename(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() - && fpContentList->GetSelectedCount() > 0); -} - - -/* - * ========================================================================== - * Edit Comment - * ========================================================================== - */ - -void MainWindow::OnActionsEditComment(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - ASSERT(!fpOpenArchive->IsReadOnly()); - - EditCommentDialog editDlg(this); - CString oldComment; - - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry == NULL) { - ASSERT(false); - return; - } - - if (!pEntry->GetHasComment()) { - CString question, title; - int result; - - CheckedLoadString(&question, IDS_NO_COMMENT_ADD); - CheckedLoadString(&title, IDS_EDIT_COMMENT); - result = MessageBox(question, title, MB_OKCANCEL | MB_ICONQUESTION); - if (result == IDCANCEL) - return; - - editDlg.fComment = ""; - editDlg.fNewComment = true; - } else { - fpOpenArchive->GetComment(this, pEntry, &editDlg.fComment); - } - - - int result; - result = editDlg.DoModal(); - if (result == IDOK) { - //fpContentList->Invalidate(); // probably unnecessary - fpOpenArchive->SetComment(this, pEntry, editDlg.fComment); - fpContentList->Reload(); - } else if (result == EditCommentDialog::kDeleteCommentID) { - //fpContentList->Invalidate(); // possibly unnecessary - fpOpenArchive->DeleteComment(this, pEntry); - fpContentList->Reload(); - } -} -void MainWindow::OnUpdateActionsEditComment(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() && - fpContentList->GetSelectedCount() == 1 && - fpOpenArchive->GetCapability(GenericArchive::kCapCanEditComment)); -} - - -/* - * ========================================================================== - * Edit Properties - * ========================================================================== - */ - -void MainWindow::OnActionsEditProps(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - - EditPropsDialog propsDlg(this); - CString oldComment; - - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry == NULL) { - ASSERT(false); - return; - } - - propsDlg.InitProps(pEntry); - if (fpOpenArchive->IsReadOnly()) - propsDlg.fReadOnly = true; - else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) - propsDlg.fReadOnly = true; - - int result; - result = propsDlg.DoModal(); - if (result == IDOK && !propsDlg.fReadOnly) { - (void) fpOpenArchive->SetProps(this, pEntry, &propsDlg.fProps); - - // only needed if underlying archive reloads - fpContentList->Reload(true); - } -} -void MainWindow::OnUpdateActionsEditProps(CCmdUI* pCmdUI) -{ - // allow it in read-only mode, so we can view the props - pCmdUI->Enable(fpContentList != NULL && - fpContentList->GetSelectedCount() == 1); -} - - -/* - * ========================================================================== - * Rename Volume - * ========================================================================== - */ - -void MainWindow::OnActionsRenameVolume(void) -{ - RenameVolumeDialog rvDialog; - - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - ASSERT(!fpOpenArchive->IsReadOnly()); - - /* only know how to deal with disk images */ - if (fpOpenArchive->GetArchiveKind() != GenericArchive::kArchiveDiskImage) { - ASSERT(false); - return; - } - - DiskImgLib::DiskFS* pDiskFS; - - pDiskFS = ((DiskArchive*) fpOpenArchive)->GetDiskFS(); - ASSERT(pDiskFS != NULL); - - rvDialog.fpArchive = (DiskArchive*) fpOpenArchive; - if (rvDialog.DoModal() != IDOK) - return; - - //LOGI("Creating '%s'", rvDialog.fNewName); - - /* rename the chosen disk to the specified name */ - bool result; - result = fpOpenArchive->RenameVolume(this, rvDialog.fpChosenDiskFS, - rvDialog.fNewName); - if (!result) { - LOGW("RenameVolume FAILED"); - /* keep going -- reload just in case something partially happened */ - } - - /* - * We need to do two things: reload the content list, because the - * underlying DiskArchive got reloaded, and update the title bar. We - * put the "volume ID" in the title, and we most likely just changed it. - * - * SetCPTitle invokes fpOpenArchive->GetDescription(), which pulls the - * volume ID out of the primary DiskFS. - */ - fpContentList->Reload(); - SetCPTitle(fOpenArchivePathName, fpOpenArchive); -} -void MainWindow::OnUpdateActionsRenameVolume(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() && - fpOpenArchive->GetCapability(GenericArchive::kCapCanRenameVolume)); -} - - -/* - * ========================================================================== - * Recompress - * ========================================================================== - */ - -void MainWindow::OnActionsRecompress(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - - /* - * Ask the user about various options. - */ - RecompressOptionsDialog selOpts(fpContentList->GetSelectedCount(), this); - selOpts.Setup(IDS_RECOMP_TITLE, IDS_RECOMP_OK, IDS_RECOMP_SELECTED_COUNT, - IDS_RECOMP_SELECTED_COUNTS_FMT, IDS_RECOMP_ALL_FILES); - if (fpContentList->GetSelectedCount() > 0) - selOpts.fFilesToAction = UseSelectionDialog::kActionSelection; - else - selOpts.fFilesToAction = UseSelectionDialog::kActionAll; - - selOpts.fCompressionType = fPreferences.GetPrefLong(kPrCompressionType); - - if (selOpts.DoModal() != IDOK) { - LOGI("Recompress cancelled"); - return; - } - - /* - * Create a "selection set" of data forks, resource forks, and disk - * images. If an entry has nothing but a comment, ignore it. - */ - SelectionSet selSet; - int threadMask = GenericEntry::kDataThread | GenericEntry::kRsrcThread | - GenericEntry::kDiskImageThread; - - if (selOpts.fFilesToAction == UseSelectionDialog::kActionSelection) { - selSet.CreateFromSelection(fpContentList, threadMask); - } else { - selSet.CreateFromAll(fpContentList, threadMask); - } - //selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - /* should be impossible */ - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK|MB_ICONEXCLAMATION); - return; - } - - LONGLONG beforeUncomp, beforeComp; - LONGLONG afterUncomp, afterComp; - CalcTotalSize(&beforeUncomp, &beforeComp); - - /* - * Set up the progress window. - */ - int result; - - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionRecompress, this); - - //fpContentList->Invalidate(); // possibly unnecessary - result = fpOpenArchive->RecompressSelection(fpActionProgress, &selSet, - &selOpts); - fpContentList->Reload(); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - - - if (result) { - CString msg, appName; - - CalcTotalSize(&afterUncomp, &afterComp); - ASSERT(beforeUncomp == afterUncomp); - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - msg.Format(L"Total uncompressed size of all files:\t%.1fK\r\n" - L"Total size before recompress:\t\t%.1fK\r\n" - L"Total size after recompress:\t\t%.1fK\r\n" - L"Overall reduction:\t\t\t%.1fK", - beforeUncomp / 1024.0, beforeComp / 1024.0, afterComp / 1024.0, - (beforeComp - afterComp) / 1024.0); - MessageBox(msg, appName, MB_OK|MB_ICONINFORMATION); - } -} -void MainWindow::OnUpdateActionsRecompress(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() && - fpContentList->GetItemCount() > 0 && - fpOpenArchive->GetCapability(GenericArchive::kCapCanRecompress)); -} - -void MainWindow::CalcTotalSize(LONGLONG* pUncomp, LONGLONG* pComp) const -{ - GenericEntry* pEntry = fpOpenArchive->GetEntries(); - LONGLONG uncomp = 0, comp = 0; - - while (pEntry != NULL) { - uncomp += pEntry->GetUncompressedLen(); - comp += pEntry->GetCompressedLen(); - pEntry = pEntry->GetNext(); - } - - *pUncomp = uncomp; - *pComp = comp; -} - - -/* - * ========================================================================== - * Convert to disk archive - * ========================================================================== - */ - -void MainWindow::OnActionsConvDisk(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - - /* - * Ask the user about various options. - */ - ConvDiskOptionsDialog selOpts(fpContentList->GetSelectedCount(), this); - selOpts.Setup(IDS_CONVDISK_TITLE, IDS_CONVDISK_OK, IDS_CONVDISK_SELECTED_COUNT, - IDS_CONVDISK_SELECTED_COUNTS_FMT, IDS_CONVDISK_ALL_FILES); - if (fpContentList->GetSelectedCount() > 0) - selOpts.fFilesToAction = UseSelectionDialog::kActionSelection; - else - selOpts.fFilesToAction = UseSelectionDialog::kActionAll; - - //selOpts.fAllowLower = - // fPreferences.GetPrefBool(kPrConvDiskAllowLower); - //selOpts.fSparseAlloc = - // fPreferences.GetPrefBool(kPrConvDiskAllocSparse); - - if (selOpts.DoModal() != IDOK) { - LOGI("ConvDisk cancelled"); - return; - } - - ASSERT(selOpts.fNumBlocks > 0); - - //fPreferences.SetPrefBool(kPrConvDiskAllowLower, - // selOpts.fAllowLower != 0); - //fPreferences.SetPrefBool(kPrConvDiskAllocSparse, - // selOpts.fSparseAlloc != 0); - - /* - * Create a "selection set" of data forks, resource forks, and - * disk images. We don't want comment threads, but we can ignore - * them later. - */ - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread; - - if (selOpts.fFilesToAction == UseSelectionDialog::kActionSelection) { - selSet.CreateFromSelection(fpContentList, threadMask); - } else { - selSet.CreateFromAll(fpContentList, threadMask); - } - //selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - /* should be impossible */ - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK|MB_ICONEXCLAMATION); - return; - } - - XferFileOptions xferOpts; - //xferOpts.fAllowLowerCase = - // fPreferences.GetPrefBool(kPrProDOSAllowLower) != 0; - //xferOpts.fUseSparseBlocks = - // fPreferences.GetPrefBool(kPrProDOSUseSparse) != 0; - - LOGI("New volume name will be '%ls'", (LPCWSTR) selOpts.fVolName); - - /* - * Create a new disk image. - */ - CString filename, saveFolder, errStr; - - CFileDialog dlg(FALSE, L"po", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - L"Disk Images (*.po)|*.po||", this); - - dlg.m_ofn.lpstrTitle = L"New Disk Image (.PO)"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) { - LOGI(" User cancelled xfer from image create dialog"); - return; - } - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - filename = dlg.GetPathName(); - LOGI(" Will xfer to file '%ls'", (LPCWSTR) filename); - - /* remove file if it already exists */ - CString errMsg; - errMsg = RemoveFile(filename); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - return; - } - - DiskArchive::NewOptions options; - memset(&options, 0, sizeof(options)); - options.base.format = DiskImg::kFormatProDOS; - options.base.sectorOrder = DiskImg::kSectorOrderProDOS; - options.prodos.volName = selOpts.fVolName; - options.prodos.numBlocks = selOpts.fNumBlocks; - - xferOpts.fTarget = new DiskArchive; - errStr = xferOpts.fTarget->New(filename, &options); - if (!errStr.IsEmpty()) { - ShowFailureMsg(this, errStr, IDS_FAILED); - delete xferOpts.fTarget; - return; - } - - /* - * Set up the progress window. - */ - GenericArchive::XferStatus result; - - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionConvDisk, this); - - result = fpOpenArchive->XferSelection(fpActionProgress, &selSet, - fpActionProgress, &xferOpts); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - - if (result == GenericArchive::kXferOK) - SuccessBeep(); - - /* clean up */ - delete xferOpts.fTarget; -} -void MainWindow::OnUpdateActionsConvDisk(CCmdUI* pCmdUI) -{ - /* right now, only NufxArchive has the Xfer stuff implemented */ - pCmdUI->Enable(fpContentList != NULL && - fpContentList->GetItemCount() > 0 && - fpOpenArchive->GetArchiveKind() == GenericArchive::kArchiveNuFX); -} - - -/* - * ========================================================================== - * Convert disk image to NuFX file archive - * ========================================================================== - */ - -void MainWindow::OnActionsConvFile(void) -{ - ASSERT(fpContentList != NULL); - ASSERT(fpOpenArchive != NULL); - - /* - * Ask the user about various options. - */ - ConvFileOptionsDialog selOpts(fpContentList->GetSelectedCount(), this); - selOpts.Setup(IDS_CONVFILE_TITLE, IDS_CONVFILE_OK, IDS_CONVFILE_SELECTED_COUNT, - IDS_CONVFILE_SELECTED_COUNTS_FMT, IDS_CONVFILE_ALL_FILES); - if (fpContentList->GetSelectedCount() > 0) - selOpts.fFilesToAction = UseSelectionDialog::kActionSelection; - else - selOpts.fFilesToAction = UseSelectionDialog::kActionAll; - - //selOpts.fConvDOSText = - // fPreferences.GetPrefBool(kPrConvFileConvDOSText); - //selOpts.fConvPascalText = - // fPreferences.GetPrefBool(kPrConvFileConvPascalText); - selOpts.fPreserveEmptyFolders = - fPreferences.GetPrefBool(kPrConvFileEmptyFolders); - - if (selOpts.DoModal() != IDOK) { - LOGI("ConvFile cancelled"); - return; - } - - //fPreferences.SetPrefBool(kPrConvFileConvDOSText, - // selOpts.fConvDOSText != 0); - //fPreferences.SetPrefBool(kPrConvFileConvPascalText, - // selOpts.fConvPascalText != 0); - fPreferences.SetPrefBool(kPrConvFileEmptyFolders, - selOpts.fPreserveEmptyFolders != 0); - - /* - * Create a "selection set" of data forks, resource forks, and - * directories. There are no comments or disk images on a disk image, - * so we just request "any" thread. - * - * We only need to explicitly include directories if "preserve - * empty folders" is set. - */ - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread; - if (selOpts.fPreserveEmptyFolders) - threadMask |= GenericEntry::kAllowDirectory; - - if (selOpts.fFilesToAction == UseSelectionDialog::kActionSelection) { - selSet.CreateFromSelection(fpContentList, threadMask); - } else { - selSet.CreateFromAll(fpContentList, threadMask); - } - //selSet.Dump(); - - if (selSet.GetNumEntries() == 0) { - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK|MB_ICONEXCLAMATION); - return; - } - - XferFileOptions xferOpts; - //xferOpts.fConvDOSText = (selOpts.fConvDOSText != 0); - //xferOpts.fConvPascalText = (selOpts.fConvPascalText != 0); - xferOpts.fPreserveEmptyFolders = (selOpts.fPreserveEmptyFolders != 0); - - /* - * Create a new NuFX archive. - */ - CString filename, saveFolder, errStr; - - CFileDialog dlg(FALSE, L"shk", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - L"ShrinkIt Archives (*.shk)|*.shk||", this); - - dlg.m_ofn.lpstrTitle = L"New Archive"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) { - LOGI(" User cancelled xfer from archive create dialog"); - return; - } - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - filename = dlg.GetPathName(); - LOGD(" Will xfer to file '%ls'", (LPCWSTR) filename); - - /* remove file if it already exists */ - CString errMsg; - errMsg = RemoveFile(filename); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - return; - } - - xferOpts.fTarget = new NufxArchive; - errStr = xferOpts.fTarget->New(filename, NULL); - if (!errStr.IsEmpty()) { - ShowFailureMsg(this, errStr, IDS_FAILED); - delete xferOpts.fTarget; - return; - } - - /* - * Set up the progress window. - */ - GenericArchive::XferStatus result; - - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionConvFile, this); - - result = fpOpenArchive->XferSelection(fpActionProgress, &selSet, - fpActionProgress, &xferOpts); - - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - if (result == GenericArchive::kXferOK) - SuccessBeep(); - - /* clean up */ - delete xferOpts.fTarget; -} -void MainWindow::OnUpdateActionsConvFile(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && - fpContentList->GetItemCount() > 0 && - fpOpenArchive->GetArchiveKind() == GenericArchive::kArchiveDiskImage); -} - - -/* - * ========================================================================== - * Cassette WAV conversions - * ========================================================================== - */ - -void MainWindow::OnActionsConvToWav(void) -{ - // do this someday - LOGI("Convert TO wav"); -} -void MainWindow::OnUpdateActionsConvToWav(CCmdUI* pCmdUI) -{ - BOOL enable = false; - - if (fpContentList != NULL && fpContentList->GetSelectedCount() == 1) { - /* only BAS, INT, and BIN shorter than 64K */ - GenericEntry* pEntry = GetSelectedItem(fpContentList); - - if ((pEntry->GetFileType() == kFileTypeBAS || - pEntry->GetFileType() == kFileTypeINT || - pEntry->GetFileType() == kFileTypeBIN) && - pEntry->GetDataForkLen() < 65536 && - pEntry->GetRecordKind() == GenericEntry::kRecordKindFile) - { - enable = true; - } - } - pCmdUI->Enable(enable); -} - -void MainWindow::OnActionsConvFromWav(void) -{ - CassetteDialog dlg; - CString fileName, saveFolder; - - CFileDialog fileDlg(TRUE, L"wav", NULL, OFN_FILEMUSTEXIST|OFN_HIDEREADONLY, - L"Sound Files (*.wav)|*.wav||", this); - fileDlg.m_ofn.lpstrTitle = L"Open Sound File"; - fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenWAVFolder); - - if (fileDlg.DoModal() != IDOK) - goto bail; - - saveFolder = fileDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(fileDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenWAVFolder, saveFolder); - - fileName = fileDlg.GetPathName(); - LOGI("Opening WAV file '%ls'", (LPCWSTR) fileName); - - dlg.fFileName = fileName; - // pass in fpOpenArchive? - - dlg.DoModal(); - if (dlg.IsDirty()) { - assert(fpContentList != NULL); - fpContentList->Reload(); - } - -bail: - return; -} -void MainWindow::OnUpdateActionsConvFromWav(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly()); -} - -/*static*/ bool MainWindow::SaveToArchive(GenericArchive::LocalFileDetails* pDetails, - const uint8_t* dataBufIn, long dataLen, - const uint8_t* rsrcBufIn, long rsrcLen, - CString* pErrMsg, CWnd* pDialog) -{ - MainWindow* pMain = GET_MAIN_WINDOW(); - GenericArchive* pArchive = pMain->GetOpenArchive(); - DiskImgLib::A2File* pTargetSubdir = NULL; - XferFileOptions xferOpts; - CString storagePrefix; - uint8_t* dataBuf = NULL; - uint8_t* rsrcBuf = NULL; - - ASSERT(pArchive != NULL); - ASSERT(pErrMsg->IsEmpty()); - - /* - * Make a copy of the data for XferFile. - */ - if (dataLen >= 0) { - if (dataLen == 0) - dataBuf = new unsigned char[1]; - else - dataBuf = new unsigned char[dataLen]; - if (dataBuf == NULL) { - pErrMsg->Format(L"Unable to allocate %ld bytes", dataLen); - goto bail; - } - memcpy(dataBuf, dataBufIn, dataLen); - } - if (rsrcLen >= 0) { - assert(false); - } - - - /* - * Figure out where we want to put the files. For a disk archive - * this can be complicated. - * - * The target DiskFS (which could be a sub-volume) gets tucked into - * the xferOpts. - */ - if (pArchive->GetArchiveKind() == GenericArchive::kArchiveDiskImage) { - if (!pMain->ChooseAddTarget(&pTargetSubdir, &xferOpts.fpTargetFS)) - goto bail; - } else if (pArchive->GetArchiveKind() == GenericArchive::kArchiveNuFX) { - // Always use ':' separator for SHK; this is a matter of - // convenience, so they can specify a full path. - //details.storageName.Replace(':', '_'); - pDetails->SetFssep(':'); - } - if (pTargetSubdir != NULL) { - storagePrefix = pTargetSubdir->GetPathName(); - LOGD(" using storagePrefix '%ls'", (LPCWSTR) storagePrefix); - } - if (!storagePrefix.IsEmpty()) { - CString tmpStr, tmpFileName; - tmpFileName = pDetails->GetStrippedLocalPathName(); - tmpFileName.Replace(':', '_'); // strip any ':'s in the name - pDetails->SetFssep(':'); - tmpStr = storagePrefix; - tmpStr += ':'; - tmpStr += tmpFileName; - pDetails->SetStrippedLocalPathName(tmpStr); - } - - /* - * Handle the transfer. - * - * On success, XferFile will null out our dataBuf and rsrcBuf pointers. - */ - pArchive->XferPrepare(&xferOpts); - - *pErrMsg = pArchive->XferFile(pDetails, &dataBuf, dataLen, - &rsrcBuf, rsrcLen); - delete[] dataBuf; - delete[] rsrcBuf; - - if (pErrMsg->IsEmpty()) - pArchive->XferFinish(pDialog); - else - pArchive->XferAbort(pDialog); - -bail: - return (pErrMsg->IsEmpty() != 0); -} - - -/* - * ========================================================================== - * Import BASIC programs from a text file - * ========================================================================== - */ - -void MainWindow::OnActionsImportBAS(void) -{ - ImportBASDialog dlg; - CString fileName, saveFolder; - - CFileDialog fileDlg(TRUE, L"txt", NULL, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, - L"Text files (*.txt)|*.txt||", this); - fileDlg.m_ofn.lpstrTitle = L"Open Text File"; - fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrAddFileFolder); - - if (fileDlg.DoModal() != IDOK) - goto bail; - - saveFolder = fileDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(fileDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrAddFileFolder, saveFolder); - - fileName = fileDlg.GetPathName(); - LOGI("Opening TXT file '%ls'", (LPCWSTR) fileName); - - dlg.SetFileName(fileName); - // pass in fpOpenArchive? - - dlg.DoModal(); - if (dlg.IsDirty()) { - assert(fpContentList != NULL); - fpContentList->Reload(); - } - -bail: - return; -} -void MainWindow::OnUpdateActionsImportBAS(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly()); -} - - -/* - * ========================================================================== - * Multiple file handling - * ========================================================================== - */ - -int MainWindow::GetFileParts(const GenericEntry* pEntry, - ReformatHolder** ppHolder) const -{ - ReformatHolder* pHolder = new ReformatHolder; - CString errMsg; - - if (pHolder == NULL) - return -1; - - if (pEntry->GetHasDataFork()) - GetFilePart(pEntry, GenericEntry::kDataThread, pHolder); - if (pEntry->GetHasRsrcFork()) - GetFilePart(pEntry, GenericEntry::kRsrcThread, pHolder); - if (pEntry->GetHasComment()) - GetFilePart(pEntry, GenericEntry::kCommentThread, pHolder); - if (pEntry->GetHasDiskImage()) - GetFilePart(pEntry, GenericEntry::kDiskImageThread, pHolder); - - *ppHolder = pHolder; - - return 0; -} - -void MainWindow::GetFilePart(const GenericEntry* pEntry, int whichThread, - ReformatHolder* pHolder) const -{ - CString errMsg; - ReformatHolder::ReformatPart part; - char* buf = NULL; - long len = 0; - di_off_t threadLen; - int result; - - switch (whichThread) { - case GenericEntry::kDataThread: - part = ReformatHolder::kPartData; - threadLen = pEntry->GetDataForkLen(); - break; - case GenericEntry::kRsrcThread: - part = ReformatHolder::kPartRsrc; - threadLen = pEntry->GetRsrcForkLen(); - break; - case GenericEntry::kCommentThread: - part = ReformatHolder::kPartCmmt; - threadLen = -1; // no comment len getter; assume it's small - break; - case GenericEntry::kDiskImageThread: - part = ReformatHolder::kPartData; // put disks into data thread - threadLen = pEntry->GetDataForkLen(); - break; - default: - ASSERT(false); - return; - } - - if (threadLen > fPreferences.GetPrefLong(kPrMaxViewFileSize)) { - errMsg.Format( - L"[File size (%I64d KBytes) exceeds file viewer maximum (%ld KBytes). " - L"The limit can be adjusted in the file viewer preferences.]\n", - ((LONGLONG) threadLen + 1023) / 1024, - (fPreferences.GetPrefLong(kPrMaxViewFileSize) + 1023) / 1024); - pHolder->SetErrorMsg(part, errMsg); - goto bail; - } - - - result = pEntry->ExtractThreadToBuffer(whichThread, &buf, &len, &errMsg); - - if (result == IDOK) { - /* on success, ETTB guarantees a buffer, even for zero-len file */ - ASSERT(buf != NULL); - pHolder->SetSourceBuf(part, (unsigned char*) buf, len); - } else if (result == IDCANCEL) { - /* not expected */ - errMsg = L"Cancelled!"; - pHolder->SetErrorMsg(part, errMsg); - ASSERT(buf == NULL); - } else { - /* transfer error message to ReformatHolder buffer */ - LOGI("Got error message from ExtractThread: '%ls'", - (LPCWSTR) errMsg); - pHolder->SetErrorMsg(part, errMsg); - ASSERT(buf == NULL); - } - -bail: - return; -} diff --git a/ciderpress/app/AddClashDialog.cpp b/ciderpress/app/AddClashDialog.cpp deleted file mode 100644 index d86ba91..0000000 --- a/ciderpress/app/AddClashDialog.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "ConfirmOverwriteDialog.h" -#include "AddClashDialog.h" - -BEGIN_MESSAGE_MAP(AddClashDialog, CDialog) - ON_BN_CLICKED(IDC_CLASH_RENAME, OnRename) - ON_BN_CLICKED(IDC_CLASH_SKIP, OnSkip) -END_MESSAGE_MAP() - -/* - * Replaces some static text fields. - */ -BOOL AddClashDialog::OnInitDialog(void) -{ - CWnd* pWnd; - - pWnd = GetDlgItem(IDC_CLASH_WINNAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fWindowsName); - - pWnd = GetDlgItem(IDC_CLASH_STORAGENAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fStorageName); - - return CDialog::OnInitDialog(); -} - -void AddClashDialog::OnSkip(void) -{ - fDoRename = false; - CDialog::OnOK(); -} - -void AddClashDialog::OnRename(void) -{ - RenameOverwriteDialog dlg; - - dlg.fNewFileSource = fWindowsName; - dlg.fExistingFile = fStorageName; - dlg.fNewName = fStorageName; - if (dlg.DoModal() == IDOK) { - fNewName = dlg.fNewName; - fDoRename = true; - CDialog::OnOK(); - } -} diff --git a/ciderpress/app/AddClashDialog.h b/ciderpress/app/AddClashDialog.h deleted file mode 100644 index acbba74..0000000 --- a/ciderpress/app/AddClashDialog.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#ifndef APP_ADDCLASHDIALOG_H -#define APP_ADDCLASHDIALOG_H - -/* - * Dialog for resolving a filename clash. - */ -class AddClashDialog : public CDialog { -public: - AddClashDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_ADD_CLASH, pParentWnd) - { - fDoRename = false; - } - ~AddClashDialog(void) {} - - CString fWindowsName; - CString fStorageName; - - bool fDoRename; // if "false", skip this file - CString fNewName; - -private: - afx_msg void OnRename(void); - afx_msg void OnSkip(void); - - virtual BOOL OnInitDialog(void) override; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_ADDCLASHDIALOG_H*/ diff --git a/ciderpress/app/AddFilesDialog.cpp b/ciderpress/app/AddFilesDialog.cpp deleted file mode 100644 index 42d38ee..0000000 --- a/ciderpress/app/AddFilesDialog.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "AddFilesDialog.h" -#include "FileNameConv.h" -#include "resource.h" - - -/* - * A lot like DoDataExchange, only different. - * - * We do some OnInitDialog-type stuff in here, because we're a subclass of - * SelectFilesDialog and don't really get to have one of those. - * - * Returns "true" if all is well, "false" if something failed. Usually a - * "false" indication occurs during saveAndValidate==true, and means that we - * shouldn't allow the dialog to close yet. - */ -bool AddFilesDialog::MyDataExchange(bool saveAndValidate) -{ - CWnd* pWnd; - - LOGD("AddFilesDialog MyDataExchange(%d)", saveAndValidate); - if (saveAndValidate) { - if (GetDlgButtonCheck(this, IDC_ADDFILES_NOPRESERVE) == BST_CHECKED) - fTypePreservation = kPreserveNone; - else if (GetDlgButtonCheck(this, IDC_ADDFILES_PRESERVE) == BST_CHECKED) - fTypePreservation = kPreserveTypes; - else if (GetDlgButtonCheck(this, IDC_ADDFILES_PRESERVEPLUS) == BST_CHECKED) - fTypePreservation = kPreserveAndExtend; - else { - ASSERT(false); - fTypePreservation = kPreserveNone; - } - - if (GetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLNONE) == BST_CHECKED) - fConvEOL = kConvEOLNone; - else if (GetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLTYPE) == BST_CHECKED) - fConvEOL = kConvEOLType; - else if (GetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLTEXT) == BST_CHECKED) - fConvEOL = kConvEOLAuto; - else if (GetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLALL) == BST_CHECKED) - fConvEOL = kConvEOLAll; - else { - ASSERT(false); - fConvEOL = kConvEOLNone; - } - - fIncludeSubfolders = - (GetDlgButtonCheck(this, IDC_ADDFILES_INCLUDE_SUBFOLDERS) == BST_CHECKED); - fStripFolderNames = - (GetDlgButtonCheck(this, IDC_ADDFILES_STRIP_FOLDER) == BST_CHECKED); - fOverwriteExisting = - (GetDlgButtonCheck(this, IDC_ADDFILES_OVERWRITE) == BST_CHECKED); - - pWnd = GetDlgItem(IDC_ADDFILES_PREFIX); - ASSERT(pWnd != NULL); - pWnd->GetWindowText(fStoragePrefix); - - if (!ValidateStoragePrefix()) - return false; - - return true; - } else { - SetDlgButtonCheck(this, IDC_ADDFILES_NOPRESERVE, - fTypePreservation == kPreserveNone); - SetDlgButtonCheck(this, IDC_ADDFILES_PRESERVE, - fTypePreservation == kPreserveTypes); - SetDlgButtonCheck(this, IDC_ADDFILES_PRESERVEPLUS, - fTypePreservation == kPreserveAndExtend); - - SetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLNONE, - fConvEOL == kConvEOLNone); - SetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLTYPE, - fConvEOL == kConvEOLType); - SetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLTEXT, - fConvEOL == kConvEOLAuto); - SetDlgButtonCheck(this, IDC_ADDFILES_CONVEOLALL, - fConvEOL == kConvEOLAll); - - SetDlgButtonCheck(this, IDC_ADDFILES_INCLUDE_SUBFOLDERS, - fIncludeSubfolders != FALSE); - SetDlgButtonCheck(this, IDC_ADDFILES_STRIP_FOLDER, - fStripFolderNames != FALSE); - SetDlgButtonCheck(this, IDC_ADDFILES_OVERWRITE, - fOverwriteExisting != FALSE); - - pWnd = GetDlgItem(IDC_ADDFILES_PREFIX); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fStoragePrefix); - if (!fStoragePrefixEnable) - pWnd->EnableWindow(FALSE); - - if (!fStripFolderNamesEnable) { - ::EnableControl(this, IDC_ADDFILES_STRIP_FOLDER, false); - } - - if (!fConvEOLEnable) { - ::EnableControl(this, IDC_ADDFILES_CONVEOLNONE, false); - ::EnableControl(this, IDC_ADDFILES_CONVEOLTYPE, false); - ::EnableControl(this, IDC_ADDFILES_CONVEOLTEXT, false); - ::EnableControl(this, IDC_ADDFILES_CONVEOLALL, false); - } - - return true; - } -} - -bool AddFilesDialog::ValidateStoragePrefix(void) -{ - if (fStoragePrefix.IsEmpty()) - return true; - - const char kFssep = PathProposal::kDefaultStoredFssep; - if (fStoragePrefix[0] == kFssep || fStoragePrefix.Right(1) == kFssep) { - CString errMsg; - errMsg.Format(L"The storage prefix may not start or end with '%c'.", - kFssep); - MessageBox(errMsg, m_ofn.lpstrTitle, MB_OK | MB_ICONWARNING); - return false; - } - - return true; -} - -void AddFilesDialog::HandleHelp() -{ - LOGD("AddFilesDialog HandleHelp"); - MyApp::HandleHelp(this, HELP_TOPIC_ADD_FILES_DLG); -} diff --git a/ciderpress/app/AddFilesDialog.h b/ciderpress/app/AddFilesDialog.h deleted file mode 100644 index 85cb9ff..0000000 --- a/ciderpress/app/AddFilesDialog.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * File selection dialog, a sub-class of "Open" that allows multiple selection - * of both files and directories. - */ -#ifndef APP_ADDFILESDIALOG_H -#define APP_ADDFILESDIALOG_H - -#include "../diskimg/DiskImg.h" -#include "../util/UtilLib.h" -#include "resource.h" - -/* - * Choose files and folders to add. - * - * This gets passed down through the file add stuff, so it needs to carry some - * extra data along as well. - */ -class AddFilesDialog : public SelectFilesDialog { -public: - AddFilesDialog(CWnd* pParentWnd = NULL) : - SelectFilesDialog(L"IDD_ADD_FILES", true, pParentWnd) - { - SetWindowTitle(L"Add Files..."); - fStoragePrefix = ""; - fStoragePrefixEnable = true; - fIncludeSubfolders = FALSE; - fStripFolderNames = FALSE; - fStripFolderNamesEnable = true; - fOverwriteExisting = FALSE; - fTypePreservation = 0; - fConvEOL = 0; - fConvEOLEnable = true; - - fpTargetDiskFS = NULL; - //fpTargetSubdir = NULL; - fpDiskImg = NULL; - } - virtual ~AddFilesDialog(void) {} - - /* values from dialog */ - CString fStoragePrefix; - bool fStoragePrefixEnable; - BOOL fIncludeSubfolders; - BOOL fStripFolderNames; - bool fStripFolderNamesEnable; - BOOL fOverwriteExisting; - - enum { kPreserveNone = 0, kPreserveTypes, kPreserveAndExtend }; - int fTypePreservation; - - enum { kConvEOLNone = 0, kConvEOLType, kConvEOLAuto, kConvEOLAll }; - int fConvEOL; - bool fConvEOLEnable; - - /* carryover from ChooseAddTargetDialog */ - DiskImgLib::DiskFS* fpTargetDiskFS; - //DiskImgLib::A2File* fpTargetSubdir; - - /* kluge; we carry this around for the benefit of AddDisk */ - DiskImgLib::DiskImg* fpDiskImg; - -private: - virtual bool MyDataExchange(bool saveAndValidate) override; - - // User hit the Help button. - virtual void HandleHelp() override; - - // Make sure the storage prefix they entered is valid. - bool ValidateStoragePrefix(); - - //DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_ADDFILESDIALOG_H*/ diff --git a/ciderpress/app/AppleSingleArchive.cpp b/ciderpress/app/AppleSingleArchive.cpp deleted file mode 100644 index 43c9bb9..0000000 --- a/ciderpress/app/AppleSingleArchive.cpp +++ /dev/null @@ -1,739 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2015 by faddenSoft. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "AppleSingleArchive.h" -#include "NufxArchive.h" // using date/time function -#include "Preferences.h" -#include "Main.h" -#include - - -/* - * =========================================================================== - * AppleSingleEntry - * =========================================================================== - */ - -int AppleSingleEntry::ExtractThreadToBuffer(int which, char** ppText, - long* pLength, CString* pErrMsg) const -{ - ExpandBuffer expBuf; - char* dataBuf = NULL; - bool needAlloc = true; - int result = -1; - - ASSERT(fpArchive != NULL); - ASSERT(fpArchive->fFp != NULL); - - if (*ppText != NULL) - needAlloc = false; - - long offset, length; - if (which == kDataThread && fDataOffset >= 0) { - offset = fDataOffset; - length = (long) GetDataForkLen(); - } else if (which == kRsrcThread && fRsrcOffset >= 0) { - offset = fRsrcOffset; - length = (long) GetRsrcForkLen(); - } else { - *pErrMsg = "No such fork"; - goto bail; - } - - SET_PROGRESS_BEGIN(); - - errno = 0; - if (fseek(fpArchive->fFp, offset, SEEK_SET) < 0) { - pErrMsg->Format(L"Unable to seek to offset %ld: %hs", - fDataOffset, strerror(errno)); - goto bail; - } - - if (needAlloc) { - dataBuf = new char[length]; - if (dataBuf == NULL) { - pErrMsg->Format(L"allocation of %ld bytes failed", length); - goto bail; - } - } else { - if (*pLength < length) { - pErrMsg->Format(L"buf size %ld too short (%ld)", - *pLength, length); - goto bail; - } - dataBuf = *ppText; - } - if (length > 0) { - if (fread(dataBuf, length, 1, fpArchive->fFp) != 1) { - pErrMsg->Format(L"File read failed: %hs", strerror(errno)); - goto bail; - } - } - - if (needAlloc) - *ppText = dataBuf; - *pLength = length; - - result = IDOK; - -bail: - if (result == IDOK) { - SET_PROGRESS_END(); - ASSERT(pErrMsg->IsEmpty()); - } else { - ASSERT(result == IDCANCEL || !pErrMsg->IsEmpty()); - if (needAlloc) { - delete[] dataBuf; - ASSERT(*ppText == NULL); - } - } - return result; -} - -int AppleSingleEntry::ExtractThreadToFile(int which, FILE* outfp, - ConvertEOL conv, ConvertHighASCII convHA, CString* pErrMsg) const -{ - int result = -1; - - ASSERT(IDOK != -1 && IDCANCEL != -1); - long offset, length; - if (which == kDataThread && fDataOffset >= 0) { - offset = fDataOffset; - length = (long) GetDataForkLen(); - } else if (which == kRsrcThread && fRsrcOffset >= 0) { - offset = fRsrcOffset; - length = (long) GetRsrcForkLen(); - } else { - *pErrMsg = "No such fork"; - goto bail; - } - - if (length == 0) { - LOGD("Empty fork"); - result = IDOK; - goto bail; - } - - errno = 0; - if (fseek(fpArchive->fFp, offset, SEEK_SET) < 0) { - pErrMsg->Format(L"Unable to seek to offset %ld: %hs", - fDataOffset, strerror(errno)); - goto bail; - } - - SET_PROGRESS_BEGIN(); - - if (CopyData(length, outfp, conv, convHA, pErrMsg) != 0) { - if (pErrMsg->IsEmpty()) { - *pErrMsg = L"Failed while copying data."; - } - goto bail; - } - - result = IDOK; - -bail: - SET_PROGRESS_END(); - return result; -} - -int AppleSingleEntry::CopyData(long srcLen, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pMsg) const -{ - int err = 0; - const int kChunkSize = 65536; - char* buf = new char[kChunkSize]; - bool lastCR = false; - long dataRem; - - ASSERT(srcLen > 0); // empty files should've been caught earlier - - /* - * Loop until all data copied. - */ - dataRem = srcLen; - while (dataRem) { - int chunkLen; - - if (dataRem > kChunkSize) { - chunkLen = kChunkSize; - } else { - chunkLen = dataRem; - } - - /* read a chunk from the source file */ - size_t result = fread(buf, 1, chunkLen, fpArchive->fFp); - if (result != chunkLen) { - pMsg->Format(L"File read failed: %hs.", strerror(errno)); - err = -1; - goto bail; - } - - /* write chunk to destination file */ - int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, - &convHA, &lastCR); - if (err != 0) { - pMsg->Format(L"File write failed: %hs.", strerror(err)); - err = -1; - goto bail; - } - - dataRem -= chunkLen; - SET_PROGRESS_UPDATE(ComputePercent(srcLen - dataRem, srcLen)); - } - -bail: - delete[] buf; - return err; -} - - -/* - * =========================================================================== - * AppleSingleArchive - * =========================================================================== - */ - -/*static*/ CString AppleSingleArchive::AppInit(void) -{ - return L""; -} - -GenericArchive::OpenResult AppleSingleArchive::Open(const WCHAR* filename, - bool readOnly, CString* pErrMsg) -{ - CString errMsg; - - errno = 0; - fFp = _wfopen(filename, L"rb"); - if (fFp == NULL) { - errMsg.Format(L"Unable to open %ls: %hs.", filename, strerror(errno)); - goto bail; - } - - // Set this before calling LoadContents() -- we may need to use it as - // the name of the archived file. - SetPathName(filename); - - { - CWaitCursor waitc; - int result; - - result = LoadContents(); - if (result < 0) { - errMsg.Format(L"The file is not an AppleSingle archive."); - goto bail; - } else if (result > 0) { - errMsg.Format(L"Failed while reading data from AppleSingle file."); - goto bail; - } - } - -bail: - *pErrMsg = errMsg; - if (!errMsg.IsEmpty()) - return kResultFailure; - else - return kResultSuccess; -} - -CString AppleSingleArchive::New(const WCHAR* /*filename*/, const void* /*options*/) -{ - return L"Sorry, AppleSingle files can't be created."; -} - -long AppleSingleArchive::GetCapability(Capability cap) -{ - switch (cap) { - case kCapCanTest: return false; break; - case kCapCanRenameFullPath: return false; break; - case kCapCanRecompress: return false; break; - case kCapCanEditComment: return true; break; - case kCapCanAddDisk: return false; break; - case kCapCanConvEOLOnAdd: return false; break; - case kCapCanCreateSubdir: return false; break; - case kCapCanRenameVolume: return false; break; - default: - ASSERT(false); - return -1; - break; - } -} - -int AppleSingleArchive::LoadContents(void) -{ - ASSERT(fFp != NULL); - rewind(fFp); - - /* - * Read the file header. - */ - uint8_t headerBuf[kHeaderLen]; - if (fread(headerBuf, 1, kHeaderLen, fFp) != kHeaderLen) { - return -1; // probably not AppleSingle - } - if (headerBuf[1] == 0x05) { - // big-endian (spec-compliant) - fIsBigEndian = true; - fHeader.magic = Get32BE(&headerBuf[0]); - fHeader.version = Get32BE(&headerBuf[4]); - fHeader.numEntries = Get16BE(&headerBuf[8 + kHomeFileSystemLen]); - } else { - // little-endian (Mac OS X generated) - fIsBigEndian = false; - fHeader.magic = Get32LE(&headerBuf[0]); - fHeader.version = Get32LE(&headerBuf[4]); - fHeader.numEntries = Get16LE(&headerBuf[8 + kHomeFileSystemLen]); - } - memcpy(fHeader.homeFileSystem, &headerBuf[8], kHomeFileSystemLen); - fHeader.homeFileSystem[kHomeFileSystemLen] = '\0'; - - if (fHeader.magic != kMagicNumber) { - LOGD("File does not have AppleSingle magic number"); - return -1; - } - if (fHeader.version != kVersion1 && fHeader.version != kVersion2) { - LOGI("AS file has unrecognized version number 0x%08x", fHeader.version); - return -1; - } - - /* - * Read the entries (a table of contents). There are at most 65535 - * entries, so we don't need to worry about capping it at a "reasonable" - * size. - */ - size_t totalEntryLen = fHeader.numEntries * kEntryLen; - uint8_t* entryBuf = new uint8_t[totalEntryLen]; - if (fread(entryBuf, 1, totalEntryLen, fFp) != totalEntryLen) { - LOGW("Unable to read entry list from AS file (err=%d)", errno); - delete[] entryBuf; - return 1; - } - fEntries = new TOCEntry[fHeader.numEntries]; - const uint8_t* ptr = entryBuf; - for (size_t i = 0; i < fHeader.numEntries; i++, ptr += kEntryLen) { - if (fIsBigEndian) { - fEntries[i].entryId = Get32BE(ptr); - fEntries[i].offset = Get32BE(ptr + 4); - fEntries[i].length = Get32BE(ptr + 8); - } else { - fEntries[i].entryId = Get32LE(ptr); - fEntries[i].offset = Get32LE(ptr + 4); - fEntries[i].length = Get32LE(ptr + 8); - } - } - - delete[] entryBuf; - - /* - * Make sure the file actually has everything. - */ - if (!CheckFileLength()) { - return 1; - } - - /* - * Walk through the TOC entries, using them to fill out the fields in an - * AppleSingleEntry class. - */ - if (!CreateEntry()) { - return 1; - } - - DumpArchive(); - - return 0; -} - -bool AppleSingleArchive::CheckFileLength() -{ - // Find the biggest offset+length. - uint64_t maxPosn = 0; - - for (size_t i = 0; i < fHeader.numEntries; i++) { - uint64_t end = (uint64_t) fEntries[i].offset + fEntries[i].length; - if (maxPosn < end) { - maxPosn = end; - } - } - - fseek(fFp, 0, SEEK_END); - long fileLen = ftell(fFp); - if (fileLen < 0) { - LOGW("Unable to determine file length"); - return false; - } - if (maxPosn > (uint64_t) fileLen) { - LOGW("AS max=%llu, file len is only %ld", maxPosn, fileLen); - return false; - } - - return true; -} - -bool AppleSingleArchive::CreateEntry() -{ - AppleSingleEntry* pNewEntry = new AppleSingleEntry(this); - uint32_t dataLen = 0, rsrcLen = 0; - bool haveInfo = false; - bool hasFileName = false; - - for (size_t i = 0; i < fHeader.numEntries; i++) { - const TOCEntry* pToc = &fEntries[i]; - switch (pToc->entryId) { - case kIdDataFork: - if (pNewEntry->GetHasDataFork()) { - LOGW("Found two data forks in AppleSingle"); - return false; - } - dataLen = pToc->length; - pNewEntry->SetHasDataFork(true); - pNewEntry->SetDataOffset(pToc->offset); - pNewEntry->SetDataForkLen(pToc->length); - break; - case kIdResourceFork: - if (pNewEntry->GetHasRsrcFork()) { - LOGW("Found two rsrc forks in AppleSingle"); - return false; - } - rsrcLen = pToc->length; - pNewEntry->SetHasRsrcFork(true); - pNewEntry->SetRsrcOffset(pToc->offset); - pNewEntry->SetRsrcForkLen(pToc->length); - break; - case kIdRealName: - hasFileName = HandleRealName(pToc, pNewEntry); - break; - case kIdComment: - // We could handle this, but I don't think this is widely used. - break; - case kIdFileInfo: - HandleFileInfo(pToc, pNewEntry); - break; - case kIdFileDatesInfo: - HandleFileDatesInfo(pToc, pNewEntry); - break; - case kIdFinderInfo: - if (!haveInfo) { - HandleFinderInfo(pToc, pNewEntry); - } - break; - case kIdProDOSFileInfo: - // this take precedence over Finder info - haveInfo = HandleProDOSFileInfo(pToc, pNewEntry); - break; - case kIdBWIcon: - case kIdColorIcon: - case kIdMacintoshFileInfo: - case kIdMSDOSFileInfo: - case kIdShortName: - case kIdAFPFileInfo: - case kIdDirectoryId: - // We're not interested in these. - break; - default: - LOGD("Ignoring entry with type=%u", pToc->entryId); - break; - } - } - - pNewEntry->SetCompressedLen(dataLen + rsrcLen); - if (rsrcLen > 0) { // could do ">=" to preserve empty resource forks - pNewEntry->SetRecordKind(GenericEntry::kRecordKindForkedFile); - } else { - pNewEntry->SetRecordKind(GenericEntry::kRecordKindFile); - } - pNewEntry->SetFormatStr(L"Uncompr"); - - // If there wasn't a file name, use the AppleSingle file's name, minus - // any ".as" extension. - if (!hasFileName) { - CString fileName(PathName::FilenameOnly(GetPathName(), '\\')); - if (fileName.GetLength() > 3 && - fileName.Right(3).CompareNoCase(L".as") == 0) { - fileName = fileName.Left(fileName.GetLength() - 3); - } - // TODO: convert UTF-16 Unicode to MOR - CStringA fileNameA(fileName); - pNewEntry->SetPathNameMOR(fileNameA); - } - - // This doesn't matter, since we only have the file name, but it keeps - // the entry from getting a weird default. - pNewEntry->SetFssep(':'); - - AddEntry(pNewEntry); - return true; -} - -bool AppleSingleArchive::HandleRealName(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry) -{ - if (tocEntry->length > 1024) { - // this is a single file name, not a full path - LOGW("Ignoring excessively long filename (%u)", tocEntry->length); - return false; - } - - (void) fseek(fFp, tocEntry->offset, SEEK_SET); - - char* buf = new char[tocEntry->length + 1]; - if (fread(buf, 1, tocEntry->length, fFp) != tocEntry->length) { - LOGW("failed reading file name"); - delete[] buf; - return false; - } - buf[tocEntry->length] = '\0'; - - if (fHeader.version == kVersion1) { - // filename is in Mac OS Roman format already - pEntry->SetPathNameMOR(buf); - } else { - // filename is in UTF-8-encoded Unicode - // TODO: convert UTF-8 to MOR, dropping invalid characters - pEntry->SetPathNameMOR(buf); - } - - delete[] buf; - return true; -} - -bool AppleSingleArchive::HandleFileInfo(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry) -{ - if (strcmp(fHeader.homeFileSystem, "ProDOS ") != 0) { - LOGD("Ignoring file info for filesystem '%s'", fHeader.homeFileSystem); - return false; - } - - const int kEntrySize = 16; - - if (tocEntry->length != kEntrySize) { - LOGW("Bad length on ProDOS File Info (%d)", tocEntry->length); - return false; - } - (void) fseek(fFp, tocEntry->offset, SEEK_SET); - - uint8_t buf[kEntrySize]; - if (fread(buf, 1, kEntrySize, fFp) != kEntrySize) { - LOGW("failed reading ProDOS File Info"); - return false; - } - - uint16_t createDate, createTime, modDate, modTime, access, fileType; - uint32_t auxType; - - if (fIsBigEndian) { - createDate = Get16BE(buf); - createTime = Get16BE(buf + 2); - modDate = Get16BE(buf + 4); - modTime = Get16BE(buf + 6); - access = Get16BE(buf + 8); - fileType = Get16BE(buf + 10); - auxType = Get32BE(buf + 12); - } else { - createDate = Get16LE(buf); - createTime = Get16LE(buf + 2); - modDate = Get16LE(buf + 4); - modTime = Get16LE(buf + 6); - access = Get16LE(buf + 8); - fileType = Get16LE(buf + 10); - auxType = Get32LE(buf + 12); - } - - pEntry->SetAccess(access); - pEntry->SetFileType(fileType); - pEntry->SetAuxType(auxType); - pEntry->SetCreateWhen(ConvertProDOSDateTime(createDate, createTime)); - pEntry->SetModWhen(ConvertProDOSDateTime(modDate, modTime)); - return true; -} - -bool AppleSingleArchive::HandleFileDatesInfo(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry) -{ - const int kEntrySize = 16; - - if (tocEntry->length != kEntrySize) { - LOGW("Bad length on File Dates info (%d)", tocEntry->length); - return false; - } - (void) fseek(fFp, tocEntry->offset, SEEK_SET); - - uint8_t buf[kEntrySize]; - if (fread(buf, 1, kEntrySize, fFp) != kEntrySize) { - LOGW("failed reading File Dates info"); - return false; - } - - int32_t createDate, modDate; - if (fIsBigEndian) { - createDate = Get32BE(buf); - modDate = Get32BE(buf + 4); - // ignore backup date and access date - } else { - createDate = Get32LE(buf); - modDate = Get32LE(buf + 4); - } - - // Number of seconds between Jan 1 1970 and Jan 1 2000, computed with - // Linux mktime(). Does not include leap-seconds. - // - const int32_t kTimeOffset = 946684800; - - // The Mac OS X applesingle tool is creating entries with some pretty - // wild values, so we have to range-check them here or the Windows - // time conversion method gets bent out of shape. - // - // TODO: these are screwy enough that I'm just going to ignore them. - // If it turns out I'm holding it wrong we can re-enable it. - time_t tmpTime = (time_t) createDate + kTimeOffset; - if (tmpTime >= 0 && tmpTime <= 0xffffffffLL) { - //pEntry->SetCreateWhen(tmpTime); - } - tmpTime = (time_t) modDate + kTimeOffset; - if (tmpTime >= 0 && tmpTime <= 0xffffffffLL) { - //pEntry->SetModWhen(tmpTime); - } - - return false; -} - -bool AppleSingleArchive::HandleProDOSFileInfo(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry) -{ - const int kEntrySize = 8; - uint16_t access, fileType; - uint32_t auxType; - - if (tocEntry->length != kEntrySize) { - LOGW("Bad length on ProDOS file info (%d)", tocEntry->length); - return false; - } - (void) fseek(fFp, tocEntry->offset, SEEK_SET); - - uint8_t buf[kEntrySize]; - if (fread(buf, 1, kEntrySize, fFp) != kEntrySize) { - LOGW("failed reading ProDOS info"); - return false; - } - - if (fIsBigEndian) { - access = Get16BE(buf); - fileType = Get16BE(buf + 2); - auxType = Get32BE(buf + 4); - } else { - access = Get16LE(buf); - fileType = Get16LE(buf + 2); - auxType = Get32LE(buf + 4); - } - - pEntry->SetAccess(access); - pEntry->SetFileType(fileType); - pEntry->SetAuxType(auxType); - return true; -} - -bool AppleSingleArchive::HandleFinderInfo(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry) -{ - const int kEntrySize = 32; - const int kPdosType = 0x70646f73; // 'pdos' - uint32_t creator, macType; - - if (tocEntry->length != kEntrySize) { - LOGW("Bad length on Finder info (%d)", tocEntry->length); - return false; - } - (void) fseek(fFp, tocEntry->offset, SEEK_SET); - - uint8_t buf[kEntrySize]; - if (fread(buf, 1, kEntrySize, fFp) != kEntrySize) { - LOGW("failed reading Finder info"); - return false; - } - - // These values are stored big-endian even on Mac OS X. - macType = Get32BE(buf); - creator = Get32BE(buf + 4); - - if (creator == kPdosType && (macType >> 24) == 'p') { - pEntry->SetFileType((macType >> 16) & 0xff); - pEntry->SetAuxType(macType & 0xffff); - } else { - pEntry->SetFileType(macType); - pEntry->SetAuxType(creator); - } - return true; -} - -CString AppleSingleArchive::Reload(void) -{ - fReloadFlag = true; // tell everybody that cached data is invalid - - DeleteEntries(); - if (LoadContents() != 0) { - return L"Reload failed."; - } - - return ""; -} - -CString AppleSingleArchive::GetInfoString() -{ - CString str; - - if (fHeader.version == kVersion1) { - str += "Version 1, "; - } else { - str += "Version 2, "; - } - if (fIsBigEndian) { - str += "big endian"; - } else { - str += "little endian"; - } - - return str; -} - - -/* - * =========================================================================== - * Utility functions - * =========================================================================== - */ - -time_t AppleSingleArchive::ConvertProDOSDateTime(uint16_t prodosDate, - uint16_t prodosTime) -{ - NuDateTime ndt; - - ndt.second = 0; - ndt.minute = prodosTime & 0x3f; - ndt.hour = (prodosTime >> 8) & 0x1f; - ndt.day = (prodosDate & 0x1f) -1; - ndt.month = ((prodosDate >> 5) & 0x0f) -1; - ndt.year = (prodosDate >> 9) & 0x7f; - if (ndt.year < 40) - ndt.year += 100; /* P8 uses 0-39 for 2000-2039 */ - ndt.extra = 0; - ndt.weekDay = 0; - - return NufxArchive::DateTimeToSeconds(&ndt); -} - -void AppleSingleArchive::DumpArchive() -{ - LOGI("AppleSingleArchive: %hs magic=0x%08x, version=%08x, entries=%u", - fIsBigEndian ? "BE" : "LE", fHeader.magic, fHeader.version, - fHeader.numEntries); - LOGI(" homeFileSystem='%hs'", fHeader.homeFileSystem); - for (size_t i = 0; i < fHeader.numEntries; i++) { - LOGI(" %2u: id=%u off=%u len=%u", i, - fEntries[i].entryId, fEntries[i].offset, fEntries[i].length); - } -} diff --git a/ciderpress/app/AppleSingleArchive.h b/ciderpress/app/AppleSingleArchive.h deleted file mode 100644 index dd90be9..0000000 --- a/ciderpress/app/AppleSingleArchive.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2015 by faddenSoft. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * AppleSingle support. This format provides a way to package a single - * forked file into an ordinary file. - * - * To create a test file from Mac OS X using NuLib2 v3.0 or later: - * - extract a forked file with "nulib2 xe " - * - rename the type-preservation header off of 's data fork - * - combine the forks with "cat #nnnnr > /..namedfork/rsrc" - * - use "xattr -l " to confirm that the file has a resource fork - * and the FinderInfo with the ProDOS file type - * - use "applesingle encode " to create .as - * - * The tool does not create a spec-compliant AppleSingle file. The v2 - * spec is mildly ambiguous, but the Apple II file type note says, - * "...which is stored reverse as $00 $05 $16 $00". It appears that - * someone decided to generate little-endian AppleSingle files, and you - * have to use the magic number to figure out which end is which. - * FWIW, the Linux "file" command only recognizes the big-endian form. - * - * Perhaps unsurprisingly, the "applesingle" tool is not able to decode the - * files it creates -- but it can handle files GS/ShrinkIt creates. - * - * The GS/ShrinkIt "create AppleSingle" function creates a version 1 file - * with Mac OS Roman filenames. The Mac OS X tool creates a version 2 file - * with UTF-8-encoded Unicode filenames. We will treat the name - * accordingly, though it's possible there are v2 files with MOR strings. - */ -#ifndef APP_APPLESINGLEARCHIVE_H -#define APP_APPLESINGLEARCHIVE_H - -#include "GenericArchive.h" - - -class AppleSingleArchive; - -/* - * AppleSingle files only have one entry, so making this a separate class - * is just in keeping with the overall structure. - */ -class AppleSingleEntry : public GenericEntry { -public: - AppleSingleEntry(AppleSingleArchive* pArchive) : - fpArchive(pArchive), fDataOffset(-1), fRsrcOffset(-1) {} - virtual ~AppleSingleEntry(void) {} - - virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const override; - virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const override; - - // doesn't matter - virtual long GetSelectionSerial(void) const override { return -1; } - - virtual bool GetFeatureFlag(Feature feature) const override { - if (feature == kFeatureHasFullAccess || - feature == kFeatureHFSTypes) - { - return true; - } else { - return false; - } - } - - void SetDataOffset(long offset) { fDataOffset = offset; } - void SetRsrcOffset(long offset) { fRsrcOffset = offset; } - -private: - /* - * Copy data from the seeked archive to outfp, possibly converting EOL along - * the way. - */ - int CopyData(long srcLen, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pMsg) const; - - AppleSingleArchive* fpArchive; // holds FILE* for archive - long fDataOffset; - long fRsrcOffset; -}; - - -/* - * AppleSingle archive definition. - */ -class AppleSingleArchive : public GenericArchive { -public: - AppleSingleArchive(void) : fFp(NULL), fEntries(NULL), fIsBigEndian(false) {} - virtual ~AppleSingleArchive(void) { - (void) Close(); - delete[] fEntries; - } - - /* - * Perform one-time initialization. There really isn't any for us. - * - * Returns an error string on failure. - */ - static CString AppInit(void); - - /* - * Open an AppleSingle archive. - * - * Returns an error string on failure, or "" on success. - */ - virtual OpenResult Open(const WCHAR* filename, bool readOnly, - CString* pErrMsg) override; - - /* - * Create a new AppleSingleArchive instance. - * - * This isn't implemented, and will always return an error. - */ - virtual CString New(const WCHAR* filename, const void* options) override; - - virtual CString Flush(void) override { return L""; } - - virtual CString Reload(void) override; - virtual bool IsReadOnly(void) const override { return true; }; - virtual bool IsModified(void) const override { return false; } - virtual CString GetDescription() const override { return L"AppleSingle"; } - virtual bool BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual CString TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const override - { ASSERT(false); return L"!"; } - virtual CString TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, char newFssep) const override - { ASSERT(false); return L"!"; } - virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) override - { ASSERT(false); return false; } - virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) override - { ASSERT(false); return kXferFailed; } - virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry, - CString* pStr) override - { ASSERT(false); return false; } - virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry, - const CString& str) override - { ASSERT(false); return false; } - virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) override - { ASSERT(false); return false; } - virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) override - { ASSERT(false); return false; } - virtual void PreferencesChanged(void) override {} - virtual long GetCapability(Capability cap) override; - - // Generate a string for the "archive info" dialog. - CString GetInfoString(); - - friend class AppleSingleEntry; - -private: - // File header. "homeFileSystem" became all-zero "filler" in v2. - static const int kHomeFileSystemLen = 16; - static const int kMagicNumber = 0x00051600; - static const int kVersion1 = 0x00010000; - static const int kVersion2 = 0x00020000; - struct FileHeader { - uint32_t magic; - uint32_t version; - char homeFileSystem[kHomeFileSystemLen + 1]; - uint16_t numEntries; - }; - static const size_t kHeaderLen = 4 + 4 + kHomeFileSystemLen + 2; - - // Array of these, just past the file header. - struct TOCEntry { - uint32_t entryId; - uint32_t offset; - uint32_t length; - }; - static const size_t kEntryLen = 4 + 4 + 4; - - // predefined values for entryId - enum { - kIdDataFork = 1, - kIdResourceFork = 2, - kIdRealName = 3, - kIdComment = 4, - kIdBWIcon = 5, - kIdColorIcon = 6, - kIdFileInfo = 7, // version 1 only - kIdFileDatesInfo = 8, // version 2 only - kIdFinderInfo = 9, - kIdMacintoshFileInfo = 10, // here and below are version 2 only - kIdProDOSFileInfo = 11, - kIdMSDOSFileInfo = 12, - kIdShortName = 13, - kIdAFPFileInfo = 14, - kIdDirectoryId = 15 - }; - - virtual CString Close(void) { - if (fFp != NULL) { - fclose(fFp); - fFp = NULL; - } - return L""; - } - virtual void XferPrepare(const XferFileOptions* pXferOpts) override - { ASSERT(false); } - virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override - { ASSERT(false); return L"!"; } - virtual void XferAbort(CWnd* pMsgWnd) override - { ASSERT(false); } - virtual void XferFinish(CWnd* pMsgWnd) override - { ASSERT(false); } - - virtual ArchiveKind GetArchiveKind(void) override { return kArchiveAppleSingle; } - virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) override - { ASSERT(false); return kNuErrGeneric; } - - - /* - * Loads the contents of the archive. - * - * Returns 0 on success, < 0 if this is not an AppleSingle file, or - * > 0 if this appears to be an AppleSingle file but it's damaged. - */ - int LoadContents(); - - /* - * Confirms that the file is big enough to hold all of the entries - * listed in the table of contents. - */ - bool CheckFileLength(); - - /* - * Creates our one and only AppleSingleEntry instance by walking through - * the various bits of info. - */ - bool CreateEntry(); - - /* - * Reads the "real name" chunk, converting the character set to - * Mac OS Roman if necessary. (If we wanted to be a general AppleSingle - * tool we wouldn't do that... but we're not.) - */ - bool HandleRealName(const TOCEntry* tocEntry, AppleSingleEntry* pEntry); - - /* - * Reads the version 1 File Info chunk, which is OS-specific. The data - * layout is determined by the "home file system" string in the header. - * - * We only really want to find a ProDOS chunk. The Macintosh chunk doesn't - * have the file type in it. - * - * This will set the access, file type, aux type, create date/time, and - * modification date/time. - */ - bool HandleFileInfo(const TOCEntry* tocEntry, AppleSingleEntry* pEntry); - - /* - * Reads the version 2 File Dates Info chunk, which provides various - * dates as 32-bit seconds since Jan 1 2000 UTC. Nothing else uses - * this, making it equally inconvenient on all systems. - */ - bool HandleFileDatesInfo(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry); - - /* - * Reads a ProDOS file info block, using the values to set the access, - * file type, and aux type fields. - */ - bool HandleProDOSFileInfo(const TOCEntry* tocEntry, - AppleSingleEntry* pEntry); - - /* - * Reads a Finder info block, using the values to set the file type and - * aux type. - */ - bool HandleFinderInfo(const TOCEntry* tocEntry, AppleSingleEntry* pEntry); - - /* - * Convert from ProDOS compact date format to time_t (time in seconds - * since Jan 1 1970 UTC). - */ - time_t ConvertProDOSDateTime(uint16_t prodosDate, uint16_t prodosTime); - - void DumpArchive(); - - FILE* fFp; - bool fIsBigEndian; - FileHeader fHeader; - TOCEntry* fEntries; -}; - -#endif /*APP_APPLESINGLEARCHIVE_H*/ diff --git a/ciderpress/app/ArchiveInfoDialog.cpp b/ciderpress/app/ArchiveInfoDialog.cpp deleted file mode 100644 index 5e7b60d..0000000 --- a/ciderpress/app/ArchiveInfoDialog.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of the various ArchiveInfo dialog classes. - */ -#include "StdAfx.h" -#include "ArchiveInfoDialog.h" -#include "../nufxlib/NufxLib.h" -#include "../reformat/Charset.h" - -/* - * =========================================================================== - * ArchiveInfoDialog - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(ArchiveInfoDialog, CDialog) - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -/* - * =========================================================================== - * NufxArchiveInfoDialog - * =========================================================================== - */ - -BOOL NufxArchiveInfoDialog::OnInitDialog(void) -{ - CString notAvailable = "(not available)"; - NuArchive* pNuArchive; - const NuMasterHeader* pMasterHeader; - CWnd* pWnd; - CString tmpStr; - NuAttr attr; - NuError nerr; - time_t when; - - ASSERT(fpArchive != NULL); - - pNuArchive = fpArchive->GetNuArchivePointer(); - ASSERT(pNuArchive != NULL); - (void) NuGetMasterHeader(pNuArchive, &pMasterHeader); - ASSERT(pMasterHeader != NULL); - - pWnd = GetDlgItem(IDC_AI_FILENAME); - CString pathName(fpArchive->GetPathName()); - pWnd->SetWindowText(pathName); - - pWnd = GetDlgItem(IDC_AINUFX_RECORDS); - nerr = NuGetAttr(pNuArchive, kNuAttrNumRecords, &attr); - if (nerr == kNuErrNone) - tmpStr.Format(L"%ld", attr); - else - tmpStr = notAvailable; - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AINUFX_FORMAT); - nerr = NuGetAttr(pNuArchive, kNuAttrArchiveType, &attr); - switch (attr) { - case kNuArchiveNuFX: tmpStr = L"NuFX"; break; - case kNuArchiveNuFXInBNY: tmpStr = L"NuFX in Binary II"; break; - case kNuArchiveNuFXSelfEx: tmpStr = L"Self-extracting NuFX"; break; - case kNuArchiveNuFXSelfExInBNY: tmpStr = L"Self-extracting NuFX in Binary II"; - break; - case kNuArchiveBNY: tmpStr = L"Binary II"; break; - default: - tmpStr = L"(unknown)"; - break; - }; - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AINUFX_MASTERVERSION); - tmpStr.Format(L"%ld", pMasterHeader->mhMasterVersion); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AINUFX_CREATEWHEN); - when = NufxArchive::DateTimeToSeconds(&pMasterHeader->mhArchiveCreateWhen); - tmpStr.Format(L"%.24hs", ctime(&when)); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AINUFX_MODIFYWHEN); - when = NufxArchive::DateTimeToSeconds(&pMasterHeader->mhArchiveModWhen); - tmpStr.Format(L"%.24hs", ctime(&when)); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AINUFX_JUNKSKIPPED); - nerr = NuGetAttr(pNuArchive, kNuAttrJunkOffset, &attr); - if (nerr == kNuErrNone) - tmpStr.Format(L"%ld bytes", attr); - else - tmpStr = notAvailable; - pWnd->SetWindowText(tmpStr); - - return ArchiveInfoDialog::OnInitDialog(); -} - - -/* - * =========================================================================== - * DiskArchiveInfoDialog - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(DiskArchiveInfoDialog, ArchiveInfoDialog) - ON_CBN_SELCHANGE(IDC_AIDISK_SUBVOLSEL, OnSubVolSelChange) -END_MESSAGE_MAP() - -BOOL DiskArchiveInfoDialog::OnInitDialog(void) -{ - CWnd* pWnd; - CString tmpStr; - const DiskImg* pDiskImg; - const DiskFS* pDiskFS; - - ASSERT(fpArchive != NULL); - - pDiskImg = fpArchive->GetDiskImg(); - ASSERT(pDiskImg != NULL); - pDiskFS = fpArchive->GetDiskFS(); - ASSERT(pDiskFS != NULL); - - /* - * Volume characteristics. - */ - pWnd = GetDlgItem(IDC_AI_FILENAME); - pWnd->SetWindowText(fpArchive->GetPathName()); - - pWnd = GetDlgItem(IDC_AIDISK_OUTERFORMAT); - CStringW outerFormat(DiskImg::ToString(pDiskImg->GetOuterFormat())); - pWnd->SetWindowText(outerFormat); - - pWnd = GetDlgItem(IDC_AIDISK_FILEFORMAT); - CStringW fileFormat(DiskImg::ToString(pDiskImg->GetFileFormat())); - pWnd->SetWindowText(fileFormat); - - pWnd = GetDlgItem(IDC_AIDISK_PHYSICALFORMAT); - DiskImg::PhysicalFormat physicalFormat = pDiskImg->GetPhysicalFormat(); - if (physicalFormat == DiskImg::kPhysicalFormatNib525_6656 || - physicalFormat == DiskImg::kPhysicalFormatNib525_6384 || - physicalFormat == DiskImg::kPhysicalFormatNib525_Var) - { - CString tmpStr; - const DiskImg::NibbleDescr* pNibbleDescr = pDiskImg->GetNibbleDescr(); - if (pNibbleDescr != NULL) - tmpStr.Format(L"%hs, layout is \"%hs\"", - DiskImg::ToString(physicalFormat), pNibbleDescr->description); - else - tmpStr = DiskImg::ToString(physicalFormat); // unexpected - pWnd->SetWindowText(tmpStr); - } else { - CString physicalFormat(DiskImg::ToString(physicalFormat)); - pWnd->SetWindowText(physicalFormat); - } - - FillInVolumeInfo(pDiskFS); - - /* - * Configure the sub-volume drop down menu. If there's only one item, - * we disable it. - */ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL); - int idx = 0; - - AddSubVolumes(pDiskFS, L"", &idx); - ASSERT(idx > 0); // must have at least the top-level DiskFS - - pCombo->SetCurSel(0); - if (idx == 1) - pCombo->EnableWindow(FALSE); - - return ArchiveInfoDialog::OnInitDialog(); -} - -void DiskArchiveInfoDialog::AddSubVolumes(const DiskFS* pDiskFS, - const WCHAR* prefix, int* pIdx) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL); - - /* - * Add the current DiskFS. - */ - CString tmpStr(prefix); - tmpStr += Charset::ConvertMORToUNI(pDiskFS->GetVolumeID()); - pCombo->AddString(tmpStr); - pCombo->SetItemData(*pIdx, (unsigned long) pDiskFS); - (*pIdx)++; - - /* - * Add everything beneath the current level. - */ - DiskFS::SubVolume* pSubVol; - pSubVol = pDiskFS->GetNextSubVolume(NULL); - tmpStr = prefix; - tmpStr += L" "; - while (pSubVol != NULL) { - AddSubVolumes(pSubVol->GetDiskFS(), tmpStr, pIdx); - - pSubVol = pDiskFS->GetNextSubVolume(pSubVol); - } -} - -void DiskArchiveInfoDialog::OnSubVolSelChange(void) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL); - ASSERT(pCombo != NULL); - //LOGI("+++ SELECTION IS NOW %d", pCombo->GetCurSel()); - - const DiskFS* pDiskFS; - pDiskFS = (DiskFS*) pCombo->GetItemData(pCombo->GetCurSel()); - ASSERT(pDiskFS != NULL); - FillInVolumeInfo(pDiskFS); -} - -void DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS) -{ - const DiskImg* pDiskImg = pDiskFS->GetDiskImg(); - CString unknown = L"(unknown)"; - CString tmpStr; - DIError dierr; - CWnd* pWnd; - - pWnd = GetDlgItem(IDC_AIDISK_SECTORORDER); - CStringW sectorOrderW(DiskImg::ToString(pDiskImg->GetSectorOrder())); - pWnd->SetWindowText(sectorOrderW); - - pWnd = GetDlgItem(IDC_AIDISK_FSFORMAT); - CStringW fsFormat(DiskImg::ToString(pDiskImg->GetFSFormat())); - pWnd->SetWindowText(fsFormat); - - pWnd = GetDlgItem(IDC_AIDISK_FILECOUNT); - tmpStr.Format(L"%ld", pDiskFS->GetFileCount()); - pWnd->SetWindowText(tmpStr); - - long totalUnits, freeUnits; - int unitSize; - CString reducedSize; - - dierr = pDiskFS->GetFreeSpaceCount(&totalUnits, &freeUnits, &unitSize); - if (dierr == kDIErrNone) { - - /* got the space; break it down by disk type */ - if (unitSize == DiskImgLib::kBlockSize) { - pWnd = GetDlgItem(IDC_AIDISK_CAPACITY); - GetReducedSize(totalUnits, unitSize, &reducedSize); - tmpStr.Format(L"%ld blocks (%ls)", - totalUnits, (LPCWSTR) reducedSize); - if (totalUnits != pDiskImg->GetNumBlocks()) { - CString tmpStr2; - tmpStr2.Format(L", image has room for %ld blocks", - pDiskImg->GetNumBlocks()); - tmpStr += tmpStr2; - } - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AIDISK_FREESPACE); - GetReducedSize(freeUnits, unitSize, &reducedSize); - tmpStr.Format(L"%ld blocks (%ls)", - freeUnits, (LPCWSTR) reducedSize); - pWnd->SetWindowText(tmpStr); - } else { - ASSERT(unitSize == DiskImgLib::kSectorSize); - - pWnd = GetDlgItem(IDC_AIDISK_CAPACITY); - GetReducedSize(totalUnits, unitSize, &reducedSize); - tmpStr.Format(L"%ld sectors (%ls)", - totalUnits, (LPCWSTR) reducedSize); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AIDISK_FREESPACE); - GetReducedSize(freeUnits, unitSize, &reducedSize); - tmpStr.Format(L"%ld sectors (%ls)", - freeUnits, (LPCWSTR) reducedSize); - pWnd->SetWindowText(tmpStr); - } - } else { - /* "free space" not supported; fill in what we do know */ - pWnd = GetDlgItem(IDC_AIDISK_CAPACITY); - if (pDiskImg->GetHasBlocks()) { - totalUnits = pDiskImg->GetNumBlocks(); - GetReducedSize(totalUnits, DiskImgLib::kBlockSize, &reducedSize); - tmpStr.Format(L"%ld blocks (%ls)", - totalUnits, (LPCWSTR) reducedSize); - } else if (pDiskImg->GetHasSectors()) { - tmpStr.Format(L"%ld tracks, %d sectors per track", - pDiskImg->GetNumTracks(), pDiskImg->GetNumSectPerTrack()); - } else { - tmpStr = unknown; - } - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AIDISK_FREESPACE); - pWnd->SetWindowText(unknown); - } - - pWnd = GetDlgItem(IDC_AIDISK_WRITEABLE); - tmpStr = pDiskFS->GetReadWriteSupported() ? L"Yes" : L"No"; - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_AIDISK_DAMAGED); - tmpStr = pDiskFS->GetFSDamaged() ? L"Yes" : L"No"; - pWnd->SetWindowText(tmpStr); - - const char* cp; - WCHAR* outp; - - pWnd = GetDlgItem(IDC_AIDISK_NOTES); - cp = pDiskImg->GetNotes(); - // GetBuffer wants length in code units, which will be 2x since it's - // wide chars. The 2x mult below is for worst-case linefeed conversion. - outp = tmpStr.GetBuffer(strlen(cp) * 2 +1); - /* convert '\n' to '\r\n' */ - while (*cp != '\0') { - if (*cp == '\n') - *outp++ = '\r'; - *outp++ = *cp++; - } - *outp = '\0'; - tmpStr.ReleaseBuffer(); - /* drop the trailing linefeed */ - if (!tmpStr.IsEmpty() && tmpStr.GetAt(tmpStr.GetLength()-1) == '\n') - tmpStr.TrimRight(); // trim the whitespace chars off - pWnd->SetWindowText(tmpStr); -} - -void DiskArchiveInfoDialog::GetReducedSize(long numUnits, int unitSize, - CString* pOut) const -{ - LONGLONG sizeInBytes = numUnits; - sizeInBytes *= unitSize; - long reducedSize; - - if (sizeInBytes < 0) { - ASSERT(false); - pOut->Format(L""); - return; - } - - if (sizeInBytes >= 1024*1024*1024) { - reducedSize = (long) (sizeInBytes / (1024*1024)); - pOut->Format(L"%.2fGB", reducedSize / 1024.0); - } else if (sizeInBytes >= 1024*1024) { - reducedSize = (long) (sizeInBytes / 1024); - pOut->Format(L"%.2fMB", reducedSize / 1024.0); - } else { - pOut->Format(L"%.2fKB", ((long) sizeInBytes) / 1024.0); - } -} - - -/* - * =========================================================================== - * BnyArchiveInfoDialog - * =========================================================================== - */ - -BOOL BnyArchiveInfoDialog::OnInitDialog(void) -{ - CWnd* pWnd; - CString tmpStr; - - ASSERT(fpArchive != NULL); - - pWnd = GetDlgItem(IDC_AI_FILENAME); - pWnd->SetWindowText(fpArchive->GetPathName()); - tmpStr.Format(L"%ld", fpArchive->GetNumEntries()); - pWnd = GetDlgItem(IDC_AIBNY_RECORDS); - pWnd->SetWindowText(tmpStr); - - return ArchiveInfoDialog::OnInitDialog(); -} - - -/* - * =========================================================================== - * AcuArchiveInfoDialog - * =========================================================================== - */ - -BOOL AcuArchiveInfoDialog::OnInitDialog(void) -{ - CWnd* pWnd; - CString tmpStr; - - ASSERT(fpArchive != NULL); - - pWnd = GetDlgItem(IDC_AI_FILENAME); - pWnd->SetWindowText(fpArchive->GetPathName()); - tmpStr.Format(L"%ld", fpArchive->GetNumEntries()); - pWnd = GetDlgItem(IDC_AIBNY_RECORDS); - pWnd->SetWindowText(tmpStr); - - return ArchiveInfoDialog::OnInitDialog(); -} - -/* - * =========================================================================== - * AppleSingleArchiveInfoDialog - * =========================================================================== - */ - -BOOL AppleSingleArchiveInfoDialog::OnInitDialog(void) -{ - CWnd* pWnd; - - ASSERT(fpArchive != NULL); - - pWnd = GetDlgItem(IDC_AI_FILENAME); - pWnd->SetWindowText(fpArchive->GetPathName()); - pWnd = GetDlgItem(IDC_AIBNY_RECORDS); - pWnd->SetWindowText(fpArchive->GetInfoString()); - - return ArchiveInfoDialog::OnInitDialog(); -} diff --git a/ciderpress/app/ArchiveInfoDialog.h b/ciderpress/app/ArchiveInfoDialog.h deleted file mode 100644 index 0b48334..0000000 --- a/ciderpress/app/ArchiveInfoDialog.h +++ /dev/null @@ -1,151 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Definitions for the ArchiveInfo set of dialog classes. - */ -#ifndef APP_ARCHIVEINFODIALOG_H -#define APP_ARCHIVEINFODIALOG_H - -#include "resource.h" -#include "GenericArchive.h" -#include "NufxArchive.h" -#include "DiskArchive.h" -#include "BnyArchive.h" -#include "AcuArchive.h" -#include "AppleSingleArchive.h" - -/* - * This is an abstract base class for the archive info dialogs. There is - * one dialog for each kind of archive (i.e. each GenericArchive sub-class). - */ -class ArchiveInfoDialog : public CDialog { -public: - ArchiveInfoDialog(UINT dialogID, CWnd* pParentWnd = NULL) : - CDialog(dialogID, pParentWnd) - {} - virtual ~ArchiveInfoDialog(void) {} - -private: - /* - * Show general help for the archive info dialogs. - */ - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_ARCHIVE_INFO); - } - - DECLARE_MESSAGE_MAP() -}; - -/* - * NuFX archive info. - */ -class NufxArchiveInfoDialog : public ArchiveInfoDialog { -public: - NufxArchiveInfoDialog(NufxArchive* pArchive, CWnd* pParentWnd = NULL) : - fpArchive(pArchive), - ArchiveInfoDialog(IDD_ARCHIVEINFO_NUFX, pParentWnd) - {} - virtual ~NufxArchiveInfoDialog(void) {} - -private: - virtual BOOL OnInitDialog(void) override; - - NufxArchive* fpArchive; -}; - -/* - * Disk image info. - */ -class DiskArchiveInfoDialog : public ArchiveInfoDialog { -public: - DiskArchiveInfoDialog(DiskArchive* pArchive, CWnd* pParentWnd = NULL) : - fpArchive(pArchive), - ArchiveInfoDialog(IDD_ARCHIVEINFO_DISK, pParentWnd) - {} - virtual ~DiskArchiveInfoDialog(void) {} - -private: - virtual BOOL OnInitDialog(void) override; - - /* - * The user has changed their selection in the sub-volume pulldown menu. - */ - afx_msg void OnSubVolSelChange(void); - - /* - * Fill in the volume-specific info fields. - */ - void FillInVolumeInfo(const DiskFS* pDiskFS); - - /* - * Recursively add sub-volumes to the list. - */ - void AddSubVolumes(const DiskFS* pDiskFS, const WCHAR* prefix, - int* pIdx); - - /* - * Reduce a size to something meaningful (KB, MB, GB). - */ - void GetReducedSize(long numUnits, int unitSize, - CString* pOut) const; - - DiskArchive* fpArchive; - - DECLARE_MESSAGE_MAP() -}; - -/* - * Binary II archive info. - */ -class BnyArchiveInfoDialog : public ArchiveInfoDialog { -public: - BnyArchiveInfoDialog(BnyArchive* pArchive, CWnd* pParentWnd = NULL) : - fpArchive(pArchive), - ArchiveInfoDialog(IDD_ARCHIVEINFO_BNY, pParentWnd) - {} - virtual ~BnyArchiveInfoDialog(void) {} - -private: - virtual BOOL OnInitDialog(void) override; - - BnyArchive* fpArchive; -}; - -/* - * ACU archive info. - */ -class AcuArchiveInfoDialog : public ArchiveInfoDialog { -public: - AcuArchiveInfoDialog(AcuArchive* pArchive, CWnd* pParentWnd = NULL) : - fpArchive(pArchive), - ArchiveInfoDialog(IDD_ARCHIVEINFO_ACU, pParentWnd) - {} - virtual ~AcuArchiveInfoDialog(void) {} - -private: - virtual BOOL OnInitDialog(void) override; - - AcuArchive* fpArchive; -}; - -/* - * AppleSingle archive info. - */ -class AppleSingleArchiveInfoDialog : public ArchiveInfoDialog { -public: - AppleSingleArchiveInfoDialog(AppleSingleArchive* pArchive, CWnd* pParentWnd = NULL) : - fpArchive(pArchive), - ArchiveInfoDialog(IDD_ARCHIVEINFO_APPLESINGLE, pParentWnd) - {} - virtual ~AppleSingleArchiveInfoDialog(void) {} - -private: - virtual BOOL OnInitDialog(void) override; - - AppleSingleArchive* fpArchive; -}; - -#endif /*APP_ARCHIVEINFODIALOG_H*/ diff --git a/ciderpress/app/BNYArchive.cpp b/ciderpress/app/BNYArchive.cpp deleted file mode 100644 index 0c762cd..0000000 --- a/ciderpress/app/BNYArchive.cpp +++ /dev/null @@ -1,888 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Binary II file support. - */ -#include "stdafx.h" -#include "BNYArchive.h" -#include "NufxArchive.h" -#include "Preferences.h" -#include "Main.h" -#include "Squeeze.h" -#include - - -/* - * =========================================================================== - * BnyEntry - * =========================================================================== - */ - -int BnyEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const -{ - NuError nerr; - ExpandBuffer expBuf; - char* dataBuf = NULL; - long len; - bool needAlloc = true; - int result = -1; - - ASSERT(fpArchive != NULL); - ASSERT(fpArchive->fFp != NULL); - - if (*ppText != NULL) - needAlloc = false; - - if (which != kDataThread) { - *pErrMsg = "No such fork"; - goto bail; - } - - len = (long) GetUncompressedLen(); - if (len == 0) { - if (needAlloc) { - *ppText = new char[1]; - **ppText = '\0'; - } - *pLength = 0; - result = IDOK; - goto bail; - } - - SET_PROGRESS_BEGIN(); - - errno = 0; - if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format(L"Unable to seek to offset %ld: %hs", - fOffset, strerror(errno)); - goto bail; - } - - if (GetSqueezed()) { - nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(), - &expBuf, true, kBNYBlockSize); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); - goto bail; - } - - char* unsqBuf = NULL; - long unsqLen = 0; - expBuf.SeizeBuffer(&unsqBuf, &unsqLen); - LOGI("Unsqueezed %ld bytes to %d", len, unsqLen); - if (unsqLen == 0) { - // some bonehead squeezed a zero-length file - delete[] unsqBuf; - ASSERT(*ppText == NULL); - LOGI("Handling zero-length squeezed file!"); - if (needAlloc) { - *ppText = new char[1]; - **ppText = '\0'; - } - *pLength = 0; - } else { - if (needAlloc) { - /* just use the seized buffer */ - *ppText = unsqBuf; - *pLength = unsqLen; - } else { - if (*pLength < unsqLen) { - pErrMsg->Format(L"buf size %ld too short (%ld)", - *pLength, unsqLen); - delete[] unsqBuf; - goto bail; - } - - memcpy(*ppText, unsqBuf, unsqLen); - delete[] unsqBuf; - *pLength = unsqLen; - } - } - - } else { - if (needAlloc) { - dataBuf = new char[len]; - if (dataBuf == NULL) { - pErrMsg->Format(L"allocation of %ld bytes failed", len); - goto bail; - } - } else { - if (*pLength < (long) len) { - pErrMsg->Format(L"buf size %ld too short (%ld)", - *pLength, len); - goto bail; - } - dataBuf = *ppText; - } - if (fread(dataBuf, len, 1, fpArchive->fFp) != 1) { - pErrMsg->Format(L"File read failed: %hs", strerror(errno)); - goto bail; - } - - if (needAlloc) - *ppText = dataBuf; - *pLength = len; - } - - result = IDOK; - -bail: - if (result == IDOK) { - SET_PROGRESS_END(); - ASSERT(pErrMsg->IsEmpty()); - } else { - ASSERT(result == IDCANCEL || !pErrMsg->IsEmpty()); - if (needAlloc) { - delete[] dataBuf; - ASSERT(*ppText == NULL); - } - } - return result; -} - -int BnyEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const -{ - NuError nerr; - long len; - int result = -1; - - ASSERT(IDOK != -1 && IDCANCEL != -1); - if (which != kDataThread) { - *pErrMsg = L"No such fork"; - goto bail; - } - - len = (long) GetUncompressedLen(); - if (len == 0) { - LOGI("Empty fork"); - result = IDOK; - goto bail; - } - - errno = 0; - if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - pErrMsg->Format(L"Unable to seek to offset %ld: %hs", - fOffset, strerror(errno)); - goto bail; - } - - SET_PROGRESS_BEGIN(); - - /* - * Generally speaking, anything in a BNY file is going to be small. The - * major exception is a BXY file, which could be huge. However, the - * SHK embedded in a BXY is never squeezed. - * - * To make life easy, we either unsqueeze the entire thing into a buffer - * and then write that, or we do a file-to-file copy of the specified - * number of bytes. - */ - if (GetSqueezed()) { - ExpandBuffer expBuf; - bool lastCR = false; - char* buf; - long uncLen; - - nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(), - &expBuf, true, kBNYBlockSize); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"File read failed: %hs", NuStrError(nerr)); - goto bail; - } - - expBuf.SeizeBuffer(&buf, &uncLen); - LOGI("Unsqueezed %ld bytes to %d", len, uncLen); - - // some bonehead squeezed a zero-length file - if (uncLen == 0) { - ASSERT(buf == NULL); - LOGI("Handling zero-length squeezed file!"); - result = IDOK; - goto bail; - } - - int err = GenericEntry::WriteConvert(outfp, buf, uncLen, &conv, - &convHA, &lastCR); - if (err != 0) { - pErrMsg->Format(L"File write failed: %hs", strerror(err)); - delete[] buf; - goto bail; - } - - delete[] buf; - } else { - nerr = CopyData(outfp, conv, convHA, pErrMsg); - if (nerr != kNuErrNone) { - if (pErrMsg->IsEmpty()) { - pErrMsg->Format(L"Failed while copying data: %hs\n", - NuStrError(nerr)); - } - goto bail; - } - } - - result = IDOK; - -bail: - SET_PROGRESS_END(); - return result; -} - -NuError BnyEntry::CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, - CString* pMsg) const -{ - NuError nerr = kNuErrNone; - const int kChunkSize = 8192; - char buf[kChunkSize]; - bool lastCR = false; - long srcLen, dataRem; - - srcLen = (long) GetUncompressedLen(); - ASSERT(srcLen > 0); // empty files should've been caught earlier - - /* - * Loop until all data copied. - */ - dataRem = srcLen; - while (dataRem) { - int chunkLen; - - if (dataRem > kChunkSize) - chunkLen = kChunkSize; - else - chunkLen = dataRem; - - /* read a chunk from the source file */ - nerr = fpArchive->BNYRead(buf, chunkLen); - if (nerr != kNuErrNone) { - pMsg->Format(L"File read failed: %hs.", NuStrError(nerr)); - goto bail; - } - - /* write chunk to destination file */ - int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, - &convHA, &lastCR); - if (err != 0) { - pMsg->Format(L"File write failed: %hs.", strerror(err)); - nerr = kNuErrGeneric; - goto bail; - } - - dataRem -= chunkLen; - SET_PROGRESS_UPDATE(ComputePercent(srcLen - dataRem, srcLen)); - } - -bail: - return nerr; -} - -NuError BnyEntry::TestEntry(CWnd* pMsgWnd) -{ - NuError nerr = kNuErrNone; - CString errMsg; - long len; - int result = -1; - - len = (long) GetUncompressedLen(); - if (len == 0) - goto bail; - - errno = 0; - if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) { - nerr = kNuErrGeneric; - errMsg.Format(L"Unable to seek to offset %ld: %hs\n", - fOffset, strerror(errno)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - if (GetSqueezed()) { - nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(), - NULL, true, kBNYBlockSize); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unsqueeze failed: %hs.", NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - } else { - errno = 0; - if (fseek(fpArchive->fFp, fOffset + len, SEEK_SET) < 0) { - nerr = kNuErrGeneric; - errMsg.Format(L"Unable to seek to offset %ld (file truncated?): %hs\n", - fOffset, strerror(errno)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - } - - if (SET_PROGRESS_UPDATE(100) == IDCANCEL) - nerr = kNuErrAborted; - -bail: - return nerr; -} - - -/* - * =========================================================================== - * BnyArchive - * =========================================================================== - */ - -/*static*/ CString BnyArchive::AppInit(void) -{ - // We don't really have anything to initialize. Having this method - // is kind of silly, but we include it for consistency. - return L""; -} - -GenericArchive::OpenResult BnyArchive::Open(const WCHAR* filename, - bool readOnly, CString* pErrMsg) -{ - CString errMsg; - - fIsReadOnly = true; // ignore "readOnly" - - errno = 0; - fFp = _wfopen(filename, L"rb"); - if (fFp == NULL) { - errMsg.Format(L"Unable to open %ls: %hs.", filename, strerror(errno)); - goto bail; - } - - { - CWaitCursor waitc; - - if (LoadContents() != 0) { - errMsg.Format(L"Failed while loading contents of Binary II file."); - goto bail; - } - } - - SetPathName(filename); - -bail: - *pErrMsg = errMsg; - if (!errMsg.IsEmpty()) - return kResultFailure; - else - return kResultSuccess; -} - -CString BnyArchive::New(const WCHAR* /*filename*/, const void* /*options*/) -{ - CString retmsg(L"Sorry, Binary II files can't be created."); - return retmsg; -} - - -long BnyArchive::GetCapability(Capability cap) -{ - switch (cap) { - case kCapCanTest: - return true; - break; - case kCapCanRenameFullPath: - return true; - break; - case kCapCanRecompress: - return true; - break; - case kCapCanEditComment: - return false; - break; - case kCapCanAddDisk: - return false; - break; - case kCapCanConvEOLOnAdd: - return false; - break; - case kCapCanCreateSubdir: - return false; - break; - case kCapCanRenameVolume: - return false; - break; - default: - ASSERT(false); - return -1; - break; - } -} - -int BnyArchive::LoadContents(void) -{ - NuError nerr; - - ASSERT(fFp != NULL); - rewind(fFp); - - nerr = BNYIterate(); - LOGI("BNYIterate returned %d", nerr); - return (nerr != kNuErrNone); -} - -CString BnyArchive::Reload(void) -{ - fReloadFlag = true; // tell everybody that cached data is invalid - - DeleteEntries(); - if (LoadContents() != 0) { - return L"Reload failed."; - } - - return ""; -} - -NuError BnyArchive::LoadContentsCallback(BnyFileEntry* pEntry) -{ - const int kBNYFssep = '/'; - NuError err = kNuErrNone; - BnyEntry* pNewEntry; - char* fileName; - - - /* make sure filename doesn't start with '/' (not allowed by BNY spec) */ - fileName = pEntry->fileName; - while (*fileName == kBNYFssep) - fileName++; - if (*fileName == '\0') - return kNuErrBadData; - - /* remove '.QQ' from end of squeezed files */ - bool isSqueezed = false; - if (pEntry->realEOF && IsSqueezed(pEntry->blockBuf[0], pEntry->blockBuf[1])) - isSqueezed = true; - - if (isSqueezed && strlen(fileName) > 3) { - char* ext; - ext = fileName + strlen(fileName) -3; - if (stricmp(ext, ".qq") == 0) - *ext = '\0'; - } - - /* - * Create the new entry. - */ - pNewEntry = new BnyEntry(this); - pNewEntry->SetPathNameMOR(fileName); - pNewEntry->SetFssep(kBNYFssep); - pNewEntry->SetFileType(pEntry->fileType); - pNewEntry->SetAuxType(pEntry->auxType); - pNewEntry->SetAccess(pEntry->access); - pNewEntry->SetCreateWhen(NufxArchive::DateTimeToSeconds(&pEntry->createWhen)); - pNewEntry->SetModWhen(NufxArchive::DateTimeToSeconds(&pEntry->modWhen)); - - /* always ProDOS */ - pNewEntry->SetSourceFS(DiskImg::kFormatProDOS); - - pNewEntry->SetHasDataFork(true); - pNewEntry->SetHasRsrcFork(false); - if (IsDir(pEntry)) { - pNewEntry->SetRecordKind(GenericEntry::kRecordKindDirectory); - } else { - pNewEntry->SetRecordKind(GenericEntry::kRecordKindFile); - } - - /* there's no way to get the uncompressed EOF from a squeezed file */ - pNewEntry->SetCompressedLen(pEntry->realEOF); - pNewEntry->SetDataForkLen(pEntry->realEOF); - - if (isSqueezed) - pNewEntry->SetFormatStr(L"Squeeze"); - else - pNewEntry->SetFormatStr(L"Uncompr"); - - pNewEntry->SetSqueezed(isSqueezed); - if (pEntry->realEOF != 0) - pNewEntry->SetOffset(ftell(fFp) - kBNYBlockSize); - else - pNewEntry->SetOffset(ftell(fFp)); - - AddEntry(pNewEntry); - - return err; -} - - -/* - * =========================================================================== - * Binary II functions - * =========================================================================== - */ - -/* - * Most of what follows was adapted directly from NuLib2 v2.0. There's no - * such thing as BnyLib, so all of the code for manipulating the file is - * included here. - */ - -bool BnyArchive::IsSqueezed(uint8_t one, uint8_t two) -{ - return (one == 0x76 && two == 0xff); -} - -bool BnyArchive::IsDir(BnyFileEntry* pEntry) -{ - /* - * NuLib and "unblu.c" compared against file type 15 (DIR), so I'm - * going to do that too, but it would probably be better to compare - * against storageType 0x0d. - */ - return (pEntry->fileType == 15); -} - -NuError BnyArchive::BNYRead(void* buf, size_t nbyte) -{ - size_t result; - - ASSERT(buf != NULL); - ASSERT(nbyte > 0); - ASSERT(fFp != NULL); - - errno = 0; - result = fread(buf, 1, nbyte, fFp); - if (result != nbyte) - return errno ? (NuError)errno : kNuErrFileRead; - return kNuErrNone; -} - -NuError BnyArchive::BNYSeek(long offset) -{ - ASSERT(fFp != NULL); - ASSERT(offset > 0); - - LOGV("--- seeking forward %ld bytes\n", offset); - - if (fseek(fFp, offset, SEEK_CUR) < 0) - return kNuErrFileSeek; - - return kNuErrNone; -} - - -void BnyArchive::BNYConvertDateTime(unsigned short prodosDate, - unsigned short prodosTime, NuDateTime* pWhen) -{ - pWhen->second = 0; - pWhen->minute = prodosTime & 0x3f; - pWhen->hour = (prodosTime >> 8) & 0x1f; - pWhen->day = (prodosDate & 0x1f) -1; - pWhen->month = ((prodosDate >> 5) & 0x0f) -1; - pWhen->year = (prodosDate >> 9) & 0x7f; - if (pWhen->year < 40) - pWhen->year += 100; /* P8 uses 0-39 for 2000-2039 */ - pWhen->extra = 0; - pWhen->weekDay = 0; -} - -NuError BnyArchive::BNYDecodeHeader(BnyFileEntry* pEntry) -{ - /* - * See the File Type Note for $e0/8000 to decipher the buffer offsets - * and meanings. - */ - NuError err = kNuErrNone; - uint8_t* raw; - int len; - - ASSERT(pEntry != NULL); - - raw = pEntry->blockBuf; - - if (raw[0] != 0x0a || raw[1] != 0x47 || raw[2] != 0x4c || raw[18] != 0x02) { - err = kNuErrBadData; - LOGI("this doesn't look like a Binary II header"); - goto bail; - } - - pEntry->access = raw[3] | raw[111] << 8; - pEntry->fileType = raw[4] | raw[112] << 8; - pEntry->auxType = raw[5] | raw[6] << 8 | raw[109] << 16 | raw[110] << 24; - pEntry->storageType = raw[7]; - pEntry->fileSize = raw[8] | raw[9] << 8; - pEntry->prodosModDate = raw[10] | raw[11] << 8; - pEntry->prodosModTime = raw[12] | raw[13] << 8; - BNYConvertDateTime(pEntry->prodosModDate, pEntry->prodosModTime, - &pEntry->modWhen); - pEntry->prodosCreateDate = raw[14] | raw[15] << 8; - pEntry->prodosCreateTime = raw[16] | raw[17] << 8; - BNYConvertDateTime(pEntry->prodosCreateDate, pEntry->prodosCreateTime, - &pEntry->createWhen); - pEntry->eof = raw[20] | raw[21] << 8 | raw[22] << 16 | raw[116] << 24; - len = raw[23]; - if (len > kBNYMaxFileName) { - err = kNuErrBadData; - LOGI("invalid filename length %d", len); - goto bail; - } - memcpy(pEntry->fileName, &raw[24], len); - pEntry->fileName[len] = '\0'; - - pEntry->nativeName[0] = '\0'; - if (len <= 15 && raw[39] != 0) { - len = raw[39]; - if (len > kBNYMaxNativeName) { - err = kNuErrBadData; - LOGI("invalid filename length %d", len); - goto bail; - } - memcpy(pEntry->nativeName, &raw[40], len); - pEntry->nativeName[len] = '\0'; - } - - pEntry->diskSpace = raw[117] | raw[118] << 8 | raw[119] << 16 | - raw[120] << 24; - - pEntry->osType = raw[121]; - pEntry->nativeFileType = raw[122] | raw[123] << 8; - pEntry->phantomFlag = raw[124]; - pEntry->dataFlags = raw[125]; - pEntry->version = raw[126]; - pEntry->filesToFollow = raw[127]; - - /* directories are given an EOF but don't actually have any content */ - if (IsDir(pEntry)) - pEntry->realEOF = 0; - else - pEntry->realEOF = pEntry->eof; - -bail: - return err; -} - -#if 0 -/* - * Normalize the pathname by running it through the usual NuLib2 - * function. The trick here is that the function usually takes a - * NuPathnameProposal, which we don't happen to have handy. Rather - * than generalize the NuLib2 code, we just create a fake proposal, - * which is a bit dicey but shouldn't break too easily. - * - * This takes care of -e, -ee, and -j. - * - * We return the new path, which is stored in NulibState's temporary - * filename buffer. - */ -const char* -BNYNormalizePath(BnyFileEntry* pEntry) -{ - NuPathnameProposal pathProposal; - NuRecord fakeRecord; - NuThread fakeThread; - - /* make uninitialized data obvious */ - memset(&fakeRecord, 0xa1, sizeof(fakeRecord)); - memset(&fakeThread, 0xa5, sizeof(fakeThread)); - - pathProposal.pathname = pEntry->fileName; - pathProposal.filenameSeparator = '/'; /* BNY always uses ProDOS conv */ - pathProposal.pRecord = &fakeRecord; - pathProposal.pThread = &fakeThread; - - pathProposal.newPathname = NULL; - pathProposal.newFilenameSeparator = '\0'; - pathProposal.newDataSink = NULL; - - /* need the filetype and auxtype for -e/-ee */ - fakeRecord.recFileType = pEntry->fileType; - fakeRecord.recExtraType = pEntry->auxType; - - /* need the components of a ThreadID */ - fakeThread.thThreadClass = kNuThreadClassData; - fakeThread.thThreadKind = 0x0000; /* data fork */ - - return NormalizePath(pBny->pState, &pathProposal); -} -#endif - -#if 0 -/* - * Copy all data from the Binary II file to "outfp", reading in 128-byte - * blocks. - * - * Uses pEntry->blockBuf, which already has the first 128 bytes in it. - */ -NuError -BnyArchive::BNYCopyBlocks(BnyFileEntry* pEntry, FILE* outfp) -{ - NuError err = kNuErrNone; - long bytesLeft; - - ASSERT(pEntry->realEOF > 0); - - bytesLeft = pEntry->realEOF; - while (bytesLeft > 0) { - long toWrite; - - toWrite = bytesLeft; - if (toWrite > kBNYBlockSize) - toWrite = kBNYBlockSize; - - if (outfp != NULL) { - if (fwrite(pEntry->blockBuf, toWrite, 1, outfp) != 1) { - err = errno ? (NuError) errno : kNuErrFileWrite; - LOGI("BNY write failed"); - goto bail; - } - } - - bytesLeft -= toWrite; - - if (bytesLeft) { - err = BNYRead(pEntry->blockBuf, kBNYBlockSize); - if (err != kNuErrNone) { - LOGI("BNY read failed"); - goto bail; - } - } - } - -bail: - return err; -} -#endif - - -NuError BnyArchive::BNYIterate(void) -{ - NuError err = kNuErrNone; - BnyFileEntry entry; - //bool consumed; - int first = true; - int toFollow; - - toFollow = 1; /* assume 1 file in archive */ - while (toFollow) { - err = BNYRead(entry.blockBuf, sizeof(entry.blockBuf)); - if (err != kNuErrNone) { - LOGI("failed while reading header"); - goto bail; - } - - err = BNYDecodeHeader(&entry); - if (err != kNuErrNone) { - if (first) { - LOGI("not a Binary II archive?"); - } - goto bail; - } - - /* - * If the file has one or more blocks, read the first block now. - * This will allow the various functions to evaluate the file - * contents for SQueeze compression. - */ - if (entry.realEOF != 0) { - err = BNYRead(entry.blockBuf, sizeof(entry.blockBuf)); - if (err != kNuErrNone) { - LOGI("failed while reading"); - goto bail; - } - } - - /* - * Invoke the load function. - */ - //consumed = false; - - err = LoadContentsCallback(&entry); - if (err != kNuErrNone) - goto bail; - - /* - * If they didn't "consume" the entire BNY entry, we need to - * do it for them. We've already read the first block (if it - * existed), so we don't need to eat that one again. - */ - if (true /*!consumed*/) { - int nblocks = (entry.realEOF + kBNYBlockSize-1) / kBNYBlockSize; - - if (nblocks > 1) { - err = BNYSeek((nblocks-1) * kBNYBlockSize); - if (err != kNuErrNone) { - LOGI("failed while seeking forward"); - goto bail; - } - } - } - - if (!first) { - if (entry.filesToFollow != toFollow -1) { - LOGI("WARNING: filesToFollow %d, expected %d", - entry.filesToFollow, toFollow -1); - } - } - toFollow = entry.filesToFollow; - - first = false; - } - -bail: - if (err != kNuErrNone) { - LOGI("--- Iterator returning failure %d", err); - } - return err; -} - - -/* - * =========================================================================== - * BnyArchive -- test files - * =========================================================================== - */ - -bool BnyArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - NuError nerr; - BnyEntry* pEntry; - CString errMsg; - bool retVal = false; - - ASSERT(fFp != NULL); - - LOGI("Testing %d entries", pSelSet->GetNumEntries()); - - SelectionEntry* pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = (BnyEntry*) pSelEntry->GetEntry(); - - LOGI(" Testing '%ls' (offset=%ld)", (LPCWSTR) pEntry->GetDisplayName(), - pEntry->GetOffset()); - - SET_PROGRESS_UPDATE2(0, pEntry->GetDisplayName(), NULL); - - nerr = pEntry->TestEntry(pMsgWnd); - if (nerr != kNuErrNone) { - if (nerr == kNuErrAborted) { - CString title; - CheckedLoadString(&title, IDS_MB_APP_NAME); - errMsg = "Cancelled."; - pMsgWnd->MessageBox(errMsg, title, MB_OK); - } else { - errMsg.Format(L"Failed while testing '%ls': %hs.", - (LPCWSTR) pEntry->GetPathNameUNI(), NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - } - goto bail; - } - - pSelEntry = pSelSet->IterNext(); - } - - /* show success message */ - errMsg.Format(L"Tested %d file%ls, no errors found.", - pSelSet->GetNumEntries(), - pSelSet->GetNumEntries() == 1 ? L"" : L"s"); - pMsgWnd->MessageBox(errMsg); - retVal = true; - -bail: - SET_PROGRESS_END(); - return retVal; -} diff --git a/ciderpress/app/BNYArchive.h b/ciderpress/app/BNYArchive.h deleted file mode 100644 index cd42332..0000000 --- a/ciderpress/app/BNYArchive.h +++ /dev/null @@ -1,263 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Binary II support. - */ -#ifndef APP_BNYARCHIVE_H -#define APP_BNYARCHIVE_H - -#include "GenericArchive.h" - - -class BnyArchive; - -/* - * One file in a BNY archive. - */ -class BnyEntry : public GenericEntry { -public: - BnyEntry(BnyArchive* pArchive) : - fpArchive(pArchive), fIsSqueezed(false), fOffset(-1) - {} - virtual ~BnyEntry(void) {} - - virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const override; - virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const override; - - virtual long GetSelectionSerial(void) const override - { return -1; } // doesn't matter - - virtual bool GetFeatureFlag(Feature feature) const override { - if (feature == kFeaturePascalTypes || feature == kFeatureDOSTypes || - feature == kFeatureHasSimpleAccess) - return false; - else - return true; - } - - /* - * Test this entry by extracting it. - * - * If the file isn't compressed, just make sure the file is big enough. If - * it's squeezed, invoke the un-squeeze function with a "NULL" buffer pointer. - */ - NuError TestEntry(CWnd* pMsgWnd); - - bool GetSqueezed(void) const { return fIsSqueezed; } - void SetSqueezed(bool val) { fIsSqueezed = val; } - long GetOffset(void) const { return fOffset; } - void SetOffset(long offset) { fOffset = offset; } - - enum { - kBNYBlockSize = 128, - }; - -private: - /* - * Copy data from the seeked archive to outfp, possibly converting EOL along - * the way. - */ - NuError CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA, - CString* pMsg) const; - //NuError BNYUnSqueeze(ExpandBuffer* outExp) const; - - BnyArchive* fpArchive; // holds FILE* for archive - bool fIsSqueezed; - long fOffset; -}; - - -/* - * BNY archive definition. - */ -class BnyArchive : public GenericArchive { -public: - BnyArchive(void) : fIsReadOnly(false), fFp(NULL) - {} - virtual ~BnyArchive(void) { (void) Close(); } - - // One-time initialization; returns an error string. - static CString AppInit(void); - - virtual OpenResult Open(const WCHAR* filename, bool readOnly, - CString* pErrMsg) override; - virtual CString New(const WCHAR* filename, const void* options) override; - virtual CString Flush(void) override { return ""; } - virtual CString Reload(void) override; - virtual bool IsReadOnly(void) const override { return fIsReadOnly; }; - virtual bool IsModified(void) const override { return false; } - virtual CString GetDescription() const override { return L"Binary II"; } - virtual bool BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual CString TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const override - { ASSERT(false); return "!"; } - virtual CString TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, - char newFssep) const override - { ASSERT(false); return "!"; } - virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) override - { ASSERT(false); return false; } - virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) override - { ASSERT(false); return kXferFailed; } - virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry, - CString* pStr) override - { ASSERT(false); return false; } - virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry, - const CString& str) override - { ASSERT(false); return false; } - virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) override - { ASSERT(false); return false; } - virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) override - { ASSERT(false); return false; } - virtual void PreferencesChanged(void) override {} - virtual long GetCapability(Capability cap) override; - - friend class BnyEntry; - -private: - virtual CString Close(void) { - if (fFp != NULL) { - fclose(fFp); - fFp = NULL; - } - return ""; - } - virtual void XferPrepare(const XferFileOptions* pXferOpts) override - { ASSERT(false); } - virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override - { ASSERT(false); return "!"; } - virtual void XferAbort(CWnd* pMsgWnd) override - { ASSERT(false); } - virtual void XferFinish(CWnd* pMsgWnd) override - { ASSERT(false); } - - virtual ArchiveKind GetArchiveKind(void) override { return kArchiveBNY; } - virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) override - { ASSERT(false); return kNuErrGeneric; } - - enum { - kBNYBlockSize = BnyEntry::kBNYBlockSize, - kBNYMaxFileName = 64, - kBNYMaxNativeName = 48, - kBNYFlagCompressed = (1<<7), - kBNYFlagEncrypted = (1<<6), - kBNYFlagSparse = (1), - }; - - /* - * An entry in a Binary II archive. Each archive is essentially a stream - * of files; only the "filesToFollow" value gives any indication that - * something else follows this entry. - * - * We read this from the archive and then unpack it into GenericEntry - * fields in a BnyEntry. - */ -// struct BnyFileEntry; // VC++6 needs these to access private enums -// friend struct BnyFileEntry; // in this class - typedef struct BnyFileEntry { - uint16_t access; - uint16_t fileType; - uint32_t auxType; - uint8_t storageType; - uint32_t fileSize; /* in 512-byte blocks */ - uint16_t prodosModDate; - uint16_t prodosModTime; - NuDateTime modWhen; /* computed from previous two fields */ - uint16_t prodosCreateDate; - uint16_t prodosCreateTime; - NuDateTime createWhen; /* computed from previous two fields */ - uint32_t eof; - uint32_t realEOF; /* eof is bogus for directories */ - char fileName[kBNYMaxFileName+1]; - char nativeName[kBNYMaxNativeName+1]; - uint32_t diskSpace; /* in 512-byte blocks */ - uint8_t osType; /* not exactly same as NuFileSysID */ - uint16_t nativeFileType; - uint8_t phantomFlag; - uint8_t dataFlags; /* advisory flags */ - uint8_t version; - uint8_t filesToFollow; /* #of files after this one */ - - uint8_t blockBuf[kBNYBlockSize]; - } BnyFileEntry; - - int LoadContents(void); - - /* - * Given a BnyFileEntry structure, add an appropriate entry to the list. - * - * Note this can mangle pEntry (notably the filename). - */ - NuError LoadContentsCallback(BnyFileEntry* pEntry); - - /* - * Test for the magic number on a file in SQueezed format. - */ - bool IsSqueezed(uint8_t one, uint8_t two); - - /* - * Test if this entry is a directory. - */ - bool IsDir(BnyFileEntry* pEntry); - - /* - * Wrapper for fread(). Note the arguments resemble read(2) rather - * than fread(3S). - */ - NuError BNYRead(void* buf, size_t nbyte); - - /* - * Seek within an archive. Because we need to handle streaming archives, - * and don't need to special-case anything, we only allow relative - * forward seeks. - */ - NuError BNYSeek(long offset); - - /* - * Convert from ProDOS compact date format to the expanded DateTime format. - */ - void BNYConvertDateTime(unsigned short prodosDate, - unsigned short prodosTime, NuDateTime* pWhen); - - /* - * Decode a Binary II header. - */ - NuError BNYDecodeHeader(BnyFileEntry* pEntry); - - /* - * Iterate through a Binary II archive, loading the data. - */ - NuError BNYIterate(void); - - FILE* fFp; - bool fIsReadOnly; -}; - -#endif /*APP_BNYARCHIVE_H*/ diff --git a/ciderpress/app/BasicImport.cpp b/ciderpress/app/BasicImport.cpp deleted file mode 100644 index 166a7f2..0000000 --- a/ciderpress/app/BasicImport.cpp +++ /dev/null @@ -1,655 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Import BASIC programs stored in a text file. - * - * The current implementation is a bit lame. It just dumps text strings into - * a read-only edit buffer, instead of providing a nicer UI. The real trouble - * with this style of interface is that i18n is even more awkward. - */ -#include "StdAfx.h" -#include "../reformat/BASIC.h" -#include "BasicImport.h" - -/* - * ========================================================================== - * BASTokenLookup - * ========================================================================== - */ - -void BASTokenLookup::Init(const char* tokenList, int numTokens, int tokenLen) -{ - int i; - - ASSERT(tokenList != NULL); - ASSERT(numTokens > 0); - ASSERT(tokenLen > 0); - - delete[] fTokenPtr; // in case we're being re-initialized - delete[] fTokenLen; - - fTokenPtr = new const char*[numTokens]; - fTokenLen = new int[numTokens]; - fNumTokens = numTokens; - - for (i = 0; i < numTokens; i++) { - fTokenPtr[i] = tokenList; - fTokenLen[i] = strlen(tokenList); - - tokenList += tokenLen; - } -} - -int BASTokenLookup::Lookup(const char* str, int len, int* pFoundLen) -{ - int longestIndex, longestLen; - int i; - - longestIndex = longestLen = -1; - for (i = 0; i < fNumTokens; i++) { - if (fTokenLen[i] <= len && fTokenLen[i] > longestLen && - strnicmp(str, fTokenPtr[i], fTokenLen[i]) == 0) - { - longestIndex = i; - longestLen = fTokenLen[i]; - } - } - - *pFoundLen = longestLen; - return longestIndex; -} - - -/* - * ========================================================================== - * ImportBASDialog - * ========================================================================== - */ - -BEGIN_MESSAGE_MAP(ImportBASDialog, CDialog) - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -BOOL ImportBASDialog::OnInitDialog(void) -{ - CDialog::OnInitDialog(); // base class init - - PathName path(fFileName); - CString fileNameOnly(path.GetFileName()); - CString ext(fileNameOnly.Right(4)); - if (ext.CompareNoCase(L".txt") == 0) { - LOGI("removing extension from '%ls'", (LPCWSTR) fileNameOnly); - fileNameOnly = fileNameOnly.Left(fileNameOnly.GetLength() - 4); - } - - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_IMPORT_BAS_SAVEAS); - pEdit->SetWindowText(fileNameOnly); - pEdit->SetSel(0, -1); - pEdit->SetFocus(); - - /* - * Do the actual import. If it fails, disable the "save" button. - */ - if (!ImportBAS(fFileName)) { - CButton* pButton = (CButton*) GetDlgItem(IDOK); - pButton->EnableWindow(FALSE); - pEdit->EnableWindow(FALSE); - } - - return FALSE; // keep our focus -} - -static const char kFailed[] = "failed.\r\n\r\n"; -static const char kSuccess[] = "success!\r\n\r\n"; - -bool ImportBASDialog::ImportBAS(const WCHAR* fileName) -{ - FILE* fp = NULL; - ExpandBuffer msgs(1024); - long fileLen, outLen, count; - char* buf = NULL; - char* outBuf = NULL; - bool result = false; - - msgs.Printf("Importing from '%ls'...", fileName); - fp = _wfopen(fileName, L"rb"); // EOL unknown, open as binary and deal - if (fp == NULL) { - msgs.Printf("%sUnable to open file.", kFailed); - goto bail; - } - - /* determine file length, and verify that it looks okay */ - fseek(fp, 0, SEEK_END); - fileLen = ftell(fp); - rewind(fp); - if (ferror(fp) || fileLen < 0) { - msgs.Printf("%sUnable to determine file length.", kFailed); - goto bail; - } - if (fileLen == 0) { - msgs.Printf("%sFile is empty.", kFailed); - goto bail; - } - if (fileLen >= 128*1024) { - msgs.Printf("%sFile is too large to be Applesoft.", kFailed); - goto bail; - } - - buf = new char[fileLen]; - if (buf == NULL) { - msgs.Printf("%sUnable to allocate memory.", kFailed); - goto bail; - } - - /* read the entire thing into memory */ - count = fread(buf, 1, fileLen, fp); - if (count != fileLen) { - msgs.Printf("%sCould only read %ld of %ld bytes.", kFailed, - count, fileLen); - goto bail; - } - - /* process it */ - if (!ConvertTextToBAS(buf, fileLen, &outBuf, &outLen, &msgs)) - goto bail; - - result = true; - SetOutput(outBuf, outLen); - -bail: - if (fp != NULL) - fclose(fp); - delete[] buf; - - /* copy our error messages out */ - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_IMPORT_BAS_RESULTS); - char* msgBuf = NULL; - long msgLen; - msgs.SeizeBuffer(&msgBuf, &msgLen); - CString msgStr(msgBuf); - pEdit->SetWindowText(msgStr); - delete[] msgBuf; - - return result; -} - -bool ImportBASDialog::ConvertTextToBAS(const char* buf, long fileLen, - char** pOutBuf, long* pOutLen, ExpandBuffer* pMsgs) -{ - ExpandBuffer output(32768); - CString msg; - const char* lineStart; - const char* lineEnd; - long textRemaining; - int lineNum; - - fBASLookup.Init(ReformatApplesoft::GetApplesoftTokens(), - ReformatApplesoft::kTokenCount, ReformatApplesoft::kTokenLen); - - lineEnd = buf; - textRemaining = fileLen; - lineNum = 0; - - while (textRemaining > 0) { - lineNum++; - lineStart = lineEnd; - lineEnd = FindEOL(lineStart, textRemaining); - - if (!ProcessBASLine(lineStart, lineEnd - lineStart, &output, - /*ref*/ msg)) - { - pMsgs->Printf("%sLine %d: %ls", kFailed, lineNum, (LPCWSTR) msg); - return false; - } - - textRemaining -= lineEnd - lineStart; - } - - /* output EOF marker */ - output.Putc(0x00); - output.Putc(0x00); - - /* grab the buffer */ - char* outBuf; - long outLen; - output.SeizeBuffer(&outBuf, &outLen); - - if (outLen >= 0xc000) { - pMsgs->Printf("%sOutput is too large to be valid", kFailed); - delete[] outBuf; - return false; - } - - /* go back and fix up the "next line" pointers, assuming a $0801 start */ - if (!FixBASLinePointers(outBuf, outLen, 0x0801)) { - pMsgs->Printf("%sFailed while fixing line pointers", kFailed); - delete[] outBuf; - return false; - } - - *pOutBuf = outBuf; - *pOutLen = outLen; - pMsgs->Printf("%sProcessed %d lines", kSuccess, lineNum); - pMsgs->Printf("\r\nTokenized file is %d bytes long", *pOutLen); - - return true; -} - -/* -From an Applesoft disassembly by Bob Sander-Cederlof: - -D56C- E8 1420 PARSE INX NEXT INPUT CHARACTER -D56D- BD 00 02 1430 .1 LDA INPUT.BUFFER,X -D570- 24 13 1440 BIT DATAFLG IN A "DATA" STATEMENT? -D572- 70 04 1450 BVS .2 YES (DATAFLG = $49) -D574- C9 20 1460 CMP #' ' IGNORE BLANKS -D576- F0 F4 1470 BEQ PARSE -D578- 85 0E 1480 .2 STA ENDCHR -D57A- C9 22 1490 CMP #'" START OF QUOTATION? -D57C- F0 74 1500 BEQ .13 -D57E- 70 4D 1510 BVS .9 BRANCH IF IN "DATA" STATEMENT -D580- C9 3F 1520 CMP #'? SHORTHAND FOR "PRINT"? -D582- D0 04 1530 BNE .3 NO -D584- A9 BA 1540 LDA #TOKEN.PRINT YES, REPLACE WITH "PRINT" TOKEN -D586- D0 45 1550 BNE .9 ...ALWAYS -D588- C9 30 1560 .3 CMP #'0 IS IT A DIGIT, COLON, OR SEMI-COLON? -D58A- 90 04 1570 BCC .4 NO, PUNCTUATION !"#$%&'()*+,-./ -D58C- C9 3C 1580 CMP #';'+1 -D58E- 90 3D 1590 BCC .9 YES, NOT A TOKEN - 1600 *-------------------------------- - 1610 * SEARCH TOKEN NAME TABLE FOR MATCH STARTING - 1620 * WITH CURRENT CHAR FROM INPUT LINE - 1630 *-------------------------------- -D590- 84 AD 1640 .4 STY STRNG2 SAVE INDEX TO OUTPUT LINE -D592- A9 D0 1650 LDA #TOKEN.NAME.TABLE-$100 -D594- 85 9D 1660 STA FAC MAKE PNTR FOR SEARCH -D596- A9 CF 1670 LDA /TOKEN.NAME.TABLE-$100 -D598- 85 9E 1680 STA FAC+1 -D59A- A0 00 1690 LDY #0 USE Y-REG WITH (FAC) TO ADDRESS TABLE -D59C- 84 0F 1700 STY TKN.CNTR HOLDS CURRENT TOKEN-$80 -D59E- 88 1710 DEY PREPARE FOR "INY" A FEW LINES DOWN -D59F- 86 B8 1720 STX TXTPTR SAVE POSITION IN INPUT LINE -D5A1- CA 1730 DEX PREPARE FOR "INX" A FEW LINES DOWN -D5A2- C8 1740 .5 INY ADVANCE POINTER TO TOKEN TABLE -D5A3- D0 02 1750 BNE .6 Y=Y+1 IS ENOUGH -D5A5- E6 9E 1760 INC FAC+1 ALSO NEED TO BUMP THE PAGE -D5A7- E8 1770 .6 INX ADVANCE POINTER TO INPUT LINE -D5A8- BD 00 02 1780 .7 LDA INPUT.BUFFER,X NEXT CHAR FROM INPUT LINE -D5AB- C9 20 1790 CMP #' ' THIS CHAR A BLANK? -D5AD- F0 F8 1800 BEQ .6 YES, IGNORE ALL BLANKS -D5AF- 38 1810 SEC NO, COMPARE TO CHAR IN TABLE -D5B0- F1 9D 1820 SBC (FAC),Y SAME AS NEXT CHAR OF TOKEN NAME? -D5B2- F0 EE 1830 BEQ .5 YES, CONTINUE MATCHING -D5B4- C9 80 1840 CMP #$80 MAYBE; WAS IT SAME EXCEPT FOR BIT 7? -D5B6- D0 41 1850 BNE .14 NO, SKIP TO NEXT TOKEN -D5B8- 05 0F 1860 ORA TKN.CNTR YES, END OF TOKEN; GET TOKEN # -D5BA- C9 C5 1870 CMP #TOKEN.AT DID WE MATCH "AT"? -D5BC- D0 0D 1880 BNE .8 NO, SO NO AMBIGUITY -D5BE- BD 01 02 1890 LDA INPUT.BUFFER+1,X "AT" COULD BE "ATN" OR "A TO" -D5C1- C9 4E 1900 CMP #'N "ATN" HAS PRECEDENCE OVER "AT" -D5C3- F0 34 1910 BEQ .14 IT IS "ATN", FIND IT THE HARD WAY -D5C5- C9 4F 1920 CMP #'O "TO" HAS PRECEDENCE OVER "AT" -D5C7- F0 30 1930 BEQ .14 IT IS "A TO", FIN IT THE HARD WAY -D5C9- A9 C5 1940 LDA #TOKEN.AT NOT "ATN" OR "A TO", SO USE "AT" - 1950 *-------------------------------- - 1960 * STORE CHARACTER OR TOKEN IN OUTPUT LINE - 1970 *-------------------------------- - -Note the special handling for "AT" and "TO". When it examines the next -character, it does NOT skip whitespace, making spaces significant when -differentiating between "at n"/"atn" and "at o"/"ato". -*/ - -bool ImportBASDialog::ProcessBASLine(const char* buf, int len, - ExpandBuffer* pOutput, CString& msg) -{ - const int kMaxTokenLen = 7; // longest token; must also hold linenum - const int kTokenAT = 0xc5 - 128; - const int kTokenATN = 0xe1 - 128; - char tokenBuf[kMaxTokenLen+1]; - bool gotOne = false; - bool haveLineNum = false; - char ch; - int tokenLen; - int lineNum; - int foundToken; - - if (!len) - return false; - - /* - * Remove the CR, LF, or CRLF from the end of the line. - */ - if (len > 1 && buf[len-2] == '\r' && buf[len-1] == '\n') { - //LOGI("removed CRLF"); - len -= 2; - } else if (buf[len-1] == '\r') { - //LOGI("removed CR"); - len--; - } else if (buf[len-1] == '\n') { - //LOGI("removed LF"); - len--; - } else { - //LOGI("no EOL marker found"); - } - - if (!len) - return true; // blank lines are okay - - /* - * Extract the line number. - */ - tokenLen = 0; - while (len > 0) { - if (!GetNextNWC(&buf, &len, &ch)) { - if (!gotOne) - return true; // blank lines with whitespace are okay - else { - // end of line reached while scanning line number is bad - msg = L"found nothing except line number"; - return false; - } - } - gotOne = true; - - if (!isdigit(ch)) - break; - if (tokenLen == 5) { // theoretical max is "65535" - msg = L"line number has too many digits"; - return false; - } - tokenBuf[tokenLen++] = ch; - } - - if (!tokenLen) { - msg = L"line did not start with a line number"; - return false; - } - tokenBuf[tokenLen] = '\0'; - lineNum = atoi(tokenBuf); - LOGI("FOUND line %d", lineNum); - - pOutput->Putc((char) 0xcc); // placeholder - pOutput->Putc((char) 0xcc); - pOutput->Putc(lineNum & 0xff); - pOutput->Putc((lineNum >> 8) & 0xff); - - /* - * Start scanning tokens. - * - * We need to find the longest matching token (i.e. prefer "ONERR" over - * "ON"). Grab a bunch of characters, ignoring whitespace, and scan - * for a match. - */ - buf--; // back up - len++; - foundToken = -1; - - while (len > 0) { - const char* dummy = buf; - int remaining = len; - - /* load up the buffer */ - for (tokenLen = 0; tokenLen < kMaxTokenLen; tokenLen++) { - if (!GetNextNWC(&dummy, &remaining, &ch)) - break; - if (ch == '"') - break; - tokenBuf[tokenLen] = ch; - } - - if (tokenLen == 0) { - if (ch == '"') { - /* - * Note it's possible for strings to be unterminated. This - * will go unnoticed by Applesoft if it's at the end of a - * line. - */ - GetNextNWC(&buf, &len, &ch); - pOutput->Putc(ch); - while (len--) { - ch = *buf++; - pOutput->Putc(ch); - if (ch == '"') - break; - } - } else { - /* end of line reached */ - break; - } - } else { - int token, foundLen; - - token = fBASLookup.Lookup(tokenBuf, tokenLen, &foundLen); - if (token >= 0) { - /* match! */ - if (token == kTokenAT || token == kTokenATN) { - /* have to go back and re-scan original */ - const char* tp = buf +1; - while (toupper(*tp++) != 'T') - ; - if (toupper(*tp) == 'N') { - /* keep this token */ - assert(token == kTokenATN); - } else if (toupper(*tp) == 'O') { - /* eat and emit the 'A' so we get the "TO" instead */ - goto output_single; - } else { - if (token == kTokenATN) { - /* reduce to "AT" */ - token = kTokenAT; - foundLen--; - } - } - } - pOutput->Putc(token + 128); - - /* consume token chars, including whitespace */ - for (int j = 0; j < foundLen; j++) - GetNextNWC(&buf, &len, &ch); - - //LOGI("TOKEN '%s' (%d)", - // fBASLookup.GetToken(token), tokenLen); - - /* special handling for REM or DATA */ - if (token == 0xb2 - 128) { - /* for a REM statement, copy verbatim to end of line */ - if (*buf == ' ') { - /* eat one leading space, if present */ - buf++; - len--; - } - while (len--) { - ch = *buf++; - pOutput->Putc(ch); - } - } else if (token == 0x83 - 128) { - bool inQuote = false; - - /* for a DATA statement, copy until ':' */ - if (*buf == ' ') { - /* eat one leading space */ - buf++; - len--; - } - while (len--) { - ch = *buf++; - if (ch == '"') // ignore ':' in quoted strings - inQuote = !inQuote; - - if (!inQuote && ch == ':') { - len++; - buf--; - break; - } - pOutput->Putc(ch); - } - } - } else { - /* - * Not a quote, and no token begins with this character. - * Output it and advance. - */ -output_single: - GetNextNWC(&buf, &len, &ch); - pOutput->Putc(toupper(ch)); - } - } - } - - pOutput->Putc('\0'); - - return true; -} - -bool ImportBASDialog::FixBASLinePointers(char* buf, long len, - uint16_t addr) -{ - uint16_t val; - char* start; - - while (len >= 4) { - start = buf; - val = (*buf) & 0xff | (*(buf+1)) << 8; - - if (val == 0) - break; - if (val != 0xcccc) { - LOGI("unexpected value 0x%04x found", val); - return false; - } - - buf += 4; - len -= 4; - - /* - * Find the next end-of-line marker. - */ - while (*buf != '\0' && len > 0) { - buf++; - len--; - } - if (!len) { - LOGI("ran off the end?"); - return false; - } - buf++; - len--; - - /* - * Set the value. - */ - val = (unsigned short) (buf - start); - ASSERT((int) val == buf - start); - addr += val; - - *start = addr & 0xff; - *(start+1) = (addr >> 8) & 0xff; - } - - return true; -} - -const char* ImportBASDialog::FindEOL(const char* buf, long max) -{ - ASSERT(max >= 0); - if (max == 0) - return NULL; - - while (max) { - if (*buf == '\r' || *buf == '\n') { - if (*buf == '\r' && max > 0 && *(buf+1) == '\n') - return buf+2; - return buf+1; - } - - buf++; - max--; - } - - /* - * Looks like the last line didn't have an EOL. That's okay. - */ - return buf; -} - -bool ImportBASDialog::GetNextNWC(const char** pBuf, int* pLen, char* pCh) -{ - static const char* kWhitespace = " \t\r\n"; - - while (*pLen > 0) { - const char* ptr; - char ch; - - ch = **pBuf; - ptr = strchr(kWhitespace, ch); - (*pBuf)++; - (*pLen)--; - - if (ptr == NULL) { - *pCh = ch; - return true; - } - } - - return false; -} - -void ImportBASDialog::OnOK(void) -{ - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_IMPORT_BAS_SAVEAS); - CString fileName; - - pEdit->GetWindowText(fileName); - if (fileName.IsEmpty()) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - MessageBox(L"You must specify a filename.", - appName, MB_OK); - } - - /* - * Write the file to the currently-open archive. - */ - GenericArchive::LocalFileDetails details; - - details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork); - details.SetLocalPathName(L"Imported BASIC"); - details.SetStrippedLocalPathName(fileName); - details.SetAccess(0xe3); // unlocked, backup bit set - details.SetFileType(kFileTypeBAS); - details.SetExtraType(0x0801); - details.SetStorageType(DiskFS::kStorageSeedling); - time_t now = time(NULL); - NuDateTime ndt; - GenericArchive::UNIXTimeToDateTime(&now, &ndt); - details.SetCreateWhen(ndt); - details.SetArchiveWhen(ndt); - details.SetModWhen(ndt); - - CString errMsg; - - fDirty = true; - if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput, - fOutputLen, NULL, -1, &errMsg, this)) - { - goto bail; - } - - /* success! close the dialog */ - CDialog::OnOK(); - -bail: - if (!errMsg.IsEmpty()) { - CString msg; - msg.Format(L"Unable to import file: %ls.", (LPCWSTR) errMsg); - ShowFailureMsg(this, msg, IDS_FAILED); - return; - } - return; -} diff --git a/ciderpress/app/BasicImport.h b/ciderpress/app/BasicImport.h deleted file mode 100644 index e8068f9..0000000 --- a/ciderpress/app/BasicImport.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Import BASIC programs from text files. - * - * THOUGHT: change the way the dialog works so that it doesn't scan until - * you say "go". Have some options for selecting language (BAS vs. INT), - * and whether to try to identify listings with line breaks (i.e. they - * neglected to "poke 33,33"). Have an optional "check syntax" box if we - * want to get really fancy. - */ -#ifndef APP_BASICIMPORT_H -#define APP_BASICIMPORT_H - -/* - * This is a helper class to scan for a token in the list. - * - * Ideally we'd create a hash table to make it faster, but that's probably - * not necessary for the small data sets we're working with. - */ -class BASTokenLookup { -public: - BASTokenLookup(void) - : fTokenPtr(NULL), fTokenLen(NULL) - {} - ~BASTokenLookup(void) { - delete[] fTokenPtr; - delete[] fTokenLen; - } - - // Initialize the array. Pass in the info for the token blob. - void Init(const char* tokenList, int numTokens, int tokenLen); - - // Return the index of the matching token, or -1 if none found. - int Lookup(const char* str, int len, int* pFoundLen); - - // Return a printable string. - const char* GetToken(int idx) { - return fTokenPtr[idx]; - } - -private: - int fNumTokens; - const char** fTokenPtr; - int* fTokenLen; -}; - - -/* - * Import a BASIC program. - * - * Currently works for Applesoft. Might work for Integer someday. - */ -class ImportBASDialog : public CDialog { -public: - ImportBASDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_IMPORT_BAS, pParentWnd), fDirty(false), - fOutput(NULL), fOutputLen(-1) - {} - virtual ~ImportBASDialog(void) { - delete[] fOutput; - } - - // did we add something to the archive? - bool IsDirty(void) const { return fDirty; } - - void SetFileName(const CString& fileName) { fFileName = fileName; } - -private: - virtual BOOL OnInitDialog(void) override; - //virtual void DoDataExchange(CDataExchange* pDX); - virtual void OnOK(void) override; - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_IMPORT_BASIC); - } - - /* - * Import an Applesoft BASIC program from the specified file. - */ - bool ImportBAS(const WCHAR* fileName); - - /* - * Do the actual conversion. - */ - bool ConvertTextToBAS(const char* buf, long fileLen, - char** pOutBuf, long* pOutLen, ExpandBuffer* pMsgs); - - /* - * Process a line of Applesoft BASIC text. - * - * Writes output to "pOutput". - * - * On failure, writes an error message to "msg" and returns false. - */ - bool ProcessBASLine(const char* buf, int len, - ExpandBuffer* pOutput, CString& msg); - - /* - * Fix up the line pointers. We left dummy nonzero values in them initially. - */ - bool FixBASLinePointers(char* buf, long len, uint16_t addr); - - /* - * Look for the end of line. - * - * Returns a pointer to the first byte *past* the EOL marker, which will point - * at unallocated space for last line in the buffer. - */ - const char* FindEOL(const char* buf, long max); - - /* - * Find the next non-whitespace character. - * - * Updates the buffer pointer and length. - * - * Returns "false" if we run off the end without finding another non-ws char. - */ - bool GetNextNWC(const char** pBuf, int* pLen, char* pCh); - - void SetOutput(char* outBuf, long outLen) { - delete[] fOutput; - fOutput = outBuf; - fOutputLen = outLen; - } - - BASTokenLookup fBASLookup; - bool fDirty; - - char* fOutput; - long fOutputLen; - - CString fFileName; // file to open - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_BASICIMPORT_H*/ diff --git a/ciderpress/app/CassImpTargetDialog.cpp b/ciderpress/app/CassImpTargetDialog.cpp deleted file mode 100644 index 32d74fb..0000000 --- a/ciderpress/app/CassImpTargetDialog.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Choose file name and characteristics for a file imported from an audio - * cassette tape. - */ -#include "StdAfx.h" -#include "CassImpTargetDialog.h" -#include "GenericArchive.h" // just want kFileTypeXXX - -BEGIN_MESSAGE_MAP(CassImpTargetDialog, CDialog) - ON_BN_CLICKED(IDC_CASSIMPTARG_BAS, OnTypeChange) - ON_BN_CLICKED(IDC_CASSIMPTARG_INT, OnTypeChange) - ON_BN_CLICKED(IDC_CASSIMPTARG_BIN, OnTypeChange) - ON_EN_CHANGE(IDC_CASSIMPTARG_BINADDR, OnAddrChange) -END_MESSAGE_MAP() - -BOOL CassImpTargetDialog::OnInitDialog(void) -{ - /* substitute our replacement edit control */ - fAddrEdit.ReplaceDlgCtrl(this, IDC_CASSIMPTARG_BINADDR); - fAddrEdit.SetProperties(MyEdit::kCapsOnly | MyEdit::kHexOnly); - - //CWnd* pWnd; - CEdit* pEdit; - - pEdit = (CEdit*) GetDlgItem(IDC_CASSIMPTARG_BINADDR); - pEdit->SetLimitText(4); // 4-digit hex value - - /* do the DDX thing, then update computed fields */ - CDialog::OnInitDialog(); - OnTypeChange(); - OnAddrChange(); - - pEdit = (CEdit*) GetDlgItem(IDC_CASSIMPTARG_FILENAME); - pEdit->SetSel(0, -1); - pEdit->SetFocus(); - return FALSE; // don't change the focus -} - -void CassImpTargetDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Radio(pDX, IDC_CASSIMPTARG_BAS, fFileTypeIndex); - DDX_Text(pDX, IDC_CASSIMPTARG_FILENAME, fFileName); - - if (pDX->m_bSaveAndValidate) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - if (fFileTypeIndex == kTypeBIN) { - if (GetStartAddr() < 0) { - MessageBox(L"The address field must be a valid 4-digit " - L" hexadecimal number.", - appName, MB_OK); - pDX->Fail(); - return; - } - fStartAddr = (unsigned short) GetStartAddr(); - } - if (fFileName.IsEmpty()) { - MessageBox(L"You must enter a filename.", appName, MB_OK); - pDX->Fail(); - return; - } - } else { - CWnd* pWnd; - CString tmpStr; - - pWnd = GetDlgItem(IDC_CASSIMPTARG_BINADDR); - tmpStr.Format(L"%04X", fStartAddr); - pWnd->SetWindowText(tmpStr); - } -} - -void CassImpTargetDialog::OnTypeChange(void) -{ - CButton* pButton; - CWnd* pWnd; - - pButton = (CButton*) GetDlgItem(IDC_CASSIMPTARG_BIN); - pWnd = GetDlgItem(IDC_CASSIMPTARG_BINADDR); - - pWnd->EnableWindow(pButton->GetCheck() == BST_CHECKED); -} - -void CassImpTargetDialog::OnAddrChange(void) -{ - CWnd* pWnd; - CString tmpStr; - long val; - - val = GetStartAddr(); - if (val < 0) - val = 0; - - tmpStr.Format(L".%04X", val + fFileLength-1); - - pWnd = GetDlgItem(IDC_CASSIMPTARG_RANGE); - pWnd->SetWindowText(tmpStr); -} - -long CassImpTargetDialog::GetStartAddr(void) const -{ - CWnd* pWnd = GetDlgItem(IDC_CASSIMPTARG_BINADDR); - ASSERT(pWnd != NULL); - - CString aux; - pWnd->GetWindowText(aux); - - const WCHAR* str = aux; - WCHAR* end; - long val; - - if (str[0] == '\0') { - LOGI(" HEY: blank addr, returning -1"); - return -1; - } - val = wcstoul(aux, &end, 16); - if (end != str + wcslen(str)) { - LOGI(" HEY: found some garbage in addr '%ls', returning -1", - (LPCWSTR) aux); - return -1; - } - return val; -} - -long CassImpTargetDialog::GetFileType(void) const -{ - switch (fFileTypeIndex) { - case kTypeBIN: return kFileTypeBIN; - case kTypeINT: return kFileTypeINT; - case kTypeBAS: return kFileTypeBAS; - default: - assert(false); - return -1; - } -} - -void CassImpTargetDialog::SetFileType(long type) -{ - switch (type) { - case kFileTypeBIN: fFileTypeIndex = kTypeBIN; break; - case kFileTypeINT: fFileTypeIndex = kTypeINT; break; - case kFileTypeBAS: fFileTypeIndex = kTypeBAS; break; - default: - assert(false); - break; - } -} diff --git a/ciderpress/app/CassImpTargetDialog.h b/ciderpress/app/CassImpTargetDialog.h deleted file mode 100644 index ec3797a..0000000 --- a/ciderpress/app/CassImpTargetDialog.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Choose file name and characteristics for a file imported from an audio - * cassette tape. - */ -#ifndef APP_CASSIMPTARGETDIALOG_H -#define APP_CASSIMPTARGETDIALOG_H - -#include "resource.h" - -/* - * Get a filename, allow them to override the file type, and get a hexadecimal - * start address for binary files. - */ -class CassImpTargetDialog : public CDialog { -public: - CassImpTargetDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_CASSIMPTARGET, pParentWnd), - fStartAddr(0x0800), - fFileTypeIndex(0) - {} - virtual ~CassImpTargetDialog(void) {} - - /* - * Get the selected file type. Call this after the modal dialog exits. - */ - long GetFileType(void) const; - - /* - * Convert a ProDOS file type into a radio button enum. - */ - void SetFileType(long type); - - CString fFileName; - unsigned short fStartAddr; // start addr for BIN files - long fFileLength; // used for BIN display - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * They selected a different file type. Enable or disable the address - * entry window. - */ - afx_msg void OnTypeChange(void); - - /* - * If the user changes the address, update the "end of range" field. - */ - afx_msg void OnAddrChange(void); - - MyEdit fAddrEdit; // replacement edit ctrl for addr field - - /* - * Get the start address (entered as a 4-digit hex value). - * - * Returns -1 if something was wrong with the string (e.g. empty or has - * invalid chars). - */ - long GetStartAddr(void) const; - - /* for radio button; enum must match order of controls in dialog */ - enum { kTypeBAS = 0, kTypeINT, kTypeBIN }; - int fFileTypeIndex; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CASSIMPTARGETDIALOG_H*/ diff --git a/ciderpress/app/CassetteDialog.cpp b/ciderpress/app/CassetteDialog.cpp deleted file mode 100644 index c5fc52a..0000000 --- a/ciderpress/app/CassetteDialog.cpp +++ /dev/null @@ -1,1105 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Apple II cassette I/O functions. - */ -#include "StdAfx.h" -#include "CassetteDialog.h" -#include "CassImpTargetDialog.h" -#include "GenericArchive.h" -#include "Main.h" -#include "../diskimg/DiskImg.h" // need kStorageSeedling -#include - -/* - * Tape layout: - * 10.6 seconds of 770Hz (8192 cycles * 1300 usec/cycle) - * 1/2 cycle at 400 usec/cycle, followed by 1/2 cycle at 500 usec/cycle - * Data, using 500 usec/cycle for '0' and 1000 usec/cycle for '1' - * There is no "end" marker, except perhaps for the absence of data - * - * The last byte of data is an XOR checksum (seeded with 0xff). - * - * BASIC uses two sections, each with the full 10-second lead-in and a - * checksum byte). Integer BASIC writes a two-byte section with the length - * of the program, while Applesoft BASIC writes a three-byte section with - * the length followed by a one-byte "run" flag (seen: 0x55 and 0xd5). - * - * Applesoft arrays, loaded with "RECALL", have a three-byte header, and - * may be confused with BASIC programs. Shape tables, loaded with "SHLOAD", - * have a two-byte header and may be confused with Integer programs. - * - * The monitor ROM routine uses a detection threshold of 700 usec to tell - * the difference between 0s and 1s. When reading, it *outputs* a tone for - * 3.5 seconds before listening. It doesn't try to detect the 770Hz tone, - * just waits for something under (40*12=)440 usec. - * - * The Apple II hardware changes the high bit read from $c060 every time it - * detects a zero-crossing on the cassette input. I assume the polarity - * of the input signal is reflected by the polarity of the high bit, but - * I'm not sure, and in the end it doesn't really matter. - * - * Typical instructions for loading data from tape look like this: - * - Type "LOAD" or "xxxx.xxxxR", but don't hit . - * - Play tape until you here the tone. - * - Immediately hit stop. - * - Plug the cable from the Apple II into the tape player. - * - Hit "play" on the recorder, then immediately hit . - * - When the Apple II beeps, it's done. Stop the tape. - * - * How quickly do we need to sample? The highest frequency we expect to - * find is 2KHz, so anything over 4KHz should be sufficient. However, we - * need to be able to resolve the time between zero transitions to some - * reasonable resolution. We need to tell the difference between a 650usec - * half-cycle and a 200usec half-cycle for the start, and 250/500usec for - * the data section. Our measurements can comfortably be off by 200 usec - * with no ill effects on the lead-in, assuming a perfect signal. (Sampling - * every 200 usec would be 5Hz.) The data itself needs to be +/- 125usec - * for half-cycles, though we can get a little sloppier if we average the - * error out by combining half-cycles. - * - * The signal is less than perfect, sometimes far less, so we need better - * sampling to avoid magnifying distortions in the signal. If we sample - * at 22.05KHz, we could see a 650usec gap as 590, 635, or 680, depending - * on when we sample and where we think the peaks lie. We're off by 15usec - * before we even start. We can reasonably expect to be off +/- twice the - * "usecPerSample" value. At 8KHz, that's +/- 250usec, which isn't - * acceptable. At 11KHz we're at +/- 191usec, which is scraping along. - * - * We can get mitigate some problems by doing an interpolation of the - * two points nearest the zero-crossing, which should give us a more - * accurate fix on the zero point than simply choosing the closest point. - * This does potentially increase our risk of errors due to noise spikes at - * points near the zero. Since we're reading from cassette, any noise spikes - * are likely to be pretty wide, so averaging the data or interpolating - * across multiple points isn't likely to help us. - * - * Some tapes seem to have a low-frequency distortion that amounts to a DC - * bias when examining a single sample. Timing the gaps between zero - * crossings is therefore not sufficient unless we also correct for the - * local DC bias. In some cases the recorder or media was unable to - * respond quickly enough, and as a result 0s have less amplitude - * than 1s. This throws off some simple correction schemes. - * - * The easiest approach is to figure out where one cycle starts and stops, and - * use the timing of the full cycle. This gets a little ugly because the - * original output was a square wave, so there's a bit of ringing in the - * peaks, especially the 1s. Of course, we have to look at half-cycles - * initially, because we need to identify the first "short 0" part. Once - * we have that, we can use full cycles, which distributes any error over - * a larger set of samples. - * - * In some cases the positive half-cycle is longer than the negative - * half-cycle (e.g. reliably 33 samples vs. 29 samples at 48KHz, when - * 31.2 is expected for 650us). Slight variations can lead to even - * greater distortion, even though the timing for the full signal is - * within tolerances. This means we need to accumulate the timing for - * a full cycle before making an evaluation, though we still need to - * examine the half-cycle timing during the lead-in to catch the "short 0". - * - * Because of these distortions, 8-bit 8KHz audio is probably not a good - * idea. 16-bit 22.05KHz sampling is a better choice for tapes that have - * been sitting around for 25-30 years. - */ -/* -; Monitor ROM dump, with memory locations rearranged for easier reading. - -; Increment 16-bit value at 0x3c (A1) and compare it to 16-bit value at -; 0x3e (A2). Returns with carry set if A1 >= A2. -; Requires 26 cycles in common case, 30 cycles in rare case. -FCBA: A5 3C 709 NXTA1 LDA A1L ;INCR 2-BYTE A1. -FCBC: C5 3E 710 CMP A2L -FCBE: A5 3D 711 LDA A1H ; AND COMPARE TO A2 -FCC0: E5 3F 712 SBC A2H -FCC2: E6 3C 713 INC A1L ; (CARRY SET IF >=) -FCC4: D0 02 714 BNE RTS4B -FCC6: E6 3D 715 INC A1H -FCC8: 60 716 RTS4B RTS - -; Write data from location in A1L up to location in A2L. -FECD: A9 40 975 WRITE LDA #$40 -FECF: 20 C9 FC 976 JSR HEADR ;WRITE 10-SEC HEADER -; Write loop. Continue until A1 reaches A2. -FED2: A0 27 977 LDY #$27 -FED4: A2 00 978 WR1 LDX #$00 -FED6: 41 3C 979 EOR (A1L,X) -FED8: 48 980 PHA -FED9: A1 3C 981 LDA (A1L,X) -FEDB: 20 ED FE 982 JSR WRBYTE -FEDE: 20 BA FC 983 JSR NXTA1 -FEE1: A0 1D 984 LDY #$1D -FEE3: 68 985 PLA -FEE4: 90 EE 986 BCC WR1 -; Write checksum byte, then beep the speaker. -FEE6: A0 22 987 LDY #$22 -FEE8: 20 ED FE 988 JSR WRBYTE -FEEB: F0 4D 989 BEQ BELL - -; Write one byte (8 bits, or 16 half-cycles). -; On exit, Z-flag is set. -FEED: A2 10 990 WRBYTE LDX #$10 -FEEF: 0A 991 WRBYT2 ASL -FEF0: 20 D6 FC 992 JSR WRBIT -FEF3: D0 FA 993 BNE WRBYT2 -FEF5: 60 994 RTS - -; Write tape header. Called by WRITE with A=$40, READ with A=$16. -; On exit, A holds $FF. -; First time through, X is undefined, so we may get slightly less than -; A*256 half-cycles (i.e. A*255 + X). If the carry is clear on entry, -; the first ADC will subtract two (yielding A*254+X), and the first X -; cycles will be "long 0s" instead of "long 1s". Doesn't really matter. -FCC9: A0 4B 717 HEADR LDY #$4B ;WRITE A*256 'LONG 1' -FCCB: 20 DB FC 718 JSR ZERDLY ; HALF CYCLES -FCCE: D0 F9 719 BNE HEADR ; (650 USEC EACH) -FCD0: 69 FE 720 ADC #$FE -FCD2: B0 F5 721 BCS HEADR ;THEN A 'SHORT 0' -; Fall through to write bit. Note carry is clear, so we'll use the zero -; delay. We've initialized Y to $21 instead of $32 to get a short '0' -; (165usec) for the first half and a normal '0' for the second half; -FCD4: A0 21 722 LDY #$21 ; (400 USEC) -; Write one bit. Called from WRITE with Y=$27. -FCD6: 20 DB FC 723 WRBIT JSR ZERDLY ;WRITE TWO HALF CYCLES -FCD9: C8 724 INY ; OF 250 USEC ('0') -FCDA: C8 725 INY ; OR 500 USEC ('0') -; Delay for '0'. X typically holds a bit count or half-cycle count. -; Y holds delay period in 5-usec increments: -; (carry clear) $21=165us $27=195us $2C=220 $4B=375us -; (carry set) $21=165+250=415us $27=195+250=445us $4B=375+250=625us -; Remember that TOTAL delay, with all other instructions, must equal target -; On exit, Y=$2C, Z-flag is set if X decremented to zero. The 2C in Y -; is for WRBYTE, which is in a tight loop and doesn't need much padding. -FCDB: 88 726 ZERDLY DEY -FCDC: D0 FD 727 BNE ZERDLY -FCDE: 90 05 728 BCC WRTAPE ;Y IS COUNT FOR -; Additional delay for '1' (always 250us). -FCE0: A0 32 729 LDY #$32 ; TIMING LOOP -FCE2: 88 730 ONEDLY DEY -FCE3: D0 FD 731 BNE ONEDLY -; Write a transition to the tape. -FCE5: AC 20 C0 732 WRTAPE LDY TAPEOUT -FCE8: A0 2C 733 LDY #$2C -FCEA: CA 734 DEX -FCEB: 60 735 RTS - -; Read data from location in A1L up to location in A2L. -FEFD: 20 FA FC 999 READ JSR RD2BIT ;FIND TAPEIN EDGE -FF00: A9 16 1000 LDA #$16 -FF02: 20 C9 FC 1001 JSR HEADR ;DELAY 3.5 SECONDS -FF05: 85 2E 1002 STA CHKSUM ;INIT CHKSUM=$FF -FF07: 20 FA FC 1003 JSR RD2BIT ;FIND TAPEIN EDGE -; Loop, waiting for edge. 11 cycles/iteration, plus 432+14 = 457usec. -FF0A: A0 24 1004 RD2 LDY #$24 ;LOOK FOR SYNC BIT -FF0C: 20 FD FC 1005 JSR RDBIT ; (SHORT 0) -FF0F: B0 F9 1006 BCS RD2 ; LOOP UNTIL FOUND -; Timing of next transition, a normal '0' half-cycle, doesn't matter. -FF11: 20 FD FC 1007 JSR RDBIT ;SKIP SECOND SYNC H-CYCLE -; Main byte read loop. Continue until A1 reaches A2. -FF14: A0 3B 1008 LDY #$3B ;INDEX FOR 0/1 TEST -FF16: 20 EC FC 1009 RD3 JSR RDBYTE ;READ A BYTE -FF19: 81 3C 1010 STA (A1L,X) ;STORE AT (A1) -FF1B: 45 2E 1011 EOR CHKSUM -FF1D: 85 2E 1012 STA CHKSUM ;UPDATE RUNNING CHKSUM -FF1F: 20 BA FC 1013 JSR NXTA1 ;INC A1, COMPARE TO A2 -FF22: A0 35 1014 LDY #$35 ;COMPENSATE 0/1 INDEX -FF24: 90 F0 1015 BCC RD3 ;LOOP UNTIL DONE -; Read checksum byte and check it. -FF26: 20 EC FC 1016 JSR RDBYTE ;READ CHKSUM BYTE -FF29: C5 2E 1017 CMP CHKSUM -FF2B: F0 0D 1018 BEQ BELL ;GOOD, SOUND BELL AND RETURN - -; Print "ERR", beep speaker. -FF2D: A9 C5 1019 PRERR LDA #$C5 -FF2F: 20 ED FD 1020 JSR COUT ;PRINT "ERR", THEN BELL -FF32: A9 D2 1021 LDA #$D2 -FF34: 20 ED FD 1022 JSR COUT -FF37: 20 ED FD 1023 JSR COUT -FF3A: A9 87 1024 BELL LDA #$87 ;OUTPUT BELL AND RETURN -FF3C: 4C ED FD 1025 JMP COUT - -; Read a byte from the tape. Y is $3B on first call, $35 on subsequent -; calls. The bits are shifted left, meaning that the high bit is read -; first. -FCEC: A2 08 736 RDBYTE LDX #$08 ;8 BITS TO READ -FCEE: 48 737 RDBYT2 PHA ;READ TWO TRANSITIONS -FCEF: 20 FA FC 738 JSR RD2BIT ; (FIND EDGE) -FCF2: 68 739 PLA -FCF3: 2A 740 ROL ;NEXT BIT -FCF4: A0 3A 741 LDY #$3A ;COUNT FOR SAMPLES -FCF6: CA 742 DEX -FCF7: D0 F5 743 BNE RDBYT2 -FCF9: 60 744 RTS - -; Read two bits from the tape. -FCFA: 20 FD FC 745 RD2BIT JSR RDBIT -; Read one bit from the tape. On entry, Y is the expected transition time: -; $3A=696usec $35=636usec $24=432usec -; Returns with the carry set if the transition time exceeds the Y value. -FCFD: 88 746 RDBIT DEY ;DECR Y UNTIL -FCFE: AD 60 C0 747 LDA TAPEIN ; TAPE TRANSITION -FD01: 45 2F 748 EOR LASTIN -FD03: 10 F8 749 BPL RDBIT -; the above loop takes 12 usec per iteration, what follows takes 14. -FD05: 45 2F 750 EOR LASTIN -FD07: 85 2F 751 STA LASTIN -FD09: C0 80 752 CPY #$80 ;SET CARRY ON Y -FD0B: 60 753 RTS - -*/ - - -/* - * ========================================================================== - * CassetteDialog - * ========================================================================== - */ - -BEGIN_MESSAGE_MAP(CassetteDialog, CDialog) - ON_NOTIFY(LVN_ITEMCHANGED, IDC_CASSETTE_LIST, OnListChange) - ON_NOTIFY(NM_DBLCLK, IDC_CASSETTE_LIST, OnListDblClick) - //ON_MESSAGE(WMU_DIALOG_READY, OnDialogReady) - ON_COMMAND(IDC_IMPORT_CHUNK, OnImport) - ON_COMMAND(IDHELP, OnHelp) - ON_CBN_SELCHANGE(IDC_CASSETTE_ALG, OnAlgorithmChange) -END_MESSAGE_MAP() - - -BOOL CassetteDialog::OnInitDialog(void) -{ - CRect rect; - const Preferences* pPreferences = GET_PREFERENCES(); - - CDialog::OnInitDialog(); // does DDX init - - CWnd* pWnd; - pWnd = GetDlgItem(IDC_IMPORT_CHUNK); - pWnd->EnableWindow(FALSE); - - pWnd = GetDlgItem(IDC_CASSETTE_INPUT); - pWnd->SetWindowText(fFileName); - - /* prep the combo box */ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_CASSETTE_ALG); - ASSERT(pCombo != NULL); - int defaultAlg = pPreferences->GetPrefLong(kPrCassetteAlgorithm); - if (defaultAlg > CassetteData::kAlgorithmMIN && - defaultAlg < CassetteData::kAlgorithmMAX) - { - pCombo->SetCurSel(defaultAlg); - } else { - LOGI("GLITCH: invalid defaultAlg in prefs (%d)", defaultAlg); - pCombo->SetCurSel(CassetteData::kAlgorithmZero); - } - fAlgorithm = (CassetteData::Algorithm) defaultAlg; - - /* - * Prep the listview control. - * - * Columns: - * [icon] Index | Format | Length | Checksum OK - */ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_CASSETTE_LIST); - ASSERT(pListView != NULL); - ListView_SetExtendedListViewStyleEx(pListView->m_hWnd, - LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); - - int width0, width1, width2, width3, width4; - - pListView->GetClientRect(&rect); - width0 = pListView->GetStringWidth(L"XXIndexX"); - width1 = pListView->GetStringWidth(L"XXFormatXmmmmmmmmmmmmmm"); - width2 = pListView->GetStringWidth(L"XXLengthXm"); - width3 = pListView->GetStringWidth(L"XXChecksumXm"); - width4 = pListView->GetStringWidth(L"XXStart sampleX"); - //width5 = pListView->GetStringWidth("XXEnd sampleX"); - - pListView->InsertColumn(0, L"Index", LVCFMT_LEFT, width0); - pListView->InsertColumn(1, L"Format", LVCFMT_LEFT, width1); - pListView->InsertColumn(2, L"Length", LVCFMT_LEFT, width2); - pListView->InsertColumn(3, L"Checksum", LVCFMT_LEFT, width3); - pListView->InsertColumn(4, L"Start sample", LVCFMT_LEFT, width4); - pListView->InsertColumn(5, L"End sample", LVCFMT_LEFT, - rect.Width() - (width0+width1+width2+width3+width4) - /*- ::GetSystemMetrics(SM_CXVSCROLL)*/ ); - - /* add images for list; this MUST be loaded before header images */ -// LoadListImages(); -// pListView->SetImageList(&fListImageList, LVSIL_SMALL); - -// LoadList(); - - CenterWindow(); - - //int cc = PostMessage(WMU_DIALOG_READY, 0, 0); - //ASSERT(cc != 0); - - if (!AnalyzeWAV()) - OnCancel(); - - return TRUE; -} - -#if 0 -/* - * Dialog construction has completed. Start the WAV analysis. - */ -LONG -CassetteDialog::OnDialogReady(UINT, LONG) -{ - //AnalyzeWAV(); - return 0; -} -#endif - - -void CassetteDialog::OnListChange(NMHDR*, LRESULT* pResult) -{ - LOGI("List change"); - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_CASSETTE_LIST); - CButton* pButton = (CButton*) GetDlgItem(IDC_IMPORT_CHUNK); - pButton->EnableWindow(pListView->GetSelectedCount() != 0); - - *pResult = 0; -} - - -void CassetteDialog::OnListDblClick(NMHDR* pNotifyStruct, LRESULT* pResult) -{ - LOGI("Double click!"); - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_CASSETTE_LIST); - - if (pListView->GetSelectedCount() == 1) - OnImport(); - - *pResult = 0; -} - -void CassetteDialog::OnAlgorithmChange(void) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_CASSETTE_ALG); - ASSERT(pCombo != NULL); - LOGI("+++ SELECTION IS NOW %d", pCombo->GetCurSel()); - fAlgorithm = (CassetteData::Algorithm) pCombo->GetCurSel(); - AnalyzeWAV(); -} - -void CassetteDialog::OnImport(void) -{ - /* - * Figure out which item they have selected. - */ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_CASSETTE_LIST); - ASSERT(pListView != NULL); - assert(pListView->GetSelectedCount() == 1); - - POSITION posn; - posn = pListView->GetFirstSelectedItemPosition(); - if (posn == NULL) { - ASSERT(false); - return; - } - int idx = pListView->GetNextSelectedItem(posn); - - /* - * Set up the import dialog. - */ - CassImpTargetDialog impDialog(this); - - impDialog.fFileName = "From.Tape"; - impDialog.fFileLength = fDataArray[idx].GetDataLen(); - impDialog.SetFileType(fDataArray[idx].GetFileType()); - - if (impDialog.DoModal() != IDOK) - return; - - /* - * Write the file to the currently-open archive. - */ - GenericArchive::LocalFileDetails details; - - details.SetEntryKind(GenericArchive::LocalFileDetails::kFileKindDataFork); - details.SetLocalPathName(L"Cassette WAV"); - details.SetStrippedLocalPathName(impDialog.fFileName); - details.SetAccess(0xe3); // unlocked, backup bit set - details.SetFileType(impDialog.GetFileType()); - if (details.GetFileType() == kFileTypeBIN) { - details.SetExtraType(impDialog.fStartAddr); - } else if (details.GetFileType() == kFileTypeBAS) { - details.SetExtraType(0x0801); - } else { - details.SetExtraType(0x0000); - } - details.SetStorageType(DiskFS::kStorageSeedling); - time_t now = time(NULL); - NuDateTime ndt; - GenericArchive::UNIXTimeToDateTime(&now, &ndt); - details.SetCreateWhen(ndt); - details.SetArchiveWhen(ndt); - details.SetModWhen(ndt); - - CString errMsg; - - fDirty = true; - if (!MainWindow::SaveToArchive(&details, fDataArray[idx].GetDataBuf(), - fDataArray[idx].GetDataLen(), NULL, -1, &errMsg, this)) - { - goto bail; - } - - -bail: - if (!errMsg.IsEmpty()) { - CString msg; - msg.Format(L"Unable to import file: %ls.", (LPCWSTR) errMsg); - ShowFailureMsg(this, msg, IDS_FAILED); - return; - } -} - -bool CassetteDialog::AnalyzeWAV(void) -{ - SoundFile soundFile; - CWaitCursor waitc; - CListCtrl* pListCtrl = (CListCtrl*) GetDlgItem(IDC_CASSETTE_LIST); - CString errMsg; - long sampleOffset; - int idx; - - if (soundFile.Create(fFileName, &errMsg) != 0) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - return false; - } - - const WAVEFORMATEX* pFormat = soundFile.GetWaveFormat(); - if (pFormat->nChannels < 1 || pFormat->nChannels > 2 || - (pFormat->wBitsPerSample != 8 && pFormat->wBitsPerSample != 16)) - { - errMsg.Format(L"Unexpected PCM format (%d channels, %d bits/sample)", - pFormat->nChannels, pFormat->wBitsPerSample); - ShowFailureMsg(this, errMsg, IDS_FAILED); - return false; - } - if (soundFile.GetDataLen() % soundFile.GetBPS() != 0) { - errMsg.Format(L"Unexpected sound data length (%ld, samples are %d bytes)", - soundFile.GetDataLen(), soundFile.GetBPS()); - ShowFailureMsg(this, errMsg, IDS_FAILED); - return false; - } - - pListCtrl->DeleteAllItems(); - - sampleOffset = 0; - for (idx = 0; idx < kMaxRecordings; idx++) { - long fileType; - bool result; - - result = fDataArray[idx].Scan(&soundFile, fAlgorithm, &sampleOffset); - if (!result) - break; - - AddEntry(idx, pListCtrl, &fileType); - fDataArray[idx].SetFileType(fileType); - } - - if (idx == 0) { - LOGI("No Apple II files found"); - /* that's okay, just show the empty list */ - } - - return true; -} - -void CassetteDialog::AddEntry(int idx, CListCtrl* pListCtrl, long* pFileType) -{ - CString tmpStr; - const CassetteData* pData = &fDataArray[idx]; - const unsigned char* pDataBuf = pData->GetDataBuf(); - - ASSERT(pDataBuf != NULL); - - tmpStr.Format(L"%d", idx); - pListCtrl->InsertItem(idx, tmpStr); - - *pFileType = kFileTypeBIN; - if (pData->GetDataLen() == 2) { - tmpStr.Format(L"Integer header ($%04X)", - pDataBuf[0] | pDataBuf[1] << 8); - } else if (pData->GetDataLen() == 3) { - tmpStr.Format(L"Applesoft header ($%04X $%02x)", - pDataBuf[0] | pDataBuf[1] << 8, pDataBuf[2]); - } else if (pData->GetDataLen() > 3 && idx > 0 && - fDataArray[idx-1].GetDataLen() == 2) - { - tmpStr = L"Integer BASIC"; - *pFileType = kFileTypeINT; - } else if (pData->GetDataLen() > 3 && idx > 0 && - fDataArray[idx-1].GetDataLen() == 3) - { - tmpStr = L"Applesoft BASIC"; - *pFileType = kFileTypeBAS; - } else { - tmpStr = L"Binary"; - } - pListCtrl->SetItemText(idx, 1, tmpStr); - - tmpStr.Format(L"%d", pData->GetDataLen()); - pListCtrl->SetItemText(idx, 2, tmpStr); - if (pData->GetDataChkGood()) - tmpStr.Format(L"Good (0x%02x)", pData->GetDataChecksum()); - else - tmpStr.Format(L"BAD (0x%02x)", pData->GetDataChecksum()); - pListCtrl->SetItemText(idx, 3, tmpStr); - tmpStr.Format(L"%ld", pData->GetDataOffset()); - pListCtrl->SetItemText(idx, 4, tmpStr); - tmpStr.Format(L"%ld", pData->GetDataEndOffset()); - pListCtrl->SetItemText(idx, 5, tmpStr); -} - - -/* - * ========================================================================== - * CassetteData - * ========================================================================== - */ - -bool CassetteDialog::CassetteData::Scan(SoundFile* pSoundFile, Algorithm alg, - long* pStartOffset) -{ - const int kSampleChunkSize = 65536; // should be multiple of 4 - const WAVEFORMATEX* pFormat; - ScanState scanState; - long initialLen, dataLen, chunkLen, byteOffset; - long sampleStartIndex; - unsigned char* buf = NULL; - float* sampleBuf = NULL; - int bytesPerSample; - bool result = false; - unsigned char checkSum; - int outByteIndex, bitAcc; - - bytesPerSample = pSoundFile->GetBPS(); - assert(bytesPerSample >= 1 && bytesPerSample <= 4); - assert(kSampleChunkSize % bytesPerSample == 0); - byteOffset = *pStartOffset; - initialLen = dataLen = pSoundFile->GetDataLen() - byteOffset; - sampleStartIndex = byteOffset/bytesPerSample; - LOGI("CassetteData::Scan(off=%ld / %ld) len=%ld alg=%d", - byteOffset, sampleStartIndex, dataLen, alg); - - pFormat = pSoundFile->GetWaveFormat(); - - buf = new unsigned char[kSampleChunkSize]; - sampleBuf = new float[kSampleChunkSize/bytesPerSample]; - if (fOutputBuf == NULL) // alloc on first use - fOutputBuf = new unsigned char[kMaxFileLen]; - if (buf == NULL || sampleBuf == NULL || fOutputBuf == NULL) { - LOGI("Buffer alloc failed"); - goto bail; - } - - memset(&scanState, 0, sizeof(scanState)); - scanState.algorithm = alg; - scanState.phase = kPhaseScanFor770Start; - scanState.mode = kModeInitial0; - scanState.positive = false; - scanState.usecPerSample = 1000000.0f / (float) pFormat->nSamplesPerSec; - - checkSum = 0xff; - outByteIndex = 0; - bitAcc = 1; - - /* - * Loop until done or out of data. - */ - while (dataLen > 0) { - int cc; - - chunkLen = dataLen; - if (chunkLen > kSampleChunkSize) - chunkLen = kSampleChunkSize; - - cc = pSoundFile->ReadData(buf, byteOffset, chunkLen); - if (cc < 0) { - LOGI("ReadData(%d) failed", chunkLen); - goto bail; - } - - ConvertSamplesToReal(pFormat, buf, chunkLen, sampleBuf); - - for (int i = 0; i < chunkLen / bytesPerSample; i++) { - int bitVal; - if (ProcessSample(sampleBuf[i], sampleStartIndex + i, - &scanState, &bitVal)) - { - if (outByteIndex >= kMaxFileLen) { - LOGI("Cassette data overflow"); - scanState.phase = kPhaseEndReached; - } else { - /* output a bit, shifting until bit 8 lights up */ - assert(bitVal == 0 || bitVal == 1); - bitAcc = (bitAcc << 1) | bitVal; - if (bitAcc > 0xff) { - fOutputBuf[outByteIndex++] = (unsigned char) bitAcc; - checkSum ^= (unsigned char) bitAcc; - bitAcc = 1; - } - } - } - if (scanState.phase == kPhaseEndReached) { - dataLen -= i * bytesPerSample; - break; - } - } - if (scanState.phase == kPhaseEndReached) - break; - - dataLen -= chunkLen; - byteOffset += chunkLen; - sampleStartIndex += chunkLen / bytesPerSample; - } - - switch (scanState.phase) { - case kPhaseScanFor770Start: - case kPhaseScanning770: - // expected case for trailing part of file - LOGI("Scan ended while searching for 770"); - goto bail; - case kPhaseScanForShort0: - case kPhaseShort0B: - LOGI("Scan ended while searching for short 0/0B"); - //DebugBreak(); // unusual - goto bail; - case kPhaseReadData: - LOGI("Scan ended while reading data"); - //DebugBreak(); // truncated WAV file? - goto bail; - case kPhaseEndReached: - LOGI("Scan found end"); - // winner! - break; - default: - LOGI("Unknown phase %d", scanState.phase); - assert(false); - goto bail; - } - - LOGI("*** Output %d bytes (bitAcc=0x%02x, checkSum=0x%02x)", - outByteIndex, bitAcc, checkSum); - - if (outByteIndex == 0) { - fOutputLen = 0; - fChecksum = 0x00; - fChecksumGood = false; - } else { - fOutputLen = outByteIndex-1; - fChecksum = fOutputBuf[outByteIndex-1]; - fChecksumGood = (checkSum == 0x00); - } - fStartSample = scanState.dataStart; - fEndSample = scanState.dataEnd; - - /* we're done with this file; advance the start offset */ - *pStartOffset = *pStartOffset + (initialLen - dataLen); - - result = true; - -bail: - delete[] buf; - delete[] sampleBuf; - return result; -} - -void CassetteDialog::CassetteData::ConvertSamplesToReal(const WAVEFORMATEX* pFormat, - const unsigned char* buf, long chunkLen, float* sampleBuf) -{ - int bps = ((pFormat->wBitsPerSample+7)/8) * pFormat->nChannels; - int bitsPerSample = pFormat->wBitsPerSample; - int offset = 0; - - assert(chunkLen % bps == 0); - - if (bitsPerSample == 8) { - while (chunkLen > 0) { - *sampleBuf++ = (*buf - 128) / 128.0f; - //LOGI("Sample8(%5d)=%d float=%.3f", offset, *buf, *(sampleBuf-1)); - //offset++; - buf += bps; - chunkLen -= bps; - } - } else if (bitsPerSample == 16) { - while (chunkLen > 0) { - short sample = *buf | *(buf+1) << 8; - *sampleBuf++ = sample / 32768.0f; - //LOGI("Sample16(%5d)=%d float=%.3f", offset, sample, *(sampleBuf-1)); - //offset++; - buf += bps; - chunkLen -= bps; - } - } else { - assert(false); - } - - //LOGI("Conv %d", bitsPerSample); -} - -/* width of 1/2 cycle in 770Hz lead-in */ -const float kLeadInHalfWidth = 650.0f; // usec -/* max error when detecting 770Hz lead-in, in usec */ -const float kLeadInMaxError = 108.0f; // usec (542 - 758) -/* width of 1/2 cycle of "short 0" */ -const float kShortZeroHalfWidth = 200.0f; // usec -/* max error when detection short 0 */ -const float kShortZeroMaxError = 150.0f; // usec (50 - 350) -/* width of 1/2 cycle of '0' */ -const float kZeroHalfWidth = 250.0f; // usec -/* max error when detecting '0' */ -const float kZeroMaxError = 94.0f; // usec -/* width of 1/2 cycle of '1' */ -const float kOneHalfWidth = 500.0f; // usec -/* max error when detecting '1' */ -const float kOneMaxError = 94.0f; // usec -/* after this many 770Hz half-cycles, start looking for short 0 */ -const long kLeadInHalfCycThreshold = 1540; // 1 full second - -/* amplitude must change by this much before we switch out of "peak" mode */ -const float kPeakThreshold = 0.2f; // 10% -/* amplitude must change by at least this much to stay in "transition" mode */ -const float kTransMinDelta = 0.02f; // 1% -/* kTransMinDelta happens over this range */ -const float kTransDeltaBase = 45.35f; // usec (1 sample at 22.05KHz) - - -bool CassetteDialog::CassetteData::ProcessSample(float sample, long sampleIndex, - ScanState* pScanState, int* pBitVal) -{ - if (pScanState->algorithm == kAlgorithmZero) - return ProcessSampleZero(sample, sampleIndex, pScanState, pBitVal); - else if (pScanState->algorithm == kAlgorithmRoundPeak || - pScanState->algorithm == kAlgorithmSharpPeak || - pScanState->algorithm == kAlgorithmShallowPeak) - return ProcessSamplePeak(sample, sampleIndex, pScanState, pBitVal); - else { - assert(false); - return false; - } -} - -bool CassetteDialog::CassetteData::ProcessSampleZero(float sample, long sampleIndex, - ScanState* pScanState, int* pBitVal) -{ - long timeDelta; - bool crossedZero = false; - bool emitBit = false; - - /* - * Analyze the mode, changing to a new one when appropriate. - */ - switch (pScanState->mode) { - case kModeInitial0: - assert(pScanState->phase == kPhaseScanFor770Start); - pScanState->mode = kModeRunning; - break; - case kModeRunning: - if (pScanState->prevSample < 0.0f && sample >= 0.0f || - pScanState->prevSample >= 0.0f && sample < 0.0f) - { - crossedZero = true; - } - break; - default: - assert(false); - break; - } - - /* - * Deal with a zero crossing. - * - * We currently just grab the first point after we cross. We should - * be grabbing the closest point or interpolating across. - */ - if (crossedZero) { - float halfCycleUsec; - int bias; - - if (fabs(pScanState->prevSample) < fabs(sample)) - bias = -1; // previous sample was closer to zero point - else - bias = 0; // current sample is closer - - /* delta time for zero-to-zero (half cycle) */ - timeDelta = (sampleIndex+bias) - pScanState->lastZeroIndex; - - halfCycleUsec = timeDelta * pScanState->usecPerSample; - //LOGI("Zero %6ld: half=%.1fusec full=%.1fusec", - // sampleIndex, halfCycleUsec, - // halfCycleUsec + pScanState->halfCycleWidth); - - emitBit = UpdatePhase(pScanState, sampleIndex+bias, halfCycleUsec, - pBitVal); - - pScanState->lastZeroIndex = sampleIndex + bias; - } - - /* record this sample for the next go-round */ - pScanState->prevSample = sample; - - return emitBit; -} - -bool CassetteDialog::CassetteData::ProcessSamplePeak(float sample, long sampleIndex, - ScanState* pScanState, int* pBitVal) -{ - /* values range from [-1.0,1.0), so range is 2.0 total */ - long timeDelta; - float ampDelta; - float transitionLimit; - bool hitPeak = false; - bool emitBit = false; - - /* - * Analyze the mode, changing to a new one when appropriate. - */ - switch (pScanState->mode) { - case kModeInitial0: - assert(pScanState->phase == kPhaseScanFor770Start); - pScanState->mode = kModeInitial1; - break; - case kModeInitial1: - assert(pScanState->phase == kPhaseScanFor770Start); - if (sample >= pScanState->prevSample) - pScanState->positive = true; - else - pScanState->positive = false; - pScanState->mode = kModeInTransition; - /* set these up with something reasonable */ - pScanState->lastPeakStartIndex = sampleIndex; - pScanState->lastPeakStartValue = sample; - break; - - case kModeInTransition: - /* - * Stay here until two adjacent samples are very close in amplitude - * (or we change direction). We need to adjust our amplitude - * threshold based on sampling frequency, or at higher sample - * rates we're going to think everything is a transition. - * - * The approach here is overly simplistic, and is prone to failure - * when the sampling rate is high, especially with 8-bit samples - * or sound cards that don't really have 16-bit resolution. The - * proper way to do this is to keep a short history, and evaluate - * the delta amplitude over longer periods. [At this point I'd - * rather just tell people to record at 22.05KHz.] - * - * Set the "hitPeak" flag and handle the consequences below. - */ - if (pScanState->algorithm == kAlgorithmRoundPeak) - transitionLimit = kTransMinDelta * - (pScanState->usecPerSample / kTransDeltaBase); - else - transitionLimit = 0.0f; - - if (pScanState->positive) { - if (sample < pScanState->prevSample + transitionLimit) { - pScanState->mode = kModeAtPeak; - hitPeak = true; - } - } else { - if (sample > pScanState->prevSample - transitionLimit) { - pScanState->mode = kModeAtPeak; - hitPeak = true; - } - } - break; - case kModeAtPeak: - /* - * Stay here until we're a certain distance above or below the - * previous peak. This also keeps us in a holding pattern for - * large flat areas. - */ - transitionLimit = kPeakThreshold; - if (pScanState->algorithm == kAlgorithmShallowPeak) - transitionLimit /= 4.0f; - - ampDelta = pScanState->lastPeakStartValue - sample; - if (ampDelta < 0) - ampDelta = -ampDelta; - if (ampDelta > transitionLimit) { - if (sample >= pScanState->lastPeakStartValue) - pScanState->positive = true; // going up - else - pScanState->positive = false; // going down - - /* mark the end of the peak; could be same as start of peak */ - pScanState->mode = kModeInTransition; - } - break; - default: - assert(false); - break; - } - - /* - * If we hit "peak" criteria, we regard the *previous* sample as the - * peak. This is very important for lower sampling rates (e.g. 8KHz). - */ - if (hitPeak) { - /* compute half-cycle amplitude and time */ - float halfCycleUsec; //, fullCycleUsec; - - /* delta time for peak-to-peak (half cycle) */ - timeDelta = (sampleIndex-1) - pScanState->lastPeakStartIndex; - /* amplitude peak-to-peak */ - ampDelta = pScanState->lastPeakStartValue - pScanState->prevSample; - if (ampDelta < 0) - ampDelta = -ampDelta; - - halfCycleUsec = timeDelta * pScanState->usecPerSample; - //if (sampleIndex > 584327 && sampleIndex < 590000) { - // LOGI("Peak %6ld: amp=%.3f height=%.3f peakWidth=%.1fusec", - // sampleIndex-1, pScanState->prevSample, ampDelta, - // halfCycleUsec); - // ::Sleep(10); - //} - if (sampleIndex == 32739) - LOGI("whee"); - - emitBit = UpdatePhase(pScanState, sampleIndex-1, halfCycleUsec, pBitVal); - - /* set the "peak start" values */ - pScanState->lastPeakStartIndex = sampleIndex-1; - pScanState->lastPeakStartValue = pScanState->prevSample; - } - - /* record this sample for the next go-round */ - pScanState->prevSample = sample; - - return emitBit; -} - -bool CassetteDialog::CassetteData::UpdatePhase(ScanState* pScanState, - long sampleIndex, float halfCycleUsec, int* pBitVal) -{ - float fullCycleUsec; - bool emitBit = false; - - if (pScanState->halfCycleWidth != 0.0f) - fullCycleUsec = halfCycleUsec + pScanState->halfCycleWidth; - else - fullCycleUsec = 0.0f; // only have first half - - switch (pScanState->phase) { - case kPhaseScanFor770Start: - /* watch for a cycle of the appropriate length */ - if (fullCycleUsec != 0.0f && - fullCycleUsec > kLeadInHalfWidth*2.0f - kLeadInMaxError*2.0f && - fullCycleUsec < kLeadInHalfWidth*2.0f + kLeadInMaxError*2.0f) - { - //LOGI(" scanning 770 at %ld", sampleIndex); - pScanState->phase = kPhaseScanning770; - pScanState->num770 = 1; - } - break; - case kPhaseScanning770: - /* count up the 770Hz cycles */ - if (fullCycleUsec != 0.0f && - fullCycleUsec > kLeadInHalfWidth*2.0f - kLeadInMaxError*2.0f && - fullCycleUsec < kLeadInHalfWidth*2.0f + kLeadInMaxError*2.0f) - { - pScanState->num770++; - if (pScanState->num770 > kLeadInHalfCycThreshold/2) { - /* looks like a solid tone, advance to next phase */ - pScanState->phase = kPhaseScanForShort0; - LOGI(" looking for short 0"); - } - } else if (fullCycleUsec != 0.0f) { - /* pattern lost, reset */ - if (pScanState->num770 > 5) { - LOGI(" lost 770 at %ld width=%.1f (count=%ld)", - sampleIndex, fullCycleUsec, pScanState->num770); - } - pScanState->phase = kPhaseScanFor770Start; - } - /* else we only have a half cycle, so do nothing */ - break; - case kPhaseScanForShort0: - /* found what looks like a 770Hz field, find the short 0 */ - if (halfCycleUsec > kShortZeroHalfWidth - kShortZeroMaxError && - halfCycleUsec < kShortZeroHalfWidth + kShortZeroMaxError) - { - LOGI(" found short zero (half=%.1f) at %ld after %ld 770s", - halfCycleUsec, sampleIndex, pScanState->num770); - pScanState->phase = kPhaseShort0B; - /* make sure we treat current sample as first half */ - pScanState->halfCycleWidth = 0.0f; - } else - if (fullCycleUsec != 0.0f && - fullCycleUsec > kLeadInHalfWidth*2.0f - kLeadInMaxError*2.0f && - fullCycleUsec < kLeadInHalfWidth*2.0f + kLeadInMaxError*2.0f) - { - /* found another 770Hz cycle */ - pScanState->num770++; - } else if (fullCycleUsec != 0.0f) { - /* full cycle of the wrong size, we've lost it */ - LOGI(" Lost 770 at %ld width=%.1f (count=%ld)", - sampleIndex, fullCycleUsec, pScanState->num770); - pScanState->phase = kPhaseScanFor770Start; - } - break; - case kPhaseShort0B: - /* pick up the second half of the start cycle */ - assert(fullCycleUsec != 0.0f); - if (fullCycleUsec > (kShortZeroHalfWidth + kZeroHalfWidth) - kZeroMaxError*2.0f && - fullCycleUsec < (kShortZeroHalfWidth + kZeroHalfWidth) + kZeroMaxError*2.0f) - { - /* as expected */ - LOGI(" Found 0B %.1f (total %.1f), advancing to 'read data' phase", - halfCycleUsec, fullCycleUsec); - pScanState->dataStart = sampleIndex; - pScanState->phase = kPhaseReadData; - } else { - /* must be a false-positive at end of tone */ - LOGI(" Didn't find post-short-0 value (half=%.1f + %.1f)", - pScanState->halfCycleWidth, halfCycleUsec); - pScanState->phase = kPhaseScanFor770Start; - } - break; - - case kPhaseReadData: - /* check width of full cycle; don't double error allowance */ - if (fullCycleUsec != 0.0f) { - if (fullCycleUsec > kZeroHalfWidth*2 - kZeroMaxError*2 && - fullCycleUsec < kZeroHalfWidth*2 + kZeroMaxError*2) - { - *pBitVal = 0; - emitBit = true; - } else - if (fullCycleUsec > kOneHalfWidth*2 - kOneMaxError*2 && - fullCycleUsec < kOneHalfWidth*2 + kOneMaxError*2) - { - *pBitVal = 1; - emitBit = true; - } else { - /* bad cycle, assume end reached */ - LOGI(" Bad full cycle time %.1f in data at %ld, bailing", - fullCycleUsec, sampleIndex); - pScanState->dataEnd = sampleIndex; - pScanState->phase = kPhaseEndReached; - } - } - break; - default: - assert(false); - break; - } - - /* save the half-cycle stats */ - if (pScanState->halfCycleWidth == 0.0f) - pScanState->halfCycleWidth = halfCycleUsec; - else - pScanState->halfCycleWidth = 0.0f; - - return emitBit; -} diff --git a/ciderpress/app/CassetteDialog.h b/ciderpress/app/CassetteDialog.h deleted file mode 100644 index ca42412..0000000 --- a/ciderpress/app/CassetteDialog.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Apple II cassette I/O functions. - */ -#ifndef APP_CASSETTEDIALOG_H -#define APP_CASSETTEDIALOG_H - -/* - * The dialog box is primarily concerned with extracting the original data - * from a WAV file recording of an Apple II cassette tape. - */ -class CassetteDialog : public CDialog { -public: - CassetteDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_IMPORTCASSETTE, pParentWnd), fDirty(false) - {} - virtual ~CassetteDialog(void) {} - - CString fFileName; // file to open - - bool IsDirty(void) const { return fDirty; } - -private: - virtual BOOL OnInitDialog(void) override; - //virtual void DoDataExchange(CDataExchange* pDX); - //virtual void OnOK(void); - - //enum { WMU_DIALOG_READY = WM_USER+2 }; - - /* - * Something changed in the list. Update the "OK" button. - */ - afx_msg void OnListChange(NMHDR* pNotifyStruct, LRESULT* pResult); - - /* - * The volume filter drop-down box has changed. - */ - afx_msg void OnAlgorithmChange(void); - - /* - * User pressed "import" button. Add the selected item to the current - * archive or disk image. - */ - afx_msg void OnImport(void); - - afx_msg void OnListDblClick(NMHDR* pNotifyStruct, LRESULT* pResult); - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_IMPORT_CASSETTE); - } - - /* - * This holds converted data from the WAV file, plus some meta-data - * like what type of file we think this is. - */ - class CassetteData { - public: - CassetteData(void) : fFileType(0x00), fOutputBuf(NULL), fOutputLen(-1), - fStartSample(-1), fEndSample(-1), fChecksum(0x00), - fChecksumGood(false) - {} - virtual ~CassetteData(void) { delete[] fOutputBuf; } - - /* - * Algorithm to use. This must match up with the order of the items - * in the dialog IDC_CASSETTE_ALG combo box. - */ - typedef enum Algorithm { - kAlgorithmMIN = -1, - - kAlgorithmZero = 0, - kAlgorithmSharpPeak, - kAlgorithmRoundPeak, - kAlgorithmShallowPeak, - - kAlgorithmMAX - } Algorithm; - - /* - * Scan the WAV file, starting from the specified byte offset. - * - * Returns "true" if we found a file, "false" if not (indicating that the - * end of the input has been reached). Updates "*pStartOffset" to point - * past the end of the data we've read. - */ - bool Scan(SoundFile* pSoundFile, Algorithm alg, long* pSampleOffset); - unsigned char* GetDataBuf(void) const { return fOutputBuf; } - int GetDataLen(void) const { return fOutputLen; } - int GetDataOffset(void) const { return fStartSample; } - int GetDataEndOffset(void) const { return fEndSample; } - unsigned char GetDataChecksum(void) const { return fChecksum; } - bool GetDataChkGood(void) const { return fChecksumGood; } - - long GetFileType(void) const { return fFileType; } - void SetFileType(long fileType) { fFileType = fileType; } - - private: - typedef enum Phase { - kPhaseUnknown = 0, - kPhaseScanFor770Start, - kPhaseScanning770, - kPhaseScanForShort0, - kPhaseShort0B, - kPhaseReadData, - kPhaseEndReached, - // kPhaseError, - } Phase; - typedef enum Mode { - kModeUnknown = 0, - kModeInitial0, - kModeInitial1, - - kModeInTransition, - kModeAtPeak, - - kModeRunning, - } Mode; - - typedef struct ScanState { - Algorithm algorithm; - Phase phase; - Mode mode; - bool positive; // rising or at +peak if true - - long lastZeroIndex; // in samples - long lastPeakStartIndex; // in samples - float lastPeakStartValue; - - float prevSample; - - float halfCycleWidth; // in usec - long num770; // #of consecutive 770Hz cycles - long dataStart; - long dataEnd; - - /* constants */ - float usecPerSample; - } ScanState; - - /* - * Convert a block of samples from PCM to float. - * - * Only the first (left) channel is converted in multi-channel formats. - */ - void ConvertSamplesToReal(const WAVEFORMATEX* pFormat, - const unsigned char* buf, long chunkLen, float* sampleBuf); - - /* - * Process one audio sample. Updates "pScanState" appropriately. - * - * If we think we found a bit, this returns "true" with 0 or 1 in "*pBitVal". - */ - bool ProcessSample(float sample, long sampleIndex, - ScanState* pScanState, int* pBitVal); - - /* - * Process the data by measuring the distance between zero crossings. - * - * This is very similar to the way the Apple II does it, though - * we have to scan for the 770Hz lead-in instead of simply assuming the - * the user has queued up the tape. - * - * To offset the effects of DC bias, we examine full cycles instead of - * half cycles. - */ - bool ProcessSampleZero(float sample, long sampleIndex, - ScanState* pScanState, int* pBitVal); - - /* - * Process the data by finding and measuring the distance between peaks. - */ - bool ProcessSamplePeak(float sample, long sampleIndex, - ScanState* pScanState, int* pBitVal); - - /* - * Given the width of a half-cycle, update "phase" and decide whether or not - * it's time to emit a bit. - * - * Updates "halfCycleWidth" too, alternating between 0.0 and a value. - * - * The "sampleIndex" parameter is largely just for display. We use it to - * set the "start" and "end" pointers, but those are also ultimately just - * for display to the user. - */ - bool UpdatePhase(ScanState* pScanState, long sampleIndex, - float halfCycleUsec, int* pBitVal); - - enum { - kMaxFileLen = 65535+2+1+1, // 64K + length + checksum + 1 slop - }; - - long fFileType; // 0x06, 0xfa, or 0xfc - unsigned char* fOutputBuf; - int fOutputLen; - long fStartSample; - long fEndSample; - unsigned char fChecksum; - bool fChecksumGood; - }; - - /* - * Analyze the contents of a WAV file. - * - * Returns true if it found anything at all, false if not. - */ - bool AnalyzeWAV(void); - - /* - * Add an entry to the list. - * - * Layout: index format length checksum start-offset - */ - void AddEntry(int idx, CListCtrl* pListCtrl, long* pFileType); - - enum { - kMaxRecordings = 100, // max A2 files per WAV file - }; - - /* array with one entry per file */ - CassetteData fDataArray[kMaxRecordings]; - - CassetteData::Algorithm fAlgorithm; - bool fDirty; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CASSETTEDIALOG_H*/ diff --git a/ciderpress/app/ChooseAddTargetDialog.cpp b/ciderpress/app/ChooseAddTargetDialog.cpp deleted file mode 100644 index 9ec1025..0000000 --- a/ciderpress/app/ChooseAddTargetDialog.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Functions for the ChooseAddTarget dialog box. - */ -#include "StdAfx.h" -#include "ChooseAddTargetDialog.h" -#include "DiskFSTree.h" - -using namespace DiskImgLib; - -BEGIN_MESSAGE_MAP(ChooseAddTargetDialog, CDialog) - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - -BOOL ChooseAddTargetDialog::OnInitDialog(void) -{ - CDialog::OnInitDialog(); - - CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_ADD_TARGET_TREE); - - ASSERT(fpDiskFS != NULL); - ASSERT(pTree != NULL); - - fDiskFSTree.fIncludeSubdirs = true; - fDiskFSTree.fExpandDepth = -1; - if (!fDiskFSTree.BuildTree(fpDiskFS, pTree)) { - LOGI("Tree load failed!"); - OnCancel(); - } - - int count = pTree->GetCount(); - LOGI("ChooseAddTargetDialog tree has %d items", count); - if (count <= 1) { - LOGI(" Skipping out of target selection"); - // adding to root volume of the sole DiskFS - fpChosenDiskFS = fpDiskFS; - ASSERT(fpChosenSubdir == NULL); - OnOK(); - } - - return TRUE; -} - -void ChooseAddTargetDialog::DoDataExchange(CDataExchange* pDX) -{ - /* - * Not much to do on the way in. On the way out, make sure that they've - * selected something acceptable, and copy the values to an easily - * accessible location. - */ - if (pDX->m_bSaveAndValidate) { - CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_ADD_TARGET_TREE); - CString errMsg, appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - /* shortcut for simple disk images */ - if (pTree->GetCount() == 1 && fpChosenDiskFS != NULL) - return; - - HTREEITEM selected; - selected = pTree->GetSelectedItem(); - if (selected == NULL) { - errMsg = L"Please select a disk or subdirectory to add files to."; - MessageBox(errMsg, appName, MB_OK); - pDX->Fail(); - return; - } - - DiskFSTree::TargetData* pTargetData; - pTargetData = (DiskFSTree::TargetData*) pTree->GetItemData(selected); - if (!pTargetData->selectable) { - errMsg = L"You can't add files there."; - MessageBox(errMsg, appName, MB_OK); - pDX->Fail(); - return; - } - - fpChosenDiskFS = pTargetData->pDiskFS; - fpChosenSubdir = pTargetData->pFile; - } -} diff --git a/ciderpress/app/ChooseAddTargetDialog.h b/ciderpress/app/ChooseAddTargetDialog.h deleted file mode 100644 index c82e2f6..0000000 --- a/ciderpress/app/ChooseAddTargetDialog.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Choose the sub-volume and directory where added files will be put. - */ -#ifndef APP_CHOOSEADDTARGETDIALOG_H -#define APP_CHOOSEADDTARGETDIALOG_H - -#include "resource.h" -#include "DiskFSTree.h" -#include "../diskimg/DiskImg.h" - -/* - * The dialog has a tree structure representing the sub-volumes and the - * directory structure within each sub-volume. - */ -class ChooseAddTargetDialog : public CDialog { -public: - ChooseAddTargetDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_CHOOSE_ADD_TARGET, pParentWnd) - { - fpDiskFS = fpChosenDiskFS = NULL; - fpChosenSubdir = NULL; - } - virtual ~ChooseAddTargetDialog(void) {} - - /* set this before calling DoModal */ - DiskImgLib::DiskFS* fpDiskFS; - - /* results; fpChosenSubdir will be NULL if root vol selected */ - DiskImgLib::DiskFS* fpChosenDiskFS; - DiskImgLib::A2File* fpChosenSubdir; - -private: - /* - * Initialize the dialog box. This requires scanning the provided disk - * archive. - */ - virtual BOOL OnInitDialog(void) override; - - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_CHOOSE_TARGET); - } - - DiskFSTree fDiskFSTree; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CHOOSEADDTARGETDIALOG_H*/ diff --git a/ciderpress/app/ChooseDirDialog.h b/ciderpress/app/ChooseDirDialog.h deleted file mode 100644 index fd4c199..0000000 --- a/ciderpress/app/ChooseDirDialog.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Dialog for choosing a directory. - */ -#ifndef APP_CHOOSEDIRDIALOG -#define APP_CHOOSEDIRDIALOG - -#include - - -/* - * Choose a directory. This is distinctly different from what the standard - * "Open" and "Save As" dialogs do, because those want to choose normal files - * only, while this wants to select a folder. - * - * Win2K added the shell "browse for folder" dialog, which does exactly - * what we want. - */ -class ChooseDirDialog { -public: - ChooseDirDialog(CWnd* pParent = NULL) { - fpParent = pParent; - } - ~ChooseDirDialog() {} - - // Gets the pathname. Call this after DoModal has updated it. - const CString& GetPathName(void) const { - return fPathName; - } - - // Sets the pathname. Call before DoModal(). - void SetPathName(const CString& str) { - fPathName = str; - } - - // Returns false if nothing was selected (e.g. the dialog was canceled). - BOOL DoModal() { - CShellManager* pMan = gMyApp.GetShellManager(); - CString outFolder; - BOOL result = pMan->BrowseForFolder(outFolder, fpParent, fPathName, - L"Select folder:", BIF_RETURNONLYFSDIRS | BIF_USENEWUI); - fPathName = outFolder; - return result; - } - -private: - CWnd* fpParent; - CString fPathName; -}; - -#endif /*APP_CHOOSEDIRDIALOG*/ diff --git a/ciderpress/app/CiderPress.rc b/ciderpress/app/CiderPress.rc deleted file mode 100644 index 4289d2b..0000000 --- a/ciderpress/app/CiderPress.rc +++ /dev/null @@ -1,2155 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -// Generated Help ID header file -#define APSTUDIO_HIDDEN_SYMBOLS -#include "resource.hm" -#undef APSTUDIO_HIDDEN_SYMBOLS - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "afxres.h" -#include "Dlgs.h" -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""afxres.h""\r\n" - "#include ""Dlgs.h""\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDR_MAINFRAME ICON "Graphics\\CiderPress.ico" - -IDI_FILE_BINARY2 ICON "Graphics\\binary2.ico" - -IDI_FILE_NUFX ICON "Graphics\\nufx.ico" - -IDI_FILE_DISKIMAGE ICON "Graphics\\diskimage.ico" - -IDI_FILE_VIEWER ICON "Graphics\\FileViewer.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDR_MAINFRAME MENU -BEGIN - POPUP "&File" - BEGIN - POPUP "New" - BEGIN - MENUITEM "&Disk image...", IDM_TOOLS_IMAGECREATOR - MENUITEM "ShrinkIt Archive...\tCtrl+N", IDM_FILE_NEW_ARCHIVE - END - MENUITEM "&Open...\tCtrl-O", IDM_FILE_OPEN - MENUITEM "Open &volume...", IDM_FILE_OPEN_VOLUME - MENUITEM "&Reopen", IDM_FILE_REOPEN - MENUITEM "&Save changes\tCtrl-S", IDM_FILE_SAVE - MENUITEM "&Close\tCtrl-W", IDM_FILE_CLOSE - MENUITEM SEPARATOR - MENUITEM "Archive &Info\tCtrl-I", IDM_FILE_ARCHIVEINFO - MENUITEM "&Print...\tCtrl-P", IDM_FILE_PRINT - MENUITEM SEPARATOR - MENUITEM "&Exit", IDM_FILE_EXIT - END - POPUP "&Edit" - BEGIN - MENUITEM "&Copy\tCtrl-C", IDM_EDIT_COPY - MENUITEM "&Paste\tCtrl-V", IDM_EDIT_PASTE - MENUITEM "Paste Special", IDM_EDIT_PASTE_SPECIAL - MENUITEM SEPARATOR - MENUITEM "&Find...\tCtrl-F", IDM_EDIT_FIND - MENUITEM SEPARATOR - POPUP "&Sort" - BEGIN - MENUITEM "By original order", IDM_SORT_ORIGINAL - MENUITEM "By pathname", IDM_SORT_PATHNAME - MENUITEM "By file type", IDM_SORT_TYPE - MENUITEM "By auxtype", IDM_SORT_AUXTYPE - MENUITEM "By modification date", IDM_SORT_MODDATE - MENUITEM "By format", IDM_SORT_FORMAT - MENUITEM "By size", IDM_SORT_SIZE - MENUITEM "By ratio", IDM_SORT_RATIO - MENUITEM "By packed size", IDM_SORT_PACKED - MENUITEM "By access", IDM_SORT_ACCESS - END - MENUITEM "Select &all\tCtrl-A", IDM_EDIT_SELECT_ALL - MENUITEM "&Invert selection", IDM_EDIT_INVERT_SELECTION - MENUITEM "&Preferences...", IDM_EDIT_PREFERENCES - END - POPUP "&Actions" - BEGIN - MENUITEM "&View...\tTab", IDM_ACTIONS_VIEW - MENUITEM "&Extract...", IDM_ACTIONS_EXTRACT - MENUITEM "&Test...", IDM_ACTIONS_TEST - MENUITEM "&Rename...", IDM_ACTIONS_RENAME - MENUITEM "&Delete...\tDel", IDM_ACTIONS_DELETE - MENUITEM "&Re-compress...", IDM_ACTIONS_RECOMPRESS - MENUITEM SEPARATOR - MENUITEM "Add &files...", IDM_ACTIONS_ADD_FILES - MENUITEM "Add &disk image...", IDM_ACTIONS_ADD_DISKS - MENUITEM "Create &subdirectory...", IDM_ACTIONS_CREATE_SUBDIR - MENUITEM SEPARATOR - MENUITEM "&Open as disk image", IDM_ACTIONS_OPENASDISK - MENUITEM "Edit &comment...", IDM_ACTIONS_EDIT_COMMENT - MENUITEM "Edit &attributes...", IDM_ACTIONS_EDIT_PROPS - MENUITEM "Rename volume...", IDM_ACTIONS_RENAME_VOLUME - MENUITEM SEPARATOR - MENUITEM "&Convert to disk image...", IDM_ACTIONS_CONV_DISK - MENUITEM "&Convert to file archive...", IDM_ACTIONS_CONV_FILE - MENUITEM "Import file from WAV...", IDM_ACTIONS_CONV_FROMWAV - MENUITEM "Import &BAS from text...", IDM_ACTIONS_IMPORT_BAS - END - POPUP "Tools" - BEGIN - MENUITEM "&Disk sector viewer", IDM_TOOLS_DISKEDIT - MENUITEM "Disk &image converter", IDM_TOOLS_DISKCONV - MENUITEM "&Bulk disk image converter", IDM_TOOLS_BULKDISKCONV - MENUITEM SEPARATOR - MENUITEM "&Volume copier (open volume)", IDM_TOOLS_VOLUMECOPIER_VOLUME - MENUITEM "Volume copier (open file)", IDM_TOOLS_VOLUMECOPIER_FILE - MENUITEM "&Merge SST images", IDM_TOOLS_SST_MERGE - MENUITEM SEPARATOR - MENUITEM "2MG properties editor", IDM_TOOLS_TWOIMGPROPS - MENUITEM "EOL scanner", IDM_TOOLS_EOLSCANNER - END - POPUP "&Help" - BEGIN - MENUITEM "&Contents...\tF1", IDM_HELP_CONTENTS - MENUITEM "Visit CiderPress &web site", IDM_HELP_WEBSITE - MENUITEM SEPARATOR - MENUITEM "&About CiderPress", IDM_HELP_ABOUT - END -END - -IDR_RIGHTCLICKMENU MENU -BEGIN - POPUP "RightClickMenu" - BEGIN - MENUITEM "View...", IDM_ACTIONS_VIEW - MENUITEM "Extract...", IDM_ACTIONS_EXTRACT - MENUITEM "Test...", IDM_ACTIONS_TEST - MENUITEM "Rename...", IDM_ACTIONS_RENAME - MENUITEM "Delete...", IDM_ACTIONS_DELETE - MENUITEM "Re-compress...", IDM_ACTIONS_RECOMPRESS - MENUITEM SEPARATOR - MENUITEM "&Copy\tCtrl-C", IDM_EDIT_COPY - MENUITEM "&Paste\tCtrl-V", IDM_EDIT_PASTE - MENUITEM SEPARATOR - MENUITEM "Add &files...", IDM_ACTIONS_ADD_FILES - MENUITEM "Create &subdirectory...", IDM_ACTIONS_CREATE_SUBDIR - MENUITEM SEPARATOR - MENUITEM "Open as disk image", IDM_ACTIONS_OPENASDISK - MENUITEM "Edit comment...", IDM_ACTIONS_EDIT_COMMENT - MENUITEM "Edit attributes...", IDM_ACTIONS_EDIT_PROPS - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Accelerator -// - -IDR_MAINFRAME ACCELERATORS -BEGIN - "A", IDM_EDIT_SELECT_ALL, VIRTKEY, CONTROL, NOINVERT - "C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT - "F", IDM_EDIT_FIND, VIRTKEY, CONTROL, NOINVERT - "I", IDM_FILE_ARCHIVEINFO, VIRTKEY, CONTROL, NOINVERT - "N", IDM_FILE_NEW_ARCHIVE, VIRTKEY, CONTROL, NOINVERT - "O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT - "O", IDM_FILE_OPEN_VOLUME, VIRTKEY, SHIFT, CONTROL, NOINVERT - "P", IDM_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT - "S", IDM_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT - "V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT - VK_DELETE, IDM_ACTIONS_DELETE, VIRTKEY, NOINVERT - VK_F1, IDHELP, VIRTKEY, NOINVERT - VK_F4, IDM_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT - VK_TAB, IDM_ACTIONS_VIEW, VIRTKEY, NOINVERT - "W", IDM_FILE_CLOSE, VIRTKEY, CONTROL, NOINVERT -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_ABOUTDLG DIALOGEX 0, 0, 251, 154 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "About CiderPress" -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,7,132,50,14 - PUSHBUTTON "Credits",IDC_ABOUT_CREDITS,63,132,50,14,0,0,HIDC_ABOUT_CREDITS - PUSHBUTTON "Enter registration code",IDC_ABOUT_ENTER_REG,157,132,86,14 - CONTROL IDB_FSLOGO,IDC_STATIC,"Static",SS_BITMAP,7,6,64,59 - LTEXT "CiderPress v%d.%d.%d%ls%ls",IDC_CIDERPRESS_VERS_TEXT,77,7,167,9 - LTEXT "Copyright © 2017 by CiderPress project authors.\rAll Rights Reserved.",IDC_STATIC,77,20,167,19 - ICON IDR_MAINFRAME,IDC_STATIC,77,45,20,20 - ICON IDI_FILE_NUFX,IDC_STATIC,106,45,20,20 - ICON IDI_FILE_BINARY2,IDC_STATIC,136,45,20,20 - ICON IDI_FILE_DISKIMAGE,IDC_STATIC,166,45,20,20 - GROUPBOX "Libraries",IDC_STATIC,7,71,237,56 - LTEXT "Using NufxLib DLL v%d.%d.%d",IDC_NUFXLIB_VERS_TEXT,16,82,170,10 - LTEXT "Using DiskImg DLL v%d.%d.%d",IDC_DISKIMG_VERS_TEXT,16,92,172,8 - LTEXT "Using zlib DLL v%ls",IDC_ZLIB_VERS_TEXT,16,103,172,8 - LTEXT "Using ASPI DLL v%ls",IDC_ASPI_VERS_TEXT,16,114,173,8 -END - -IDD_PREF_GENERAL DIALOGEX 0, 0, 263, 180 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION -CAPTION "General" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - GROUPBOX "Columns",IDC_STATIC,4,7,80,144 - CONTROL "Pathname",IDC_COL_PATHNAME,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,12,19,65,10 - CONTROL "Type",IDC_COL_TYPE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,31,66,10 - CONTROL "Aux Type",IDC_COL_AUXTYPE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,43,65,10 - CONTROL "Mod Date",IDC_COL_MODDATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,55,66,10 - CONTROL "Format",IDC_COL_FORMAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,67,68,10 - CONTROL "Size",IDC_COL_SIZE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,79,66,10 - CONTROL "Ratio",IDC_COL_RATIO,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,91,67,10 - CONTROL "Packed",IDC_COL_PACKED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,103,66,10 - CONTROL "Access",IDC_COL_ACCESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,115,67,10 - PUSHBUTTON "&Defaults",IDC_COL_DEFAULTS,12,131,63,13 - GROUPBOX "NuFX (ShrinkIt) archives",IDC_STATIC,96,7,162,51 - CONTROL "&Mimic ShrinkIt quirks",IDC_PREF_SHRINKIT_COMPAT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,19,142,10 - CONTROL "Handle ""&bad Mac"" archives",IDC_PREF_SHK_BAD_MAC, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,31,149,10 - CONTROL "Reduce error checking (not recommended)",IDC_PREF_REDUCE_SHK_ERROR_CHECKS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,43,151,10 - GROUPBOX "Filename munging",IDC_STATIC,96,60,162,39 - CONTROL "Display &DOS 3.3 filenames in lower case",IDC_PREF_COERCE_DOS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,72,144,8 - CONTROL "Show spaces as &underscores",IDC_PREF_SPACES_TO_UNDER, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,84,145,10 - GROUPBOX "System",IDC_STATIC,96,101,162,33 - PUSHBUTTON "File type &associations...",IDC_PREF_ASSOCIATIONS,104,113,92,14 - GROUPBOX "Miscellaneous",IDC_STATIC,96,136,162,38 - CONTROL "Strip pathnames when pasting files",IDC_PREF_PASTE_JUNKPATHS, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,147,144,10 - CONTROL "Beep when actions complete successfully",IDC_PREF_SUCCESS_BEEP, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,104,159,145,10 -END - -IDD_PREF_COMPRESSION DIALOGEX 0, 0, 212, 212 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION -CAPTION "Compression" -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - LTEXT "Default compression method:",IDC_STATIC,4,6,184,10 - CONTROL "No compression",IDC_DEFC_UNCOMPRESSED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,4,18,92,8 - CONTROL "Squeeze",IDC_DEFC_SQUEEZE,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,30,92,8 - CONTROL "Dynamic LZW/1",IDC_DEFC_LZW1,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,57,96,8 - CONTROL "Dynamic LZW/2 (recommended)",IDC_DEFC_LZW2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,78,152,8 - CONTROL "12-bit LZC",IDC_DEFC_LZC12,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,99,92,8 - CONTROL "16-bit LZC",IDC_DEFC_LZC16,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,126,96,8 - CONTROL "Deflate",IDC_DEFC_DEFLATE,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,153,100,8 - CONTROL "Bzip2",IDC_DEFC_BZIP2,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,4,180,108,8 - LTEXT "Uses a combination of RLE and Huffman.\rNot compatible with ProDOS 8 ShrinkIt.",IDC_STATIC,16,39,192,18 - LTEXT "The compression method used by ProDOS 8 ShrinkIt.",IDC_STATIC,16,66,180,10,0,0,HIDC_STATIC - LTEXT "The compression method used by GS/ShrinkIt.",IDC_STATIC,16,87,185,8,0,0,HIDC_STATIC - LTEXT "Compression used by UNIX ""compress"" command.\rNot compatible with ProDOS 8 ShrinkIt.",IDC_STATIC,16,108,190,18 - LTEXT "Compression used by UNIX ""compress"" command.\rNot compatible with ProDOS 8 ShrinkIt.",IDC_STATIC,16,135,195,18 - LTEXT "Compression used by ZIP and gzip.\rNot compatible with any Apple II applications.",IDC_STATIC,16,162,196,18 - LTEXT "Compression used by bzip2.\rNot compatible with any Apple II applications.",IDC_STATIC,16,189,196,18 -END - -IDD_FILE_VIEWER DIALOGEX 0, 0, 486, 257 -STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_MAXIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME -CAPTION "File Viewer" -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - DEFPUSHBUTTON "&Done",IDOK,432,224,50,14,WS_GROUP - CONTROL "",IDC_FVIEW_EDITBOX,"RICHEDIT",TCS_HOTTRACK | TCS_VERTICAL | TCS_RAGGEDRIGHT | TCS_MULTISELECT | WS_BORDER | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP,4,3,478,217,WS_EX_CLIENTEDGE - COMBOBOX IDC_FVIEW_FORMATSEL,152,225,148,54,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "Data fork",IDC_FVIEW_DATA,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,5,224,68,10 - CONTROL "Resource fork",IDC_FVIEW_RSRC,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,5,235,69,10 - CONTROL "Comment",IDC_FVIEW_CMMT,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,5,246,69,10 - PUSHBUTTON "&Next",IDC_FVIEW_NEXT,82,224,50,14,WS_GROUP - PUSHBUTTON "&Prev",IDC_FVIEW_PREV,82,242,50,14 - PUSHBUTTON "Best",IDC_FVIEW_FMT_BEST,151,242,32,14,WS_GROUP - PUSHBUTTON "&Hex",IDC_FVIEW_FMT_HEX,187,242,32,14 - PUSHBUTTON "&Raw",IDC_FVIEW_FMT_RAW,223,242,32,14 - PUSHBUTTON "&Find...",IDC_FVIEW_FIND,320,224,50,14 - PUSHBUTTON "Prin&t",IDC_FVIEW_PRINT,320,242,50,14 - PUSHBUTTON "F&ont",IDC_FVIEW_FONT,376,224,50,14 - PUSHBUTTON "Help",IDHELP,376,242,50,14 -END - -IDD_PREF_FVIEW DIALOGEX 0, 0, 217, 274 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTEXTHELP | WS_CHILD | WS_CAPTION -CAPTION "File Viewer" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - GROUPBOX "Converters",IDC_STATIC,4,7,209,139 - CONTROL "High-ASCII &text",IDC_PVIEW_HITEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,19,88,10 - CONTROL "&CP/M text",IDC_PVIEW_CPMTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,30,92,10 - CONTROL "&Pascal text",IDC_PVIEW_PASCALTEXT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,41,87,10 - CONTROL "Pascal &code",IDC_PVIEW_PASCALCODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,52,89,10 - CONTROL "&Applesoft BASIC",IDC_PVIEW_APPLESOFT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,63,88,10 - CONTROL "&Integer BASIC",IDC_PVIEW_INTEGER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,74,88,10 - CONTROL "Assembly source",IDC_PVIEW_SCASSEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,85,94,10 - CONTROL "ProDOS folders",IDC_PVIEW_PRODOSFOLDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,96,88,10 - CONTROL "Disassemble code",IDC_PVIEW_DISASM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,107,91,10 - CONTROL "Resource forks",IDC_PVIEW_RESOURCES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,118,90,10 - CONTROL "GWP (Teach, AWGS)",IDC_PVIEW_GWP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,19,92,10 - CONTROL "8-bit word processor",IDC_PVIEW_TEXT8,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,30,94,10 - CONTROL "AppleWorks &WP",IDC_PVIEW_AWP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,41,88,10 - CONTROL "AppleWorks &DB",IDC_PVIEW_ADB,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,52,94,10 - CONTROL "AppleWorks &SS",IDC_PVIEW_ASP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,63,94,10 - CONTROL "&Hi-Res images",IDC_PVIEW_HIRES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,74,90,10 - CONTROL "&Double Hi-Res images",IDC_PVIEW_DHR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,85,86,10 - CONTROL "&Super Hi-Res images",IDC_PVIEW_SHR,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,96,89,10 - CONTROL "Print Shop graphics",IDC_PVIEW_PRINTSHOP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,107,77,10 - CONTROL "MacPaint images",IDC_PVIEW_MACPAINT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,112,118,93,10 - CONTROL "&Relax type-checking on graphics",IDC_PVIEW_RELAX_GFX, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,50,131,116,10 - GROUPBOX "Conversion options",IDC_STATIC,4,150,209,101 - CONTROL "&Scroll horizontally instead of wrapping words",IDC_PVIEW_NOWRAP_TEXT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,162,192,10 - CONTROL "Highlight &hex dump columns (small files)",IDC_PVIEW_BOLD_HEXDUMP, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,173,156,10 - CONTROL "Prefer syntax &highlighting on BASIC programs",IDC_PVIEW_BOLD_BASIC, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,184,187,10 - CONTROL "Disassemble BRK/COP as single-byte instructions",IDC_PVIEW_DISASM_ONEBYTEBRKCOP, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,195,189,10 - CONTROL "Prefer &B&&W for hi-res images",IDC_PVIEW_HIRES_BW, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,206,148,10 - CONTROL "Convert MouseText to ASCII",IDC_PVIEW_MOUSETEXT_TO_ASCII, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,12,217,194,10 - LTEXT "Preferred DHR mode:",IDC_STATIC,12,234,69,8 - COMBOBOX IDC_PVIEW_DHR_CONV_COMBO,84,231,118,48,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP - LTEXT "Viewer file size &limit:",IDC_STATIC,4,258,62,8 - EDITTEXT IDC_PVIEW_SIZE_EDIT,69,256,40,14,ES_AUTOHSCROLL | ES_NUMBER - CONTROL "Spin2",IDC_PVIEW_SIZE_SPIN,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,112,258,11,14 - LTEXT "KBytes",IDC_STATIC,110,258,36,8 -END - -IDD_DISKEDIT DIALOGEX 0, 0, 458, 197 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Disk Edit" -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - CONTROL "",IDC_DISKEDIT_EDIT,"RICHEDIT",TCS_HOTTRACK | TCS_MULTISELECT | WS_BORDER | WS_VSCROLL | WS_TABSTOP,4,12,384,164,WS_EX_CLIENTEDGE,HIDC_DISKEDIT_EDIT - PUSHBUTTON "&Done",IDC_DISKEDIT_DONE,4,180,50,14,0,0,HIDC_DISKEDIT_DONE - PUSHBUTTON "&Open File",IDC_DISKEDIT_OPENFILE,60,180,50,14,0,0,HIDC_DISKEDIT_OPENFILE - EDITTEXT IDC_DISKEDIT_TRACK,397,20,49,14,ES_AUTOHSCROLL,0,HIDC_DISKEDIT_TRACK - CONTROL "Spin1",IDC_DISKEDIT_TRACKSPIN,"msctls_updown32",UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,434,27,11,14 - EDITTEXT IDC_DISKEDIT_SECTOR,397,49,49,14,ES_AUTOHSCROLL,0,HIDC_DISKEDIT_SECTOR - CONTROL "Spin2",IDC_DISKEDIT_SECTORSPIN,"msctls_updown32",UDS_ALIGNRIGHT | UDS_ARROWKEYS | UDS_NOTHOUSANDS,434,58,11,14 - CONTROL "&Hex",IDC_DISKEDIT_HEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,399,68,44,10,0,HIDC_DISKEDIT_HEX - PUSHBUTTON "&Read",IDC_DISKEDIT_DOREAD,399,89,50,14,0,0,HIDC_DISKEDIT_DOREAD - PUSHBUTTON "&Write",IDC_DISKEDIT_DOWRITE,399,105,50,14,0,0,HIDC_DISKEDIT_DOWRITE - PUSHBUTTON "Read &Prev",IDC_DISKEDIT_PREV,399,132,50,14,0,0,HIDC_DISKEDIT_PREV - PUSHBUTTON "Read &Next",IDC_DISKEDIT_NEXT,399,148,50,14,0,0,HIDC_DISKEDIT_NEXT - LTEXT "Track:",IDC_STEXT_TRACK,396,12,58,8,0,0,HIDC_STEXT_TRACK - LTEXT "Sector:",IDC_STEXT_SECTOR,396,41,52,8,0,0,HIDC_STEXT_SECTOR - PUSHBUTTON "Sub &Volume",IDC_DISKEDIT_SUBVOLUME,116,180,50,14,0,0,HIDC_DISKEDIT_SUBVOLUME - PUSHBUTTON "Help",IDHELP,172,180,50,14,0,0,HIDHELP - LTEXT " 0",IDC_STATIC,32,4,8,8 - LTEXT " 1",IDC_STATIC,48,4,8,8 - LTEXT " 2",IDC_STATIC,64,4,8,8 - LTEXT " 3",IDC_STATIC,80,4,8,8 - LTEXT " 4",IDC_STATIC,96,4,8,8 - LTEXT " 5",IDC_STATIC,112,4,8,8 - LTEXT " 6",IDC_STATIC,128,4,8,8 - LTEXT " 7",IDC_STATIC,144,4,8,8 - LTEXT " 8",IDC_STATIC,160,4,8,8 - LTEXT " 9",IDC_STATIC,176,4,8,8 - LTEXT " a",IDC_STATIC,192,4,8,8 - LTEXT " b",IDC_STATIC,208,4,8,8 - LTEXT " c",IDC_STATIC,224,4,8,8 - LTEXT " d",IDC_STATIC,240,4,8,8 - LTEXT " e",IDC_STATIC,256,4,8,8 - LTEXT " f",IDC_STATIC,272,4,8,8 - COMBOBOX IDC_DISKEDIT_NIBBLE_PARMS,276,182,112,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP -END - -IDD_DECONF DIALOGEX 0, 0, 188, 173 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU -CAPTION "Disk Image Characteristics" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,8,152,50,14 - PUSHBUTTON "Cancel",IDCANCEL,68,152,50,14 - PUSHBUTTON "Help",IDC_DECONF_HELP,128,152,50,14 - LTEXT "File source:",IDC_STATIC,7,10,37,8 - EDITTEXT IDC_DECONF_SOURCE,68,8,112,14,ES_AUTOHSCROLL | ES_READONLY - LTEXT "File Format:",IDC_STATIC,7,50,54,8 - COMBOBOX IDC_DECONF_FILEFORMAT,68,47,112,30,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP - LTEXT "Physical format:",IDC_STATIC,7,69,56,8 - COMBOBOX IDC_DECONF_PHYSICAL,68,66,112,30,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP - LTEXT "Sector ordering:",IDC_STATIC,7,88,55,8 - COMBOBOX IDC_DECONF_SECTORORDER,68,85,112,53,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Filesystem format:",IDC_STATIC,7,107,56,8 - COMBOBOX IDC_DECONF_FSFORMAT,68,104,112,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "blocks",IDC_DECONF_VIEWASBLOCKS,"Button",BS_AUTORADIOBUTTON,18,135,41,10 - CONTROL "sectors",IDC_DECONF_VIEWASSECTORS,"Button",BS_AUTORADIOBUTTON,63,135,43,10 - CONTROL "track nibbles",IDC_DECONF_VIEWASNIBBLES,"Button",BS_AUTORADIOBUTTON,109,135,59,10 - COMBOBOX IDC_DECONF_OUTERFORMAT,68,28,112,49,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP - LTEXT "Outer Format:",IDC_STATIC,7,30,56,8 - LTEXT "View data as...",IDC_DECONF_VIEWAS,7,123,48,8 -END - -IDD_SUBV DIALOGEX 0, 0, 153, 135 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Select Sub-Volume" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,21,114,50,14 - PUSHBUTTON "Cancel",IDCANCEL,81,114,50,14 - LISTBOX IDC_SUBV_LIST,7,19,139,87,LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP - LTEXT "Choose the sub-volume to open:",IDC_STATIC,7,7,139,8 -END - -IDD_DEFILE DIALOGEX 0, 0, 199, 97 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Open file on disk image..." -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - LTEXT "Enter the name of the file to open:",IDC_STATIC,7,7,108,8 - EDITTEXT IDC_DEFILE_FILENAME,7,19,185,14,ES_AUTOHSCROLL - CONTROL "Open &resource fork instead of data fork",IDC_DEFILE_RSRC, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,39,185,10 - DEFPUSHBUTTON "OK",IDOK,45,76,50,14 - PUSHBUTTON "Cancel",IDCANCEL,103,76,50,14 - LTEXT "To open a file in a sub-volume, first open the sub-volume.",IDC_STATIC,7,59,180,8 -END - -IDD_PREF_FILES DIALOGEX 0, 0, 231, 89 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION -CAPTION "Files" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - LTEXT "Folder for temporary files:",IDC_STATIC,7,13,131,8 - EDITTEXT IDC_PREF_TEMP_FOLDER,7,26,197,14,ES_AUTOHSCROLL - PUSHBUTTON "",IDC_PREF_CHOOSE_TEMP_FOLDER,208,26,16,14,BS_BITMAP | BS_CENTER | BS_VCENTER - LTEXT "External file viewer extensions (separated with semicolons):",IDC_STATIC,7,55,217,11 - EDITTEXT IDC_PREF_EXTVIEWER_EXTS,7,68,197,14,ES_AUTOHSCROLL -END - -IDD_EXTRACT_FILES DIALOGEX 0, 0, 319, 242 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Extract Files" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Extract",IDOK,78,220,50,14 - PUSHBUTTON "Cancel",IDCANCEL,134,220,50,14 - PUSHBUTTON "Help",IDHELP,190,220,50,14 - LTEXT "Folder where files will be extracted:",IDC_STATIC,8,8,143,8 - EDITTEXT IDC_EXT_PATH,8,18,263,14,ES_AUTOHSCROLL | WS_GROUP - PUSHBUTTON "",IDC_EXT_CHOOSE_FOLDER,296,18,16,14,BS_BITMAP | BS_CENTER | BS_VCENTER | WS_GROUP - GROUPBOX "Files to extract",IDC_STATIC,8,40,150,35 - CONTROL ">Extract 65536 selected files<",IDC_EXT_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,50,135,10 - CONTROL "Extract &all files",IDC_EXT_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,61,124,10 - GROUPBOX "Parts to extract",IDC_STATIC,162,40,150,47,WS_GROUP - CONTROL "&Data forks",IDC_EXT_DATAFORK,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,170,50,128,10 - CONTROL "&Resource forks",IDC_EXT_RSRCFORK,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,170,61,128,10 - CONTROL "Disk &images",IDC_EXT_DISKIMAGE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,170,72,128,10 - GROUPBOX "Format conversion",IDC_STATIC,8,77,150,37 - CONTROL "&Convert to non-Apple II formats",IDC_EXT_REFORMAT, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,16,88,124,10 - CONTROL "Extract disks as .&2MG",IDC_EXT_DISK_2MG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,99,124,10 - GROUPBOX "Filenames",IDC_STATIC,162,98,150,40 - CONTROL "Add file attribute &preservation",IDC_EXT_ADD_PRESERVE, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,170,110,128,10 - CONTROL "Add type &extension",IDC_EXT_ADD_EXTEN,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,170,122,128,10 - GROUPBOX "Text conversion",IDC_STATIC,8,117,150,70,WS_GROUP - CONTROL "&Don't convert text files",IDC_EXT_CONVEOLNONE,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,127,124,10 - CONTROL "Convert text files by file type",IDC_EXT_CONVEOLTYPE, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,138,126,10 - CONTROL "Auto-detect && &convert files with text",IDC_EXT_CONVEOLTEXT, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,149,135,10 - CONTROL "Convert &ALL files",IDC_EXT_CONVEOLALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,160,124,10 - CONTROL "Strip ""&high ASCII"" files",IDC_EXT_CONVHIGHASCII, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,16,171,124,10 - GROUPBOX "Miscellaneous",IDC_STATIC,162,151,150,36,WS_GROUP - CONTROL "&Strip folder names",IDC_EXT_STRIP_FOLDER,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,170,161,128,10 - CONTROL "&Overwrite existing files",IDC_EXT_OVERWRITE_EXIST, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,170,172,128,10 - PUSHBUTTON "Configure to preserve Apple II formats",IDC_EXT_CONFIG_PRESERVE,8,196,150,16 - PUSHBUTTON "Configure for easy access in Windows",IDC_EXT_CONFIG_CONVERT,162,196,150,16 -END - -IDD_ACTION_PROGRESS DIALOGEX 0, 0, 276, 111 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS | WS_BORDER -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - CONTROL "Progress1",IDC_PROG_PROGRESS,"msctls_progress32",WS_BORDER,7,65,262,14 - PUSHBUTTON "Cancel",IDCANCEL,112,90,50,14 - LTEXT "Now extracting:",IDC_PROG_VERB,7,7,113,8 - LTEXT ">source<",IDC_PROG_ARC_NAME,7,17,262,8 - LTEXT "To:",IDC_PROG_TOFROM,7,36,115,8 - LTEXT ">target<",IDC_PROG_FILE_NAME,7,47,262,8 -END - -IDD_CONFIRM_OVERWRITE DIALOGEX 0, 0, 292, 105 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Confirm Overwrite" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Yes",IDC_OVWR_YES,8,64,50,14 - PUSHBUTTON "Yes to All",IDC_OVWR_YESALL,64,64,50,14 - PUSHBUTTON "No",IDC_OVWR_NO,120,64,50,14 - PUSHBUTTON "No to All",IDC_OVWR_NOALL,176,64,50,14 - PUSHBUTTON "Rename",IDC_OVWR_RENAME,232,64,50,14 - PUSHBUTTON "Cancel",IDCANCEL,120,84,50,14 - LTEXT "Replace file:",IDC_STATIC,8,8,40,8 - LTEXT ">existing file name<",IDC_OVWR_EXIST_NAME,52,8,228,8 - LTEXT ">existing file info<",IDC_OVWR_EXIST_INFO,52,20,228,8 - LTEXT "With file:",IDC_STATIC,8,36,28,8 - LTEXT ">new file name<",IDC_OVWR_NEW_NAME,52,36,228,8 - LTEXT ">new file info<",IDC_OVWR_NEW_INFO,52,48,228,8 -END - -IDD_RENAME_OVERWRITE DIALOGEX 0, 0, 316, 124 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Rename File" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,103,103,50,14 - PUSHBUTTON "Cancel",IDCANCEL,162,103,50,14 - LTEXT "Original name:",IDC_STATIC,8,8,46,8 - LTEXT ">original name<",IDC_RENOVWR_SOURCE_NAME,8,20,301,8 - EDITTEXT IDC_RENOVWR_ORIG_NAME,8,48,302,14,ES_AUTOHSCROLL | ES_READONLY - LTEXT "Full pathname:",IDC_STATIC,8,36,47,8 - LTEXT "New pathname:",IDC_STATIC,8,68,51,8 - EDITTEXT IDC_RENOVWR_NEW_NAME,8,80,302,14,ES_AUTOHSCROLL -END - -IDD_ADD_FILES DIALOGEX 0, 0, 292, 231 -STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | DS_CONTEXTHELP | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - LTEXT "",1119,0,0,291,105,NOT WS_GROUP - GROUPBOX "File attribute preservation",IDC_ADDFILES_STATIC1,4,112,169,47 - CONTROL "&Ignore file attribute preservation tags",IDC_ADDFILES_NOPRESERVE, - "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,8,122,157,10 - CONTROL "&Use file attribute preservation tags",IDC_ADDFILES_PRESERVE, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,133,152,10 - CONTROL "Use tags and &guess type from extension",IDC_ADDFILES_PRESERVEPLUS, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,144,150,10 - GROUPBOX "Text conversion",IDC_ADDFILES_STATIC4,4,168,169,56 - CONTROL "&Don't convert text files",IDC_ADDFILES_CONVEOLNONE, - "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,8,178,146,10 - CONTROL "Convert text files by file type",IDC_ADDFILES_CONVEOLTYPE, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,189,153,10 - CONTROL "Auto-detect && &convert files with text",IDC_ADDFILES_CONVEOLTEXT, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,200,148,10 - CONTROL "Convert &ALL files",IDC_ADDFILES_CONVEOLALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,8,211,141,10 - GROUPBOX "Miscellaneous",IDC_ADDFILES_STATIC2,175,112,116,47 - CONTROL "&Include subfolders",IDC_ADDFILES_INCLUDE_SUBFOLDERS, - "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,179,122,88,10 - CONTROL "&Strip folder names",IDC_ADDFILES_STRIP_FOLDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,179,133,88,10 - CONTROL "&Overwrite existing files",IDC_ADDFILES_OVERWRITE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,179,144,85,10 - LTEXT "Storage prefix (optional):",IDC_ADDFILES_STATIC3,178,168,77,8 - EDITTEXT IDC_ADDFILES_PREFIX,176,179,116,14,ES_AUTOHSCROLL -END - -IDD_USE_SELECTION DIALOGEX 0, 0, 139, 79 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Action Files" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Action",IDOK,16,58,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,73,58,50,14 - GROUPBOX "Files",IDC_STATIC,7,7,125,40 - CONTROL ">Action 65536 selected files<",IDC_USE_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP,14,19,113,10 - CONTROL "Action &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON,14,30,113,10 -END - -IDD_RENAME_ENTRY DIALOGEX 0, 0, 284, 118 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Rename" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,34,97,50,14 - PUSHBUTTON "Skip",IDC_RENAME_SKIP,89,97,50,14 - PUSHBUTTON "Cancel",IDCANCEL,144,97,50,14 - PUSHBUTTON "Help",IDHELP,199,97,50,14 - LTEXT "Current name:",IDC_STATIC,7,7,126,8 - EDITTEXT IDC_RENAME_OLD,7,18,270,14,ES_AUTOHSCROLL | ES_READONLY - LTEXT "New name:",IDC_STATIC,7,42,125,8 - EDITTEXT IDC_RENAME_NEW,7,52,270,14,ES_AUTOHSCROLL - LTEXT "Path separator character:",IDC_STATIC,7,76,81,8 - EDITTEXT IDC_RENAME_PATHSEP,91,74,11,14,ES_AUTOHSCROLL | NOT WS_TABSTOP -END - -IDD_COMMENT_EDIT DIALOGEX 0, 0, 362, 151 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Edit Comment" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - EDITTEXT IDC_COMMENT_EDIT,7,7,290,138,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN | WS_VSCROLL - DEFPUSHBUTTON "OK",IDOK,305,7,50,14 - PUSHBUTTON "Cancel",IDCANCEL,305,25,50,14 - PUSHBUTTON "Help",IDHELP,305,43,50,14 - PUSHBUTTON "Delete",IDC_COMMENT_DELETE,305,71,50,14 -END - -IDD_RECOMPRESS_OPTS DIALOGEX 0, 0, 171, 149 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Recompress Files" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Action",IDOK,32,128,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,88,128,50,14 - GROUPBOX "Files",IDC_STATIC,7,7,157,40 - CONTROL ">Recompress 65536 selected files<",IDC_USE_SELECTED, - "Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 - CONTROL "Action &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 - COMBOBOX IDC_RECOMP_COMP,7,65,157,55,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "New compression type:",IDC_STATIC,7,53,157,8 - LTEXT "IMPORTANT: for broad compatibility, use only Dynamic LZW/2 compression. Other formats will not work with all software, and ""Deflate"" cannot be unpacked on an Apple II.",IDC_STATIC,7,87,157,35 -END - -IDD_PRINT_CANCEL DIALOGEX 0, 0, 116, 46 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Printing..." -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,33,25,50,14 - LTEXT "Sending file list to the printer.",IDC_STATIC,7,7,102,8 -END - -IDD_ASSOCIATIONS DIALOGEX 0, 0, 258, 281 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "CiderPress Associations" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,201,70,50,14 - PUSHBUTTON "Cancel",IDCANCEL,201,88,50,14 - PUSHBUTTON "Help",IDHELP,201,106,50,14 - LTEXT "Place a checkmark next to the file extensions that you want associated with CiderPress. Opening files with these extensions will cause CiderPress to be launched automatically.",IDC_STATIC,7,7,244,27 - LTEXT "Press OK in this screen to accept the changes, and hit OK or Apply in the Preferences screen to apply them.",IDC_STATIC,7,36,244,21 - LTEXT "Current file associations:",IDC_STATIC,7,59,244,8 - CONTROL "List4",IDC_ASSOCIATION_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,70,185,204 -END - -IDD_REGISTRATION DIALOGEX 0, 0, 233, 191 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Registration Info" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,37,170,50,14 - PUSHBUTTON "Cancel",IDCANCEL,92,170,50,14 - PUSHBUTTON "Help",IDHELP,147,170,50,14 - LTEXT "Please enter your registration information. Fill in all fields exactly as they appear on the registration letter. Copying and pasting directly from the registration letter is the easiest way to do this.",IDC_STATIC,7,7,219,27 - LTEXT "Your name:",IDC_STATIC,7,70,122,8 - EDITTEXT IDC_REGENTER_USER,7,81,181,14,ES_AUTOHSCROLL - LTEXT "Your company:",IDC_STATIC,7,102,119,8 - EDITTEXT IDC_REGENTER_COMPANY,7,113,181,14,ES_AUTOHSCROLL - LTEXT "Registration key:",IDC_STATIC,7,134,128,8 - EDITTEXT IDC_REGENTER_REG,7,145,180,14,ES_AUTOHSCROLL - LTEXT "When you have entered the data, make sure the checksum values match those in the registration letter. If they don't, re-check what you have entered. Punctuation and capitalization are important.",IDC_STATIC,7,37,219,25 - LTEXT "Checksum",IDC_STATIC,192,70,34,8 - LTEXT "CCCC",IDC_REGENTER_USERCRC,201,85,25,8 - LTEXT "CCCC",IDC_REGENTER_COMPCRC,201,117,25,8 - LTEXT "CCCC",IDC_REGENTER_REGCRC,201,149,25,8 -END - -IDD_DISKCONV DIALOGEX 0, 0, 174, 223 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Convert Disk Image" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,7,202,50,14 - PUSHBUTTON "Cancel",IDCANCEL,62,202,50,14 - PUSHBUTTON "Help",IDHELP,117,202,50,14 - LTEXT "Image size:",IDC_IMAGE_SIZE_TEXT,7,7,36,8 - LTEXT ">800K floppy<",IDC_IMAGE_TYPE,47,7,120,8 - GROUPBOX "Convert to:",IDC_STATIC,7,20,160,157 - CONTROL "Unadorned DOS-order (.DO)",IDC_DISKCONV_DOS,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,31,146,10 - CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_DOS2MG,"Button",BS_AUTORADIOBUTTON,27,43,133,10 - CONTROL "Unadorned ProDOS-order (.PO)",IDC_DISKCONV_PRODOS, - "Button",BS_AUTORADIOBUTTON,14,55,146,10 - CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_PRODOS2MG,"Button",BS_AUTORADIOBUTTON,27,67,133,10 - CONTROL "Unadorned nibble (.NIB)",IDC_DISKCONV_NIB,"Button",BS_AUTORADIOBUTTON,14,79,146,10 - CONTROL "With 2MG header (.2MG)",IDC_DISKCONV_NIB2MG,"Button",BS_AUTORADIOBUTTON,27,91,133,10 - CONTROL "Unadorned 13-sector disk (.D13)",IDC_DISKCONV_D13, - "Button",BS_AUTORADIOBUTTON,14,103,146,10 - CONTROL "DiskCopy 4.2 disk image (.DSK)",IDC_DISKCONV_DC42, - "Button",BS_AUTORADIOBUTTON,14,115,146,10 - CONTROL "ShrinkIt disk archve (.SDK)",IDC_DISKCONV_SDK,"Button",BS_AUTORADIOBUTTON,14,127,146,10 - CONTROL "TrackStar image (.APP)",IDC_DISKCONV_TRACKSTAR,"Button",BS_AUTORADIOBUTTON,14,139,146,10 - CONTROL """Sim //e"" virtual hard drive (.HDV)",IDC_DISKCONV_HDV, - "Button",BS_AUTORADIOBUTTON,14,151,146,10 - CONTROL "DDD Pro (.DDD)",IDC_DISKCONV_DDD,"Button",BS_AUTORADIOBUTTON,14,163,146,10 - CONTROL "Compress with gzip (.gz)",IDC_DISKCONV_GZIP,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,7,184,91,10 -END - -IDD_DONEOPEN DIALOGEX 0, 0, 119, 55 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Success" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDCANCEL,7,34,50,14 - PUSHBUTTON "&Open Image",IDOK,62,34,50,14 - LTEXT "Would you like to open the disk image you just created?",IDC_STATIC,7,7,105,25 -END - -IDD_PROPS_EDIT DIALOGEX 0, 0, 239, 157 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Edit Attributes" -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - DEFPUSHBUTTON "OK",IDOK,38,135,50,14 - PUSHBUTTON "Cancel",IDCANCEL,94,135,50,14 - PUSHBUTTON "Help",IDHELP,150,135,50,14 - RTEXT "Pathname:",IDC_STATIC,7,9,65,8 - EDITTEXT IDC_PROPS_PATHNAME,79,7,153,14,ES_AUTOHSCROLL | ES_READONLY - RTEXT "Creation date:",IDC_STATIC,7,26,65,8 - LTEXT ">create date/time<",IDC_PROPS_CREATEWHEN,79,26,153,8 - RTEXT "Modification date:",IDC_STATIC,7,37,65,8 - LTEXT ">mod date/time<",IDC_PROPS_MODWHEN,79,37,153,8 - RTEXT "File type:",IDC_STATIC,7,53,65,8 - COMBOBOX IDC_PROPS_FILETYPE,79,50,52,105,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - RTEXT "Aux type (hex):",IDC_STATIC,7,69,65,8 - EDITTEXT IDC_PROPS_AUXTYPE,79,67,52,14,ES_AUTOHSCROLL - RTEXT "Type description:",IDC_STATIC,7,86,65,8 - LTEXT ">type description<",IDC_PROPS_TYPEDESCR,79,86,153,8 - RTEXT "Access:",IDC_STATIC,7,102,65,8 - CONTROL "Read",IDC_PROPS_ACCESS_R,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,102,33,10 - CONTROL "Backup",IDC_PROPS_ACCESS_B,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,123,102,41,10 - CONTROL "Invisible",IDC_PROPS_ACCESS_I,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,175,102,41,10 - CONTROL "Write",IDC_PROPS_ACCESS_W,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,79,115,33,10 - CONTROL "Rename",IDC_PROPS_ACCESS_N,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,124,115,43,10 - CONTROL "Delete",IDC_PROPS_ACCESS_D,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,115,37,10 - EDITTEXT IDC_PROPS_HFS_FILETYPE,192,49,40,14,ES_AUTOHSCROLL - EDITTEXT IDC_PROPS_HFS_AUXTYPE,192,67,40,14,ES_AUTOHSCROLL - CONTROL "HFS type:",IDC_PROPS_HFS_MODE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,145,51,45,10 - LTEXT "Creator:",IDC_PROPS_HFS_LABEL,156,69,34,8,0,WS_EX_RIGHT -END - -IDD_CONVFILE_OPTS DIALOGEX 0, 0, 171, 93 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Convert to file archive" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Action",IDOK,33,72,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,89,72,50,14 - GROUPBOX "Files",IDC_STATIC,7,7,157,40 - CONTROL ">Convert 65536 selected files<",IDC_USE_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 - CONTROL "Convert &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 - CONTROL "Preserve &empty folders",IDC_CONVFILE_PRESERVEDIR, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,55,157,10 -END - -IDD_CONVDISK_OPTS DIALOGEX 0, 0, 175, 225 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Convert to ProDOS disk" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Action",IDOK,34,203,50,14,WS_GROUP - PUSHBUTTON "Cancel",IDCANCEL,90,203,50,14 - GROUPBOX "Files",IDC_STATIC,7,7,161,40 - CONTROL ">Convert 65536 selected files<",IDC_USE_SELECTED,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,19,130,10 - CONTROL "Convert &all files",IDC_USE_ALL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,30,113,10 - GROUPBOX "New disk size",IDC_STATIC,7,52,161,103,WS_GROUP - CONTROL "140KB (5.25"" floppy)",IDC_CONVDISK_140K,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,63,148,10 - CONTROL "800KB (3.5"" floppy)",IDC_CONVDISK_800K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,74,148,10 - CONTROL "1.4MB (3.5"" PC floppy)",IDC_CONVDISK_1440K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,85,88,10 - CONTROL "5MB",IDC_CONVDISK_5MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,96,148,10 - CONTROL "16MB",IDC_CONVDISK_16MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,107,148,10 - CONTROL "20MB (same as 20MB floptical)",IDC_CONVDISK_20MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,118,148,10 - CONTROL "32MB (largest ProDOS volume)",IDC_CONVDISK_32MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,129,148,10 - CONTROL "Specify size:",IDC_CONVDISK_SPECIFY,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,140,55,10 - EDITTEXT IDC_CONVDISK_SPECIFY_EDIT,72,139,43,14,ES_AUTOHSCROLL | ES_NUMBER | WS_GROUP - LTEXT ">Space required: 1000KB<",IDC_CONVDISK_SPACEREQ,7,161,96,8 - PUSHBUTTON "Compute",IDC_CONVDISK_COMPUTE,118,158,50,14 - LTEXT "ProDOS volume name:",IDC_STATIC,7,182,73,8 - EDITTEXT IDC_CONVDISK_VOLNAME,87,180,81,14,ES_AUTOHSCROLL - LTEXT "blocks",IDC_STATIC,118,140,22,8 -END - -IDD_BULKCONV DIALOGEX 0, 0, 237, 57 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Bulk Conversion Progress" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,93,36,50,14 - LTEXT "Now converting:",IDC_STATIC,7,7,223,8 - LTEXT ">pathname<",IDC_BULKCONV_PATHNAME,7,18,223,8 -END - -IDD_OPENVOLUMEDLG DIALOGEX 0, 0, 300, 166 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Select Volume" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,7,145,50,14 - PUSHBUTTON "Cancel",IDCANCEL,64,145,50,14 - PUSHBUTTON "Help",IDHELP,120,145,50,14 - LTEXT "Show:",IDC_STATIC,7,9,27,8 - COMBOBOX IDC_VOLUME_FILTER,34,7,151,45,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - CONTROL "List1",IDC_VOLUME_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,25,286,100 - CONTROL "Open as read-only (writing to the volume will be disabled)",IDC_OPENVOL_READONLY, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,131,242,10 -END - -IDD_VOLUMECOPYPROG DIALOGEX 0, 0, 276, 111 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Volume Copy Progress" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,112,90,50,14 - LTEXT "Copy from:",IDC_STATIC,7,7,79,8 - LTEXT "To:",IDC_STATIC,7,36,79,8 - LTEXT ">path-or-vol<",IDC_VOLUMECOPYPROG_FROM,7,17,262,8 - LTEXT ">path-or-vol<",IDC_VOLUMECOPYPROG_TO,7,47,245,8 - CONTROL "Progress1",IDC_VOLUMECOPYPROG_PROGRESS, - "msctls_progress32",PBS_SMOOTH | WS_BORDER,7,65,262,14 -END - -IDD_DISKEDIT_OPENWHICH DIALOGEX 0, 0, 134, 103 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Open disk image file",IDC_DEOW_FILE,7,7,120,14 - PUSHBUTTON "Open logical or physical volume",IDC_DEOW_VOLUME,7,32,120,14 - PUSHBUTTON "Open current archive",IDC_DEOW_CURRENT,7,57,120,14 - PUSHBUTTON "Cancel",IDCANCEL,42,82,50,14 -END - -IDD_VOLUMECOPYSEL DIALOGEX 0, 0, 386, 138 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Volume Copy" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDOK,316,117,63,14 - LTEXT "Choose volume or sub-volume to copy:",IDC_STATIC,7,7,293,8 - CONTROL "List2",IDC_VOLUMECOPYSEL_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,22,297,109 - PUSHBUTTON "Copy &to file",IDC_VOLUEMCOPYSEL_TOFILE,316,22,63,14 - PUSHBUTTON "Load &from file",IDC_VOLUEMCOPYSEL_FROMFILE,316,42,63,14 - PUSHBUTTON "Help",IDHELP,316,97,63,14 -END - -IDD_FORMATTING DIALOGEX 0, 0, 146, 38 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - CTEXT "Preparing disk image, please wait...",IDC_STATIC,7,15,132,8 -END - -IDD_CREATEIMAGE DIALOGEX 0, 0, 342, 222 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Create Disk Image" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,88,201,50,14 - PUSHBUTTON "Cancel",IDCANCEL,145,201,50,14 - PUSHBUTTON "Help",IDHELP,202,201,50,14 - GROUPBOX "Filesystem",IDC_STATIC,7,7,160,80 - CONTROL "DOS 3.2 (13-sector)",IDC_CREATEFS_DOS32,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,18,86,10 - CONTROL "DOS 3.3",IDC_CREATEFS_DOS33,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,29,86,10 - CONTROL "ProDOS",IDC_CREATEFS_PRODOS,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,40,86,10 - CONTROL "Pascal",IDC_CREATEFS_PASCAL,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,51,86,10 - CONTROL "HFS",IDC_CREATEFS_HFS,"Button",BS_AUTORADIOBUTTON,14,62,86,10 - CONTROL "Blank",IDC_CREATEFS_BLANK,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,73,86,10 - GROUPBOX "New disk size",IDC_STATIC,7,93,160,104,WS_GROUP - CONTROL "140KB (5.25"" floppy)",IDC_CONVDISK_140K,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,14,104,148,10 - CONTROL "800KB (3.5"" floppy)",IDC_CONVDISK_800K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,115,148,10 - CONTROL "1.4MB (3.5"" PC floppy)",IDC_CONVDISK_1440K,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,126,88,10 - CONTROL "5MB",IDC_CONVDISK_5MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,137,148,10 - CONTROL "16MB",IDC_CONVDISK_16MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,148,148,10 - CONTROL "20MB (same as 20MB floptical)",IDC_CONVDISK_20MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,159,148,10 - CONTROL "32MB (largest ProDOS volume)",IDC_CONVDISK_32MB,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,170,148,10 - CONTROL "Specify size:",IDC_CONVDISK_SPECIFY,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,14,181,55,10 - EDITTEXT IDC_CONVDISK_SPECIFY_EDIT,72,180,43,14,ES_AUTOHSCROLL | ES_NUMBER | WS_GROUP - LTEXT "blocks",IDC_STATIC,118,181,22,8 - GROUPBOX "DOS options",IDC_STATIC,175,7,160,46 - CONTROL "Allocate DOS tracks",IDC_CREATEFSDOS_ALLOCDOS,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,184,20,143,10 - LTEXT "Disk volume:",IDC_STATIC,184,33,46,8 - EDITTEXT IDC_CREATEFSDOS_VOLNUM,235,32,40,14,ES_AUTOHSCROLL | ES_NUMBER - GROUPBOX "ProDOS options",IDC_STATIC,175,56,160,45 - LTEXT "ProDOS volume name (15 chars):",IDC_STATIC,184,69,143,8 - EDITTEXT IDC_CREATEFSPRODOS_VOLNAME,184,80,115,14,ES_AUTOHSCROLL - GROUPBOX "Pascal options",IDC_STATIC,175,104,160,45 - LTEXT "Pascal volume name (7 chars):",IDC_STATIC,184,116,145,8 - EDITTEXT IDC_CREATEFSPASCAL_VOLNAME,184,127,115,14,ES_UPPERCASE | ES_AUTOHSCROLL - GROUPBOX "HFS options",IDC_STATIC,175,152,160,45 - EDITTEXT IDC_CREATEFSHFS_VOLNAME,184,178,145,14,ES_AUTOHSCROLL - LTEXT "HFS volume name (27 chars):",IDC_STATIC,184,166,145,8 -END - -IDD_CHOOSE_ADD_TARGET DIALOGEX 0, 0, 220, 279 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Select location" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,26,258,50,14 - PUSHBUTTON "Cancel",IDCANCEL,85,258,50,14 - PUSHBUTTON "Help",IDHELP,144,258,50,14 - CONTROL "Tree1",IDC_ADD_TARGET_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,7,20,206,209 - LTEXT "Select location where files will be added:",IDC_STATIC,7,7,194,8 - LTEXT "Tip: you can skip this step on ProDOS disks by selecting a directory from the file list before using ""add files"".",IDC_STATIC,7,235,206,17 -END - -IDD_ARCHIVEINFO_NUFX DIALOGEX 0, 0, 320, 127 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "NuFX (ShrinkIt) Archive" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDOK,164,106,50,14 - LTEXT "Filename:",IDC_STATIC,7,18,68,8,0,WS_EX_RIGHT - LTEXT "Format:",IDC_STATIC,7,29,68,8,0,WS_EX_RIGHT - LTEXT "Master Version:",IDC_STATIC,7,51,68,8,0,WS_EX_RIGHT - LTEXT "Junk Skipped:",IDC_STATIC,7,84,68,8,0,WS_EX_RIGHT - LTEXT "Created:",IDC_STATIC,7,62,68,8,0,WS_EX_RIGHT - LTEXT "Modified:",IDC_STATIC,7,73,68,8,0,WS_EX_RIGHT - LTEXT "Records:",IDC_STATIC,7,40,68,8,0,WS_EX_RIGHT - GROUPBOX "Archive Info",IDC_STATIC,6,7,307,92 - LTEXT "",IDC_AI_FILENAME,84,18,218,8 - LTEXT "",IDC_AINUFX_FORMAT,84,29,218,8 - LTEXT "",IDC_AINUFX_RECORDS,84,40,218,8 - LTEXT "",IDC_AINUFX_MASTERVERSION,84,51,218,8 - LTEXT "",IDC_AINUFX_CREATEWHEN,84,62,218,8 - LTEXT "",IDC_AINUFX_MODIFYWHEN,84,73,218,8 - LTEXT "",IDC_AINUFX_JUNKSKIPPED,84,84,218,8 - PUSHBUTTON "Help",IDHELP,105,106,50,14 -END - -IDD_ARCHIVEINFO_DISK DIALOGEX 0, 0, 320, 250 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Disk Image Info" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDOK,164,229,50,14 - LTEXT "Filename:",IDC_STATIC,7,18,68,8,0,WS_EX_RIGHT - GROUPBOX "File Characteristics",IDC_STATIC,6,7,307,59 - LTEXT "Outer Format:",IDC_STATIC,7,29,68,8,0,WS_EX_RIGHT - LTEXT "File Format:",IDC_STATIC,7,40,68,8,0,WS_EX_RIGHT - LTEXT "Physical Format:",IDC_STATIC,7,51,68,8,0,WS_EX_RIGHT - LTEXT "Sector Ordering:",IDC_STATIC,7,95,68,8,0,WS_EX_RIGHT - LTEXT "Filesystem Format:",IDC_STATIC,7,106,68,8,0,WS_EX_RIGHT - LTEXT "Sub-Volume:",IDC_STATIC,7,81,68,8,0,WS_EX_RIGHT - COMBOBOX IDC_AIDISK_SUBVOLSEL,85,79,221,58,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Files+Directories:",IDC_STATIC,7,117,68,8,0,WS_EX_RIGHT - LTEXT "Storage Capacity:",IDC_STATIC,7,128,68,8,0,WS_EX_RIGHT - LTEXT "Free Space:",IDC_STATIC,7,139,68,8,0,WS_EX_RIGHT - LTEXT "Damaged?",IDC_STATIC,7,161,68,8,0,WS_EX_RIGHT - LTEXT "Notes:",IDC_STATIC,7,176,68,8,0,WS_EX_RIGHT - EDITTEXT IDC_AIDISK_NOTES,85,175,221,39,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL | WS_HSCROLL - GROUPBOX "Disk Characteristics",IDC_STATIC,6,68,307,153 - LTEXT "",IDC_AI_FILENAME,85,18,221,8 - LTEXT "",IDC_AIDISK_OUTERFORMAT,85,29,221,8 - LTEXT "",IDC_AIDISK_FILEFORMAT,85,40,221,8 - LTEXT "",IDC_AIDISK_PHYSICALFORMAT,85,51,221,8 - LTEXT "",IDC_AIDISK_SECTORORDER,85,95,221,8 - LTEXT "",IDC_AIDISK_FSFORMAT,85,106,221,8 - LTEXT "",IDC_AIDISK_FILECOUNT,85,117,221,8 - LTEXT "",IDC_AIDISK_CAPACITY,85,128,221,8 - LTEXT "",IDC_AIDISK_FREESPACE,85,139,221,8 - LTEXT "",IDC_AIDISK_DAMAGED,85,161,221,8 - LTEXT "Writeable Format?",IDC_STATIC,7,150,68,8,0,WS_EX_RIGHT - LTEXT "",IDC_AIDISK_WRITEABLE,85,150,221,8 - PUSHBUTTON "Help",IDHELP,104,229,50,14 -END - -IDD_ARCHIVEINFO_BNY DIALOGEX 0, 0, 320, 74 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Binary II Archive" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDOK,164,53,50,14 - LTEXT "Filename:",IDC_STATIC,7,18,67,8,0,WS_EX_RIGHT - LTEXT "Records:",IDC_STATIC,7,29,67,8,0,WS_EX_RIGHT - GROUPBOX "Archive Info",IDC_STATIC,6,7,307,36 - LTEXT "",IDC_AI_FILENAME,84,18,218,8 - LTEXT "",IDC_AIBNY_RECORDS,84,29,218,8 - PUSHBUTTON "Help",IDHELP,105,53,50,14 -END - -IDD_PREF_DISKIMAGE DIALOGEX 0, 0, 219, 105 -STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTEXTHELP | WS_CHILD | WS_DISABLED | WS_CAPTION -CAPTION "Disk Images" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - GROUPBOX "General",IDC_STATIC,7,7,205,48 - CONTROL "&Confirm disk image format",IDC_PDISK_CONFIRM_FORMAT, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,18,185,10 - CONTROL "Default to read-only when opening volumes",IDC_PDISK_OPENVOL_RO, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,29,185,10 - CONTROL "Allow write access to physical disk 0 (not recommended)",IDC_PDISK_OPENVOL_PHYS0, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,40,192,10 - GROUPBOX "ProDOS",IDC_STATIC,7,61,205,38 - CONTROL "Allow &lower-case letters and spaces in filenames",IDC_PDISK_PRODOS_ALLOWLOWER, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,72,186,10 - CONTROL "Use ""sparse"" allocation for empty blocks",IDC_PDISK_PRODOS_USESPARSE, - "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,83,186,10 -END - -IDD_CREATE_SUBDIR DIALOGEX 0, 0, 284, 98 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Create Subdirectory" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,87,77,50,14 - PUSHBUTTON "Cancel",IDCANCEL,145,77,50,14 - LTEXT "Parent directory:",IDC_STATIC,7,7,150,8 - EDITTEXT IDC_CREATESUBDIR_BASE,7,18,270,14,ES_AUTOHSCROLL | ES_READONLY - LTEXT "New subdirectory name:",IDC_STATIC,7,42,151,8 - EDITTEXT IDC_CREATESUBDIR_NEW,7,52,270,14,ES_AUTOHSCROLL -END - -IDD_RENAME_VOLUME DIALOGEX 0, 0, 218, 172 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Rename volume" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,26,151,50,14 - PUSHBUTTON "Cancel",IDCANCEL,83,151,50,14 - LTEXT "Select volume to rename:",IDC_STATIC,7,7,175,8 - CONTROL "Tree1",IDC_RENAMEVOL_TREE,"SysTreeView32",TVS_HASBUTTONS | TVS_HASLINES | TVS_LINESATROOT | TVS_DISABLEDRAGDROP | TVS_SHOWSELALWAYS | WS_BORDER | WS_HSCROLL | WS_TABSTOP,7,19,204,87 - LTEXT "New name:",IDC_STATIC,7,116,167,8 - EDITTEXT IDC_RENAMEVOL_NEW,7,128,204,14,ES_AUTOHSCROLL - PUSHBUTTON "Help",IDHELP,140,151,50,14 -END - -IDD_EOLSCAN DIALOGEX 0, 0, 173, 106 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "EOL Scanner" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,33,85,50,14 - PUSHBUTTON "Help",IDHELP,90,85,50,14 - LTEXT "Results (click ""Help"" for explanation):",IDC_STATIC,7,7,159,8 - RTEXT ">cr<",IDC_EOLSCAN_CR,7,45,61,8 - LTEXT "carriage returns (0x0d)",IDC_STATIC,72,45,94,8 - RTEXT ">lf<",IDC_EOLSCAN_LF,7,56,61,8 - LTEXT "line feeds (0x0a)",IDC_STATIC,72,56,94,8 - RTEXT ">crlf<",IDC_EOLSCAN_CRLF,7,67,61,8 - LTEXT "CRLFs (0x0d0a)",IDC_STATIC,72,67,94,8 - RTEXT ">chars<",IDC_EOLSCAN_CHARS,7,23,61,8 - LTEXT "characters",IDC_STATIC,72,23,94,8 - RTEXT ">high-ascii<",IDC_EOLSCAN_HIGHASCII,7,34,61,8 - LTEXT "high-ASCII characters",IDC_STATIC,72,34,94,8 -END - -IDD_TWOIMG_PROPS DIALOGEX 0, 0, 220, 198 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_CONTEXTHELP | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "2MG Disk Image Properties" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Save",IDOK,56,176,50,14 - PUSHBUTTON "Cancel",IDCANCEL,113,176,50,14 - GROUPBOX "Header fields:",IDC_STATIC,7,7,206,56 - RTEXT "Creator:",IDC_STATIC,9,18,50,8 - LTEXT ">creator<",IDC_TWOIMG_CREATOR,65,18,139,8 - RTEXT "Version:",IDC_STATIC,9,29,50,8 - LTEXT ">version<",IDC_TWOIMG_VERSION,65,29,140,8 - RTEXT "Image format:",IDC_STATIC,9,40,50,8 - LTEXT ">format<",IDC_TWOIMG_FORMAT,65,40,140,8 - RTEXT "Blocks:",IDC_STATIC,9,51,50,8 - LTEXT ">blocks<",IDC_TWOIMG_BLOCKS,65,51,140,8 - CONTROL "Locked",IDC_TWOIMG_LOCKED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,68,120,10 - CONTROL "Specify disk volume number",IDC_TWOIMG_DOSVOLSET,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,79,118,10 - LTEXT "Volume number:",IDC_STATIC,7,92,52,8 - EDITTEXT IDC_TWOIMG_DOSVOLNUM,65,91,40,14,ES_AUTOHSCROLL | ES_NUMBER - LTEXT "Comment:",IDC_STATIC,7,110,52,8 - EDITTEXT IDC_TWOIMG_COMMENT,7,121,206,48,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL | WS_HSCROLL -END - -IDD_LOADING DIALOGEX 0, 0, 146, 38 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - CTEXT "Loading data, please wait...",IDC_STATIC,7,15,132,8 -END - -IDD_IMPORTCASSETTE DIALOGEX 0, 0, 355, 157 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Import cassette WAV" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Import",IDC_IMPORT_CHUNK,81,136,50,14 - PUSHBUTTON "Done",IDCANCEL,136,136,50,14 - PUSHBUTTON "Help",IDHELP,191,136,50,14 - LTEXT "Input file:",IDC_STATIC,7,7,30,8 - LTEXT ">input-file<",IDC_CASSETTE_INPUT,43,7,305,8 - LTEXT "Algorithm:",IDC_STATIC,7,21,34,8 - COMBOBOX IDC_CASSETTE_ALG,43,19,173,63,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP - LTEXT "Select the chunk to import:",IDC_STATIC,7,40,180,8 - CONTROL "List2",IDC_CASSETTE_LIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,51,341,75 -END - -IDD_CASSIMPTARGET DIALOGEX 0, 0, 186, 127 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Import..." -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,39,106,50,14 - PUSHBUTTON "Cancel",IDCANCEL,96,106,50,14 - LTEXT "Name of file to create?",IDC_STATIC,7,7,113,8 - EDITTEXT IDC_CASSIMPTARG_FILENAME,7,18,172,14,ES_AUTOHSCROLL - GROUPBOX "Static",IDC_STATIC,7,36,172,60 - CONTROL "Applesoft BASIC",IDC_CASSIMPTARG_BAS,"Button",BS_AUTORADIOBUTTON | WS_GROUP,13,47,87,10 - CONTROL "Integer BASIC",IDC_CASSIMPTARG_INT,"Button",BS_AUTORADIOBUTTON,13,58,94,10 - CONTROL "Binary",IDC_CASSIMPTARG_BIN,"Button",BS_AUTORADIOBUTTON,13,69,77,10 - LTEXT "Start address (hex):",IDC_STATIC,25,80,63,8 - EDITTEXT IDC_CASSIMPTARG_BINADDR,93,78,31,14,ES_AUTOHSCROLL - LTEXT ".XXXX",IDC_CASSIMPTARG_RANGE,126,80,29,8 -END - -IDD_ADD_CLASH DIALOGEX 0, 0, 307, 113 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Filename Clash" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - LTEXT "A file being added to the archive has the same name as another file being added. This can happen when directory names or file attribute preservations strings are stripped from Windows filenames.",IDC_STATIC,7,7,293,29 - LTEXT "You can rename this file, skip adding it, or cancel the entire operation.",IDC_STATIC,7,39,290,8 - LTEXT "Windows name:",IDC_STATIC,7,57,58,8 - LTEXT ">windows name<",IDC_CLASH_WINNAME,70,57,230,8 - LTEXT "Storage name:",IDC_STATIC,7,72,58,8 - LTEXT ">storage name<",IDC_CLASH_STORAGENAME,70,72,230,8 - PUSHBUTTON "Rename",IDC_CLASH_RENAME,7,91,50,14 - PUSHBUTTON "Skip",IDC_CLASH_SKIP,61,91,50,14 - PUSHBUTTON "Cancel",IDCANCEL,116,91,50,14 -END - -IDD_ARCHIVEINFO_ACU DIALOGEX 0, 0, 320, 74 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "AppleLink ACU Archive" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDOK,164,53,50,14 - LTEXT "Filename:",IDC_STATIC,7,18,68,8,0,WS_EX_RIGHT - LTEXT "Records:",IDC_STATIC,7,29,68,8,0,WS_EX_RIGHT - GROUPBOX "Archive Info",IDC_STATIC,6,7,307,36 - LTEXT "",IDC_AI_FILENAME,84,18,218,8 - LTEXT "",IDC_AIBNY_RECORDS,84,29,218,8 - PUSHBUTTON "Help",IDHELP,105,53,50,14 -END - -IDD_IMPORT_BAS DIALOGEX 0, 0, 247, 118 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Import BAS from text file" -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Save",IDOK,44,97,50,14 - PUSHBUTTON "Cancel",IDCANCEL,98,97,50,14 - LTEXT "Results:",IDC_STATIC,7,7,40,8 - EDITTEXT IDC_IMPORT_BAS_RESULTS,7,19,233,42,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY | WS_VSCROLL - LTEXT "Save as:",IDC_STATIC,7,74,34,8 - EDITTEXT IDC_IMPORT_BAS_SAVEAS,39,72,201,14,ES_AUTOHSCROLL - PUSHBUTTON "Help",IDHELP,153,97,50,14 -END - -IDD_PASTE_SPECIAL DIALOGEX 0, 0, 186, 78 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "Paste..." -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - DEFPUSHBUTTON "OK",IDOK,40,57,50,14 - PUSHBUTTON "Cancel",IDCANCEL,95,57,50,14 - LTEXT "Choose how you would like to handle filenames:",IDC_PASTE_SPECIAL_COUNT,7,7,172,8 - CONTROL "Keep full pathnames",IDC_PASTE_SPECIAL_PATHS,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,18,22,161,10 - CONTROL "Strip paths, keep filename only",IDC_PASTE_SPECIAL_NOPATHS, - "Button",BS_AUTORADIOBUTTON | WS_TABSTOP,18,34,161,10 -END - -IDD_PROGRESS_COUNTER DIALOGEX 0, 0, 186, 57 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,67,36,50,14 - CTEXT ">descr<",IDC_PROGRESS_COUNTER_DESC,7,7,172,8 - CTEXT ">count<",IDC_PROGRESS_COUNTER_COUNT,7,19,172,8 -END - -IDD_ARCHIVEINFO_APPLESINGLE DIALOGEX 0, 0, 320, 74 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "AppleSingle File" -FONT 8, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - DEFPUSHBUTTON "Done",IDOK,158,53,50,14 - LTEXT "Filename:",IDC_STATIC,7,18,68,8,0,WS_EX_RIGHT - LTEXT "Info:",IDC_STATIC,7,29,68,8,0,WS_EX_RIGHT - GROUPBOX "Archive Info",IDC_STATIC,6,7,307,36 - LTEXT "",IDC_AI_FILENAME,84,18,218,8 - LTEXT "",IDC_AIBNY_RECORDS,84,29,218,8 - PUSHBUTTON "Help",IDHELP,105,53,50,14 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Bitmap -// - -IDB_FSLOGO BITMAP "Graphics\\fslogo.bmp" - -IDR_TOOLBAR1 BITMAP "Graphics\\toolbar1.bmp" - -IDB_LIST_PICS BITMAP "Graphics\\list-pics.bmp" - -IDB_HDRBAR BITMAP "Graphics\\hdrbar.bmp" - -IDB_NEW_FOLDER BITMAP "graphics\\NewFolder.bmp" - -IDB_CHOOSE_FOLDER BITMAP "Graphics\\ChooseFolder.bmp" - -IDB_VOL_PICS BITMAP "graphics\\vol_pics.bmp" - -IDB_TREE_PICS BITMAP "graphics\\tree_pics.bmp" - - -///////////////////////////////////////////////////////////////////////////// -// -// Toolbar -// - -IDR_TOOLBAR1 TOOLBAR 24, 23 -BEGIN - BUTTON IDM_FILE_OPEN - BUTTON IDM_FILE_OPEN_VOLUME - BUTTON IDM_FILE_NEW_ARCHIVE - BUTTON IDM_TOOLS_IMAGECREATOR - BUTTON IDM_FILE_PRINT - SEPARATOR - BUTTON IDM_ACTIONS_ADD_FILES - BUTTON IDM_ACTIONS_ADD_DISKS - BUTTON IDM_ACTIONS_VIEW - BUTTON IDM_ACTIONS_EXTRACT - BUTTON IDM_ACTIONS_TEST - BUTTON IDM_ACTIONS_RENAME - BUTTON IDM_ACTIONS_DELETE - BUTTON IDM_ACTIONS_RECOMPRESS - BUTTON IDM_ACTIONS_EDIT_COMMENT - SEPARATOR - BUTTON IDM_TOOLS_DISKEDIT - BUTTON IDM_TOOLS_DISKCONV - BUTTON IDM_TOOLS_VOLUMECOPIER_VOLUME - BUTTON IDM_TOOLS_SST_MERGE -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 4,0,0,0 - PRODUCTVERSION 4,0,0,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x1L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904b0" - BEGIN - VALUE "Comments", "Rising like the phoenix." - VALUE "CompanyName", "CiderPress Project" - VALUE "FileDescription", "CiderPress" - VALUE "FileVersion", "4.0.0.0" - VALUE "InternalName", "CiderPress" - VALUE "LegalCopyright", "Copyright © 2015 CiderPress project authors" - VALUE "OriginalFilename", "CiderPress.exe" - VALUE "ProductName", "CiderPress" - VALUE "ProductVersion", "4.0.0.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1200 - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_ABOUTDLG, DIALOG - BEGIN - RIGHTMARGIN, 244 - VERTGUIDE, 7 - VERTGUIDE, 16 - VERTGUIDE, 77 - BOTTOMMARGIN, 146 - HORZGUIDE, 65 - END - - IDD_PREF_GENERAL, DIALOG - BEGIN - LEFTMARGIN, 4 - RIGHTMARGIN, 259 - VERTGUIDE, 14 - VERTGUIDE, 96 - VERTGUIDE, 104 - TOPMARGIN, 7 - BOTTOMMARGIN, 173 - END - - IDD_PREF_COMPRESSION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 205 - VERTGUIDE, 22 - VERTGUIDE, 114 - VERTGUIDE, 124 - TOPMARGIN, 7 - BOTTOMMARGIN, 205 - END - - IDD_FILE_VIEWER, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 485 - VERTGUIDE, 8 - VERTGUIDE, 482 - TOPMARGIN, 7 - HORZGUIDE, 7 - HORZGUIDE, 155 - HORZGUIDE, 238 - END - - IDD_PREF_FVIEW, DIALOG - BEGIN - LEFTMARGIN, 4 - RIGHTMARGIN, 213 - VERTGUIDE, 12 - VERTGUIDE, 50 - VERTGUIDE, 112 - TOPMARGIN, 7 - BOTTOMMARGIN, 272 - END - - IDD_DISKEDIT, DIALOG - BEGIN - LEFTMARGIN, 4 - RIGHTMARGIN, 451 - VERTGUIDE, 388 - TOPMARGIN, 7 - BOTTOMMARGIN, 194 - END - - IDD_DECONF, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 180 - VERTGUIDE, 68 - TOPMARGIN, 7 - BOTTOMMARGIN, 166 - END - - IDD_SUBV, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 146 - TOPMARGIN, 7 - BOTTOMMARGIN, 128 - END - - IDD_DEFILE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 192 - TOPMARGIN, 7 - BOTTOMMARGIN, 90 - END - - IDD_PREF_FILES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 224 - VERTGUIDE, 204 - TOPMARGIN, 7 - BOTTOMMARGIN, 82 - END - - IDD_EXTRACT_FILES, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 312 - VERTGUIDE, 16 - VERTGUIDE, 158 - VERTGUIDE, 170 - TOPMARGIN, 7 - BOTTOMMARGIN, 235 - END - - IDD_ACTION_PROGRESS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 269 - TOPMARGIN, 7 - BOTTOMMARGIN, 104 - END - - IDD_CONFIRM_OVERWRITE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 285 - TOPMARGIN, 7 - BOTTOMMARGIN, 98 - END - - IDD_RENAME_OVERWRITE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 309 - TOPMARGIN, 7 - BOTTOMMARGIN, 117 - END - - "IDD_ADD_FILES", DIALOG - BEGIN - VERTGUIDE, 8 - BOTTOMMARGIN, 224 - HORZGUIDE, 112 - HORZGUIDE, 168 - END - - IDD_USE_SELECTION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 132 - TOPMARGIN, 7 - BOTTOMMARGIN, 72 - END - - IDD_RENAME_ENTRY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 277 - TOPMARGIN, 7 - BOTTOMMARGIN, 111 - END - - IDD_COMMENT_EDIT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 355 - TOPMARGIN, 7 - BOTTOMMARGIN, 144 - END - - IDD_RECOMPRESS_OPTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 164 - TOPMARGIN, 7 - BOTTOMMARGIN, 142 - END - - IDD_PRINT_CANCEL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 109 - TOPMARGIN, 7 - BOTTOMMARGIN, 39 - END - - IDD_ASSOCIATIONS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 251 - TOPMARGIN, 7 - BOTTOMMARGIN, 274 - HORZGUIDE, 70 - END - - IDD_REGISTRATION, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 226 - VERTGUIDE, 201 - TOPMARGIN, 7 - BOTTOMMARGIN, 184 - END - - IDD_DISKCONV, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 167 - VERTGUIDE, 14 - VERTGUIDE, 27 - VERTGUIDE, 160 - TOPMARGIN, 7 - BOTTOMMARGIN, 216 - END - - IDD_DONEOPEN, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 112 - TOPMARGIN, 7 - BOTTOMMARGIN, 48 - END - - IDD_PROPS_EDIT, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 232 - VERTGUIDE, 72 - VERTGUIDE, 79 - VERTGUIDE, 124 - VERTGUIDE, 176 - VERTGUIDE, 190 - TOPMARGIN, 7 - BOTTOMMARGIN, 150 - HORZGUIDE, 7 - HORZGUIDE, 91 - HORZGUIDE, 104 - END - - IDD_CONVFILE_OPTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 164 - VERTGUIDE, 14 - TOPMARGIN, 7 - BOTTOMMARGIN, 86 - END - - IDD_CONVDISK_OPTS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 168 - VERTGUIDE, 14 - VERTGUIDE, 162 - TOPMARGIN, 7 - BOTTOMMARGIN, 218 - HORZGUIDE, 170 - END - - IDD_BULKCONV, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 230 - TOPMARGIN, 7 - BOTTOMMARGIN, 50 - END - - IDD_OPENVOLUMEDLG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 293 - VERTGUIDE, 34 - TOPMARGIN, 7 - BOTTOMMARGIN, 159 - END - - IDD_VOLUMECOPYPROG, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 269 - VERTGUIDE, 86 - TOPMARGIN, 7 - BOTTOMMARGIN, 104 - END - - IDD_DISKEDIT_OPENWHICH, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 127 - TOPMARGIN, 7 - BOTTOMMARGIN, 96 - END - - IDD_VOLUMECOPYSEL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 379 - VERTGUIDE, 288 - VERTGUIDE, 300 - TOPMARGIN, 7 - BOTTOMMARGIN, 131 - HORZGUIDE, 22 - END - - IDD_FORMATTING, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 139 - TOPMARGIN, 7 - BOTTOMMARGIN, 31 - END - - IDD_CREATEIMAGE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 335 - VERTGUIDE, 14 - VERTGUIDE, 100 - VERTGUIDE, 167 - VERTGUIDE, 175 - VERTGUIDE, 184 - TOPMARGIN, 7 - BOTTOMMARGIN, 215 - END - - IDD_CHOOSE_ADD_TARGET, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 213 - TOPMARGIN, 7 - BOTTOMMARGIN, 272 - END - - IDD_ARCHIVEINFO_NUFX, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 313 - VERTGUIDE, 75 - VERTGUIDE, 84 - VERTGUIDE, 302 - TOPMARGIN, 7 - BOTTOMMARGIN, 120 - END - - IDD_ARCHIVEINFO_DISK, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 313 - VERTGUIDE, 75 - VERTGUIDE, 85 - VERTGUIDE, 306 - TOPMARGIN, 7 - BOTTOMMARGIN, 243 - END - - IDD_ARCHIVEINFO_BNY, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 313 - VERTGUIDE, 74 - VERTGUIDE, 84 - VERTGUIDE, 302 - TOPMARGIN, 7 - BOTTOMMARGIN, 67 - END - - IDD_PREF_DISKIMAGE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 212 - VERTGUIDE, 15 - TOPMARGIN, 7 - BOTTOMMARGIN, 98 - END - - IDD_CREATE_SUBDIR, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 277 - TOPMARGIN, 7 - BOTTOMMARGIN, 91 - END - - IDD_RENAME_VOLUME, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 211 - TOPMARGIN, 7 - BOTTOMMARGIN, 165 - END - - IDD_EOLSCAN, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 166 - VERTGUIDE, 68 - VERTGUIDE, 72 - TOPMARGIN, 7 - BOTTOMMARGIN, 99 - END - - IDD_TWOIMG_PROPS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 213 - VERTGUIDE, 59 - VERTGUIDE, 65 - TOPMARGIN, 7 - BOTTOMMARGIN, 191 - END - - IDD_LOADING, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 139 - TOPMARGIN, 7 - BOTTOMMARGIN, 31 - END - - IDD_IMPORTCASSETTE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 348 - VERTGUIDE, 43 - TOPMARGIN, 7 - BOTTOMMARGIN, 150 - END - - IDD_CASSIMPTARGET, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - VERTGUIDE, 13 - TOPMARGIN, 7 - BOTTOMMARGIN, 120 - END - - IDD_ADD_CLASH, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 300 - TOPMARGIN, 7 - BOTTOMMARGIN, 106 - END - - IDD_ARCHIVEINFO_ACU, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 313 - VERTGUIDE, 74 - VERTGUIDE, 84 - TOPMARGIN, 7 - BOTTOMMARGIN, 67 - END - - IDD_IMPORT_BAS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 240 - TOPMARGIN, 7 - BOTTOMMARGIN, 111 - END - - IDD_PASTE_SPECIAL, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - VERTGUIDE, 18 - TOPMARGIN, 7 - BOTTOMMARGIN, 71 - END - - IDD_PROGRESS_COUNTER, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 179 - TOPMARGIN, 7 - BOTTOMMARGIN, 50 - END - - IDD_ARCHIVEINFO_APPLESINGLE, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 302 - TOPMARGIN, 7 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog Info -// - -IDD_PREF_FVIEW DLGINIT -BEGIN - IDC_PVIEW_DHR_CONV_COMBO, 0x403, 14, 0 -0x6c42, 0x6361, 0x206b, 0x2026, 0x6857, 0x7469, 0x0065, - IDC_PVIEW_DHR_CONV_COMBO, 0x403, 28, 0 -0x614c, 0x6374, 0x6568, 0x2064, 0x6f63, 0x6f6c, 0x2072, 0x7228, 0x6365, -0x6d6f, 0x656d, 0x646e, 0x6465, 0x0029, - IDC_PVIEW_DHR_CONV_COMBO, 0x403, 11, 0 -0x6953, 0x706d, 0x656c, 0x3120, 0x3034, "\000" - IDC_PVIEW_DHR_CONV_COMBO, 0x403, 15, 0 -0x6c53, 0x6469, 0x6e69, 0x2067, 0x6977, 0x646e, 0x776f, "\000" - 0 -END - -IDD_OPENVOLUMEDLG DLGINIT -BEGIN - IDC_VOLUME_FILTER, 0x403, 26, 0 -0x6f42, 0x6874, 0x6c20, 0x676f, 0x6369, 0x6c61, 0x6120, 0x646e, 0x7020, -0x7968, 0x6973, 0x6163, 0x006c, - IDC_VOLUME_FILTER, 0x403, 16, 0 -0x6f4c, 0x6967, 0x6163, 0x206c, 0x6f76, 0x756c, 0x656d, 0x0073, - IDC_VOLUME_FILTER, 0x403, 15, 0 -0x6850, 0x7379, 0x6369, 0x6c61, 0x6420, 0x7369, 0x736b, "\000" - 0 -END - -IDD_IMPORTCASSETTE DLGINIT -BEGIN - IDC_CASSETTE_ALG, 0x403, 14, 0 -0x655a, 0x6f72, 0x4320, 0x6f72, 0x7373, 0x6e69, 0x0067, - IDC_CASSETTE_ALG, 0x403, 27, 0 -0x6550, 0x6b61, 0x742d, 0x2d6f, 0x6550, 0x6b61, 0x5720, 0x6469, 0x6874, -0x2820, 0x6853, 0x7261, 0x2970, "\000" - IDC_CASSETTE_ALG, 0x403, 27, 0 -0x6550, 0x6b61, 0x742d, 0x2d6f, 0x6550, 0x6b61, 0x5720, 0x6469, 0x6874, -0x2820, 0x6f52, 0x6e75, 0x2964, "\000" - IDC_CASSETTE_ALG, 0x403, 29, 0 -0x6550, 0x6b61, 0x742d, 0x2d6f, 0x6550, 0x6b61, 0x5720, 0x6469, 0x6874, -0x2820, 0x6853, 0x6c61, 0x6f6c, 0x2977, "\000" - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// AFX_DIALOG_LAYOUT -// - -IDD_ABOUTDLG AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_EXTRACT_FILES AFX_DIALOG_LAYOUT -BEGIN - 0 -END - -IDD_PREF_FVIEW AFX_DIALOG_LAYOUT -BEGIN - 0 -END - - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDM_FILE_NEW_ARCHIVE "Create a new NuFX (ShrinkIt) archive\nNew archive (Ctrl-N)" - IDM_FILE_OPEN "Open an archive or disk image\nOpen (Ctrl-O)" - IDM_FILE_CLOSE "Close current archive or disk image\nClose (Ctrl-W)" - IDM_FILE_PRINT "Print the current list of files\nPrint (Ctrl-P)" - IDM_ACTIONS_ADD_FILES "Add files to the current archive\nAdd files" - IDM_FILE_EXIT "Exit CiderPress\nExit" - IDM_ACTIONS_ADD_DISKS "Add files as disk images\nAdd disk image" - IDM_ACTIONS_EXTRACT "Extract one or more files\nExtract" - IDM_ACTIONS_VIEW "View the contents of the selected files\nView (Tab)" - IDM_ACTIONS_TEST "Test the integrity of files\nTest" - IDM_ACTIONS_DELETE "Delete files from the archive\nDelete (DEL)" - IDM_EDIT_SELECT_ALL "Select all files in an archive" - IDM_ACTIONS_INVERT_SELECTION - "Select un-selected items, and un-select selected items" -END - -STRINGTABLE -BEGIN - IDM_EDIT_PREFERENCES "Set application preferences" - IDM_HELP_CONTENTS "Table of contents for on-line help" - IDM_HELP_ORDERING "How to register this product" - IDM_HELP_ABOUT "About this product\nAbout" - IDM_ACTIONS_RECOMPRESS "Re-compress one or more files\nRecompress" - IDM_SORT_PATHNAME "Sort the list by pathname" - IDM_SORT_TYPE "Sort list by file type" - IDM_SORT_AUXTYPE "Sort by auxtype" - IDM_SORT_MODDATE "Sort by modification date" - IDM_SORT_FORMAT "Sort by compression format" - IDM_SORT_SIZE "Sort by uncompressed size" - IDM_SORT_RATIO "Sort by compression ratio" -END - -STRINGTABLE -BEGIN - IDM_SORT_PACKED "Sort by compressed size" - IDM_SORT_ACCESS "Sort by access flags" - IDM_SORT_ORIGINAL "Show the files in the order in which they appear in the archive" - IDM_CONVERT_TOBSC "Convert a file to BinSCII format" - IDM_CONVERT_FROMBSC "Convert BinSCII files" - ID_INDICATOR_COMPLETE "100%" - IDM_TOOLS_DISKEDIT "View disk images at the sector/block level\nDisk Sector Viewer" - IDM_HELP_WEBSITE "Visit the CiderPress web site\nGo to web site" -END - -STRINGTABLE -BEGIN - IDM_EDIT_INVERT_SELECTION "Invert selection" - IDM_ACTIONS_RENAME "Rename a file\nRename" - IDM_ACTIONS_EDIT_COMMENT "Add, update, or delete a comment\nEdit comment" - IDM_TOOLS_DISKCONV "Convert disk image formats\nDisk Image Converter" - IDM_TOOLS_SST_MERGE "Combine two SST images into one NIB image\nMerge SST Images" - IDM_ACTIONS_OPENASDISK "Open the selected file as a disk image in a new window\nOpen as disk image" -END - -STRINGTABLE -BEGIN - IDS_READONLY "(read only)" - IDS_FAILED "Failed" - IDS_ERROR "Error" - IDS_NOT_ALLOWED "Not allowed" - IDS_WARNING "Warning" - IDS_CANCELLED "Cancelled" -END - -STRINGTABLE -BEGIN - IDS_SELECTED_COUNT "1 file selected" - IDS_SELECTED_COUNTS_FMT "%d files selected" - IDS_BLOCK "Block:" - IDS_DEFILE_FIND_FAILED "Unable to open %ls: file not found." -END - -STRINGTABLE -BEGIN - IDS_DEFILE_OPEN_FAILED "Unable to open %ls: %hs." - IDS_DISKEDIT_NOREADTS "Unable to read track %d sector %d." - IDS_DISKEDIT_NOREADBLOCK "Unable to read block %d." - IDS_DISKEDIT_FIRDFAILED "Unable to read from file: %hs." - IDS_EXT_SELECTED_COUNT "Extract 1 selected file" - IDS_EXT_SELECTED_COUNTS_FMT "Extract %d selected files" - IDS_INDIC_RSRC " " - IDS_INDIC_DISK " " -END - -STRINGTABLE -BEGIN - IDS_INDIC_COMMENT " " - IDS_INDIC_DATA "" - IDS_NOW_ADDING "Now adding:" - IDS_ADDING_AS "As:" - IDS_DEL_SELECTED_COUNTS_FMT "Delete %d selected files" - IDS_DEL_SELECTED_COUNT "Delete 1 selected file" - IDS_DEL_ALL_FILES "Delete all files" - IDS_DEL_OK "Delete" - IDS_DEL_TITLE "Delete Files" - IDS_TEST_SELECTED_COUNTS_FMT "Test %d selected files" - IDS_TEST_SELECTED_COUNT "Test 1 selected file" - IDS_TEST_ALL_FILES "Test all files" - IDS_TEST_OK "Test" - IDS_TEST_TITLE "Test Files" - IDS_NOW_TESTING "Now testing:" - IDS_MB_APP_NAME "CiderPress" -END - -STRINGTABLE -BEGIN - AFX_IDS_IDLEMESSAGE "Ready" -END - -STRINGTABLE -BEGIN - IDS_NO_COMMENT_ADD "This entry does not have a comment. Did you want to add one?" - IDS_EDIT_COMMENT "Edit Comment" - IDS_DEL_COMMENT_OK "Okay to delete comment?" - IDS_RECOMP_SELECTED_COUNT "Recompress 1 selected file" - IDS_RECOMP_SELECTED_COUNTS_FMT "Recompress %d selected files" - IDS_RECOMP_ALL_FILES "Recompress all files" - IDS_RECOMP_OK "Go" - IDS_RECOMP_TITLE "Recompress Files" - IDS_NOW_EXPANDING "Now expanding:" - IDS_NOW_COMPRESSING "Now compressing:" - IDS_PRINT_CL_JOB_TITLE "CiderPress File Listing" - IDS_REG_EXPIRED "The evaluation period has expired. To continue using CiderPress, you will need to register your copy." - IDS_REG_FAILURE "Unable to access system registry. Reboot and try again." - IDS_REG_INVALID "Your registration key appears to be invalid. Please re-enter your registration data." - IDS_REG_EVAL_REM "Evaluation period ends in %d days." - IDS_REG_BAD_ENTRY "The information you have entered does not appear to be correct.\r\n\r\nPlease make sure that all values are entered exactly as they appear in the confirmation message." -END - -STRINGTABLE -BEGIN - IDS_OPEN_AS_NUFX "The file could not be opened as a disk image. However, it appears to be a valid NuFX file archive. Open it?" - IDS_ABOUT_UNREGISTERED "Unregistered - register today!" - IDS_CDESC_140K "140K (5.25"" floppy)" - IDS_CDESC_800K "800K (3.5"" floppy)" - IDS_CDESC_BLOCKS "%ldK (block image)" - IDS_CDEC_140K_13 "13-sector disk (5.25"" floppy)" - IDS_BAD_SST_IMAGE "This image doesn't quite look right. Make sure that:\n - The disk image was created by SST.\n - The image has the correct extension for its sector ordering.\n - You selected side 1 first and side 2 second.\n\nContinue anyway?" - IDS_NIBBLE_TO_SECTOR_WARNING - "Moving from a nibble format to a sector format can cause some data to be lost, especially on copy-protected disks.\n\nContinue anyway?" - IDS_CDEC_RAWNIB "35 tracks (5.25"" floppy)" - IDS_DISKEDITMSG_EMPTY "Empty file -- no storage allocated." - IDS_DISKEDITMSG_SPARSE "Index %ld is sparse (zero-filled)." - IDS_DISKEDITMSG_BADSECTOR "Unable to read sector." - IDS_DISKEDITMSG_BADBLOCK "Unable to read block." - IDS_DISKEDITMSG_BADTRACK "Unable to read track." - IDS_CONVFILE_SELECTED_COUNT "Convert 1 selected file" - IDS_CONVFILE_SELECTED_COUNTS_FMT "Convert %d selected files" -END - -STRINGTABLE -BEGIN - IDM_ACTIONS_EDIT_PROPS "Edit file type and access permissions\nEdit file attributes" - IDM_ACTIONS_CONV_DISK "Convert part or all of this archive to a ProDOS disk image\nConvert to disk image" - IDM_ACTIONS_CONV_FILE "Extract one or more files and put them in a ShrinkIt file archive\nConvert to file archive" - IDM_TOOLS_BULKDISKCONV "Convert several disk images to a different format\nBulk disk image converter" - IDM_FILE_OPEN_VOLUME "Open a disk volume (e.g. CD-ROM, CFFA card, or 1.44MB ProDOS floppy)\nOpen volume (Ctrl-Shift-O)" - IDM_TOOLS_IMAGECREATOR "Create empty disk image\nCreate disk image" - IDM_TOOLS_VOLUMECOPIER_FILE - "Copy a Windows volume (e.g. floppy disk) to or from a file\nVolume copier (open file)" - IDM_TOOLS_VOLUMECOPIER_VOLUME - "Copy a Windows volume (e.g. floppy disk) to or from a file\nVolume copier (open volume)" - IDM_FILE_ARCHIVEINFO "Show detailed information on this archive or disk image\nArchive Info (Ctrl-I)" -END - -STRINGTABLE -BEGIN - IDS_CONVFILE_ALL_FILES "Convert all files" - IDS_CONVFILE_OK "Go" - IDS_CONVFILE_TITLE "Convert to file archive" - IDS_CONVDISK_SELECTED_COUNT "Convert 1 selected file" - IDS_CONVDISK_SELECTED_COUNTS_FMT "Convert %d selected files" - IDS_CONVDISK_ALL_FILES "Convert all files" - IDS_CONVDISK_OK "Go" - IDS_CONVDISK_TITLE "Convert to disk archive" - IDS_CONVDISK_SPACEREQ "Space required: %ls" - IDS_CDESC_40TRACK "40-track image (5.25"" floppy)" - IDS_TRACKSTAR_TO_OTHER_WARNING - "TrackStar images are stored as 40-track variable-length nibble images. No other supported format is directly compatible, so images are converted as formatted 35-track 16-sector disks. This probably won't work well for copy-protected disks.\n\nContinue anyway?" - IDS_DIFFERENT_NIBBLE_WARNING - "You are converting an image between formats that use different nibble track lengths. A direct conversion isn't possible, so the image will be converted as a formatted 16-sector disk. This will likely cause problems for copy-protected disks.\n\nContinue anyway?" - IDS_VOLUME_NO_REMOTE "Network volumes are not supported." - IDS_VOLUME_NO_CDROM "CD-ROM drives are not currently supported." - IDS_VOLUME_NO_RAMDISK "RAM disks are not supported." - IDS_VOLUME_NO_GENERIC "That type of drive is not supported." -END - -STRINGTABLE -BEGIN - IDS_VOLUME_NO_CDRIVE "For safety, access to C:\\ is not allowed." - IDS_VOLUME_SELECT_ONE "Please select the volume to access." - IDS_OPERATION_CANCELLED "Operation cancelled." - IDS_NOT_READY "Not ready" - IDS_ASPI_NOT_LOADED "ASPI driver not loaded" - IDS_NOW_DELETING "Now deleting:" - IDS_VALID_FILENAME_PRODOS - "Valid ProDOS filenames are 1-15 characters long, start with a letter, and use only letters numbers, spaces, and '.'. If the ""allow lower case"" preference is disabled, lower case letters are converted to upper case, and space becomes '.'." - IDS_VALID_FILENAME_DOS "Valid DOS file names are 1-30 characters long, and use upper case letters, numbers, spaces, and symbols other than ','. Trailing spaces are not allowed." - IDS_VALID_FILENAME_PASCAL - "Valid Pascal file names are 1-15 characters long, and use upper case letters, numbers, and symbols other than '$=?,]#:'." - IDS_VALID_VOLNAME_PRODOS - "Valid ProDOS volume names are 1-15 characters long, begin with a letter, and have only letters, numbers, and '.'. If the ""allow lower case"" preference is disabled, lower case letters will be converted to upper case. Trailing spaces are not allowed." - IDS_VALID_VOLNAME_DOS "DOS volume numbers must be between 1 and 254." - IDS_VALID_VOLNAME_PASCAL - "Valid Pascal volume names are 1-7 characters long and use upper case letters, numbers, and symbols other than '$=?,]#:'." - IDS_CLIPBOARD_REGFAILED "ERROR: unable to register clipboard format." - IDS_CLIPBOARD_OPENFAILED "ERROR: unable to open clipboard." - IDS_CLIPBOARD_NOITEMS "Nothing copied to clipboard." - IDS_CLIPBOARD_ALLOCFAILED "Unable to allocate memory for clipboard data." -END - -STRINGTABLE -BEGIN - IDM_ACTIONS_CREATE_SUBDIR - "Create a new subdirectory on a ProDOS disk\nCreate subdirectory" - IDM_ACTIONS_RENAME_VOLUME - "Change a disk's volume name or number\nRename volume" - IDM_TOOLS_EOLSCANNER "Scans files for end-of-line markers\nEOL scanner" - IDM_FILE_FLUSH "Save any unwritten data to disk\nSave changes" - IDM_FILE_SAVE "Flush any pending changes to disk.\nSave (Ctrl-S)" - IDM_EDIT_COPY "Copy the selected files to the clipboard\nCopy" - IDM_EDIT_PASTE "Paste files from the clipboard" - IDM_TOOLS_TWOMGPROPS "Edit the properties of a .2MG disk image.\n2MG image editor" - IDM_TOOLS_TWOIMGPROPS "Edit header fields of a 2MG disk image\n2MG properties editor" - IDM_ACTIONS_CONV_TOWAV "Convert a BASIC or BIN file to Apple II cassette format\nConvert to WAV" - IDM_ACTIONS_CONV_FROMWAV - "Convert a WAV file recording of an Apple II cassette into an Apple II file\nImport file from WAV" -END - -STRINGTABLE -BEGIN - IDS_CLIPBOARD_NOTFOUND "No CiderPress data found in the clipboard." - IDS_CLIPBOARD_READFAILURE "Failed while reading data from the clipboard." - IDS_CLIPBOARD_WRITEFAILURE "Failed while copying data to the clipboard." - IDS_CLIPBOARD_WIN9XMAX "In Win98/ME the clipboard is limited to %ldMB of data. Your request includes %.3fMB. Please select less data." - IDS_PRINTER_NOT_USABLE "Attempting to print to the requested printer failed." - IDS_NLIST_DATA_FAILED "Unable to load NList.Data from '%ls' or '%ls'." - IDS_PROPS_DOS_TYPE_CHANGE - "Changing the type of a DOS 3.2/3.3 file to BIN, INT, or BAS can cause problems. Consult the ""help"" file for details. Do you wish to continue?" - IDS_FDI_TO_OTHER_WARNING - "FDI images are stored as variable-length nibble images. No other supported format is directly compatible, so images are converted to formatted 35-track 16-sector disks. This probably won't work well for copy-protected disks.\n\nContinue anyway?" - IDS_VALID_VOLNAME_HFS "Valid HFS volume names are 1-27 characters long, and may not contain a colon (:)." - IDS_VALID_FILENAME_HFS "Valid HFS filenames are 1-31 characters long, and do not contain colons (':')." - IDS_PASTE_SPECIAL_COUNT "%d files in clipboard" - IDS_NO_FORKS_SPECIFIED "You must select at least one item in ""Parts to extract""." -END - -STRINGTABLE -BEGIN - IDM_ACTIONS_IMPORT_BAS "Import Applesoft BASIC program from text file" - IDM_FILE_REOPEN "Re-open the existing archive or disk image\nReopen" - IDM_EDIT_FIND "Search for an entry in the file list" - IDM_EDIT_PASTE_SPECIAL "Choose how files on the clipboard will be pasted" -END - -STRINGTABLE -BEGIN - ID_EDIT_FIND "Search for a " -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/ciderpress/app/Clipboard.cpp b/ciderpress/app/Clipboard.cpp deleted file mode 100644 index 70845cb..0000000 --- a/ciderpress/app/Clipboard.cpp +++ /dev/null @@ -1,1031 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Handle clipboard functions (copy, paste). This is part of MainWindow, - * split out into a separate file for clarity. - */ -#include "StdAfx.h" -#include "Main.h" -#include "PasteSpecialDialog.h" - - -static const WCHAR kClipboardFmtName[] = L"faddenSoft:CiderPress:v2"; -const int kClipVersion = 2; // should match "vN" in fmt name -const uint16_t kEntrySignature = 0x4350; - -/* - * Win98 is quietly dying on large (20MB-ish) copies. Everything - * runs along nicely and then, the next time we try to interact with - * Windows, the entire system locks up and must be Ctrl-Alt-Deleted. - * - * In tests, it blew up on 16762187 but not 16682322, both of which - * are shy of the 16MB mark (the former by about 15K). This includes the - * text version, which is potentially copied multiple times. Windows doesn't - * create the additional stuff (CF_OEMTEXT and CF_LOCALE; Win2K also creates - * CF_UNICODETEXT) until after the clipboard is closed. We can open & close - * the clipboard to get an exact count, or just multiply by a small integer - * to get a reasonable estimate (1x for alternate text, 2x for UNICODE). - * - * Microsoft Excel limits its clipboard to 4MB on small systems, and 8MB - * on systems with at least 64MB of physical memory. My guess is they - * haven't really tried larger values. - * - * The description of GlobalAlloc suggests that it gets a little weird - * when you go above 4MB, which makes me a little nervous about using - * anything larger. However, it seems to work very reliably until you - * cross 16MB, at which point it seizes up 100% of the time. It's possible - * we're stomping on stuff and just getting lucky, but it's too-reliably - * successful and too-reliably failing for me to believe that. - */ -const long kWin98ClipboardMax = 16 * 1024 * 1024; -const long kWin98NeutralZone = 512; // extra padding -const int kClipTextMult = 4; // CF_OEMTEXT, CF_LOCALE, CF_UNICODETEXT*2 - -/* - * File collection header. - */ -typedef struct FileCollection { - uint16_t version; // currently 1 - uint16_t dataOffset; // offset to start of data - uint32_t length; // total length; - uint32_t count; // #of entries -} FileCollection; - -/* what kind of entry is this */ -typedef enum EntryKind { - kEntryKindUnknown = 0, - kEntryKindFileDataFork, - kEntryKindFileRsrcFork, - kEntryKindFileBothForks, - kEntryKindDirectory, - kEntryKindDiskImage, -} EntryKind; - -/* - * One of these per entry in the collection. - * - * The next file starts at (start + dataOffset + dataLen + rsrcLen + cmmtLen). - * - * TODO: filename should be 8-bit from original, not 16-bit conversion - */ -typedef struct FileCollectionEntry { - uint16_t signature; // let's be paranoid - uint16_t dataOffset; // offset to start of data - uint16_t fileNameLen; // len of filename, in bytes - uint32_t dataLen; // len of data fork - uint32_t rsrcLen; // len of rsrc fork - uint32_t cmmtLen; // len of comments - uint32_t fileType; - uint32_t auxType; - int64_t createWhen; // holds time_t - int64_t modWhen; // holds time_t - uint8_t access; // ProDOS access flags - uint8_t entryKind; // GenericArchive::FileDetails::FileKind - uint8_t sourceFS; // holds DiskImgLib::DiskImg::FSFormat - uint8_t fssep; // filesystem separator char, e.g. ':' - - /* data comes next: null-terminated WCHAR filename, then data fork, then - resource fork, then comment */ -} FileCollectionEntry; - - -/* - * ========================================================================== - * Copy - * ========================================================================== - */ - -void MainWindow::OnEditCopy(void) -{ - CString errStr, fileList; - SelectionSet selSet; - UINT myFormat; - bool isOpen = false; - HGLOBAL hGlobal; - LPVOID pGlobal; - uint8_t* buf = NULL; - long bufLen = -1; - - /* associate a number with the format name */ - myFormat = RegisterClipboardFormat(kClipboardFmtName); - if (myFormat == 0) { - CheckedLoadString(&errStr, IDS_CLIPBOARD_REGFAILED); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - LOGI("myFormat = %u", myFormat); - - /* open & empty the clipboard, even if we fail later */ - if (OpenClipboard() == false) { - CheckedLoadString(&errStr, IDS_CLIPBOARD_OPENFAILED); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - isOpen = true; - EmptyClipboard(); - - /* - * Create a selection set with the entries. - * - * Strictly speaking we don't need the directories, since we recreate - * them as needed. However, storing them explicitly will allow us - * to preserve empty subdirs. - */ - selSet.CreateFromSelection(fpContentList, - GenericEntry::kAnyThread | GenericEntry::kAllowDirectory); - if (selSet.GetNumEntries() == 0) { - CheckedLoadString(&errStr, IDS_CLIPBOARD_NOITEMS); - MessageBox(errStr, L"No match", MB_OK | MB_ICONEXCLAMATION); - goto bail; - } - - /* - * Make a big string with a file listing. - */ - fileList = CreateFileList(&selSet); - - /* - * Add the string to the clipboard. The clipboard will own the memory we - * allocate. - */ - size_t neededLen = (fileList.GetLength() + 1) * sizeof(WCHAR); - hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, neededLen); - if (hGlobal == NULL) { - LOGI("Failed allocating %d bytes", neededLen); - CheckedLoadString(&errStr, IDS_CLIPBOARD_ALLOCFAILED); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - LOGI(" Allocated %ld bytes for file list on clipboard", neededLen); - pGlobal = ::GlobalLock(hGlobal); - ASSERT(pGlobal != NULL); - wcscpy((WCHAR*) pGlobal, fileList); - ::GlobalUnlock(hGlobal); - - SetClipboardData(CF_UNICODETEXT, hGlobal); - - /* - * Create a (potentially very large) buffer with the contents of the - * files in it. This may fail for any number of reasons. - */ - hGlobal = CreateFileCollection(&selSet); - if (hGlobal != NULL) { - SetClipboardData(myFormat, hGlobal); - // beep annoys me on copy - //SuccessBeep(); - } - -bail: - CloseClipboard(); -} - -void MainWindow::OnUpdateEditCopy(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && - fpContentList->GetSelectedCount() > 0); -} - -CString MainWindow::CreateFileList(SelectionSet* pSelSet) -{ - SelectionEntry* pSelEntry; - GenericEntry* pEntry; - CString tmpStr, fullStr; - WCHAR fileTypeBuf[ContentList::kFileTypeBufLen]; - WCHAR auxTypeBuf[ContentList::kAuxTypeBufLen]; - CString fileName, subVol, fileType, auxType, modDate, format, length; - - pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = pSelEntry->GetEntry(); - ASSERT(pEntry != NULL); - - fileName = DblDblQuote(pEntry->GetPathNameUNI()); - subVol = pEntry->GetSubVolName(); - ContentList::MakeFileTypeDisplayString(pEntry, fileTypeBuf); - fileType = DblDblQuote(fileTypeBuf); // Mac HFS types might have '"'? - ContentList::MakeAuxTypeDisplayString(pEntry, auxTypeBuf); - auxType = DblDblQuote(auxTypeBuf); - FormatDate(pEntry->GetModWhen(), &modDate); - format = pEntry->GetFormatStr(); - length.Format(L"%I64d", (LONGLONG) pEntry->GetUncompressedLen()); - - tmpStr.Format(L"\"%ls\"\t%ls\t\"%ls\"\t\"%ls\"\t%ls\t%ls\t%ls\r\n", - (LPCWSTR) fileName, (LPCWSTR) subVol, (LPCWSTR) fileType, - (LPCWSTR) auxType, (LPCWSTR) modDate, (LPCWSTR) format, - (LPCWSTR) length); - fullStr += tmpStr; - - pSelEntry = pSelSet->IterNext(); - } - - return fullStr; -} - -/*static*/ CString MainWindow::DblDblQuote(const WCHAR* str) -{ - CString result; - WCHAR* buf; - - buf = result.GetBuffer(wcslen(str) * 2 +1); - while (*str != '\0') { - if (*str == '"') { - *buf++ = *str; - *buf++ = *str; - } else { - *buf++ = *str; - } - str++; - } - *buf = *str; - - result.ReleaseBuffer(); - - return result; -} - -long MainWindow::GetClipboardContentLen(void) -{ - long len = 0; - UINT format = 0; - HGLOBAL hGlobal; - - while ((format = EnumClipboardFormats(format)) != 0) { - hGlobal = GetClipboardData(format); - ASSERT(hGlobal != NULL); - len += GlobalSize(hGlobal); - } - - return len; -} - -HGLOBAL MainWindow::CreateFileCollection(SelectionSet* pSelSet) -{ - SelectionEntry* pSelEntry; - GenericEntry* pEntry; - HGLOBAL hGlobal = NULL; - HGLOBAL hResult = NULL; - LPVOID pGlobal; - size_t totalLength, numFiles; - long priorLength; - - /* get len of text version(s), with kluge to avoid close & reopen */ - priorLength = GetClipboardContentLen() * kClipTextMult; - /* add some padding -- textmult doesn't work for fixed-size CF_LOCALE */ - priorLength += kWin98NeutralZone; - - totalLength = sizeof(FileCollection); - numFiles = 0; - - /* - * Compute the amount of space required to hold it all. - */ - pSelSet->IterReset(); - pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = pSelEntry->GetEntry(); - ASSERT(pEntry != NULL); - - //LOGI("+++ Examining '%s'", pEntry->GetDisplayName()); - - if (pEntry->GetRecordKind() != GenericEntry::kRecordKindVolumeDir) { - totalLength += sizeof(FileCollectionEntry); - totalLength += (wcslen(pEntry->GetPathNameUNI()) +1) * sizeof(WCHAR); - numFiles++; - if (pEntry->GetRecordKind() != GenericEntry::kRecordKindDirectory) { - totalLength += (long) pEntry->GetDataForkLen(); - totalLength += (long) pEntry->GetRsrcForkLen(); - } - } - - if (totalLength < 0) { - DebugBreak(); - LOGI("Overflow"); // pretty hard to do right now! - return NULL; - } - - pSelEntry = pSelSet->IterNext(); - } - -#if 0 - { - CString msg; - msg.Format("totalLength is %ld+%ld = %ld", - totalLength, priorLength, totalLength+priorLength); - if (MessageBox(msg, NULL, MB_OKCANCEL) == IDCANCEL) - goto bail; - } -#endif - - LOGI("Total length required is %ld + %ld = %ld", - totalLength, priorLength, totalLength+priorLength); - if (IsWin9x() && totalLength+priorLength >= kWin98ClipboardMax) { - CString errMsg; - errMsg.Format(IDS_CLIPBOARD_WIN9XMAX, - kWin98ClipboardMax / (1024*1024), - ((float) (totalLength+priorLength)) / (1024.0*1024.0)); - ShowFailureMsg(this, errMsg, IDS_MB_APP_NAME); - goto bail; - } - - /* - * Create a big buffer to hold it all. - */ - hGlobal = ::GlobalAlloc(GHND | GMEM_SHARE, totalLength); - if (hGlobal == NULL) { - CString errMsg; - errMsg.Format(L"ERROR: unable to allocate %ld bytes for copy", - totalLength); - LOGI("%ls", (LPCWSTR) errMsg); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - pGlobal = ::GlobalLock(hGlobal); - - ASSERT(pGlobal != NULL); - ASSERT(GlobalSize(hGlobal) >= (DWORD) totalLength); - LOGI("hGlobal=0x%08lx pGlobal=0x%08lx size=%ld", - (long) hGlobal, (long) pGlobal, GlobalSize(hGlobal)); - - /* - * Set up a progress dialog to track it. - */ - ASSERT(fpActionProgress == NULL); - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionExtract, this); - fpActionProgress->SetFileName(L"Clipboard"); - - /* - * Extract the data into the buffer. - */ - long remainingLen; - void* buf; - - remainingLen = totalLength - sizeof(FileCollection); - buf = (uint8_t*) pGlobal + sizeof(FileCollection); - pSelSet->IterReset(); - pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - CString errStr; - - pEntry = pSelEntry->GetEntry(); - ASSERT(pEntry != NULL); - - fpActionProgress->SetArcName(pEntry->GetDisplayName()); - - errStr = CopyToCollection(pEntry, &buf, &remainingLen); - if (!errStr.IsEmpty()) { - ShowFailureMsg(fpActionProgress, errStr, IDS_MB_APP_NAME); - goto bail; - } - //LOGI("remainingLen now %ld", remainingLen); - - pSelEntry = pSelSet->IterNext(); - } - - ASSERT(remainingLen == 0); - ASSERT(buf == (uint8_t*) pGlobal + totalLength); - - /* - * Write the header. - */ - FileCollection fileColl; - fileColl.version = kClipVersion; - fileColl.dataOffset = sizeof(FileCollection); - fileColl.length = totalLength; - fileColl.count = numFiles; - memcpy(pGlobal, &fileColl, sizeof(fileColl)); - - /* - * Success! - */ - ::GlobalUnlock(hGlobal); - - hResult = hGlobal; - hGlobal = NULL; - -bail: - if (hGlobal != NULL) { - ASSERT(hResult == NULL); - ::GlobalUnlock(hGlobal); - ::GlobalFree(hGlobal); - } - if (fpActionProgress != NULL) { - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - } - return hResult; -} - -CString MainWindow::CopyToCollection(GenericEntry* pEntry, void** pBuf, - long* pBufLen) -{ - FileCollectionEntry collEnt; - CString errStr; - uint8_t* buf = (uint8_t*) *pBuf; - long remLen = *pBufLen; - - CheckedLoadString(&errStr, IDS_CLIPBOARD_WRITEFAILURE); - - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) { - LOGI("Not copying volume dir to collection"); - return ""; - } - - if (remLen < sizeof(collEnt)) { - ASSERT(false); - return errStr; - } - - GenericArchive::LocalFileDetails::FileKind entryKind; - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) - entryKind = GenericArchive::LocalFileDetails::kFileKindDirectory; - else if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) - entryKind = GenericArchive::LocalFileDetails::kFileKindBothForks; - else if (pEntry->GetHasDataFork()) - entryKind = GenericArchive::LocalFileDetails::kFileKindDataFork; - else if (pEntry->GetHasRsrcFork()) - entryKind = GenericArchive::LocalFileDetails::kFileKindRsrcFork; - else if (pEntry->GetHasDiskImage()) - entryKind = GenericArchive::LocalFileDetails::kFileKindDiskImage; - else { - ASSERT(false); - return errStr; - } - ASSERT((int) entryKind >= 0 && (int) entryKind <= 255); - - memset(&collEnt, 0x99, sizeof(collEnt)); - collEnt.signature = kEntrySignature; - collEnt.dataOffset = sizeof(collEnt); - collEnt.fileNameLen = (wcslen(pEntry->GetPathNameUNI()) +1) * sizeof(WCHAR); - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { - collEnt.dataLen = collEnt.rsrcLen = collEnt.cmmtLen = 0; - } else { - collEnt.dataLen = (uint32_t) pEntry->GetDataForkLen(); - collEnt.rsrcLen = (uint32_t) pEntry->GetRsrcForkLen(); - collEnt.cmmtLen = 0; // CMMT FIX -- length unknown?? - } - collEnt.fileType = pEntry->GetFileType(); - collEnt.auxType = pEntry->GetAuxType(); - collEnt.createWhen = pEntry->GetCreateWhen(); - collEnt.modWhen = pEntry->GetModWhen(); - collEnt.access = (uint8_t) pEntry->GetAccess(); - collEnt.entryKind = (uint8_t) entryKind; - collEnt.sourceFS = pEntry->GetSourceFS(); - collEnt.fssep = pEntry->GetFssep(); - - /* verify there's enough space to hold everything */ - if ((uint32_t) remLen < collEnt.fileNameLen + - collEnt.dataLen + collEnt.rsrcLen + collEnt.cmmtLen) - { - ASSERT(false); - return errStr; - } - - memcpy(buf, &collEnt, sizeof(collEnt)); - buf += sizeof(collEnt); - remLen -= sizeof(collEnt); - - /* copy string with terminating null */ - memcpy(buf, pEntry->GetPathNameUNI(), collEnt.fileNameLen); - buf += collEnt.fileNameLen; - remLen -= collEnt.fileNameLen; - - /* - * Extract data forks, resource forks, and disk images as appropriate. - */ - char* bufCopy; - long lenCopy; - int result, which; - - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { - ASSERT(collEnt.dataLen == 0); - ASSERT(collEnt.rsrcLen == 0); - ASSERT(collEnt.cmmtLen == 0); - ASSERT(!pEntry->GetHasRsrcFork()); - } else if (pEntry->GetHasDataFork() || pEntry->GetHasDiskImage()) { - bufCopy = (char*) buf; - lenCopy = remLen; - if (pEntry->GetHasDiskImage()) - which = GenericEntry::kDiskImageThread; - else - which = GenericEntry::kDataThread; - - CString extractErrStr; - result = pEntry->ExtractThreadToBuffer(which, &bufCopy, &lenCopy, - &extractErrStr); - if (result == IDCANCEL) { - CheckedLoadString(&errStr, IDS_CANCELLED); - return errStr; - } else if (result != IDOK) { - LOGW("ExtractThreadToBuffer (data) failed: %ls", - (LPCWSTR) extractErrStr); - return errStr; - } - - ASSERT(lenCopy == (long) collEnt.dataLen); - buf += collEnt.dataLen; - remLen -= collEnt.dataLen; - } else { - ASSERT(collEnt.dataLen == 0); - } - - if (pEntry->GetHasRsrcFork()) { - bufCopy = (char*) buf; - lenCopy = remLen; - which = GenericEntry::kRsrcThread; - - CString extractErrStr; - result = pEntry->ExtractThreadToBuffer(which, &bufCopy, &lenCopy, - &extractErrStr); - if (result == IDCANCEL) { - CheckedLoadString(&errStr, IDS_CANCELLED); - return errStr; - } else if (result != IDOK) { - LOGI("ExtractThreadToBuffer (rsrc) failed: %ls", - (LPCWSTR) extractErrStr); - return errStr; - } - - ASSERT(lenCopy == (long) collEnt.rsrcLen); - buf += collEnt.rsrcLen; - remLen -= collEnt.rsrcLen; - } - - if (pEntry->GetHasComment()) { -#if 0 // CMMT FIX - bufCopy = (char*) buf; - lenCopy = remLen; - which = GenericEntry::kCommentThread; - - result = pEntry->ExtractThreadToBuffer(which, &bufCopy, &lenCopy); - if (result == IDCANCEL) { - errStr.LoadString(IDS_CANCELLED); - return errStr; - } else if (result != IDOK) - return errStr; - - ASSERT(lenCopy == (long) collEnt.cmmtLen); - buf += collEnt.cmmtLen; - remLen -= collEnt.cmmtLen; -#else - ASSERT(collEnt.cmmtLen == 0); -#endif - } - - *pBuf = buf; - *pBufLen = remLen; - return ""; -} - - -/* - * ========================================================================== - * Paste - * ========================================================================== - */ - -void MainWindow::OnEditPaste(void) -{ - bool pasteJunkPaths = fPreferences.GetPrefBool(kPrPasteJunkPaths); - - DoPaste(pasteJunkPaths); -} - -void MainWindow::OnUpdateEditPaste(CCmdUI* pCmdUI) -{ - bool dataAvailable = false; - UINT myFormat; - - myFormat = RegisterClipboardFormat(kClipboardFmtName); - if (myFormat != 0 && IsClipboardFormatAvailable(myFormat)) - dataAvailable = true; - - pCmdUI->Enable(fpContentList != NULL && !fpOpenArchive->IsReadOnly() && - dataAvailable); -} - -void MainWindow::OnEditPasteSpecial(void) -{ - PasteSpecialDialog dlg; - bool pasteJunkPaths = fPreferences.GetPrefBool(kPrPasteJunkPaths); - - // invert the meaning, so non-default mode is default in dialog - if (pasteJunkPaths) - dlg.fPasteHow = PasteSpecialDialog::kPastePaths; - else - dlg.fPasteHow = PasteSpecialDialog::kPasteNoPaths; - if (dlg.DoModal() != IDOK) - return; - - switch (dlg.fPasteHow) { - case PasteSpecialDialog::kPastePaths: - pasteJunkPaths = false; - break; - case PasteSpecialDialog::kPasteNoPaths: - pasteJunkPaths = true; - break; - default: - assert(false); - break; - } - - DoPaste(pasteJunkPaths); -} - -void MainWindow::OnUpdateEditPasteSpecial(CCmdUI* pCmdUI) -{ - OnUpdateEditPaste(pCmdUI); -} - -void MainWindow::DoPaste(bool pasteJunkPaths) -{ - CString errStr, buildStr; - UINT format = 0; - UINT myFormat; - bool isOpen = false; - - if (fpContentList == NULL || fpOpenArchive->IsReadOnly()) { - ASSERT(false); - return; - } - - myFormat = RegisterClipboardFormat(kClipboardFmtName); - if (myFormat == 0) { - CheckedLoadString(&errStr, IDS_CLIPBOARD_REGFAILED); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - LOGI("myFormat = %u", myFormat); - - if (OpenClipboard() == false) { - CheckedLoadString(&errStr, IDS_CLIPBOARD_OPENFAILED); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail;; - } - isOpen = true; - - LOGI("Found %d clipboard formats", CountClipboardFormats()); - while ((format = EnumClipboardFormats(format)) != 0) { - CString tmpStr; - tmpStr.Format(L" %u", format); - buildStr += tmpStr; - } - LOGI(" %ls", (LPCWSTR) buildStr); - -#if 0 - if (IsClipboardFormatAvailable(CF_HDROP)) { - errStr.LoadString(IDS_CLIPBOARD_NO_HDROP); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } -#endif - - /* impossible unless OnUpdateEditPaste was bypassed */ - if (!IsClipboardFormatAvailable(myFormat)) { - CheckedLoadString(&errStr, IDS_CLIPBOARD_NOTFOUND); - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - - LOGI("+++ total data on clipboard: %ld bytes", - GetClipboardContentLen()); - - HGLOBAL hGlobal; - LPVOID pGlobal; - - hGlobal = GetClipboardData(myFormat); - if (hGlobal == NULL) { - ASSERT(false); - goto bail; - } - pGlobal = GlobalLock(hGlobal); - ASSERT(pGlobal != NULL); - errStr = ProcessClipboard(pGlobal, GlobalSize(hGlobal), pasteJunkPaths); - fpContentList->Reload(); - - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - else - SuccessBeep(); - - GlobalUnlock(hGlobal); - -bail: - if (isOpen) - CloseClipboard(); -} - -CString MainWindow::ProcessClipboard(const void* vbuf, long bufLen, - bool pasteJunkPaths) -{ - FileCollection fileColl; - CString errMsg, storagePrefix; - const uint8_t* buf = (const uint8_t*) vbuf; - DiskImgLib::A2File* pTargetSubdir = NULL; - XferFileOptions xferOpts; - bool xferPrepped = false; - - /* set a standard error message */ - CheckedLoadString(&errMsg, IDS_CLIPBOARD_READFAILURE); - - /* - * Pull the header out. - */ - if (bufLen < sizeof(fileColl)) { - LOGW("Clipboard contents too small!"); - goto bail; - } - memcpy(&fileColl, buf, sizeof(fileColl)); - - /* - * Verify the length. Win98 seems to like to round things up to 16-byte - * boundaries, which screws up our "bufLen > 0" while condition below. - */ - if ((long) fileColl.length > bufLen) { - LOGW("GLITCH: stored len=%ld, clip len=%ld", - fileColl.length, bufLen); - goto bail; - } - if (bufLen > (long) fileColl.length) { - /* trim off extra */ - LOGI("NOTE: Windows reports excess length (%ld vs %ld)", - fileColl.length, bufLen); - bufLen = fileColl.length; - } - - buf += sizeof(fileColl); - bufLen -= sizeof(fileColl); - - LOGI("FileCollection found: vers=%d off=%d len=%ld count=%ld", - fileColl.version, fileColl.dataOffset, fileColl.length, - fileColl.count); - if (fileColl.count == 0) { - /* nothing to do? */ - ASSERT(false); - return errMsg; - } - - /* - * Figure out where we want to put the files. For a disk archive - * this can be complicated. - * - * The target DiskFS (which could be a sub-volume) gets tucked into - * the xferOpts. - */ - if (fpOpenArchive->GetArchiveKind() == GenericArchive::kArchiveDiskImage) { - if (!ChooseAddTarget(&pTargetSubdir, &xferOpts.fpTargetFS)) - return L""; - } - fpOpenArchive->XferPrepare(&xferOpts); - xferPrepped = true; - - if (pTargetSubdir != NULL) { - storagePrefix = pTargetSubdir->GetPathName(); - LOGD("--- using storagePrefix '%ls'", (LPCWSTR) storagePrefix); - } - - /* - * Set up a progress dialog to track it. - */ - ASSERT(fpActionProgress == NULL); - fpActionProgress = new ActionProgressDialog; - fpActionProgress->Create(ActionProgressDialog::kActionAdd, this); - fpActionProgress->SetArcName(L"Clipboard data"); - - /* - * Loop over all files. - */ - LOGI("+++ Starting paste, bufLen=%ld", bufLen); - while (bufLen > 0) { - FileCollectionEntry collEnt; - CString fileName; - CString processErrStr; - - /* read the entry info */ - if (bufLen < sizeof(collEnt)) { - LOGW("GLITCH: bufLen=%ld, sizeof(collEnt)=%d", - bufLen, sizeof(collEnt)); - ASSERT(false); - goto bail; - } - memcpy(&collEnt, buf, sizeof(collEnt)); - if (collEnt.signature != kEntrySignature) { - ASSERT(false); - goto bail; - } - - /* advance to the start of data */ - if (bufLen < collEnt.dataOffset) { - ASSERT(false); - goto bail; - } - buf += collEnt.dataOffset; - bufLen -= collEnt.dataOffset; - - /* extract the filename */ - if (bufLen < collEnt.fileNameLen) { - ASSERT(false); - goto bail; - } - // TODO: consider moving filename as raw 8-bit data - fileName = (const WCHAR*) buf; - buf += collEnt.fileNameLen; - bufLen -= collEnt.fileNameLen; - - LOGD("+++ pasting '%ls'", (LPCWSTR) fileName); - - /* strip the path (if requested) and prepend the storage prefix */ - ASSERT((fileName.GetLength() +1 ) * sizeof(WCHAR) == collEnt.fileNameLen); - if (pasteJunkPaths && collEnt.fssep != '\0') { - int idx; - idx = fileName.ReverseFind(collEnt.fssep); - if (idx >= 0) - fileName = fileName.Right(fileName.GetLength() - idx -1); - } - if (!storagePrefix.IsEmpty()) { - CString tmpStr, tmpFileName; - tmpFileName = fileName; - if (collEnt.fssep == '\0') { - tmpFileName.Replace(':', '_'); // strip any ':'s in the name - collEnt.fssep = ':'; // define an fssep - } - - tmpStr = storagePrefix; - - /* storagePrefix fssep is always ':'; change it to match */ - if (collEnt.fssep != ':') - tmpStr.Replace(':', collEnt.fssep); - - tmpStr += collEnt.fssep; - tmpStr += tmpFileName; - fileName = tmpStr; - - } - fpActionProgress->SetFileName(fileName); - - /* make sure the data is there */ - if (bufLen < (long) (collEnt.dataLen + collEnt.rsrcLen + collEnt.cmmtLen)) - { - ASSERT(false); - goto bail; - } - - /* - * Process the entry. - * - * If the user hits "cancel" in the progress dialog we'll get thrown - * back out. For the time being I'm just treating it like any other - * failure. - */ - processErrStr = ProcessClipboardEntry(&collEnt, fileName, buf, bufLen); - if (!processErrStr.IsEmpty()) { - errMsg.Format(L"Unable to paste '%ls': %ls.", - (LPCWSTR) fileName, (LPCWSTR) processErrStr); - goto bail; - } - - buf += collEnt.dataLen + collEnt.rsrcLen + collEnt.cmmtLen; - bufLen -= collEnt.dataLen + collEnt.rsrcLen + collEnt.cmmtLen; - } - - ASSERT(bufLen == 0); - errMsg = ""; - -bail: - if (xferPrepped) { - if (errMsg.IsEmpty()) - fpOpenArchive->XferFinish(this); - else - fpOpenArchive->XferAbort(this); - } - if (fpActionProgress != NULL) { - fpActionProgress->Cleanup(this); - fpActionProgress = NULL; - } - return errMsg; -} - -CString MainWindow::ProcessClipboardEntry(const FileCollectionEntry* pCollEnt, - const WCHAR* pathName, const uint8_t* buf, long remLen) -{ - GenericArchive::LocalFileDetails::FileKind entryKind; - GenericArchive::LocalFileDetails details; - uint8_t* dataBuf = NULL; - uint8_t* rsrcBuf = NULL; - long dataLen, rsrcLen, cmmtLen; - CString errMsg; - - entryKind = (GenericArchive::LocalFileDetails::FileKind) pCollEnt->entryKind; - LOGD(" Processing '%ls' (%d)", pathName, entryKind); - - details.SetEntryKind(entryKind); - details.SetLocalPathName(L"Clipboard"); - details.SetStrippedLocalPathName(pathName); - details.SetFileSysFmt((DiskImg::FSFormat) pCollEnt->sourceFS); - details.SetFssep(pCollEnt->fssep); - details.SetAccess(pCollEnt->access); - details.SetFileType(pCollEnt->fileType); - details.SetExtraType(pCollEnt->auxType); - NuDateTime ndt; - GenericArchive::UNIXTimeToDateTime(&pCollEnt->createWhen, &ndt); - details.SetCreateWhen(ndt); - GenericArchive::UNIXTimeToDateTime(&pCollEnt->modWhen, &ndt); - details.SetModWhen(ndt); - time_t now = time(NULL); - GenericArchive::UNIXTimeToDateTime(&now, &ndt); - details.SetArchiveWhen(ndt); - - /* - * Because of the way XferFile works, we need to make a copy of - * the data. (For NufxLib, it's going to gather up all of the - * data and flush it all at once, so it needs to own the memory.) - * - * Ideally we'd use a different interface that didn't require a - * data copy -- NufxLib can do it that way as well -- but it's - * not worth maintaining two separate interfaces. - * - * This approach does allow the xfer code to handle DOS high-ASCII - * text conversions in place, though. If we didn't do it this way - * we'd have to make a copy in the xfer code to avoid contaminating - * the clipboard data. That would be more efficient, but probably - * a bit messier. - * - * The stuff below figures out which forks we're expected to have based - * on the file type info. This helps us distinguish between a file - * with a zero-length fork and a file without that kind of fork. - */ - bool hasData = false; - bool hasRsrc = false; - if (entryKind == GenericArchive::LocalFileDetails::kFileKindDataFork) { - hasData = true; - details.SetStorageType(kNuStorageSeedling); - } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindRsrcFork) { - hasRsrc = true; - details.SetStorageType(kNuStorageExtended); - } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindBothForks) { - hasData = hasRsrc = true; - details.SetStorageType(kNuStorageExtended); - } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDiskImage) { - hasData = true; - details.SetStorageType(kNuStorageSeedling); - } else if (entryKind == GenericArchive::LocalFileDetails::kFileKindDirectory) { - details.SetStorageType(kNuStorageDirectory); - } else { - ASSERT(false); - return L"Internal error."; - } - - if (hasData) { - if (pCollEnt->dataLen == 0) { - dataBuf = new uint8_t[1]; - dataLen = 0; - } else { - dataLen = pCollEnt->dataLen; - dataBuf = new uint8_t[dataLen]; - if (dataBuf == NULL) - return L"memory allocation failed."; - memcpy(dataBuf, buf, dataLen); - buf += dataLen; - remLen -= dataLen; - } - } else { - ASSERT(dataBuf == NULL); - dataLen = -1; - } - - if (hasRsrc) { - if (pCollEnt->rsrcLen == 0) { - rsrcBuf = new uint8_t[1]; - rsrcLen = 0; - } else { - rsrcLen = pCollEnt->rsrcLen; - rsrcBuf = new uint8_t[rsrcLen]; - if (rsrcBuf == NULL) - return L"Memory allocation failed."; - memcpy(rsrcBuf, buf, rsrcLen); - buf += rsrcLen; - remLen -= rsrcLen; - } - } else { - ASSERT(rsrcBuf == NULL); - rsrcLen = -1; - } - - if (pCollEnt->cmmtLen > 0) { - cmmtLen = pCollEnt->cmmtLen; - /* CMMT FIX -- not supported by XferFile */ - } - - ASSERT(remLen >= 0); - - errMsg = fpOpenArchive->XferFile(&details, &dataBuf, dataLen, - &rsrcBuf, rsrcLen); - delete[] dataBuf; - delete[] rsrcBuf; - dataBuf = rsrcBuf = NULL; - - return errMsg; -} diff --git a/ciderpress/app/ConfirmOverwriteDialog.cpp b/ciderpress/app/ConfirmOverwriteDialog.cpp deleted file mode 100644 index f557742..0000000 --- a/ciderpress/app/ConfirmOverwriteDialog.cpp +++ /dev/null @@ -1,140 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for ConfirmOverwriteDialog and RenameOverwriteDialog classes. - */ -#include "stdafx.h" -#include "ConfirmOverwriteDialog.h" -#include "GenericArchive.h" -#include - - -/* - * ========================================================================== - * RenameOverwriteDialog - * ========================================================================== - */ - -BEGIN_MESSAGE_MAP(RenameOverwriteDialog, CDialog) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - -BOOL RenameOverwriteDialog::OnInitDialog(void) -{ - CWnd* pWnd; - - pWnd = GetDlgItem(IDC_RENOVWR_SOURCE_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fNewFileSource); - - return CDialog::OnInitDialog(); -} - -void RenameOverwriteDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Text(pDX, IDC_RENOVWR_ORIG_NAME, fExistingFile); - DDX_Text(pDX, IDC_RENOVWR_NEW_NAME, fNewName); - - /* validate the path field */ - if (pDX->m_bSaveAndValidate) { - if (fNewName.IsEmpty()) { - MessageBox(L"You must specify a new name.", - L"CiderPress", MB_OK); - pDX->Fail(); - } - - // we *could* try to validate the path here... - } -} - - -/* - * ========================================================================== - * ConfirmOverwriteDialog - * ========================================================================== - */ - -BEGIN_MESSAGE_MAP(ConfirmOverwriteDialog, CDialog) - ON_BN_CLICKED(IDC_OVWR_YES, OnYes) - ON_BN_CLICKED(IDC_OVWR_YESALL, OnYesToAll) - ON_BN_CLICKED(IDC_OVWR_NO, OnNo) - ON_BN_CLICKED(IDC_OVWR_NOALL, OnNoToAll) - ON_BN_CLICKED(IDC_OVWR_RENAME, OnRename) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - - -BOOL ConfirmOverwriteDialog::OnInitDialog(void) -{ - CWnd* pWnd; - CString tmpStr, dateStr; - - pWnd = GetDlgItem(IDC_OVWR_EXIST_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fExistingFile); - - pWnd = GetDlgItem(IDC_OVWR_EXIST_INFO); - ASSERT(pWnd != NULL); - FormatDate(fExistingFileModWhen, &dateStr); - tmpStr.Format(L"Modified %ls", (LPCWSTR) dateStr); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_OVWR_NEW_NAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fNewFileSource); - - pWnd = GetDlgItem(IDC_OVWR_NEW_INFO); - ASSERT(pWnd != NULL); - FormatDate(fNewFileModWhen, &dateStr); - tmpStr.Format(L"Modified %ls", (LPCWSTR) dateStr); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_OVWR_RENAME); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(fAllowRename); - - return CDialog::OnInitDialog(); -} - -void ConfirmOverwriteDialog::OnYes(void) -{ - fResultOverwrite = true; - CDialog::OnOK(); -} - -void ConfirmOverwriteDialog::OnYesToAll(void) -{ - fResultOverwrite = true; - fResultApplyToAll = true; - CDialog::OnOK(); -} - -void ConfirmOverwriteDialog::OnNo(void) -{ - //fResultOverwrite = false; - CDialog::OnOK(); -} - -void ConfirmOverwriteDialog::OnNoToAll(void) -{ - //fResultOverwrite = true; - fResultApplyToAll = true; - CDialog::OnOK(); -} - -void ConfirmOverwriteDialog::OnRename(void) -{ - RenameOverwriteDialog dlg; - - dlg.fNewFileSource = fNewFileSource; - dlg.fExistingFile = fExistingFile; - dlg.fNewName = fExistingFile; - if (dlg.DoModal() == IDOK) { - fExistingFile = dlg.fNewName; - fResultRename = true; - CDialog::OnOK(); - } -} diff --git a/ciderpress/app/ConfirmOverwriteDialog.h b/ciderpress/app/ConfirmOverwriteDialog.h deleted file mode 100644 index 4c079b0..0000000 --- a/ciderpress/app/ConfirmOverwriteDialog.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Ask for confirmation before overwriting a file. - */ -#ifndef APP_CONFIRMOVERWRITEDIALOG_H -#define APP_CONFIRMOVERWRITEDIALOG_H - -#include "resource.h" - -/* - * Accept or reject overwriting an existing file or archive record. - */ -class ConfirmOverwriteDialog : public CDialog { -public: - ConfirmOverwriteDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_CONFIRM_OVERWRITE, pParentWnd) - { - fResultOverwrite = false; - fResultApplyToAll = false; - fResultRename = false; - fAllowRename = true; - fNewFileModWhen = -1; - fExistingFileModWhen = -1; - } - ~ConfirmOverwriteDialog(void) {} - - // name of file in archive (during extraction) or disk (for add) - CString fNewFileSource; - time_t fNewFileModWhen; - - // full path of file being extracted onto (or record name for add) - CString fExistingFile; - time_t fExistingFileModWhen; - - // result flags: yes/no/yes-all/no-all - bool fResultOverwrite; - bool fResultApplyToAll; - // if this flag is set, try again with updated "fExistingFile" value - bool fResultRename; - // set this to enable the "Rename" button - bool fAllowRename; - -private: - virtual BOOL OnInitDialog(void) override; - - afx_msg void OnYes(void); - afx_msg void OnYesToAll(void); - afx_msg void OnNo(void); - afx_msg void OnNoToAll(void); - afx_msg void OnRename(void); - - // Handle a click on the question-mark button. - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - DECLARE_MESSAGE_MAP() -}; - -/* - * Allow the user to rename a file being added or extracted, rather than - * overwriting an existing file. ConfirmOverwriteDialog creates one of these - * when the "rename" button is clicked on. - * - * The names of the fields here correspond directly to those in - * ConfirmOverwriteDialog. - */ -class RenameOverwriteDialog : public CDialog { -public: - RenameOverwriteDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_RENAME_OVERWRITE, pParentWnd) - {} - ~RenameOverwriteDialog(void) {} - - // name of file on source medium - CString fNewFileSource; - - // converted name, which already exists in destination medium - CString fExistingFile; - - // result: what the user has renamed it to - CString fNewName; - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CONFIRMOVERWRITEDIALOG_H*/ diff --git a/ciderpress/app/ContentList.cpp b/ciderpress/app/ContentList.cpp deleted file mode 100644 index bf901ae..0000000 --- a/ciderpress/app/ContentList.cpp +++ /dev/null @@ -1,967 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of list control showing archive contents. - */ -#include "stdafx.h" -#include "Main.h" -#include "ContentList.h" - -const LPARAM kDescendingFlag = 0x0100; - - -BEGIN_MESSAGE_MAP(ContentList, CListCtrl) - ON_WM_CREATE() - ON_WM_DESTROY() - ON_WM_SYSCOLORCHANGE() - //ON_WM_MOUSEWHEEL() - ON_NOTIFY_REFLECT(NM_DBLCLK, OnDoubleClick) - ON_NOTIFY_REFLECT(NM_RCLICK, OnRightClick) - ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnClick) - ON_NOTIFY_REFLECT(LVN_GETDISPINFO, OnGetDispInfo) -END_MESSAGE_MAP() - -#if 0 -afx_msg BOOL -ContentList::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) -{ - LOGI("MOUSE WHEEL"); - return CWnd::OnMouseWheel(nFlags, zDelta, pt); -// return TRUE; -} -#endif - - -BOOL ContentList::PreCreateWindow(CREATESTRUCT& cs) -{ - if (!CListCtrl::PreCreateWindow(cs)) - return FALSE; - - cs.style &= ~LVS_TYPEMASK; - cs.style |= LVS_REPORT; - cs.style |= LVS_SHOWSELALWAYS; - cs.dwExStyle |= WS_EX_CLIENTEDGE; - - return TRUE; -} - -void ContentList::PostNcDestroy(void) -{ - LOGI("ContentList PostNcDestroy"); - delete this; -} - -static inline int MaxVal(int a, int b) -{ - return a > b ? a : b; -} - -int ContentList::OnCreate(LPCREATESTRUCT lpcs) -{ - CString colHdrs[kNumVisibleColumns] = { - L"Pathname", L"Type", L"Aux", L"Mod Date", - L"Format", L"Size", L"Ratio", L"Packed", L"Access" - }; // these should come from string table, not hard-coded - static int colFmt[kNumVisibleColumns] = { - LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT, LVCFMT_LEFT, - LVCFMT_LEFT, LVCFMT_RIGHT, LVCFMT_RIGHT, LVCFMT_RIGHT, LVCFMT_LEFT - }; - - if (CListCtrl::OnCreate(lpcs) == -1) - return -1; - - /* - * Create all of the columns with an initial width of 1, then set - * them to the correct values with NewColumnWidths() (which handles - * defaulted values). - */ - for (int i = 0; i < kNumVisibleColumns; i++) - InsertColumn(i, colHdrs[i], colFmt[i], 1); - NewColumnWidths(); - - /* add images for list; this MUST be loaded before header images */ - LoadListImages(); - SetImageList(&fListImageList, LVSIL_SMALL); - - /* add our up/down arrow bitmaps */ - LoadHeaderImages(); - CHeaderCtrl* pHeader = GetHeaderCtrl(); - if (pHeader == NULL) - LOGW("GLITCH: couldn't get header ctrl"); - ASSERT(pHeader != NULL); - pHeader->SetImageList(&fHdrImageList); - - /* load the data and sort it */ - if (LoadData() != 0) { - MessageBox(L"Not all entries were loaded.", L"Error", - MB_OK | MB_ICONSTOP); - /* keep going with what we've got; the error only affects display */ - } - NewSortOrder(); - - /* grab the focus so we get keyboard and mouse wheel messages */ - SetFocus(); - - /* highlight/select entire line, not just filename */ - ListView_SetExtendedListViewStyleEx(m_hWnd, - LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); - - return 0; -} - -void ContentList::OnDestroy(void) -{ - LOGD("ContentList OnDestroy"); - - ExportColumnWidths(); - CListCtrl::OnDestroy(); -} - -void ContentList::OnSysColorChange(void) -{ - fHdrImageList.DeleteImageList(); - LoadHeaderImages(); -} - -void ContentList::OnColumnClick(NMHDR* pnmh, LRESULT* pResult) -{ - NM_LISTVIEW* pnmlv = (NM_LISTVIEW*) pnmh; - - LOGD("ContentList OnColumnClick"); - - if (fpLayout->GetSortColumn() == pnmlv->iSubItem) - fpLayout->SetAscending(!fpLayout->GetAscending()); - else { - fpLayout->SetSortColumn(pnmlv->iSubItem); - fpLayout->SetAscending(true); - } - - NewSortOrder(); - *pResult = 0; -} - -void ContentList::ExportColumnWidths(void) -{ - //LOGI("ExportColumnWidths"); - for (int i = 0; i < kNumVisibleColumns; i++) - fpLayout->SetColumnWidth(i, GetColumnWidth(i)); -} - -void ContentList::NewColumnWidths(void) -{ - for (int i = 0; i < kNumVisibleColumns; i++) { - int width = fpLayout->GetColumnWidth(i); - if (width == ColumnLayout::kWidthDefaulted) { - width = GetDefaultWidth(i); - LOGD("Defaulting width %d to %d", i, width); - fpLayout->SetColumnWidth(i, width); - } - SetColumnWidth(i, width); - } -} - -void ContentList::Reload(bool saveSelection) -{ - LOGI("Reloading ContentList"); - CWaitCursor waitc; - -// fInvalid = false; - fpArchive->ClearReloadFlag(); - - long* savedSel = NULL; - long selCount = 0; - - if (saveSelection) { - /* get the serials for the current selection (if any) */ - savedSel = GetSelectionSerials(&selCount); - } - - /* get the item that's currently at the top of the page */ - int top = GetTopIndex(); - int bottom = top + GetCountPerPage() -1; - - /* reload the list */ - LoadData(); - NewSortOrder(); - - if (savedSel != NULL) { - /* restore the selection */ - RestoreSelection(savedSel, selCount); - delete[] savedSel; - } - - /* try to put us back in the same place */ - EnsureVisible(bottom, false); - EnsureVisible(top, false); -} - -long* ContentList::GetSelectionSerials(long* pSelCount) -{ - long* savedSel = NULL; - long maxCount; - - maxCount = GetSelectedCount(); - LOGD("GetSelectionSerials (maxCount=%d)", maxCount); - - if (maxCount > 0) { - savedSel = new long[maxCount]; - int idx = 0; - - POSITION posn; - posn = GetFirstSelectedItemPosition(); - ASSERT(posn != NULL); - if (posn == NULL) - return NULL; - while (posn != NULL) { - int num = GetNextSelectedItem(posn); - GenericEntry* pEntry = (GenericEntry*) GetItemData(num); - - if (idx == maxCount) { - ASSERT(false); - break; - } - savedSel[idx++] = pEntry->GetSelectionSerial(); - } - - ASSERT(idx == maxCount); - } - - *pSelCount = maxCount; - return savedSel; -} - -void ContentList::RestoreSelection(const long* savedSel, long selCount) -{ - LOGI("RestoreSelection (selCount=%d)", selCount); - if (savedSel == NULL) - return; - - int i, j; - - for (i = GetItemCount()-1; i >= 0; i--) { - GenericEntry* pEntry = (GenericEntry*) GetItemData(i); - - for (j = 0; j < selCount; j++) { - if (pEntry->GetSelectionSerial() == savedSel[j] && - pEntry->GetSelectionSerial() != -1) - { - /* match! */ - if (SetItemState(i, LVIS_SELECTED, LVIS_SELECTED) == FALSE) { - LOGW("WHOA: unable to set selected on item=%d", i); - } - break; - } - } - } -} - -void ContentList::NewSortOrder(void) -{ - CWaitCursor wait; // automatically changes mouse to hourglass - int column; - - column = fpLayout->GetSortColumn(); - if (!fpLayout->GetAscending()) - column |= kDescendingFlag; - - SetSortIcon(); - SortItems(CompareFunc, column); -} - -/*static*/ void ContentList::MakeFileTypeDisplayString(const GenericEntry* pEntry, - WCHAR* buf) -{ - bool isDir = - pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir || - pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory; - - if (pEntry->GetSourceFS() == DiskImg::kFormatMacHFS && isDir) { - /* HFS directories don't have types; fake it */ - wcscpy(buf, L"DIR/"); - } else if (!(pEntry->GetFileType() >= 0 && pEntry->GetFileType() <= 0xff)) - { - /* oversized type; assume it's HFS */ - WCHAR typeBuf[kFileTypeBufLen]; - MakeMacTypeString(pEntry->GetFileType(), typeBuf); - - switch (pEntry->GetRecordKind()) { - case GenericEntry::kRecordKindFile: - wcscpy(buf, typeBuf); - break; - case GenericEntry::kRecordKindForkedFile: - wsprintf(buf, L"%ls+", typeBuf); - break; - case GenericEntry::kRecordKindUnknown: - // shouldn't happen - wsprintf(buf, L"%ls-", typeBuf); - break; - case GenericEntry::kRecordKindVolumeDir: - case GenericEntry::kRecordKindDirectory: - case GenericEntry::kRecordKindDisk: - default: - ASSERT(FALSE); - wcscpy(buf, L"!!!"); - break; - } - } else { - /* typical ProDOS-style stuff */ - switch (pEntry->GetRecordKind()) { - case GenericEntry::kRecordKindVolumeDir: - case GenericEntry::kRecordKindDirectory: - wsprintf(buf, L"%ls/", pEntry->GetFileTypeString()); - break; - case GenericEntry::kRecordKindFile: - wsprintf(buf, L"%ls", pEntry->GetFileTypeString()); - break; - case GenericEntry::kRecordKindForkedFile: - wsprintf(buf, L"%ls+", pEntry->GetFileTypeString()); - break; - case GenericEntry::kRecordKindDisk: - wcscpy(buf, L"Disk"); - break; - case GenericEntry::kRecordKindUnknown: - // usually a GSHK-archived empty data file does this - wsprintf(buf, L"%ls-", pEntry->GetFileTypeString()); - break; - default: - ASSERT(FALSE); - wcscpy(buf, L"!!!"); - break; - } - } -} - -/*static*/ void ContentList::MakeMacTypeString(unsigned long val, WCHAR* buf) -{ - /* expand longword with ASCII type bytes */ - buf[0] = (unsigned char) (val >> 24); - buf[1] = (unsigned char) (val >> 16); - buf[2] = (unsigned char) (val >> 8); - buf[3] = (unsigned char) val; - buf[4] = '\0'; - - /* sanitize */ - while (*buf != '\0') { - *buf = DiskImg::MacToASCII((unsigned char)*buf); - buf++; - } -} - -/*static*/ void ContentList::MakeAuxTypeDisplayString(const GenericEntry* pEntry, - WCHAR* buf) -{ - bool isDir = - pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir || - pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory; - - if (pEntry->GetSourceFS() == DiskImg::kFormatMacHFS && isDir) { - /* HFS directories don't have types; fake it */ - wcscpy(buf, L" "); - } else if (!(pEntry->GetFileType() >= 0 && pEntry->GetFileType() <= 0xff)) - { - /* oversized type; assume it's HFS */ - MakeMacTypeString(pEntry->GetAuxType(), buf); - } else { - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) - wsprintf(buf, L"%I64dk", pEntry->GetUncompressedLen() / 1024); - else - wsprintf(buf, L"$%04lX", pEntry->GetAuxType()); - } -} - -void ContentList::MakeRatioDisplayString(const GenericEntry* pEntry, WCHAR* buf, - int* pPerc) -{ - LONGLONG totalLen, totalCompLen; - totalLen = pEntry->GetUncompressedLen(); - totalCompLen = pEntry->GetCompressedLen(); - - if ((!totalLen && totalCompLen) || (totalLen && !totalCompLen)) { - wcscpy(buf, L"---"); /* weird */ - *pPerc = -1; - } else if (totalLen < totalCompLen) { - wcscpy(buf, L">100%"); /* compression failed? */ - *pPerc = 101; - } else { - *pPerc = ComputePercent(totalCompLen, totalLen); - wsprintf(buf, L"%d%%", *pPerc); - } -} - -void ContentList::OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult) -{ - static const WCHAR kAccessBits[] = L"dnb iwr"; - LV_DISPINFO* plvdi = (LV_DISPINFO*) pnmh; - CString str; - - if (fpArchive->GetReloadFlag()) { - wcscpy(plvdi->item.pszText, L""); - *pResult = 0; - return; - } - - //LOGI("OnGetDispInfo"); - - if (plvdi->item.mask & LVIF_TEXT) { - GenericEntry* pEntry = (GenericEntry*) plvdi->item.lParam; - //GenericEntry* pEntry = fpArchive->GetEntry(plvdi->item.iItem); - - switch (plvdi->item.iSubItem) { - case 0: // pathname - if ((int) wcslen(pEntry->GetDisplayName()) > plvdi->item.cchTextMax) { - // looks like current limit is 264 chars, which we could hit - wcsncpy(plvdi->item.pszText, pEntry->GetDisplayName(), - plvdi->item.cchTextMax); - plvdi->item.pszText[plvdi->item.cchTextMax-1] = '\0'; - } else { - wcscpy(plvdi->item.pszText, pEntry->GetDisplayName()); - } - -#if 0 // no longer needed -- "display names" are converted to Unicode - /* - * Sanitize the string. This is really only necessary for - * HFS, which has 8-bit "Macintosh Roman" filenames. The Win32 - * controls can deal with it, but it looks better if we massage - * it a little. - */ - { - WCHAR* str = plvdi->item.pszText; - - while (*str != '\0') { - *str = DiskImg::MacToASCII((unsigned char) (*str)); - str++; - } - } -#endif - break; - case 1: // type - MakeFileTypeDisplayString(pEntry, plvdi->item.pszText); - break; - case 2: // auxtype - MakeAuxTypeDisplayString(pEntry, plvdi->item.pszText); - break; - case 3: // mod date - { - CString modDate; - FormatDate(pEntry->GetModWhen(), &modDate); - ::lstrcpy(plvdi->item.pszText, (LPCWSTR) modDate); - } - break; - case 4: // format - ASSERT(pEntry->GetFormatStr() != NULL); - wcscpy(plvdi->item.pszText, pEntry->GetFormatStr()); - break; - case 5: // size - wsprintf(plvdi->item.pszText, L"%I64d", pEntry->GetUncompressedLen()); - break; - case 6: // ratio - int crud; - MakeRatioDisplayString(pEntry, plvdi->item.pszText, &crud); - break; - case 7: // packed - wsprintf(plvdi->item.pszText, L"%I64d", pEntry->GetCompressedLen()); - break; - case 8: // access - WCHAR bitLabels[sizeof(kAccessBits)]; - int i, j, mask; - - for (i = 0, j = 0, mask = 0x80; i < 8; i++, mask >>= 1) { - if (pEntry->GetAccess() & mask) - bitLabels[j++] = kAccessBits[i]; - } - bitLabels[j] = '\0'; - ASSERT(j < sizeof(bitLabels)); - //::sprintf(plvdi->item.pszText, "0x%02x", pEntry->GetAccess()); - wcscpy(plvdi->item.pszText, bitLabels); - break; - case 9: // NuRecordIdx [hidden] - break; - default: - ASSERT(false); - break; - } - } - - //if (plvdi->item.mask & LVIF_IMAGE) { - // LOGI("IMAGE req item=%d subitem=%d", - // plvdi->item.iItem, plvdi->item.iSubItem); - //} - - *pResult = 0; -} - -/* - * Helper functions for sort routine. - */ -static inline int CompareUnsignedLong(uint32_t u1, uint32_t u2) -{ - if (u1 < u2) - return -1; - else if (u1 > u2) - return 1; - else - return 0; -} -static inline int CompareLONGLONG(LONGLONG u1, LONGLONG u2) -{ - if (u1 < u2) - return -1; - else if (u1 > u2) - return 1; - else - return 0; -} -static inline int CompareTime(time_t t1, time_t t2) -{ - if (t1 < t2) - return -1; - else if (t1 > t2) - return 1; - else - return 0; -} - -int CALLBACK ContentList::CompareFunc(LPARAM lParam1, LPARAM lParam2, - LPARAM lParamSort) -{ - const GenericEntry* pEntry1 = (const GenericEntry*) lParam1; - const GenericEntry* pEntry2 = (const GenericEntry*) lParam2; - WCHAR tmpBuf1[16]; // needs >= 5 for file type compare, and - WCHAR tmpBuf2[16]; // >= 7 for ratio string - int result; - - /* for descending order, flip the parameters */ - if (lParamSort & kDescendingFlag) { - const GenericEntry* tmp; - lParamSort &= ~(kDescendingFlag); - tmp = pEntry1; - pEntry1 = pEntry2; - pEntry2 = tmp; - } - - switch (lParamSort) { - case 0: // pathname - result = wcsicmp(pEntry1->GetDisplayName(), pEntry2->GetDisplayName()); - break; - case 1: // file type - MakeFileTypeDisplayString(pEntry1, tmpBuf1); - MakeFileTypeDisplayString(pEntry2, tmpBuf2); - result = wcsicmp(tmpBuf1, tmpBuf2); - if (result != 0) - break; - /* else fall through to case 2 */ - case 2: // aux type - if (pEntry1->GetRecordKind() == GenericEntry::kRecordKindDisk) { - if (pEntry2->GetRecordKind() == GenericEntry::kRecordKindDisk) { - result = pEntry1->GetAuxType() - pEntry2->GetAuxType(); - } else { - result = -1; - } - } else if (pEntry2->GetRecordKind() == GenericEntry::kRecordKindDisk) { - result = 1; - } else { - result = pEntry1->GetAuxType() - pEntry2->GetAuxType(); - } - break; - case 3: // mod date - result = CompareTime(pEntry1->GetModWhen(), - pEntry2->GetModWhen()); - break; - case 4: // format - result = ::lstrcmp(pEntry1->GetFormatStr(), pEntry2->GetFormatStr()); - break; - case 5: // size - result = CompareLONGLONG(pEntry1->GetUncompressedLen(), - pEntry2->GetUncompressedLen()); - break; - case 6: // ratio - int perc1, perc2; - MakeRatioDisplayString(pEntry1, tmpBuf1, &perc1); - MakeRatioDisplayString(pEntry2, tmpBuf2, &perc2); - result = perc1 - perc2; - break; - case 7: // packed - result = CompareLONGLONG(pEntry1->GetCompressedLen(), - pEntry2->GetCompressedLen()); - break; - case 8: // access - result = CompareUnsignedLong(pEntry1->GetAccess(), - pEntry2->GetAccess()); - break; - case kNumVisibleColumns: // file-order sort - default: - result = pEntry1->GetIndex() - pEntry2->GetIndex(); - break; - } - - return result; -} - -int ContentList::LoadData(void) -{ - GenericEntry* pEntry; - LV_ITEM lvi; - int dirCount = 0; - int idx = 0; - - DeleteAllItems(); // for Reload case - - pEntry = fpArchive->GetEntries(); - while (pEntry != NULL) { - pEntry->SetIndex(idx); - - lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM; - lvi.iItem = idx++; - lvi.iSubItem = 0; - if (pEntry->GetDamaged()) - lvi.iImage = kListIconDamaged; - else if (pEntry->GetSuspicious()) - lvi.iImage = kListIconSuspicious; - else if (pEntry->GetHasNonEmptyComment()) - lvi.iImage = kListIconNonEmptyComment; - else if (pEntry->GetHasComment()) - lvi.iImage = kListIconComment; - else - lvi.iImage = kListIconNone; - lvi.pszText = LPSTR_TEXTCALLBACK; - lvi.lParam = (LPARAM) pEntry; - - if (InsertItem(&lvi) == -1) { - ASSERT(false); - return -1; - } - - pEntry = pEntry->GetNext(); - } - - LOGI("ContentList got %d entries (%d files + %d unseen directories)", - idx, idx - dirCount, dirCount); - return 0; -} - -int ContentList::GetDefaultWidth(int col) -{ - int retval; - - switch (col) { - case 0: // pathname - retval = 200; - break; - case 1: // type (need "$XY" and long HFS types) - retval = MaxVal(GetStringWidth(L"XXMMMM+"), GetStringWidth(L"XXType")); - break; - case 2: // auxtype (hex or long HFS type) - retval = MaxVal(GetStringWidth(L"XX$CCCC"), GetStringWidth(L"XXAux")); - break; - case 3: // mod date - retval = GetStringWidth(L"XX88-MMM-88 88:88"); - break; - case 4: // format - retval = GetStringWidth(L"XXUncompr"); - break; - case 5: // uncompressed size - retval = GetStringWidth(L"XX88888888"); - break; - case 6: // ratio - retval = MaxVal(GetStringWidth(L"XXRatio"), GetStringWidth(L"XX100%")); - break; - case 7: // packed - retval = GetStringWidth(L"XX88888888"); - break; - case 8: // access - retval = MaxVal(GetStringWidth(L"XXAccess"), GetStringWidth(L"XXdnbiwr")); - break; - default: - ASSERT(false); - retval = 0; - } - - return retval; -} - -void ContentList::SetSortIcon(void) -{ - CHeaderCtrl* pHeader = GetHeaderCtrl(); - ASSERT(pHeader != NULL); - HDITEM curItem; - - /* update all column headers */ - for (int i = 0; i < kNumVisibleColumns; i++) { - curItem.mask = HDI_IMAGE | HDI_FORMAT; - pHeader->GetItem(i, &curItem); - - if (fpLayout->GetSortColumn() != i) { - curItem.fmt &= ~(HDF_IMAGE | HDF_BITMAP_ON_RIGHT); - } else { - //LOGI(" Sorting on %d", i); - curItem.fmt |= HDF_IMAGE | HDF_BITMAP_ON_RIGHT; - if (fpLayout->GetAscending()) - curItem.iImage = 0; - else - curItem.iImage = 1; - } - - pHeader->SetItem(i, &curItem); - } -} - -void ContentList::OnDoubleClick(NMHDR*, LRESULT* pResult) -{ - /* test */ - DWORD dwPos = ::GetMessagePos(); - CPoint point ((int) LOWORD(dwPos), (int) HIWORD(dwPos)); - ScreenToClient(&point); - - int idx = HitTest(point); - if (idx != -1) { - CString str = GetItemText(idx, 0); - LOGI("%ls was double-clicked", (LPCWSTR) str); - } - - ((MainWindow*) ::AfxGetMainWnd())->HandleDoubleClick(); - *pResult = 0; -} - -void ContentList::OnRightClick(NMHDR*, LRESULT* pResult) -{ - /* - * -The first item in the menu performs the double-click action on the - * -item clicked on. The rest of the menu is simply a mirror of the items - * -in the "Actions" menu. To make this work, we let the main window handle - * -everything, but save a copy of the index of the menu item that was - * -clicked on. - * - * [We do this differently now?? ++ATM 20040722] - */ - DWORD dwPos = ::GetMessagePos(); - CPoint point ((int) LOWORD(dwPos), (int) HIWORD(dwPos)); - ScreenToClient(&point); - -#if 0 - int idx = HitTest(point); - if (idx != -1) { - CString str = GetItemText(idx, 0); - LOGI("%ls was right-clicked", (LPCWSTR) str); - - //fRightClickItem = idx; -#else - { -#endif - - CMenu menu; - menu.LoadMenu(IDR_RIGHTCLICKMENU); - CMenu* pContextMenu = menu.GetSubMenu(0); - ClientToScreen(&point); - pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, - point.x, point.y, ::AfxGetMainWnd()); - } - *pResult = 0; -} - -void ContentList::SelectAll(void) -{ - int i; - - for (i = GetItemCount()-1; i >= 0; i--) { - if (!SetItemState(i, LVIS_SELECTED, LVIS_SELECTED)) { - LOGI("Glitch: SetItemState failed on %d", i); - } - } -} - -void ContentList::InvertSelection(void) -{ - int i, oldState; - - for (i = GetItemCount()-1; i >= 0; i--) { - oldState = GetItemState(i, LVIS_SELECTED); - if (!SetItemState(i, oldState ? 0 : LVIS_SELECTED, LVIS_SELECTED)) { - LOGI("Glitch: SetItemState failed on %d", i); - } - } -} - -void ContentList::SelectSubdirContents(void) -{ - /* - * We do the selection by prefix matching on the display name. This means - * we do one pass through the list for the contents of a subdir, including - * all of its subdirs. However, the subdirs we select as we're going will - * be indistinguishable from subdirs selected by the user, which could - * result in O(n^2) behavior. - * - * We mark the user's selection with LVIS_CUT, process them all, then go - * back and clear all of the LVIS_CUT flags. Of course, if they select - * the entire archive, we're approach O(n^2) anyway. If efficiency is a - * problem we will need to sort the list, do some work, then sort it back - * the way it was. - * - * This doesn't work for volume directories, because their display name - * isn't quite right. That's okay for now -- we document that we don't - * allow deletion of the volume directory. (We don't currently have a test - * to see if a GenericEntry is a volume dir; might want to add one.) - */ - POSITION posn; - posn = GetFirstSelectedItemPosition(); - if (posn == NULL) { - LOGI("SelectSubdirContents: nothing is selected"); - return; - } - /* mark all selected items with LVIS_CUT */ - while (posn != NULL) { - int num = GetNextSelectedItem(/*ref*/ posn); - SetItemState(num, LVIS_CUT, LVIS_CUT); - } - - /* for each LVIS_CUT entry, select all prefix matches */ - CString prefix; - for (int i = GetItemCount()-1; i >= 0; i--) { - GenericEntry* pEntry = (GenericEntry*) GetItemData(i); - bool origSel; - - origSel = GetItemState(i, LVIS_CUT) != 0; - - if (origSel && - (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory || - pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir)) - { - prefix = pEntry->GetDisplayName(); - prefix += pEntry->GetFssep(); - SelectSubdir(prefix); - } - -// if (!SetItemState(i, oldState ? 0 : LVIS_SELECTED, LVIS_SELECTED)) { -// LOGI("GLITCH: SetItemState failed on %d", i); -// } - } - - /* clear the LVIS_CUT flags */ - posn = GetFirstSelectedItemPosition(); - while (posn != NULL) { - int num = GetNextSelectedItem(/*ref*/ posn); - SetItemState(num, 0, LVIS_CUT); - } -} - -void ContentList::SelectSubdir(const WCHAR* displayPrefix) -{ - LOGI(" ContentList selecting all in '%ls'", displayPrefix); - int len = wcslen(displayPrefix); - - for (int i = GetItemCount()-1; i >= 0; i--) { - GenericEntry* pEntry = (GenericEntry*) GetItemData(i); - - if (wcsnicmp(displayPrefix, pEntry->GetDisplayName(), len) == 0) - SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); - } -} - -void ContentList::ClearSelection(void) -{ - for (int i = GetItemCount()-1; i >= 0; i--) - SetItemState(i, 0, LVIS_SELECTED); -} - -void ContentList::FindNext(const WCHAR* str, bool down, bool matchCase, - bool wholeWord) -{ - POSITION posn; - int i, num; - bool found = false; - - LOGI("FindNext '%ls' d=%d c=%d w=%d", str, down, matchCase, wholeWord); - - posn = GetFirstSelectedItemPosition(); - num = GetNextSelectedItem(/*ref*/ posn); - if (num < 0) { // num will be -1 if nothing is selected - if (down) - num = -1; - else - num = GetItemCount(); - } - - LOGI(" starting search from entry %d", num); - - if (down) { - for (i = num+1; i < GetItemCount(); i++) { - found = CompareFindString(i, str, matchCase, wholeWord); - if (found) - break; - } - if (!found) { // wrap - for (i = 0; i <= num; i++) { - found = CompareFindString(i, str, matchCase, wholeWord); - if (found) - break; - } - } - } else { - for (i = num-1; i >= 0; i--) { - found = CompareFindString(i, str, matchCase, wholeWord); - if (found) - break; - } - if (!found) { // wrap - for (i = GetItemCount()-1; i >= num; i--) { - found = CompareFindString(i, str, matchCase, wholeWord); - if (found) - break; - } - } - } - - if (found) { - LOGI("Found, i=%d", i); - ClearSelection(); - SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); - EnsureVisible(i, false); - } else { - LOGI("Not found"); - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - pMain->FailureBeep(); - } -} - -bool ContentList::CompareFindString(int num, const WCHAR* str, bool matchCase, - bool wholeWord) -{ - GenericEntry* pEntry = (GenericEntry*) GetItemData(num); - char fssep = pEntry->GetFssep(); - const WCHAR* (*pSubCompare)(const WCHAR* str, const WCHAR* subStr) = NULL; - - if (matchCase) - pSubCompare = wcsstr; - else - pSubCompare = Stristr; - - if (wholeWord) { - const WCHAR* src = pEntry->GetDisplayName(); - const WCHAR* start = src; - size_t strLen = wcslen(str); - - /* scan forward, looking for a match that starts & ends on fssep */ - while (*start != '\0') { - const WCHAR* match; - - match = (*pSubCompare)(start, str); - - if (match == NULL) - break; - if ((match == src || *(match-1) == fssep) && - (match[strLen] == '\0' || match[strLen] == fssep)) - { - return true; - } - - start++; - } - } else { - if ((*pSubCompare)(pEntry->GetDisplayName(), str) != NULL) - return true; - } - - return false; -} diff --git a/ciderpress/app/ContentList.h b/ciderpress/app/ContentList.h deleted file mode 100644 index 8787624..0000000 --- a/ciderpress/app/ContentList.h +++ /dev/null @@ -1,283 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Declarations for a list control showing archive contents. - */ -#ifndef APP_CONTENTLIST_H -#define APP_CONTENTLIST_H - -#include "GenericArchive.h" -#include "Preferences.h" -#include "Resource.h" -#include -#include - - -/* - * A ListCtrl with headers appropriate for viewing archive contents. - * - * NOTE: this class performs auto-cleanup, and must be allocated on the heap. - * - * We currently use the underlying GenericArchive as our storage for the stuff - * we display. This works great until we change or delete entries from - * GenericArchive. At that point we run the risk of displaying bad pointers. - * - * The GenericArchive has local copies of everything interesting, so the only - * time things go badly for us is when somebody inside GenericArchive calls - * Reload. That frees and reallocates the storage we're pointing to. So, - * GenericArchive maintains a "I have reloaded" flag that we test before we - * draw. - */ -class ContentList: public CListCtrl -{ -public: - ContentList(GenericArchive* pArchive, ColumnLayout* pLayout) { - ASSERT(pArchive != NULL); - ASSERT(pLayout != NULL); - fpArchive = pArchive; - fpLayout = pLayout; -// fInvalid = false; - //fRightClickItem = -1; - - fpArchive->ClearReloadFlag(); - } - - /* - * The archive contents have changed. Reload the list from the - * GenericArchive. - * - * Reloading causes the current selection and view position to be lost. This - * is sort of annoying if all we did is add a comment, so we try to save the - * selection and reapply it. To do this correctly we need some sort of - * unique identifier so we can spot the records that have come back. - * - * Nothing in GenericArchive should be considered valid at this point. - */ - void Reload(bool saveSelection = false); - - /* - * Call this when the sort order changes. - */ - void NewSortOrder(void); - - /* - * Call this when the column widths are changed programmatically (e.g. by - * the preferences page enabling or disabling columns). - * - * We want to set any defaulted entries to actual values so that, if the - * font properties change, column A doesn't resize when column B is tweaked - * in the Preferences dialog. (If it's still set to "default", then when - * we say "update all widths" the defaultedness will be re-evaluated.) - */ - void NewColumnWidths(void); - - /* - * Copy the current column widths out to the Preferences object. - */ - void ExportColumnWidths(void); - - /* - * Mark everything as selected. - */ - void SelectAll(void); - - /* - * Toggle the "selected" state flag. - */ - void InvertSelection(void); - - /* - * Mark all items as unselected. - */ - void ClearSelection(void); - - /* - * Select the contents of any selected subdirs. - */ - void SelectSubdirContents(void); - - /* - * Find the next matching entry. We start after the first selected item. - * If we find a matching entry, we clear the current selection and select it. - */ - void FindNext(const WCHAR* str, bool down, bool matchCase, bool wholeWord); - - /* - * Compare "str" against the contents of entry "num". - */ - bool CompareFindString(int num, const WCHAR* str, bool matchCase, - bool wholeWord); - - //int GetRightClickItem(void) const { return fRightClickItem; } - //void ClearRightClickItem(void) { fRightClickItem = -1; } - - enum { kFileTypeBufLen = 5, kAuxTypeBufLen = 6 }; - - /* - * Get the file type display string. - * - * "buf" must be able to hold at least 4 characters plus the NUL (i.e. 5). - * Use kFileTypeBufLen. - */ - static void MakeFileTypeDisplayString(const GenericEntry* pEntry, - WCHAR* buf); - - /* - * Get the aux type display string. - * - * "buf" must be able to hold at least 5 characters plus the NUL (i.e. 6). - * Use kAuxTypeBufLen. - */ - static void MakeAuxTypeDisplayString(const GenericEntry* pEntry, - WCHAR* buf); - -protected: - /* - * Puts the window into "report" mode, and add a client edge since we're not - * using one on the frame window. - */ - virtual BOOL PreCreateWindow(CREATESTRUCT& cs) override; - - // Destroy "this". - virtual void PostNcDestroy(void) override; - - /* - * Create and populate list control. - */ - afx_msg int OnCreate(LPCREATESTRUCT); - - /* - * When being shut down, save off the column width info before the window - * gets destroyed. - */ - afx_msg void OnDestroy(void); - - /* - * The system colors are changing; delete the image list and re-load it. - */ - afx_msg void OnSysColorChange(void); - - /* - * They've clicked on a header. Figure out what kind of sort order we want - * to use. - */ - afx_msg void OnColumnClick(NMHDR*, LRESULT*); - - /* - * Return the value for a particular row and column. - * - * This gets called *a lot* while the list is being drawn, scrolled, etc. - * Don't do anything too expensive. - */ - afx_msg void OnGetDispInfo(NMHDR* pnmh, LRESULT* pResult); - -private: - // Load the header images. Must do this every time the syscolors change. - // (Ideally this would re-map all 3dface colors. Note the current - // implementation relies on the top left pixel color.) - void LoadHeaderImages(void) { - if (!fHdrImageList.Create(IDB_HDRBAR, 16, 1, CLR_DEFAULT)) - LOGW("GLITCH: header list create failed"); - fHdrImageList.SetBkColor(::GetSysColor(COLOR_BTNFACE)); - } - void LoadListImages(void) { - if (!fListImageList.Create(IDB_LIST_PICS, 16, 1, CLR_DEFAULT)) - LOGW("GLITCH: list image create failed"); - fListImageList.SetBkColor(::GetSysColor(COLOR_WINDOW)); - } - enum { // defs for IDB_LIST_PICS - kListIconNone = 0, - kListIconComment = 1, - kListIconNonEmptyComment = 2, - kListIconDamaged = 3, - kListIconSuspicious = 4, - }; - - /* - * Fill the columns with data from the archive entries. We use a "virtual" - * list control to avoid storing everything multiple times. However, we - * still create one item per entry so that the list control will do most - * of the sorting for us (otherwise we have to do the sorting ourselves). - * - * Someday we should probably move to a wholly virtual list view. - */ - int LoadData(void); - - /* - * Get the "selection serials" from the list of selected items. - * - * The caller is responsible for delete[]ing the return value. - */ - long* GetSelectionSerials(long* pSelCount); - - /* - * Restore the selection from the "savedSel" list. - */ - void RestoreSelection(const long* savedSel, long selCount); - - /* - * Return the default width for the specified column. - */ - int GetDefaultWidth(int col); - - /* - * Convert an HFS file/creator type into a string. - * - * "buf" must be able to hold at least 4 characters plus the NUL. Use - * kFileTypeBufLen. - */ - static void MakeMacTypeString(unsigned long val, WCHAR* buf); - - /* - * Generate the funky ratio display string. While we're at it, return a - * numeric value that we can sort on. - * - * "buf" must be able to hold at least 6 chars plus the NULL. - */ - static void MakeRatioDisplayString(const GenericEntry* pEntry, WCHAR* buf, - int* pPerc); - - /* - * Set the up/down sorting arrow as appropriate. - */ - void SetSortIcon(void); - - /* - * Static comparison function for list sorting. - */ - static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2, - LPARAM lParamSort); - - /* - * Handle a double-click on an item. - * - * The double-click should single-select the item, so we can throw it - * straight into the viewer. However, there are some uses for bulk - * double-clicking. - */ - void OnDoubleClick(NMHDR* pnmh, LRESULT* pResult); - - /* - * Handle a right-click on an item. - */ - void OnRightClick(NMHDR* pnmh, LRESULT* pResult); - - /* - * Select every entry whose display name has "displayPrefix" as a prefix. - */ - void SelectSubdir(const WCHAR* displayPrefix); - - CImageList fHdrImageList; - CImageList fListImageList; - GenericArchive* fpArchive; // data we're expected to display - ColumnLayout* fpLayout; -// int fRightClickItem; -// bool fInvalid; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CONTENTLIST_H*/ diff --git a/ciderpress/app/ConvDiskOptionsDialog.cpp b/ciderpress/app/ConvDiskOptionsDialog.cpp deleted file mode 100644 index 27cbef2..0000000 --- a/ciderpress/app/ConvDiskOptionsDialog.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "ConvDiskOptionsDialog.h" -#include "NufxArchive.h" -#include "Main.h" -#include "ActionProgressDialog.h" -#include "DiskArchive.h" -#include "NewDiskSize.h" -#include "../diskimg/DiskImgDetail.h" // need ProDOS filename validator - -BEGIN_MESSAGE_MAP(ConvDiskOptionsDialog, CDialog) - ON_WM_HELPINFO() - ON_BN_CLICKED(IDC_CONVDISK_COMPUTE, OnCompute) - ON_BN_CLICKED(IDC_USE_SELECTED, ResetSizeControls) - ON_BN_CLICKED(IDC_USE_ALL, ResetSizeControls) - //ON_BN_CLICKED(IDC_CONVDISK_SPARSE, ResetSizeControls) - ON_CONTROL_RANGE(BN_CLICKED, IDC_CONVDISK_140K, IDC_CONVDISK_SPECIFY, - OnRadioChangeRange) -END_MESSAGE_MAP() - - -// TODO: get this from DiskImgLib header? -const int kProDOSVolNameMax = 15; // longest possible ProDOS volume name - -BOOL ConvDiskOptionsDialog::OnInitDialog(void) -{ - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_VOLNAME); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(kProDOSVolNameMax); - - ResetSizeControls(); - - pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_SPECIFY_EDIT); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(5); // enough for "65535" - pEdit->EnableWindow(FALSE); - - return UseSelectionDialog::OnInitDialog(); -} - -void ConvDiskOptionsDialog::DoDataExchange(CDataExchange* pDX) -{ - UINT specifyBlocks = 280; - CString errMsg; - - DDX_Radio(pDX, IDC_CONVDISK_140K, fDiskSizeIdx); - //DDX_Check(pDX, IDC_CONVDISK_ALLOWLOWER, fAllowLower); - //DDX_Check(pDX, IDC_CONVDISK_SPARSE, fSparseAlloc); - DDX_Text(pDX, IDC_CONVDISK_VOLNAME, fVolName); - DDX_Text(pDX, IDC_CONVDISK_SPECIFY_EDIT, specifyBlocks); - - ASSERT(fDiskSizeIdx >= 0 && fDiskSizeIdx < (int)NewDiskSize::GetNumSizeEntries()); - - if (pDX->m_bSaveAndValidate) { - - fNumBlocks = NewDiskSize::GetDiskSizeByIndex(fDiskSizeIdx); - if (fNumBlocks == NewDiskSize::kSpecified) { - fNumBlocks = specifyBlocks; - - // Max is really 65535, but we allow 65536 for creation of volumes - // that can be copied to CFFA cards. - if (specifyBlocks < 16 || specifyBlocks > 65536) - errMsg = "Specify a size of at least 16 blocks and no more" - " than 65536 blocks."; - } - - - if (fVolName.IsEmpty() || fVolName.GetLength() > kProDOSVolNameMax) { - errMsg = "You must specify a volume name 1-15 characters long."; - } else { - if (!IsValidVolumeName_ProDOS(fVolName)) { - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_PRODOS); - } - } - } - - if (!errMsg.IsEmpty()) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - MessageBox(errMsg, appName, MB_OK); - pDX->Fail(); - } - - UseSelectionDialog::DoDataExchange(pDX); -} - -void ConvDiskOptionsDialog::OnRadioChangeRange(UINT nID) -{ - LOGI("OnChangeRange id=%d", nID); - - CButton* pButton = (CButton*) GetDlgItem(IDC_CONVDISK_SPECIFY); - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_SPECIFY_EDIT); - pEdit->EnableWindow(pButton->GetCheck() == BST_CHECKED); - - NewDiskSize::UpdateSpecifyEdit(this); -} - -bool ConvDiskOptionsDialog::IsValidVolumeName_ProDOS(const WCHAR* name) -{ - CStringA nameA(name); - return DiskImgLib::DiskFSProDOS::IsValidVolumeName(nameA); -} - -void ConvDiskOptionsDialog::ResetSizeControls(void) -{ - CWnd* pWnd; - CString spaceReq; - - LOGI("Resetting size controls"); - spaceReq.Format(IDS_CONVDISK_SPACEREQ, L"(unknown)"); - pWnd = GetDlgItem(IDC_CONVDISK_SPACEREQ); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(spaceReq); - -#if 0 - int i; - for (i = 0; i < NELEM(gDiskSizes); i++) { - pWnd = GetDlgItem(gDiskSizes[i].ctrlID); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(TRUE); - } -#endif - NewDiskSize::EnableButtons(this); -} - -void ConvDiskOptionsDialog::LimitSizeControls(long totalBlocks, long blocksUsed) -{ - LOGD("LimitSizeControls %ld %ld", totalBlocks, blocksUsed); - LOGD("Full volume requires %ld bitmap blocks", - NewDiskSize::GetNumBitmapBlocks_ProDOS(totalBlocks)); - - CWnd* pWnd; - long usedWithoutBitmap = - blocksUsed - NewDiskSize::GetNumBitmapBlocks_ProDOS(totalBlocks); - long sizeInK = usedWithoutBitmap / 2; - CString sizeStr, spaceReq; - sizeStr.Format(L"%dK", sizeInK); - spaceReq.Format(IDS_CONVDISK_SPACEREQ, sizeStr); - - pWnd = GetDlgItem(IDC_CONVDISK_SPACEREQ); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(spaceReq); - - NewDiskSize::EnableButtons_ProDOS(this, totalBlocks, blocksUsed); - -#if 0 - bool first = true; - for (int i = 0; i < NELEM(gDiskSizes); i++) { - if (gDiskSizes[i].blocks == -1) - continue; - - CButton* pButton; - pButton = (CButton*) GetDlgItem(gDiskSizes[i].ctrlID); - ASSERT(pButton != NULL); - if (usedWithoutBitmap + GetNumBitmapBlocks(gDiskSizes[i].blocks) <= - gDiskSizes[i].blocks) - { - pButton->EnableWindow(TRUE); - if (first) { - pButton->SetCheck(BST_CHECKED); - first = false; - } else { - pButton->SetCheck(BST_UNCHECKED); - } - } else { - pButton->EnableWindow(FALSE); - pButton->SetCheck(BST_UNCHECKED); - } - } -#endif -} - -void ConvDiskOptionsDialog::OnCompute(void) -{ - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - const Preferences* pPreferences = GET_PREFERENCES(); - - if (UpdateData() == FALSE) - return; - - /* - * Create a "selection set" of data forks, resource forks, and - * disk images. We don't want comment threads. We can filter all that - * out later, though, so we just specify "any". - */ - SelectionSet selSet; - int threadMask = GenericEntry::kAnyThread; - - if (fFilesToAction == UseSelectionDialog::kActionSelection) { - selSet.CreateFromSelection(pMain->GetContentList(), threadMask); - } else { - selSet.CreateFromAll(pMain->GetContentList(), threadMask); - } - - if (selSet.GetNumEntries() == 0) { - /* should be impossible */ - MessageBox(L"No files matched the selection criteria.", - L"No match", MB_OK|MB_ICONEXCLAMATION); - return; - } - - XferFileOptions xferOpts; - //xferOpts.fAllowLowerCase = - // pPreferences->GetPrefBool(kPrProDOSAllowLower) != 0; - //xferOpts.fUseSparseBlocks = - // pPreferences->GetPrefBool(kPrProDOSUseSparse) != 0; - - LOGI("New volume name will be '%ls'", (LPCWSTR) fVolName); - - /* - * Create a new disk image file. - */ - CString errStr; - WCHAR nameBuf[MAX_PATH]; - UINT unique; - unique = GetTempFileName(pMain->GetPreferences()->GetPrefString(kPrTempPath), - L"CPdisk", 0, nameBuf); - if (unique == 0) { - DWORD dwerr = ::GetLastError(); - errStr.Format(L"GetTempFileName failed on '%ls' (err=0x%08lx)\n", - pMain->GetPreferences()->GetPrefString(kPrTempPath), dwerr); - ShowFailureMsg(this, errStr, IDS_FAILED); - return; - } - LOGI(" Will xfer to file '%ls'", nameBuf); - // annoying -- DiskArchive insists on creating it - (void) _wunlink(nameBuf); - - DiskArchive::NewOptions options; - memset(&options, 0, sizeof(options)); - options.base.format = DiskImg::kFormatProDOS; - options.base.sectorOrder = DiskImg::kSectorOrderProDOS; - options.prodos.volName = fVolName; - options.prodos.numBlocks = 65535; - - xferOpts.fTarget = new DiskArchive; - - { - CWaitCursor waitc; - errStr = xferOpts.fTarget->New(nameBuf, &options); - } - if (!errStr.IsEmpty()) { - ShowFailureMsg(this, errStr, IDS_FAILED); - } else { - /* - * Set up the progress window as a modal dialog. - * - * TODO: there's a weird issue where this un-modals the conversion - * options dialog. While this is running, and after it finishes, - * you can use menu items and perform other actions. Noted on Win7. - */ - GenericArchive::XferStatus result; - - ActionProgressDialog* pActionProgress = new ActionProgressDialog; - pMain->SetActionProgressDialog(pActionProgress); - pActionProgress->Create(ActionProgressDialog::kActionConvFile, this); - pMain->PeekAndPump(); - result = pMain->GetOpenArchive()->XferSelection(pActionProgress, &selSet, - pActionProgress, &xferOpts); - pActionProgress->Cleanup(this); - pMain->SetActionProgressDialog(NULL); - - if (result == GenericArchive::kXferOK) { - DiskFS* pDiskFS; - long totalBlocks, freeBlocks; - int unitSize; - DIError dierr; - - LOGI("SUCCESS"); - - pDiskFS = ((DiskArchive*) xferOpts.fTarget)->GetDiskFS(); - ASSERT(pDiskFS != NULL); - - dierr = pDiskFS->GetFreeSpaceCount(&totalBlocks, &freeBlocks, - &unitSize); - if (dierr != kDIErrNone) { - errStr.Format(L"Unable to get free space count: %hs.\n", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errStr, IDS_FAILED); - } else { - ASSERT(totalBlocks >= freeBlocks); - ASSERT(unitSize == DiskImgLib::kBlockSize); - LimitSizeControls(totalBlocks, totalBlocks - freeBlocks); - } - } else if (result == GenericArchive::kXferCancelled) { - LOGI("CANCEL - cancel button hit"); - ResetSizeControls(); - } else { - LOGI("FAILURE (result=%d)", result); - ResetSizeControls(); - } - } - - // debug - ((DiskArchive*) (xferOpts.fTarget))->GetDiskFS()->DumpFileList(); - - /* clean up */ - delete xferOpts.fTarget; - (void) _wunlink(nameBuf); -} diff --git a/ciderpress/app/ConvDiskOptionsDialog.h b/ciderpress/app/ConvDiskOptionsDialog.h deleted file mode 100644 index 5c3227d..0000000 --- a/ciderpress/app/ConvDiskOptionsDialog.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Options for converting a disk image to a file archive. - */ -#ifndef APP_CONVDISKOPTIONSDIALOG_H -#define APP_CONVDISKOPTIONSDIALOG_H - -#include "UseSelectionDialog.h" -#include "resource.h" - -/* - * Get some options. - */ -class ConvDiskOptionsDialog : public UseSelectionDialog { -public: - ConvDiskOptionsDialog(int selCount, CWnd* pParentWnd = NULL) : - UseSelectionDialog(selCount, pParentWnd, IDD_CONVDISK_OPTS) - { - fDiskSizeIdx = 0; - //fAllowLower = fSparseAlloc = FALSE; - fVolName = L"NEW.DISK"; - fNumBlocks = -1; - } - virtual ~ConvDiskOptionsDialog(void) {} - - int fDiskSizeIdx; - //BOOL fAllowLower; - //BOOL fSparseAlloc; - CString fVolName; - - long fNumBlocks; // computed when DoModal finishes - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * Enable all size radio buttons and reset the "size required" display. - * - * This should be invoked whenever the convert selection changes, and may be - * called at any time. - */ - afx_msg void ResetSizeControls(void); - - /* - * Compute the amount of space required for the files. We use the result to - * disable the controls that can't be used. - * - * We don't need to enable controls here, because the only way to change the - * set of files is by flipping between "all" and "selected", and we can handle - * that separately. - */ - afx_msg void OnCompute(void); - - /* - * When one of the radio buttons is clicked on, update the active status - * and contents of the "specify size" edit box. - */ - afx_msg void OnRadioChangeRange(UINT nID); - - /* - * Display the space requirements and disable radio button controls that are - * for values that are too small. - * - * Pass in the number of blocks required on a 32MB ProDOS volume. - */ - void LimitSizeControls(long totalBlocks, long blocksUsed); - - /* - * Test a ProDOS filename for validity. - */ - bool IsValidVolumeName_ProDOS(const WCHAR* name); - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CONVDISKOPTIONSDIALOG_H*/ diff --git a/ciderpress/app/ConvFileOptionsDialog.cpp b/ciderpress/app/ConvFileOptionsDialog.cpp deleted file mode 100644 index c6452e8..0000000 --- a/ciderpress/app/ConvFileOptionsDialog.cpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "ConvFileOptionsDialog.h" - - -void ConvFileOptionsDialog::DoDataExchange(CDataExchange* pDX) -{ - //DDX_Check(pDX, IDC_CONVFILE_CONVDOS, fConvDOSText); - //DDX_Check(pDX, IDC_CONVFILE_CONVPASCAL, fConvPascalText); - DDX_Check(pDX, IDC_CONVFILE_PRESERVEDIR, fPreserveEmptyFolders); - - UseSelectionDialog::DoDataExchange(pDX); -} diff --git a/ciderpress/app/ConvFileOptionsDialog.h b/ciderpress/app/ConvFileOptionsDialog.h deleted file mode 100644 index ffb6387..0000000 --- a/ciderpress/app/ConvFileOptionsDialog.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Options for converting a disk image to a file archive. - */ -#ifndef APP_CONFFILEOPTIONSDIALOG_H -#define APP_CONFFILEOPTIONSDIALOG_H - -#include "UseSelectionDialog.h" -#include "resource.h" - -/* - * Get some options. - */ -class ConvFileOptionsDialog : public UseSelectionDialog { -public: - ConvFileOptionsDialog(int selCount, CWnd* pParentWnd = NULL) : - UseSelectionDialog(selCount, pParentWnd, IDD_CONVFILE_OPTS) - { - fPreserveEmptyFolders = FALSE; - } - virtual ~ConvFileOptionsDialog(void) {} - - //BOOL fConvDOSText; - //BOOL fConvPascalText; - BOOL fPreserveEmptyFolders; - -private: - virtual void DoDataExchange(CDataExchange* pDX) override; - - //DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CONFFILEOPTIONSDIALOG_H*/ diff --git a/ciderpress/app/CreateImageDialog.cpp b/ciderpress/app/CreateImageDialog.cpp deleted file mode 100644 index 17c34e9..0000000 --- a/ciderpress/app/CreateImageDialog.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for ConvDiskOptionsDialog. - */ -#include "stdafx.h" -#include "CreateImageDialog.h" -#include "NewDiskSize.h" -#include "../diskimg/DiskImgDetail.h" // need ProDOS filename validator - -BEGIN_MESSAGE_MAP(CreateImageDialog, CDialog) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) - ON_CONTROL_RANGE(BN_CLICKED, IDC_CREATEFS_DOS32, IDC_CREATEFS_BLANK, - OnFormatChangeRange) - ON_CONTROL_RANGE(BN_CLICKED, IDC_CONVDISK_140K, IDC_CONVDISK_SPECIFY, - OnSizeChangeRange) -END_MESSAGE_MAP() - - -// TODO: obtain from DiskImgLib header? -const int kProDOSVolNameMax = 15; // longest possible ProDOS volume name -const int kPascalVolNameMax = 7; // longest possible Pascal volume name -const int kHFSVolNameMax = 27; // longest possible HFS volume name -const long kMaxBlankBlocks = 16777216; // 8GB in 512-byte blocks - -BOOL CreateImageDialog::OnInitDialog(void) -{ - // high bit set in signed short means key is down - if (::GetKeyState(VK_SHIFT) < 0) { - LOGI("Shift key is down, enabling extended options"); - fExtendedOpts = true; - } - - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSPRODOS_VOLNAME); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(kProDOSVolNameMax); - - pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSPASCAL_VOLNAME); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(kPascalVolNameMax); - - pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSHFS_VOLNAME); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(kHFSVolNameMax); - - pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSDOS_VOLNUM); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(3); // 3 digit volume number - - pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_SPECIFY_EDIT); - ASSERT(pEdit != NULL); - pEdit->EnableWindow(FALSE); - - return CDialog::OnInitDialog(); -} - -void CreateImageDialog::DoDataExchange(CDataExchange* pDX) -{ - UINT specifyBlocks = 280; - CString errMsg; - - DDX_Radio(pDX, IDC_CONVDISK_140K, fDiskSizeIdx); - DDX_Radio(pDX, IDC_CREATEFS_DOS32, fDiskFormatIdx); - DDX_Check(pDX, IDC_CREATEFSDOS_ALLOCDOS, fAllocTracks_DOS); - DDX_Text(pDX, IDC_CREATEFSDOS_VOLNUM, fDOSVolumeNum); - DDX_Text(pDX, IDC_CREATEFSPRODOS_VOLNAME, fVolName_ProDOS); - DDX_Text(pDX, IDC_CREATEFSPASCAL_VOLNAME, fVolName_Pascal); - DDX_Text(pDX, IDC_CREATEFSHFS_VOLNAME, fVolName_HFS); - DDX_Text(pDX, IDC_CONVDISK_SPECIFY_EDIT, specifyBlocks); - - ASSERT(fDiskSizeIdx >= 0 && fDiskSizeIdx < (int)NewDiskSize::GetNumSizeEntries()); - - if (pDX->m_bSaveAndValidate) { - fNumBlocks = NewDiskSize::GetDiskSizeByIndex(fDiskSizeIdx); - if (fNumBlocks == NewDiskSize::kSpecified) - fNumBlocks = specifyBlocks; - - if (fDiskFormatIdx == kFmtDOS32) { - CString tmpStr; - tmpStr.Format(L"%d", fDOSVolumeNum); - if (!IsValidVolumeName_DOS(tmpStr)) { - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_DOS); - } - } else if (fDiskFormatIdx == kFmtDOS33) { - CString tmpStr; - tmpStr.Format(L"%d", fDOSVolumeNum); - if (!IsValidVolumeName_DOS(tmpStr)) { - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_DOS); - } - - // only needed in "extended" mode -- this stuff is too painful to - // inflict on the average user - if (fNumBlocks < 18*8 || fNumBlocks > 800 || - (fNumBlocks <= 400 && (fNumBlocks % 8) != 0) || - (fNumBlocks > 400 && (fNumBlocks % 16) != 0)) - { - errMsg = L"Specify a size between 144 blocks (18 tracks) and" - L" 800 blocks (50 tracks/32 sectors). The block count" - L" must be a multiple of 8 for 16-sector disks, or a" - L" multiple of 16 for 32-sector disks. 32 sector" - L" formatting starts at 400 blocks. Disks larger than" - L" 400 blocks but less than 800 aren't recognized by" - L" CiderPress."; - } - } else if (fDiskFormatIdx == kFmtProDOS) { - // Max is really 65535, but we allow 65536 for creation of volumes - // that can be copied to CFFA cards. - if (fNumBlocks < 16 || fNumBlocks > 65536) { - errMsg = L"Specify a size of at least 16 blocks and no more" - L" than 65536 blocks."; - } else if (fVolName_ProDOS.IsEmpty() || - fVolName_ProDOS.GetLength() > kProDOSVolNameMax) - { - errMsg = L"You must specify a volume name 1-15 characters long."; - } else { - if (!IsValidVolumeName_ProDOS(fVolName_ProDOS)) { - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_PRODOS); - } - } - } else if (fDiskFormatIdx == kFmtPascal) { - if (fVolName_Pascal.IsEmpty() || - fVolName_Pascal.GetLength() > kPascalVolNameMax) - { - errMsg = L"You must specify a volume name 1-7 characters long."; - } else { - if (!IsValidVolumeName_Pascal(fVolName_Pascal)) { - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_PASCAL); - } - } - } else if (fDiskFormatIdx == kFmtHFS) { - if (fNumBlocks < 1600 || fNumBlocks > 4194303) { - errMsg = L"Specify a size of at least 1600 blocks and no more" - L" than 4194303 blocks."; - } else if (fVolName_HFS.IsEmpty() || - fVolName_HFS.GetLength() > kHFSVolNameMax) - { - errMsg = L"You must specify a volume name 1-27 characters long."; - } else { - if (!IsValidVolumeName_HFS(fVolName_HFS)) { - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_HFS); - } - } - } else if (fDiskFormatIdx == kFmtBlank) { - if (fNumBlocks < 1 || fNumBlocks > kMaxBlankBlocks) - errMsg = L"Specify a size of at least 1 block and no more" - L" than 16777216 blocks."; - } else { - ASSERT(false); - } - } else { - OnFormatChangeRange(IDC_CREATEFS_DOS32 + fDiskFormatIdx); - } - - if (!errMsg.IsEmpty()) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - MessageBox(errMsg, appName, MB_OK); - pDX->Fail(); - } - - CDialog::DoDataExchange(pDX); -} - -void CreateImageDialog::OnFormatChangeRange(UINT nID) -{ - static const struct { - UINT buttonID; - UINT ctrlID; - } kFormatTab[] = { - { IDC_CREATEFS_DOS32, IDC_CREATEFSDOS_ALLOCDOS }, - { IDC_CREATEFS_DOS32, IDC_CREATEFSDOS_VOLNUM }, - { IDC_CREATEFS_DOS32, IDC_CONVDISK_140K }, - { IDC_CREATEFS_DOS33, IDC_CREATEFSDOS_ALLOCDOS }, - { IDC_CREATEFS_DOS33, IDC_CREATEFSDOS_VOLNUM }, - { IDC_CREATEFS_DOS33, IDC_CONVDISK_140K }, - { IDC_CREATEFS_PRODOS, IDC_CREATEFSPRODOS_VOLNAME }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_140K }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_800K }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_1440K }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_5MB }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_16MB }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_20MB }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_32MB }, - { IDC_CREATEFS_PRODOS, IDC_CONVDISK_SPECIFY }, - { IDC_CREATEFS_PASCAL, IDC_CREATEFSPASCAL_VOLNAME }, - { IDC_CREATEFS_PASCAL, IDC_CONVDISK_140K }, - { IDC_CREATEFS_PASCAL, IDC_CONVDISK_800K }, - { IDC_CREATEFS_HFS, IDC_CREATEFSHFS_VOLNAME }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_800K }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_1440K }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_5MB }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_16MB }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_20MB }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_32MB }, - { IDC_CREATEFS_HFS, IDC_CONVDISK_SPECIFY }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_140K }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_800K }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_1440K }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_5MB }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_16MB }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_20MB }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_32MB }, - { IDC_CREATEFS_BLANK, IDC_CONVDISK_SPECIFY }, - }; - static const UINT kDetailControls[] = { - IDC_CREATEFSDOS_ALLOCDOS, - IDC_CREATEFSDOS_VOLNUM, - IDC_CREATEFSPRODOS_VOLNAME, - IDC_CREATEFSPASCAL_VOLNAME, - IDC_CREATEFSHFS_VOLNAME - }; - int i; - - LOGI("OnFormatChangeRange id=%d", nID); - - /* reset so 140K is highlighted */ - NewDiskSize::EnableButtons_ProDOS(this, 32, 16); - - /* disable all buttons */ - NewDiskSize::EnableButtons(this, FALSE); - - for (i = 0; i < NELEM(kDetailControls); i++) { - CWnd* pWnd = GetDlgItem(kDetailControls[i]); - if (pWnd != NULL) - pWnd->EnableWindow(FALSE); - } - - /* re-enable just the ones we like */ - for (i = 0; i < NELEM(kFormatTab); i++) { - if (kFormatTab[i].buttonID == nID) { - CWnd* pWnd = GetDlgItem(kFormatTab[i].ctrlID); - ASSERT(pWnd != NULL); - if (pWnd != NULL) - pWnd->EnableWindow(TRUE); - } - } - if (fExtendedOpts && nID != IDC_CREATEFS_DOS32) { - CWnd* pWnd = GetDlgItem(IDC_CONVDISK_SPECIFY); - pWnd->EnableWindow(TRUE); - } - - /* make sure 140K is viable; doesn't work for HFS */ - CButton* pButton; - pButton = (CButton*) GetDlgItem(IDC_CONVDISK_140K); - if (!pButton->IsWindowEnabled()) { - pButton->SetCheck(BST_UNCHECKED); - pButton = (CButton*) GetDlgItem(IDC_CONVDISK_800K); - pButton->SetCheck(BST_CHECKED); - } -} - -void CreateImageDialog::OnSizeChangeRange(UINT nID) -{ - LOGI("OnSizeChangeRange id=%d", nID); - - CButton* pButton = (CButton*) GetDlgItem(IDC_CONVDISK_SPECIFY); - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_SPECIFY_EDIT); - pEdit->EnableWindow(pButton->GetCheck() == BST_CHECKED); - - CButton* pBlank; - CButton* pHFS; - pBlank = (CButton*) GetDlgItem(IDC_CREATEFS_BLANK); - pHFS = (CButton*) GetDlgItem(IDC_CREATEFS_HFS); - if (pHFS->GetCheck() == BST_CHECKED) - pEdit->SetLimitText(10); // enough for "2147483647" - else if (pBlank->GetCheck() == BST_CHECKED) - pEdit->SetLimitText(8); // enough for "16777216" - else - pEdit->SetLimitText(5); // enough for "65535" - - NewDiskSize::UpdateSpecifyEdit(this); -} - - -bool CreateImageDialog::IsValidVolumeName_DOS(const WCHAR* name) -{ - CStringA nameStr(name); - return DiskImgLib::DiskFSDOS33::IsValidVolumeName(nameStr); -} - -bool CreateImageDialog::IsValidVolumeName_ProDOS(const WCHAR* name) -{ - CStringA nameStr(name); - return DiskImgLib::DiskFSProDOS::IsValidVolumeName(nameStr); -} - -bool CreateImageDialog::IsValidVolumeName_Pascal(const WCHAR* name) -{ - CStringA nameStr(name); - return DiskImgLib::DiskFSPascal::IsValidVolumeName(nameStr); -} - -bool CreateImageDialog::IsValidVolumeName_HFS(const WCHAR* name) -{ - CStringA nameStr(name); - return DiskImgLib::DiskFSHFS::IsValidVolumeName(nameStr); -} diff --git a/ciderpress/app/CreateImageDialog.h b/ciderpress/app/CreateImageDialog.h deleted file mode 100644 index c65c012..0000000 --- a/ciderpress/app/CreateImageDialog.h +++ /dev/null @@ -1,92 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Options for creating a blank disk image. - */ -#ifndef APP_CREATEIMAGEDIALOG_H -#define APP_CREATEIMAGEDIALOG_H - -#include "resource.h" - -/* - * Get some options. - */ -class CreateImageDialog : public CDialog { -public: - /* this must match up with control IDs in dialog */ - enum { - kFmtDOS32 = 0, - kFmtDOS33, - kFmtProDOS, - kFmtPascal, - kFmtHFS, - kFmtBlank - }; - - CreateImageDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_CREATEIMAGE, pParentWnd) - { - fDiskSizeIdx = 0; - fDiskFormatIdx = kFmtProDOS; - fAllocTracks_DOS = TRUE; - fDOSVolumeNum = 254; - fVolName_ProDOS = L"NEW.DISK"; - fVolName_Pascal = L"BLANK"; - fVolName_HFS = L"New Disk"; - fNumBlocks = -2; // -1 has special meaning - fExtendedOpts = false; - } - virtual ~CreateImageDialog(void) {} - - int fDiskSizeIdx; - int fDiskFormatIdx; - BOOL fAllocTracks_DOS; - int fDOSVolumeNum; - CString fVolName_ProDOS; - CString fVolName_Pascal; - CString fVolName_HFS; - - long fNumBlocks; // computed when DoModal finishes - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - -// afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo); - - /* - * When the user chooses a format, enable and disable controls as - * appropriate. - */ - afx_msg void OnFormatChangeRange(UINT nID); - - /* - * When one of the radio buttons is clicked on, update the active status - * and contents of the "specify size" edit box. - */ - afx_msg void OnSizeChangeRange(UINT nID); - - // Context help (question mark). - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - // Dialog help ("help" button). - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_IMAGE_CREATOR); - } - - bool IsValidVolumeName_DOS(const WCHAR* name); - bool IsValidVolumeName_ProDOS(const WCHAR* name); - bool IsValidVolumeName_Pascal(const WCHAR* name); - bool IsValidVolumeName_HFS(const WCHAR* name); - - bool fExtendedOpts; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CREATEIMAGEDIALOG_H*/ diff --git a/ciderpress/app/CreateSubdirDialog.cpp b/ciderpress/app/CreateSubdirDialog.cpp deleted file mode 100644 index 4e1ba39..0000000 --- a/ciderpress/app/CreateSubdirDialog.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of CreateSubdirDialog. - * - * Gets the name from the user, validates it against the supplied - * GenericArchive, and returns. - */ -#include "stdafx.h" -#include "CreateSubdirDialog.h" - -BEGIN_MESSAGE_MAP(CreateSubdirDialog, CDialog) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - -BOOL CreateSubdirDialog::OnInitDialog(void) -{ - /* do the DoDataExchange stuff */ - CDialog::OnInitDialog(); - - /* select the default text and set the focus */ - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CREATESUBDIR_NEW); - ASSERT(pEdit != NULL); - pEdit->SetSel(0, -1); - pEdit->SetFocus(); - - return FALSE; // we set the focus -} - -void CreateSubdirDialog::DoDataExchange(CDataExchange* pDX) -{ - CString msg, failed; - - CheckedLoadString(&failed, IDS_MB_APP_NAME); - - /* put fNewName last so it gets the focus after failure */ - DDX_Text(pDX, IDC_CREATESUBDIR_BASE, fBasePath); - DDX_Text(pDX, IDC_CREATESUBDIR_NEW, fNewName); - - /* validate the path field */ - if (pDX->m_bSaveAndValidate) { - if (fNewName.IsEmpty()) { - msg = L"You must specify a new name."; - goto fail; - } - - msg = fpArchive->TestPathName(fpParentEntry, fBasePath, fNewName, - '\0'); - if (!msg.IsEmpty()) - goto fail; - } - - return; - -fail: - ASSERT(!msg.IsEmpty()); - MessageBox(msg, failed, MB_OK); - pDX->Fail(); - return; -} diff --git a/ciderpress/app/CreateSubdirDialog.h b/ciderpress/app/CreateSubdirDialog.h deleted file mode 100644 index 64c60bd..0000000 --- a/ciderpress/app/CreateSubdirDialog.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Create a subdirectory (e.g. on a ProDOS disk image). - */ -#ifndef APP_CREATESUBDIRDIALOG_H -#define APP_CREATESUBDIRDIALOG_H - -#include "GenericArchive.h" -#include "resource.h" - -/* - * Get the name of the subdirectory to create. - */ -class CreateSubdirDialog : public CDialog { -public: - CreateSubdirDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_CREATE_SUBDIR, pParentWnd) - { - fpArchive = NULL; - fpParentEntry = NULL; - } - virtual ~CreateSubdirDialog(void) {} - - CString fBasePath; // where subdir will be created - CString fNewName; - const GenericArchive* fpArchive; - const GenericEntry* fpParentEntry; - -protected: - // overrides - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - // Context help request (question mark button). - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - -private: - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_CREATESUBDIRDIALOG_H*/ diff --git a/ciderpress/app/DEFileDialog.cpp b/ciderpress/app/DEFileDialog.cpp deleted file mode 100644 index 4b42bf6..0000000 --- a/ciderpress/app/DEFileDialog.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of Disk Editor "open file" dialog. - */ -#include "stdafx.h" -#include "DEFileDialog.h" - - -BEGIN_MESSAGE_MAP(DEFileDialog, CDialog) - ON_EN_CHANGE(IDC_DEFILE_FILENAME, OnChange) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - -BOOL DEFileDialog::OnInitDialog(void) -{ - CWnd* pWnd = GetDlgItem(IDOK); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(FALSE); - - return CDialog::OnInitDialog(); -} - -void DEFileDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Text(pDX, IDC_DEFILE_FILENAME, fName); - DDX_Check(pDX, IDC_DEFILE_RSRC, fOpenRsrcFork); -} - -void DEFileDialog::OnChange(void) -{ - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_DEFILE_FILENAME); - ASSERT(pEdit != NULL); - - CString str; - pEdit->GetWindowText(str); - //LOGI("STR is '%ls' (%d)", str, str.GetLength()); - - CWnd* pWnd = GetDlgItem(IDOK); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(!str.IsEmpty()); -} diff --git a/ciderpress/app/DEFileDialog.h b/ciderpress/app/DEFileDialog.h deleted file mode 100644 index f307bb5..0000000 --- a/ciderpress/app/DEFileDialog.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Disk Edit "open file" dialog. - * - * If the dialog returns with IDOK, "fName" will be a string at least 1 - * character long. - * - * Currently this is a trivial edit box that asks for a name. In the future - * this might present a list or tree view to choose from. - * - * NOTE: we probably want to have a read-only flag here, defaulted to the - * state of the sector editor. The read-only state of the underlying FS - * doesn't matter, since we're writing sectors, not really editing files. - */ -#ifndef APP_DEFILEDIALOG_H -#define APP_DEFILEDIALOG_H - -#include "resource.h" -#include "../diskimg/DiskImg.h" - -using namespace DiskImgLib; - - -/* - * Class declaration. Nothing special. - */ -class DEFileDialog : public CDialog { -public: - DEFileDialog(CWnd* pParentWnd = NULL) : CDialog(IDD_DEFILE, pParentWnd) - { - fOpenRsrcFork = false; - fName = L""; - } - virtual ~DEFileDialog(void) {} - - void Setup(DiskFS* pDiskFS) { - fpDiskFS = pDiskFS; - } - - CString fName; - int fOpenRsrcFork; - -protected: - /* - * Turn off the "OK" button, which is only active when some text - * has been typed in the window. - */ - virtual BOOL OnInitDialog(void) override; - - /* - * Get the filename and the "open resource fork" check box. - */ - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * The text has changed. If there's nothing in the box, dim the - * "OK" button. - */ - afx_msg virtual void OnChange(void); - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - -private: - DiskFS* fpDiskFS; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_DEFILEDIALOG_H*/ diff --git a/ciderpress/app/DiskArchive.cpp b/ciderpress/app/DiskArchive.cpp deleted file mode 100644 index 35edafd..0000000 --- a/ciderpress/app/DiskArchive.cpp +++ /dev/null @@ -1,2891 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2009 by CiderPress authors. All Rights Reserved. - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Bridge between DiskImg and GenericArchive. - */ -#include "stdafx.h" -#include "DiskArchive.h" -#include "NufxArchive.h" -#include "Preferences.h" -#include "Main.h" -#include "ImageFormatDialog.h" -#include "RenameEntryDialog.h" -#include "ConfirmOverwriteDialog.h" -#include "../diskimg/DiskImgDetail.h" -#include "../reformat/Charset.h" - -static const char kEmptyFolderMarker[] = ".$$EmptyFolder"; - - -/* - * =========================================================================== - * DiskEntry - * =========================================================================== - */ - -int DiskEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const -{ - DIError dierr; - A2FileDescr* pOpenFile = NULL; - char* dataBuf = NULL; - bool rsrcFork; - bool needAlloc = true; - int result = -1; - - ASSERT(fpFile != NULL); - ASSERT(pErrMsg != NULL); - *pErrMsg = ""; - - if (*ppText != NULL) - needAlloc = false; - - if (GetDamaged()) { - *pErrMsg = "File is damaged"; - goto bail; - } - - if (which == kDataThread) - rsrcFork = false; - else if (which == kRsrcThread) - rsrcFork = true; - else { - *pErrMsg = "No such fork"; - goto bail; - } - - LONGLONG len; - if (rsrcFork) - len = fpFile->GetRsrcLength(); - else - len = fpFile->GetDataLength(); - - if (len == 0) { - if (needAlloc) { - *ppText = new char[1]; - **ppText = '\0'; - } - *pLength = 0; - result = IDOK; - goto bail; - } else if (len < 0) { - assert(rsrcFork); // forked files always have a data fork - *pErrMsg = L"That fork doesn't exist"; - goto bail; - } - - dierr = fpFile->Open(&pOpenFile, true, rsrcFork); - if (dierr != kDIErrNone) { - *pErrMsg = L"File open failed"; - goto bail; - } - - SET_PROGRESS_BEGIN(); - pOpenFile->SetProgressUpdater(DiskArchive::ProgressCallback, len, NULL); - - if (needAlloc) { - dataBuf = new char[(int) len]; - if (dataBuf == NULL) { - pErrMsg->Format(L"ERROR: allocation of %I64d bytes failed", len); - goto bail; - } - } else { - if (*pLength < (long) len) { - pErrMsg->Format(L"ERROR: buf size %ld too short (%ld)", - *pLength, (long) len); - goto bail; - } - dataBuf = *ppText; - } - - dierr = pOpenFile->Read(dataBuf, (size_t) len); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) { - result = IDCANCEL; - } else { - pErrMsg->Format(L"File read failed: %hs", - DiskImgLib::DIStrError(dierr)); - } - goto bail; - } - - if (needAlloc) - *ppText = dataBuf; - *pLength = (long) len; - result = IDOK; - -bail: - if (pOpenFile != NULL) - pOpenFile->Close(); - if (result == IDOK) { - SET_PROGRESS_END(); - ASSERT(pErrMsg->IsEmpty()); - } else { - ASSERT(result == IDCANCEL || !pErrMsg->IsEmpty()); - if (needAlloc) { - delete[] dataBuf; - ASSERT(*ppText == NULL); - } - } - return result; -} - -int DiskEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const -{ - A2FileDescr* pOpenFile = NULL; - bool rsrcFork; - int result = -1; - - ASSERT(IDOK != -1 && IDCANCEL != -1); - ASSERT(fpFile != NULL); - - if (which == kDataThread) - rsrcFork = false; - else if (which == kRsrcThread) - rsrcFork = true; - else { - /* if we handle disk images, make sure we disable "conv" */ - *pErrMsg = L"No such fork"; - goto bail; - } - - LONGLONG len; - if (rsrcFork) - len = fpFile->GetRsrcLength(); - else - len = fpFile->GetDataLength(); - - if (len == 0) { - LOGI("Empty fork"); - result = IDOK; - goto bail; - } else if (len < 0) { - assert(rsrcFork); // forked files always have a data fork - *pErrMsg = L"That fork doesn't exist"; - goto bail; - } - - DIError dierr; - dierr = fpFile->Open(&pOpenFile, true, rsrcFork); - if (dierr != kDIErrNone) { - *pErrMsg = L"Unable to open file on disk image"; - goto bail; - } - - dierr = CopyData(pOpenFile, outfp, conv, convHA, pErrMsg); - if (dierr != kDIErrNone) { - if (pErrMsg->IsEmpty()) { - pErrMsg->Format(L"Failed while copying data: %hs\n", - DiskImgLib::DIStrError(dierr)); - } - goto bail; - } - - result = IDOK; - -bail: - if (pOpenFile != NULL) - pOpenFile->Close(); - return result; -} - -DIError DiskEntry::CopyData(A2FileDescr* pOpenFile, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pMsg) const -{ - DIError dierr = kDIErrNone; - const int kChunkSize = 16384; - char buf[kChunkSize]; - //bool firstChunk = true; - //EOLType sourceType; - bool lastCR = false; - LONGLONG srcLen, dataRem; - - /* get the length of the open file */ - dierr = pOpenFile->Seek(0, DiskImgLib::kSeekEnd); - if (dierr != kDIErrNone) - goto bail; - srcLen = pOpenFile->Tell(); - dierr = pOpenFile->Rewind(); - if (dierr != kDIErrNone) - goto bail; - ASSERT(srcLen > 0); // empty files should've been caught earlier - - SET_PROGRESS_BEGIN(); - pOpenFile->SetProgressUpdater(DiskArchive::ProgressCallback, srcLen, NULL); - - /* - * Loop until all data copied. - */ - dataRem = srcLen; - while (dataRem) { - int chunkLen; - - if (dataRem > kChunkSize) - chunkLen = kChunkSize; - else - chunkLen = (int) dataRem; - - /* read a chunk from the source file */ - dierr = pOpenFile->Read(buf, chunkLen); - if (dierr != kDIErrNone) { - pMsg->Format(L"File read failed: %hs", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* write chunk to destination file */ - int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv, - &convHA, &lastCR); - if (err != 0) { - pMsg->Format(L"File write failed: %hs", strerror(err)); - dierr = kDIErrGeneric; - goto bail; - } - - dataRem -= chunkLen; - //SET_PROGRESS_UPDATE(ComputePercent(srcLen - dataRem, srcLen)); - } - -bail: - pOpenFile->ClearProgressUpdater(); - SET_PROGRESS_END(); - return dierr; -} - -bool DiskEntry::GetFeatureFlag(Feature feature) const -{ - DiskImg::FSFormat format; - - format = fpFile->GetDiskFS()->GetDiskImg()->GetFSFormat(); - - switch (feature) { - case kFeatureCanChangeType: - { - //if (GetRecordKind() == kRecordKindVolumeDir) - // return false; - - switch (format) { - case DiskImg::kFormatProDOS: - case DiskImg::kFormatPascal: - case DiskImg::kFormatMacHFS: - case DiskImg::kFormatDOS32: - case DiskImg::kFormatDOS33: - return true; - default: - return false; - } - } - case kFeaturePascalTypes: - { - switch (format) { - case DiskImg::kFormatPascal: - return true; - default: - return false; - } - } - case kFeatureDOSTypes: - { - switch (format) { - case DiskImg::kFormatDOS32: - case DiskImg::kFormatDOS33: - return true; - default: - return false; - } - } - case kFeatureHFSTypes: - { - switch (format) { - case DiskImg::kFormatMacHFS: - return true; - default: - return false; - } - } - case kFeatureHasFullAccess: - { - switch (format) { - case DiskImg::kFormatProDOS: - return true; - default: - return false; - } - } - case kFeatureHasSimpleAccess: - { - switch (format) { - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - case DiskImg::kFormatCPM: - case DiskImg::kFormatMacHFS: - return true; - default: - return false; - } - } - case kFeatureHasInvisibleFlag: - { - switch(format) { - case DiskImg::kFormatProDOS: - case DiskImg::kFormatMacHFS: - return true; - default: - return false; - } - } - default: - LOGI("Unexpected feature flag %d", feature); - assert(false); - return false; - } - - assert(false); - return false; -} - - -/* - * =========================================================================== - * DiskArchive - * =========================================================================== - */ - -/*static*/ CString DiskArchive::AppInit(void) -{ - CString result(""); - DIError dierr; - int32_t major, minor, bug; - - LOGI("Initializing DiskImg library"); - - // set this before initializing, so we can see init debug msgs - DiskImgLib::Global::SetDebugMsgHandler(DebugMsgHandler); - - dierr = DiskImgLib::Global::AppInit(); - if (dierr != kDIErrNone) { - result.Format(L"DiskImg DLL failed to initialize: %hs\n", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - DiskImgLib::Global::GetVersion(&major, &minor, &bug); - if (major != kDiskImgVersionMajor || minor < kDiskImgVersionMinor) { - result.Format(L"Older or incompatible version of DiskImg DLL found.\r\r" - L"Wanted v%d.%d.x, found %ld.%ld.%ld.", - kDiskImgVersionMajor, kDiskImgVersionMinor, - major, minor, bug); - goto bail; - } - -bail: - return result; -} - -/*static*/ void DiskArchive::AppCleanup(void) -{ - DiskImgLib::Global::AppCleanup(); -} - -/*static*/ void DiskArchive::DebugMsgHandler(const char* file, int line, - const char* msg) -{ - ASSERT(file != NULL); - ASSERT(msg != NULL); - - LOG_BASE(DebugLog::LOG_INFO, file, line, " %hs", msg); -} - -/*static*/ bool DiskArchive::ProgressCallback(DiskImgLib::A2FileDescr* pFile, - DiskImgLib::di_off_t max, DiskImgLib::di_off_t current, void* state) -{ - int status; - - //::Sleep(10); - status = SET_PROGRESS_UPDATE(ComputePercent(current, max)); - if (status == IDCANCEL) { - LOGI("IDCANCEL returned from Main progress updater"); - return false; - } - - return true; // tell DiskImgLib to continue what it's doing -} - -/*static*/ bool DiskArchive::ScanProgressCallback(void* cookie, const char* str, - int count) -{ - CString fmt; - bool cont; - - if (count == 0) - fmt = str; - else - fmt.Format(L"%hs (%%d)", str); - cont = SET_PROGRESS_COUNTER_2(fmt, count); - - if (!cont) { - LOGI("cancelled"); - } - - return cont; -} - -GenericArchive::OpenResult DiskArchive::Open(const WCHAR* filename, - bool readOnly, CString* pErrMsg) -{ - DIError dierr; - CString errMsg; - OpenResult result = kResultUnknown; - const Preferences* pPreferences = GET_PREFERENCES(); - - ASSERT(fpPrimaryDiskFS == NULL); - ASSERT(filename != NULL); - //ASSERT(ext != NULL); - - ASSERT(pPreferences != NULL); - - fIsReadOnly = readOnly; - - // special case for volume open - bool isVolume = false; - if (filename[0] >= 'A' && filename[0] <= 'Z' && - filename[1] == ':' && filename[2] == '\\' && - filename[3] == '\0') - { - isVolume = true; - } - - /* - * Open the image. This can be very slow for compressed images, - * especially 3.5" FDI images. - */ - { - CWaitCursor waitc; - - // TODO(Unicode): modify DiskImg lib to accept wide paths - CStringA fileNameA(filename); - if (!PathName::TestNarrowConversion(filename, fileNameA, &errMsg)) { - result = kResultFailure; - goto bail; - } - dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep, - readOnly); - if (dierr == kDIErrAccessDenied && !readOnly && !isVolume) { - // retry file open with read-only set - // don't do that for volumes -- assume they know what they want - LOGD(" Retrying open with read-only set"); - fIsReadOnly = readOnly = true; - dierr = fDiskImg.OpenImage(fileNameA, PathProposal::kLocalFssep, - readOnly); - } - if (dierr != kDIErrNone) { - if (dierr == kDIErrFileArchive) { - result = kResultFileArchive; - } else { - result = kResultFailure; - errMsg.Format(L"Unable to open '%ls': %hs.", filename, - DiskImgLib::DIStrError(dierr)); - } - goto bail; - } - } - - dierr = fDiskImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - result = kResultFailure; - errMsg.Format(L"Analysis of '%ls' failed: %hs", filename, - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* allow them to override sector order and filesystem, if requested */ - if (pPreferences->GetPrefBool(kPrQueryImageFormat)) { - ImageFormatDialog imf; - - imf.InitializeValues(&fDiskImg); - imf.fFileSource = filename; - imf.SetQueryDisplayFormat(false); - imf.SetAllowGenericFormats(false); - - if (imf.DoModal() != IDOK) { - LOGD("User bailed on IMF dialog"); - result = kResultCancel; - goto bail; - } - - if (imf.fSectorOrder != fDiskImg.GetSectorOrder() || - imf.fFSFormat != fDiskImg.GetFSFormat()) - { - LOGI("Initial values overridden, forcing img format"); - dierr = fDiskImg.OverrideFormat(fDiskImg.GetPhysicalFormat(), - imf.fFSFormat, imf.fSectorOrder); - if (dierr != kDIErrNone) { - result = kResultFailure; - errMsg.Format(L"Unable to access disk image using selected" - L" parameters. Error: %hs.", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - } - } - - if (fDiskImg.GetFSFormat() == DiskImg::kFormatUnknown || - fDiskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - result = kResultFailure; - errMsg.Format(L"Unable to identify filesystem on '%ls'", filename); - goto bail; - } - - /* create an appropriate DiskFS object */ - fpPrimaryDiskFS = fDiskImg.OpenAppropriateDiskFS(); - if (fpPrimaryDiskFS == NULL) { - /* unknown FS should've been caught above! */ - ASSERT(false); - result = kResultFailure; - errMsg.Format(L"Format of '%ls' not recognized.", filename); - goto bail; - } - - fpPrimaryDiskFS->SetScanForSubVolumes(DiskFS::kScanSubEnabled); - - /* - * Scan all files and on the disk image, and recursively descend into - * sub-volumes. Can be slow on physical volumes. - * - * This is really only useful for ProDOS and HFS disks. Nothing else - * can be large enough to really get slow, and nothing else is likely - * to show up in a large multi-partition image. - * - * THOUGHT: only show the dialog if the volume is over a certain size. - */ - { - MainWindow* pMain = GET_MAIN_WINDOW(); - ProgressCounterDialog* pProgress; - - pProgress = new ProgressCounterDialog; - pProgress->Create(L"Examining contents, please wait...", pMain); - pProgress->SetCounterFormat(L"Scanning..."); - pProgress->CenterWindow(); - //pMain->PeekAndPump(); // redraw - CWaitCursor waitc; - - /* set up progress dialog and scan all files */ - pMain->SetProgressCounterDialog(pProgress); - fDiskImg.SetScanProgressCallback(ScanProgressCallback, this); - - dierr = fpPrimaryDiskFS->Initialize(&fDiskImg, DiskFS::kInitFull); - - fDiskImg.SetScanProgressCallback(NULL, NULL); - pMain->SetProgressCounterDialog(NULL); - pProgress->DestroyWindow(); - - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) { - result = kResultCancel; - } else { - result = kResultFailure; - errMsg.Format(L"Error reading list of files from disk: %hs", - DiskImgLib::DIStrError(dierr)); - } - goto bail; - } - } - - if (LoadContents() != 0) { - result = kResultFailure; - errMsg = L"Failed while loading contents of disk image."; - goto bail; - } - - /* - * Force read-only flag if underlying FS doesn't allow RW. We need to - * consider embedded filesystems, so we only set RO if none of the - * filesystems are writable. - * - * BUG: this only checks the first level. Should be fully recursive. - */ - if (!fpPrimaryDiskFS->GetReadWriteSupported()) { - const DiskFS::SubVolume* pSubVol; - - fIsReadOnly = true; - pSubVol = fpPrimaryDiskFS->GetNextSubVolume(NULL); - while (pSubVol != NULL) { - if (pSubVol->GetDiskFS()->GetReadWriteSupported()) { - fIsReadOnly = false; - break; - } - - pSubVol = fpPrimaryDiskFS->GetNextSubVolume(pSubVol); - } - } - - /* force read-only if the primary is damaged */ - if (fpPrimaryDiskFS->GetFSDamaged()) - fIsReadOnly = true; - /* force read-only if the DiskImg thinks a wrapper is damaged */ - if (fpPrimaryDiskFS->GetDiskImg()->GetReadOnly()) - fIsReadOnly = true; - -// /* force read-only on .gz/.zip unless pref allows */ -// if (fDiskImg.GetOuterFormat() != DiskImg::kOuterFormatNone) { -// if (pPreferences->GetPrefBool(kPrWriteZips) == 0) -// fIsReadOnly = true; -// } - - SetPathName(filename); - result = kResultSuccess; - - /* set any preferences-based settings */ - PreferencesChanged(); - -bail: - *pErrMsg = errMsg; - if (!errMsg.IsEmpty()) { - assert(result == kResultFailure); - delete fpPrimaryDiskFS; - fpPrimaryDiskFS = NULL; - } else { - assert(result != kResultFailure); - } - return result; -} - -CString DiskArchive::New(const WCHAR* fileName, const void* vOptions) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - NewOptions* pOptions = (NewOptions*) vOptions; - CString volName; - CStringA volNameA, fileNameA; - long numBlocks = -1; - long numTracks = -1; - int numSectors; - CString retmsg; - DIError dierr; - bool allowLowerCase; - - ASSERT(fileName != NULL); - ASSERT(pOptions != NULL); - - allowLowerCase = pPreferences->GetPrefBool(kPrProDOSAllowLower) != 0; - - switch (pOptions->base.format) { - case DiskImg::kFormatUnknown: - numBlocks = pOptions->blank.numBlocks; - break; - case DiskImg::kFormatProDOS: - volName = pOptions->prodos.volName; - numBlocks = pOptions->prodos.numBlocks; - break; - case DiskImg::kFormatPascal: - volName = pOptions->pascalfs.volName; - numBlocks = pOptions->pascalfs.numBlocks; - break; - case DiskImg::kFormatMacHFS: - volName = pOptions->hfs.volName; - numBlocks = pOptions->hfs.numBlocks; - break; - case DiskImg::kFormatDOS32: - numTracks = pOptions->dos.numTracks; - numSectors = pOptions->dos.numSectors; - - if (numTracks < DiskFSDOS33::kMinTracks || - numTracks > DiskFSDOS33::kMaxTracks) - { - retmsg = L"Invalid DOS32 track count"; - goto bail; - } - if (numSectors != 13) { - retmsg = L"Invalid DOS32 sector count"; - goto bail; - } - if (pOptions->dos.allocDOSTracks) - volName = L"DOS"; - break; - case DiskImg::kFormatDOS33: - numTracks = pOptions->dos.numTracks; - numSectors = pOptions->dos.numSectors; - - if (numTracks < DiskFSDOS33::kMinTracks || - numTracks > DiskFSDOS33::kMaxTracks) - { - retmsg = L"Invalid DOS33 track count"; - goto bail; - } - if (numSectors != 16 && numSectors != 32) { // no 13-sector (yet) - retmsg = L"Invalid DOS33 sector count"; - goto bail; - } - if (pOptions->dos.allocDOSTracks) - volName = L"DOS"; - break; - default: - retmsg = L"Unsupported disk format"; - goto bail; - } - - LOGI("DiskArchive: new '%ls' %ld %hs in '%ls'", - (LPCWSTR) volName, numBlocks, - DiskImg::ToString(pOptions->base.format), fileName); - - bool canSkipFormat; - if (IsWin9x()) - canSkipFormat = false; - else - canSkipFormat = true; - - /* - * Create an image with the appropriate characteristics. We set - * "skipFormat" because we know this will be a brand-new file, and - * we're not currently creating nibble images. - * - * GLITCH: under Win98/ME, brand-new files contain the previous contents - * of the hard drive. We need to explicitly zero them out. We don't - * want to do it under Win2K/XP because it can be slow for larger - * volumes. - */ - fileNameA = fileName; // TODO(Unicode) - if (numBlocks > 0) { - dierr = fDiskImg.CreateImage(fileNameA, NULL, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - NULL, - pOptions->base.sectorOrder, - DiskImg::kFormatGenericProDOSOrd, // arg must be generic - numBlocks, - canSkipFormat); - } else { - ASSERT(numTracks > 0); - dierr = fDiskImg.CreateImage(fileNameA, NULL, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - NULL, - pOptions->base.sectorOrder, - DiskImg::kFormatGenericProDOSOrd, // arg must be generic - numTracks, numSectors, - canSkipFormat); - } - if (dierr != kDIErrNone) { - retmsg.Format(L"Unable to create disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - if (pOptions->base.format == DiskImg::kFormatUnknown) - goto skip_format; - - if (pOptions->base.format == DiskImg::kFormatDOS33 || - pOptions->base.format == DiskImg::kFormatDOS32) - fDiskImg.SetDOSVolumeNum(pOptions->dos.volumeNum); - - /* - * If we don't allow lower case in ProDOS filenames, don't allow them - * in volume names either. This works because we don't allow ' ' in - * volume names; otherwise we'd need to invoke a ProDOS-specific call - * to convert the ' ' to '.'. (Or we could just do it ourselves.) - * - * We can't ask the ProDOS DiskFS to force upper case for us because - * the ProDOS DiskFS object doesn't yet exist. - */ - if (pOptions->base.format == DiskImg::kFormatProDOS && !allowLowerCase) - volName.MakeUpper(); - - /* format it */ - // TODO(Unicode): for HFS, we need to convert Unicode to MOR - volNameA = volName; - dierr = fDiskImg.FormatImage(pOptions->base.format, volNameA); - if (dierr != kDIErrNone) { - retmsg.Format(L"Unable to format disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - fpPrimaryDiskFS = fDiskImg.OpenAppropriateDiskFS(false); - if (fpPrimaryDiskFS == NULL) { - retmsg = L"Unable to create DiskFS."; - goto bail; - } - - /* prep it */ - dierr = fpPrimaryDiskFS->Initialize(&fDiskImg, DiskFS::kInitFull); - if (dierr != kDIErrNone) { - retmsg.Format(L"Error reading list of files from disk: %hs", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* this is pretty meaningless, but do it to ensure we're initialized */ - if (LoadContents() != 0) { - retmsg = L"Failed while loading contents of disk image."; - goto bail; - } - -skip_format: - SetPathName(fileName); - - /* set any preferences-based settings */ - PreferencesChanged(); - -bail: - return retmsg; -} - -CString DiskArchive::Close(void) -{ - if (fpPrimaryDiskFS != NULL) { - LOGI("DiskArchive shutdown closing disk image"); - delete fpPrimaryDiskFS; - fpPrimaryDiskFS = NULL; - } - - DIError dierr; - dierr = fDiskImg.CloseImage(); - if (dierr != kDIErrNone) { - MainWindow* pMainWin = (MainWindow*)::AfxGetMainWnd(); - CString msg, failed; - - msg.Format(L"Failed while closing disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - CheckedLoadString(&failed, IDS_FAILED); - LOGE("During close: %ls", (LPCWSTR) msg); - - pMainWin->MessageBox(msg, failed, MB_OK); - } - - return L""; -} - -CString DiskArchive::Flush(void) -{ - DIError dierr; - CWaitCursor waitc; - - assert(fpPrimaryDiskFS != NULL); - - dierr = fpPrimaryDiskFS->Flush(DiskImg::kFlushAll); - if (dierr != kDIErrNone) { - CString errMsg; - - errMsg.Format(L"Attempt to flush the current archive failed: %hs.", - DiskImgLib::DIStrError(dierr)); - return errMsg; - } - - return L""; -} - -bool DiskArchive::IsModified(void) const -{ - assert(fpPrimaryDiskFS != NULL); - - return fpPrimaryDiskFS->GetDiskImg()->GetDirtyFlag(); -} - -CString DiskArchive::GetDescription() const -{ - CString str = L"Disk Image"; - - if (fpPrimaryDiskFS != NULL && fpPrimaryDiskFS->GetVolumeID() != NULL) { - CString volumeId(Charset::ConvertMORToUNI(fpPrimaryDiskFS->GetVolumeID())); - str.Format(L"Disk Image - %ls", (LPCWSTR) volumeId); - } - return str; -} - -int DiskArchive::LoadContents(void) -{ - int result; - - LOGI("DiskArchive LoadContents"); - ASSERT(fpPrimaryDiskFS != NULL); - - { - MainWindow* pMain = GET_MAIN_WINDOW(); - ExclusiveModelessDialog* pWaitDlg = new ExclusiveModelessDialog; - pWaitDlg->Create(IDD_LOADING, pMain); - pWaitDlg->CenterWindow(); - pMain->PeekAndPump(); // redraw - CWaitCursor waitc; - - result = LoadDiskFSContents(fpPrimaryDiskFS, L""); - - SET_PROGRESS_COUNTER(-1); - - pWaitDlg->DestroyWindow(); - //pMain->PeekAndPump(); // redraw - } - - return result; -} - -CString DiskArchive::Reload() -{ - fReloadFlag = true; // tell everybody that cached data is invalid - - (void) fpPrimaryDiskFS->Flush(DiskImg::kFlushFastOnly); - - DeleteEntries(); // a GenericArchive operation - - if (LoadContents() != 0) - return "Disk image reload failed."; - - return ""; -} - -int DiskArchive::InternalReload(CWnd* pMsgWnd) -{ - CString errMsg; - - errMsg = Reload(); - - if (!errMsg.IsEmpty()) { - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - return -1; - } - - return 0; -} - -int DiskArchive::LoadDiskFSContents(DiskFS* pDiskFS, const WCHAR* volName) -{ - static const char* kBlankFileNameMOR = ""; - A2File* pFile; - DiskEntry* pNewEntry; - DiskFS::SubVolume* pSubVol; - const Preferences* pPreferences = GET_PREFERENCES(); - bool wantCoerceDOSFilenames = false; - CString ourSubVolName; - - wantCoerceDOSFilenames = pPreferences->GetPrefBool(kPrCoerceDOSFilenames); - - LOGI("Notes for disk image '%ls':", volName); - LOGI("%hs", pDiskFS->GetDiskImg()->GetNotes()); - - ASSERT(pDiskFS != NULL); - pFile = pDiskFS->GetNextFile(NULL); - while (pFile != NULL) { - pNewEntry = new DiskEntry(pFile); - if (pNewEntry == NULL) - return -1; - - CStringA path(pFile->GetPathName()); - if (path.IsEmpty()) - path = kBlankFileNameMOR; - if (DiskImg::UsesDOSFileStructure(pFile->GetFSFormat()) && - wantCoerceDOSFilenames) - { - InjectLowercase(&path); - } - pNewEntry->SetPathNameMOR(path); - if (volName[0] != '\0') - pNewEntry->SetSubVolName(volName); - pNewEntry->SetFssep(pFile->GetFssep()); - pNewEntry->SetFileType(pFile->GetFileType()); - pNewEntry->SetAuxType(pFile->GetAuxType()); - pNewEntry->SetAccess(pFile->GetAccess()); - if (pFile->GetCreateWhen() == 0) - pNewEntry->SetCreateWhen(kDateNone); - else - pNewEntry->SetCreateWhen(pFile->GetCreateWhen()); - if (pFile->GetModWhen() == 0) - pNewEntry->SetModWhen(kDateNone); - else - pNewEntry->SetModWhen(pFile->GetModWhen()); - pNewEntry->SetSourceFS(pFile->GetFSFormat()); - pNewEntry->SetHasDataFork(true); - if (pFile->IsVolumeDirectory()) { - /* volume directory entry; only on ProDOS/HFS */ - ASSERT(pFile->GetRsrcLength() < 0); - pNewEntry->SetRecordKind(GenericEntry::kRecordKindVolumeDir); - //pNewEntry->SetUncompressedLen(pFile->GetDataLength()); - pNewEntry->SetDataForkLen(pFile->GetDataLength()); - pNewEntry->SetCompressedLen(pFile->GetDataLength()); - } else if (pFile->IsDirectory()) { - /* directory entry */ - ASSERT(pFile->GetRsrcLength() < 0); - pNewEntry->SetRecordKind(GenericEntry::kRecordKindDirectory); - //pNewEntry->SetUncompressedLen(pFile->GetDataLength()); - pNewEntry->SetDataForkLen(pFile->GetDataLength()); - pNewEntry->SetCompressedLen(pFile->GetDataLength()); - } else if (pFile->GetRsrcLength() >= 0) { - /* has resource fork */ - pNewEntry->SetRecordKind(GenericEntry::kRecordKindForkedFile); - pNewEntry->SetDataForkLen(pFile->GetDataLength()); - pNewEntry->SetRsrcForkLen(pFile->GetRsrcLength()); - //pNewEntry->SetUncompressedLen( - // pFile->GetDataLength() + pFile->GetRsrcLength() ); - pNewEntry->SetCompressedLen( - pFile->GetDataSparseLength() + pFile->GetRsrcSparseLength() ); - pNewEntry->SetHasRsrcFork(true); - } else { - /* just data fork */ - pNewEntry->SetRecordKind(GenericEntry::kRecordKindFile); - //pNewEntry->SetUncompressedLen(pFile->GetDataLength()); - pNewEntry->SetDataForkLen(pFile->GetDataLength()); - pNewEntry->SetCompressedLen(pFile->GetDataSparseLength()); - } - - switch (pNewEntry->GetSourceFS()) { - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - case DiskImg::kFormatUNIDOS: - pNewEntry->SetFormatStr(L"DOS"); - break; - case DiskImg::kFormatGutenberg: - pNewEntry->SetFormatStr(L"Gutenb"); - break; - case DiskImg::kFormatProDOS: - pNewEntry->SetFormatStr(L"ProDOS"); - break; - case DiskImg::kFormatPascal: - pNewEntry->SetFormatStr(L"Pascal"); - break; - case DiskImg::kFormatCPM: - pNewEntry->SetFormatStr(L"CP/M"); - break; - case DiskImg::kFormatMSDOS: - pNewEntry->SetFormatStr(L"MS-DOS"); - break; - case DiskImg::kFormatRDOS33: - case DiskImg::kFormatRDOS32: - case DiskImg::kFormatRDOS3: - pNewEntry->SetFormatStr(L"RDOS"); - break; - case DiskImg::kFormatMacHFS: - pNewEntry->SetFormatStr(L"HFS"); - break; - default: - pNewEntry->SetFormatStr(L"???"); - break; - } - - pNewEntry->SetDamaged(pFile->GetQuality() == A2File::kQualityDamaged); - pNewEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious); - - AddEntry(pNewEntry); - - /* this is not very useful -- all the heavy lifting was done earlier */ - if ((GetNumEntries() % 100) == 0) - SET_PROGRESS_COUNTER(GetNumEntries()); - - pFile = pDiskFS->GetNextFile(pFile); - } - - /* - * Load all sub-volumes. - * - * We define the sub-volume name to use for the next layer down. We - * prepend an underscore to the unmodified name. So long as the volume - * name is a valid Windows path -- which should hold true for most disks, - * though possibly not for Pascal -- it can be extracted directly with - * its full path with no risk of conflict. (The extraction code relies - * on this, so don't put a ':' in the subvol name or Windows will choke.) - */ - pSubVol = pDiskFS->GetNextSubVolume(NULL); - while (pSubVol != NULL) { - CString concatSubVolName; - int ret; - - const char* subVolNameMOR = pSubVol->GetDiskFS()->GetVolumeName(); - if (subVolNameMOR == NULL) { - subVolNameMOR = "+++"; // call it *something* - } - CString subVolName(Charset::ConvertMORToUNI(subVolNameMOR)); - - if (volName[0] == '\0') { - concatSubVolName.Format(L"_%ls", (LPCWSTR) subVolName); - } else { - concatSubVolName.Format(L"%ls_%ls", volName, (LPCWSTR) subVolName); - } - ret = LoadDiskFSContents(pSubVol->GetDiskFS(), concatSubVolName); - if (ret != 0) - return ret; - pSubVol = pDiskFS->GetNextSubVolume(pSubVol); - } - - return 0; -} - -void DiskArchive::PreferencesChanged(void) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - if (fpPrimaryDiskFS != NULL) { - fpPrimaryDiskFS->SetParameter(DiskFS::kParmProDOS_AllowLowerCase, - pPreferences->GetPrefBool(kPrProDOSAllowLower) != 0); - fpPrimaryDiskFS->SetParameter(DiskFS::kParmProDOS_AllocSparse, - pPreferences->GetPrefBool(kPrProDOSUseSparse) != 0); - } -} - -long DiskArchive::GetCapability(Capability cap) -{ - switch (cap) { - case kCapCanTest: - return false; - break; - case kCapCanRenameFullPath: - return false; - break; - case kCapCanRecompress: - return false; - break; - case kCapCanEditComment: - return false; - break; - case kCapCanAddDisk: - return false; - break; - case kCapCanConvEOLOnAdd: - return true; - break; - case kCapCanCreateSubdir: - return true; - break; - case kCapCanRenameVolume: - return true; - break; - default: - ASSERT(false); - return -1; - break; - } -} - - -/* - * =========================================================================== - * DiskArchive -- add files - * =========================================================================== - */ - -bool DiskArchive::BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) -{ - NuError nerr; - CString errMsg; - WCHAR curDir[MAX_PATH] = L""; - bool retVal = false; - - LOGD("Opts: '%ls' typePres=%d", (LPCWSTR) pAddOpts->fStoragePrefix, - pAddOpts->fTypePreservation); - LOGD(" sub=%d strip=%d ovwr=%d", - pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, - pAddOpts->fOverwriteExisting); - - ASSERT(fpAddDataHead == NULL); - - /* these reset on every new add */ - fOverwriteExisting = false; - fOverwriteNoAsk = false; - - /* we screen for clashes with existing files later; this just ensures - "batch uniqueness" */ - fpPrimaryDiskFS->SetParameter(DiskFS::kParm_CreateUnique, true); - - /* - * Save the current directory and change to the one from the file dialog. - */ - const CString& directory = pAddOpts->GetDirectory(); - LOGI("Selected path = '%ls'", (LPCWSTR) directory); - - if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { - errMsg = L"Unable to get current directory.\n"; - ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); - goto bail; - } - if (SetCurrentDirectory(directory) == false) { - errMsg.Format(L"Unable to set current directory to '%ls'.\n", - (LPCWSTR) directory); - ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); - goto bail; - } - - const CStringArray& fileNames = pAddOpts->GetFileNames(); - for (int i = 0; i < fileNames.GetCount(); i++) { - const CString& name = fileNames.GetAt(i); - LOGI(" file '%ls'", (LPCWSTR) name); - - /* add the file, calling DoAddFile via the generic AddFile */ - nerr = AddFile(pAddOpts, name, &errMsg); - if (nerr != kNuErrNone) { - if (errMsg.IsEmpty()) - errMsg.Format(L"Failed while adding file '%ls': %hs.", - (LPCWSTR) name, NuStrError(nerr)); - if (nerr != kNuErrAborted) { - ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); - } - goto bail; - } - } - - if (fpAddDataHead == NULL) { - CString title; - CheckedLoadString(&title, IDS_MB_APP_NAME); - errMsg = L"No files added.\n"; - pActionProgress->MessageBox(errMsg, title, MB_OK | MB_ICONWARNING); - } else { - /* add all pending files */ - retVal = true; - errMsg = ProcessFileAddData(pAddOpts->fpTargetDiskFS, - pAddOpts->fConvEOL); - if (!errMsg.IsEmpty()) { - CString title; - ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); - retVal = false; - } - - /* success or failure, reload the contents */ - errMsg = Reload(); - if (!errMsg.IsEmpty()) - retVal = false; - } - -bail: - FreeAddDataList(); - if (SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); - ShowFailureMsg(pActionProgress, errMsg, IDS_FAILED); - // bummer, but don't signal failure - } - return retVal; -} - -NuError DiskArchive::DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) -{ - /* - * Add a file to a disk image Unfortunately we can't just add the files - * here. We need to figure which pairs of files should be combined into - * a single "extended" file (yes, the cursed forked files strike again). - * - * The way you tell if two files should be one is by comparing their - * filenames and type info. If they match, and one is a data fork and - * one is a resource fork, we have a single split file. - * - * We have to be careful here because we don't know which will be seen - * first and whether they'll be adjacent. We have to dig through the - * list of previously-added files for a match (O(n^2) behavior currently). - * - * We also have to compare the right filename. Comparing the Windows - * filename is a bad idea, because by definition one of them has a resource - * fork tag on it. We need to compare the normalized filename before - * the ProDOS normalizer/uniqifier gets a chance to mangle it. As luck - * would have it, that's exactly what we have in "storageName". - * - * For a NuFX archive, NufxLib does all this nonsense for us, but we have - * to manage it ourselves here. The good news is that, since we have to - * wade through all the filenames, we have an opportunity to make the names - * unique. So long as we ensure that the names we have don't clash with - * anything currently on the disk, we know that anything we add that does - * clash is running into something we just added, which means we can turn - * on CreateFile's "make unique" feature and let the filesystem-specific - * code handle uniqueness. - * - * Any fields we want to keep from the FileDetails struct need to be - * copied out. It's a "hairy" struct, so we need to duplicate the strings. - */ - NuError nuerr = kNuErrNone; - DiskFS* pDiskFS = pAddOpts->fpTargetDiskFS; - - DIError dierr; - int neededLen = 64; // reasonable guess - char* fsNormalBuf = NULL; // name as it will appear on disk image - - LOGI(" +++ ADD file: orig='%ls' strip='%ls'", - (LPCWSTR) pDetails->GetLocalPathName(), - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - -retry: - /* - * Convert "storageName" to a filesystem-normalized path. - */ - delete[] fsNormalBuf; - fsNormalBuf = new char[neededLen]; - dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(), - PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen); - if (dierr == kDIErrDataOverrun) { - /* not long enough, try again *once* */ - delete[] fsNormalBuf; - fsNormalBuf = new char[neededLen]; - dierr = pDiskFS->NormalizePath(pDetails->GetStoragePathNameMOR(), - PathProposal::kDefaultStoredFssep, fsNormalBuf, &neededLen); - } - if (dierr != kDIErrNone) { - nuerr = kNuErrInternal; - goto bail; - } - - /* - * Test to see if the file already exists. If it does, give the user - * the opportunity to rename it, overwrite the original, or skip - * adding it. - * - * The FS-normalized path may not reflect the actual storage name, - * because some features (like ProDOS "allow lower case") aren't - * factored in until later. However, it should be close enough -- it - * has to be, or we'd be in trouble for saying it's going to overwrite - * the file in the archive. - */ - A2File* pExisting; - pExisting = pDiskFS->GetFileByName(fsNormalBuf); - if (pExisting != NULL) { - NuResult result; - - result = HandleReplaceExisting(pExisting, pDetails); - if (result == kNuAbort) { - nuerr = kNuErrAborted; - goto bail; - } else if (result == kNuSkip) { - nuerr = kNuErrSkipped; - goto bail; - } else if (result == kNuRename) { - goto retry; - } else if (result == kNuOverwrite) { - /* delete the existing file immediately */ - LOGD(" Deleting existing file '%hs'", fsNormalBuf); - dierr = pDiskFS->DeleteFile(pExisting); - if (dierr != kDIErrNone) { - // Would be nice to show a dialog and explain *why*, but - // I'm not sure we have a window here. - LOGE(" Deletion failed (err=%d)", dierr); - goto bail; - } - } else { - LOGE("GLITCH: bad return %d from HandleReplaceExisting",result); - assert(false); - nuerr = kNuErrInternal; - goto bail; - } - } - - /* - * Put all the goodies into a new FileAddData object, and add it to - * the end of the list. - */ - FileAddData* pAddData; - pAddData = new FileAddData(pDetails, fsNormalBuf); - if (pAddData == NULL) { - nuerr = kNuErrMalloc; - goto bail; - } - - LOGD("FSNormalized is '%hs'", pAddData->GetFSNormalPath()); - - AddToAddDataList(pAddData); - -bail: - delete[] fsNormalBuf; - return nuerr; -} - -NuResult DiskArchive::HandleReplaceExisting(const A2File* pExisting, - LocalFileDetails* pDetails) -{ - NuResult result; - - if (fOverwriteNoAsk) { - if (fOverwriteExisting) - return kNuOverwrite; - else - return kNuSkip; - } - - ConfirmOverwriteDialog confOvwr; - - confOvwr.fExistingFile = Charset::ConvertMORToUNI(pExisting->GetPathName()); - confOvwr.fExistingFileModWhen = pExisting->GetModWhen(); - - PathName srcPath(pDetails->GetLocalPathName()); - confOvwr.fNewFileSource = pDetails->GetLocalPathName(); - confOvwr.fNewFileModWhen = srcPath.GetModWhen(); - - if (confOvwr.DoModal() == IDCANCEL) { - LOGI("User cancelled out of add-to-diskimg replace-existing"); - return kNuAbort; - } - - // TODO: if they rename one fork, we need to track that fact and - // carry it over to the other fork -- otherwise they can rename - // the data and resource forks into separate files. - if (confOvwr.fResultRename) { - /* - * Replace the name in FileDetails. They were asked to modify - * the already-normalized version of the filename. We will run - * it back through the FS-specific normalizer, which will handle - * any oddities they type in. - * - * We don't want to run it through PathProposal.LocalToArchive - * because that'll strip out ':' in the pathnames. - * - * Ideally the rename dialog would have a way to validate the - * full path and reject "OK" if it's not valid. Instead, we just - * allow the FS normalizer to force the filename to be valid. - */ - pDetails->SetStrippedLocalPathName(confOvwr.fExistingFile); - LOGI("Trying rename to '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - return kNuRename; - } - - if (confOvwr.fResultApplyToAll) { - fOverwriteNoAsk = true; - if (confOvwr.fResultOverwrite) - fOverwriteExisting = true; - else - fOverwriteExisting = false; - } - if (confOvwr.fResultOverwrite) - result = kNuOverwrite; - else - result = kNuSkip; - - return result; -} - -CString DiskArchive::ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL) -{ - CString errMsg; - FileAddData* pData; - unsigned char* dataBuf = NULL; - unsigned char* rsrcBuf = NULL; - long dataLen, rsrcLen; - MainWindow* pMainWin = (MainWindow*)::AfxGetMainWnd(); - - LOGI("--- ProcessFileAddData"); - - /* map the EOL conversion to something we can use */ - GenericEntry::ConvertEOL convEOL; - - switch (addOptsConvEOL) { - case AddFilesDialog::kConvEOLNone: - convEOL = GenericEntry::kConvertEOLOff; - break; - case AddFilesDialog::kConvEOLType: - // will be adjusted each time through the loop - convEOL = GenericEntry::kConvertEOLOff; - break; - case AddFilesDialog::kConvEOLAuto: - convEOL = GenericEntry::kConvertEOLAuto; - break; - case AddFilesDialog::kConvEOLAll: - convEOL = GenericEntry::kConvertEOLOn; - break; - default: - assert(false); - convEOL = GenericEntry::kConvertEOLOff; - break; - } - - - pData = fpAddDataHead; - while (pData != NULL) { - const LocalFileDetails* pDataDetails = NULL; - const LocalFileDetails* pRsrcDetails = NULL; - const LocalFileDetails* pDetails = pData->GetDetails(); - const char* typeStr = "????"; // for debug msg only - - switch (pDetails->GetEntryKind()) { - case LocalFileDetails::kFileKindDataFork: - pDataDetails = pDetails; - typeStr = "data"; - break; - case LocalFileDetails::kFileKindRsrcFork: - pRsrcDetails = pDetails; - typeStr = "rsrc"; - break; - case LocalFileDetails::kFileKindDiskImage: - pDataDetails = pDetails; - typeStr = "disk"; - break; - case LocalFileDetails::kFileKindBothForks: - case LocalFileDetails::kFileKindDirectory: - default: - assert(false); - return L"internal error"; - } - - if (pData->GetOtherFork() != NULL) { - pDetails = pData->GetOtherFork()->GetDetails(); - typeStr = "both"; - - switch (pDetails->GetEntryKind()) { - case LocalFileDetails::kFileKindDataFork: - assert(pDataDetails == NULL); - pDataDetails = pDetails; - break; - case LocalFileDetails::kFileKindRsrcFork: - assert(pRsrcDetails == NULL); - pRsrcDetails = pDetails; - break; - case LocalFileDetails::kFileKindDiskImage: - assert(false); - return L"(internal) add other disk error"; - case LocalFileDetails::kFileKindBothForks: - case LocalFileDetails::kFileKindDirectory: - default: - assert(false); - return L"internal error"; - } - } - - LOGI("Adding file '%ls' (%hs)", - (LPCWSTR) pDetails->GetStrippedLocalPathName(), typeStr); - ASSERT(pDataDetails != NULL || pRsrcDetails != NULL); - - /* - * The current implementation of DiskImg/DiskFS requires writing each - * fork in one shot. This means loading the entire thing into - * memory. Not so bad for ProDOS, with its 16MB maximum file size, - * but it could be awkward for HFS (not to mention HFS Plus!). - */ - DiskFS::CreateParms parms; - /* use the FS-normalized path here */ - /* (do we have to? do we want to?) */ - parms.pathName = pData->GetFSNormalPath(); - if (pRsrcDetails != NULL) - parms.storageType = kNuStorageExtended; - else - parms.storageType = kNuStorageSeedling; - /* copy the rest out of the LocalFileDetails */ - parms.fssep = pDetails->GetFssep(); - parms.fileType = pDetails->GetFileType(); - parms.auxType = pDetails->GetExtraType(); - parms.access = pDetails->GetAccess(); - parms.createWhen = NufxArchive::DateTimeToSeconds(&pDetails->GetCreateWhen()); - parms.modWhen = NufxArchive::DateTimeToSeconds(&pDetails->GetModWhen()); - - dataLen = rsrcLen = -1; - if (pDataDetails != NULL) { - /* figure out text conversion, including high ASCII for DOS */ - /* (HA conversion only happens if text conversion happens) */ - GenericEntry::ConvertHighASCII convHA; - if (addOptsConvEOL == AddFilesDialog::kConvEOLType) { - if (pDataDetails->GetFileType() == kFileTypeTXT || - pDataDetails->GetFileType() == kFileTypeSRC) - { - LOGI("Enabling text conversion by type"); - convEOL = GenericEntry::kConvertEOLOn; - } else { - convEOL = GenericEntry::kConvertEOLOff; - } - } - if (DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat())) - convHA = GenericEntry::kConvertHAOn; - else - convHA = GenericEntry::kConvertHAOff; - - errMsg = LoadFile(pDataDetails->GetLocalPathName(), &dataBuf, &dataLen, - convEOL, convHA); - if (!errMsg.IsEmpty()) - goto bail; - } - if (pRsrcDetails != NULL) { - /* no text conversion on resource forks */ - errMsg = LoadFile(pRsrcDetails->GetLocalPathName(), &rsrcBuf, &rsrcLen, - GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff); - if (!errMsg.IsEmpty()) - goto bail; - } - - /* really ought to do this separately for each thread */ - SET_PROGRESS_BEGIN(); - CString pathNameW(Charset::ConvertMORToUNI(parms.pathName)); - SET_PROGRESS_UPDATE2(0, pDetails->GetLocalPathName(), pathNameW); - - DIError dierr; - dierr = AddForksToDisk(pDiskFS, &parms, dataBuf, dataLen, - rsrcBuf, rsrcLen); - SET_PROGRESS_END(); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to add '%ls' to image: %hs.", - (LPCWSTR) pathNameW, DiskImgLib::DIStrError(dierr)); - goto bail; - } - delete[] dataBuf; - delete[] rsrcBuf; - dataBuf = rsrcBuf = NULL; - - pData = pData->GetNext(); - } - -bail: - delete[] dataBuf; - delete[] rsrcBuf; - return errMsg; -} - - -// TODO: really ought to update the progress counter, especially when reading -// really large files. -CString DiskArchive::LoadFile(const WCHAR* pathName, uint8_t** pBuf, long* pLen, - GenericEntry::ConvertEOL conv, GenericEntry::ConvertHighASCII convHA) const -{ - const char kCharLF = '\n'; - const char kCharCR = '\r'; - CString errMsg; - FILE* fp; - long fileLen; - - ASSERT(convHA == GenericEntry::kConvertHAOn || - convHA == GenericEntry::kConvertHAOff); - ASSERT(conv == GenericEntry::kConvertEOLOn || - conv == GenericEntry::kConvertEOLOff || - conv == GenericEntry::kConvertEOLAuto); - ASSERT(pathName != NULL); - ASSERT(pBuf != NULL); - ASSERT(pLen != NULL); - - fp = _wfopen(pathName, L"rb"); - if (fp == NULL) { - errMsg.Format(L"Unable to open '%ls': %hs.", pathName, - strerror(errno)); - goto bail; - } - - if (fseek(fp, 0, SEEK_END) != 0) { - errMsg.Format(L"Unable to seek to end of '%ls': %hs", pathName, - strerror(errno)); - goto bail; - } - fileLen = ftell(fp); - if (fileLen < 0) { - errMsg.Format(L"Unable to determine length of '%ls': %hs", pathName, - strerror(errno)); - goto bail; - } - rewind(fp); - - if (fileLen == 0) { // handle zero-length files - *pBuf = NULL; - *pLen = 0; - goto bail; - } else if (fileLen > 0x00ffffff) { - errMsg = L"Cannot add files larger than 16MB to a disk image."; - goto bail; - } - - *pBuf = new uint8_t[fileLen]; - if (*pBuf == NULL) { - errMsg.Format(L"Unable to allocate %ld bytes for '%ls'.", - fileLen, pathName); - goto bail; - } - - /* - * We're ready to load the file. We need to sort out EOL conversion. - * Since we always convert to CR, we know the file will stay the same - * size or get smaller, which means the buffer we've allocated is - * guaranteed to hold the file even if we convert it. - * - * If the text mode is "auto", we need to load a piece of the file and - * analyze it. - */ - if (conv == GenericEntry::kConvertEOLAuto) { - GenericEntry::EOLType eolType; - GenericEntry::ConvertHighASCII dummy; - - int chunkLen = 16384; // nice big chunk - if (chunkLen > fileLen) - chunkLen = fileLen; - - if (fread(*pBuf, chunkLen, 1, fp) != 1) { - errMsg.Format(L"Unable to read initial chunk of '%ls': %hs.", - pathName, strerror(errno)); - delete[] *pBuf; - *pBuf = NULL; - goto bail; - } - rewind(fp); - - conv = GenericEntry::DetermineConversion(*pBuf, chunkLen, - &eolType, &dummy); - LOGI("LoadFile DetermineConv returned conv=%d eolType=%d", - conv, eolType); - if (conv == GenericEntry::kConvertEOLOn && - eolType == GenericEntry::kEOLCR) - { - LOGI(" (skipping conversion due to matching eolType)"); - conv = GenericEntry::kConvertEOLOff; - } - } - ASSERT(conv != GenericEntry::kConvertEOLAuto); - - /* - * The "high ASCII" conversion is either on or off. In this context, - * "on" means "convert all text files", and "off" means "don't convert - * text files". We never convert non-text files. Conversion should - * always be "on" for DOS 3.2/3.3, and "off" for everything else (except - * RDOS, should we choose to make that writeable). - */ - if (conv == GenericEntry::kConvertEOLOff) { - /* fast path */ - LOGI(" +++ NOT converting text '%ls'", pathName); - if (fread(*pBuf, fileLen, 1, fp) != 1) { - errMsg.Format(L"Unable to read '%ls': %hs.", pathName, strerror(errno)); - delete[] *pBuf; - *pBuf = NULL; - goto bail; - } - } else { - /* - * Convert as we go. - * - * Observation: if we copy a binary file to a DOS disk, and force - * the text conversion, we will convert 0x0a to 0x0d, and thence - * to 0x8d. However, we may still have some 0x8a bytes lying around, - * because we don't convert 0x8a in the original file to anything. - * This means that a CR->CRLF or LF->CRLF conversion can't be - * "undone" on a DOS disk. (Not that anyone cares.) - */ - long count = fileLen; - int mask, ic; - bool lastCR = false; - unsigned char* buf = *pBuf; - - if (convHA == GenericEntry::kConvertHAOn) - mask = 0x80; - else - mask = 0x00; - - LOGI(" +++ Converting text '%ls', mask=0x%02x", pathName, mask); - - while (count--) { - ic = getc(fp); - - if (ic == kCharCR) { - *buf++ = (unsigned char) (kCharCR | mask); - lastCR = true; - } else if (ic == kCharLF) { - if (!lastCR) - *buf++ = (unsigned char) (kCharCR | mask); - lastCR = false; - } else { - if (ic == '\0') - *buf++ = (unsigned char) ic; // don't conv 0x00 - else - *buf++ = (unsigned char) (ic | mask); - lastCR = false; - } - } - fileLen = buf - *pBuf; - } - - (void) fclose(fp); - - *pLen = fileLen; - -bail: - return errMsg; -} - -DIError DiskArchive::AddForksToDisk(DiskFS* pDiskFS, - const DiskFS::CreateParms* pParms, - const unsigned char* dataBuf, long dataLen, - const unsigned char* rsrcBuf, long rsrcLen) const -{ - DIError dierr = kDIErrNone; - const int kFileTypeBIN = 0x06; - const int kFileTypeINT = 0xfa; - const int kFileTypeBAS = 0xfc; - A2File* pNewFile = NULL; - A2FileDescr* pOpenFile = NULL; - DiskFS::CreateParms parmCopy; - - /* - * Make a temporary copy, pointers and all, so we can rewrite some of - * the fields. This is sort of bad, because we're making copies of a - * const char* filename pointer whose underlying storage we're not - * really familiar with. However, so long as we don't try to retain - * it after this function returns we should be fine. - * - * Might be better to make CreateParms a class instead of a struct, - * make the pathName field new[] storage, and write a copy constructor - * for the operation below. This will be fine for now. - */ - memcpy(&parmCopy, pParms, sizeof(parmCopy)); - - if (rsrcLen >= 0) { - ASSERT(parmCopy.storageType == kNuStorageExtended); - } - - /* - * Look for "empty directory holders" that we put into NuFX archives - * when doing disk-to-archive conversions. These make no sense if - * there's no fssep (because it's coming from DOS), or if there's no - * base path, so we can ignore those cases. We can also ignore it if - * the file is forked or is already a directory. - */ - if (parmCopy.fssep != '\0' && parmCopy.storageType == kNuStorageSeedling) { - const char* cp; - cp = strrchr(parmCopy.pathName, parmCopy.fssep); - if (cp != NULL) { - if (strcmp(cp+1, kEmptyFolderMarker) == 0 && dataLen == 0) { - /* drop the junk on the end */ - parmCopy.storageType = kNuStorageDirectory; - CStringA replacementFileName(parmCopy.pathName);; - replacementFileName = - replacementFileName.Left(cp - parmCopy.pathName -1); - parmCopy.pathName = replacementFileName; - parmCopy.fileType = 0x0f; // DIR - parmCopy.access &= ~(A2FileProDOS::kAccessInvisible); - dataLen = -1; - } - } - } - - /* - * If this is a subdir create request (from the clipboard or an "empty - * directory placeholder" in a NuFX archive), handle it here. If we're - * on a filesystem that doesn't have subdirectories, just skip it. - */ - if (parmCopy.storageType == kNuStorageDirectory) { - A2File* pDummyFile; - ASSERT(dataLen < 0 && rsrcLen < 0); - - if (DiskImg::IsHierarchical(pDiskFS->GetDiskImg()->GetFSFormat())) { - dierr = pDiskFS->CreateFile(&parmCopy, &pDummyFile); - if (dierr == kDIErrDirectoryExists) - dierr = kDIErrNone; // dirs are not made unique - goto bail; - } else { - LOGI(" Ignoring subdir create req on non-hierarchic FS"); - goto bail; - } - } - - /* don't try to put resource forks onto a DOS disk */ - if (!DiskImg::HasResourceForks(pDiskFS->GetDiskImg()->GetFSFormat())) { - if (rsrcLen >= 0) { - rsrcLen = -1; - parmCopy.storageType = kNuStorageSeedling; - - if (dataLen < 0) { - /* this was a resource-fork-only file */ - LOGI("--- nothing left to write for '%hs'", - parmCopy.pathName); - goto bail; - } - } else { - ASSERT(parmCopy.storageType == kNuStorageSeedling); - } - } - - /* quick kluge to get the right file type on large DOS files */ - if (DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat()) && - dataLen >= 65536) - { - if (parmCopy.fileType == kFileTypeBIN || - parmCopy.fileType == kFileTypeINT || - parmCopy.fileType == kFileTypeBAS) - { - LOGI("+++ switching DOS file type to $f2"); - parmCopy.fileType = 0xf2; // DOS 'S' file - } - } - - /* - * Create the file on the disk. The storage type determines whether - * it has data+rsrc forks or just data (there's no such thing in - * ProDOS as "just a resource fork"). There's no need to open the - * fork if we're not going to write to it. - * - * This holds for resource forks as well, because the storage type - * determines whether or not the file is forked, and we've asserted - * that a file with a non-(-1) rsrcLen is forked. - */ - dierr = pDiskFS->CreateFile(&parmCopy, &pNewFile); - if (dierr != kDIErrNone) { - LOGI(" CreateFile failed: %hs", DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* - * Note: if this was an empty directory holder, pNewFile will be set - * to NULL. We used to avoid handling this by just not opening the file - * if it had a length of zero. However, DOS 3.3 needs to write some - * kinds of zero-length files, because e.g. a zero-length 'B' file - * actually has 4 bytes of data in it. - * - * Of course, if dataLen is zero then dataBuf is NULL, so we need to - * supply a dummy write buffer. None of this is an issue for resource - * forks, because DOS 3.3 doesn't have those. - */ - - if (dataLen > 0 || - (dataLen == 0 && pNewFile != NULL)) - { - ASSERT(pNewFile != NULL); - unsigned char dummyBuf[1] = { '\0' }; - - dierr = pNewFile->Open(&pOpenFile, false, false); - if (dierr != kDIErrNone) - goto bail; - - pOpenFile->SetProgressUpdater(DiskArchive::ProgressCallback, - dataLen, NULL); - - dierr = pOpenFile->Write(dataBuf != NULL ? dataBuf : dummyBuf, dataLen); - if (dierr != kDIErrNone) - goto bail; - - dierr = pOpenFile->Close(); - if (dierr != kDIErrNone) - goto bail; - pOpenFile = NULL; - } - - if (rsrcLen > 0) { - ASSERT(pNewFile != NULL); - - dierr = pNewFile->Open(&pOpenFile, false, true); - if (dierr != kDIErrNone) - goto bail; - - pOpenFile->SetProgressUpdater(DiskArchive::ProgressCallback, - rsrcLen, NULL); - - dierr = pOpenFile->Write(rsrcBuf, rsrcLen); - if (dierr != kDIErrNone) - goto bail; - - dierr = pOpenFile->Close(); - if (dierr != kDIErrNone) - goto bail; - pOpenFile = NULL; - } - -bail: - if (pOpenFile != NULL) - pOpenFile->Close(); - if (dierr != kDIErrNone && pNewFile != NULL) { - /* - * Clean up the partially-written file. This does not, of course, - * erase any subdirectories that were created to contain this file. - * Not worth worrying about. - */ - LOGI(" Deleting newly-created file '%hs'", parmCopy.pathName); - (void) pDiskFS->DeleteFile(pNewFile); - } - return dierr; -} - -void DiskArchive::AddToAddDataList(FileAddData* pData) -{ - ASSERT(pData != NULL); - ASSERT(pData->GetNext() == NULL); - - /* - * Run through the entire existing list, looking for a match. This is - * O(n^2) behavior, but I'm expecting N to be relatively small (under - * 1000 in almost all cases). - */ - FileAddData* pSearch = fpAddDataHead; - LocalFileDetails::FileKind dataKind, listKind; - - dataKind = pData->GetDetails()->GetEntryKind(); - while (pSearch != NULL) { - if (pSearch->GetOtherFork() == NULL && - wcscmp(pSearch->GetDetails()->GetStrippedLocalPathName(), - pData->GetDetails()->GetStrippedLocalPathName()) == 0) - { - //NuThreadID dataID = pData->GetDetails()->threadID; - //NuThreadID listID = pSearch->GetDetails()->threadID; - - listKind = pSearch->GetDetails()->GetEntryKind(); - - /* got a name match */ - if (dataKind != listKind && - (dataKind == LocalFileDetails::kFileKindDataFork || - dataKind == LocalFileDetails::kFileKindRsrcFork) && - (listKind == LocalFileDetails::kFileKindDataFork || - listKind == LocalFileDetails::kFileKindRsrcFork)) - { - /* looks good, hook it in here instead of the list */ - LOGD("--- connecting forks of '%ls' and '%ls'", - (LPCWSTR) pData->GetDetails()->GetLocalPathName(), - (LPCWSTR) pSearch->GetDetails()->GetLocalPathName()); - pSearch->SetOtherFork(pData); - return; - } - } - - pSearch = pSearch->GetNext(); - } - - if (fpAddDataHead == NULL) { - assert(fpAddDataTail == NULL); - fpAddDataHead = fpAddDataTail = pData; - } else { - fpAddDataTail->SetNext(pData); - fpAddDataTail = pData; - } -} - -void DiskArchive::FreeAddDataList(void) -{ - FileAddData* pData; - FileAddData* pNext; - - pData = fpAddDataHead; - while (pData != NULL) { - pNext = pData->GetNext(); - delete pData->GetOtherFork(); - delete pData; - pData = pNext; - } - - fpAddDataHead = fpAddDataTail = NULL; -} - - -/* - * =========================================================================== - * DiskArchive -- create subdir - * =========================================================================== - */ - -bool DiskArchive::CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) -{ - ASSERT(newName != NULL && wcslen(newName) > 0); - DiskEntry* pEntry = (DiskEntry*) pParentEntry; - ASSERT(pEntry != NULL); - A2File* pFile = pEntry->GetA2File(); - ASSERT(pFile != NULL); - DiskFS* pDiskFS = pFile->GetDiskFS(); - ASSERT(pDiskFS != NULL); - - if (!pFile->IsDirectory()) { - ASSERT(false); - return false; - } - - DIError dierr; - A2File* pNewFile = NULL; - DiskFS::CreateParms parms; - CStringA pathNameMOR; - time_t now = time(NULL); - - /* - * Create the full path. - */ - if (pFile->IsVolumeDirectory()) { - pathNameMOR = newName; - } else { - pathNameMOR = pParentEntry->GetPathNameMOR(); - pathNameMOR += pParentEntry->GetFssep(); - pathNameMOR += newName; - } - ASSERT(wcschr(newName, pParentEntry->GetFssep()) == NULL); - - /* using NufxLib constants; they match with ProDOS */ - memset(&parms, 0, sizeof(parms)); - parms.pathName = pathNameMOR; - parms.fssep = pParentEntry->GetFssep(); - parms.storageType = kNuStorageDirectory; - parms.fileType = 0x0f; // ProDOS DIR - parms.auxType = 0; - parms.access = kNuAccessUnlocked; - parms.createWhen = now; - parms.modWhen = now; - - dierr = pDiskFS->CreateFile(&parms, &pNewFile); - if (dierr != kDIErrNone) { - CString errMsg; - errMsg.Format(L"Unable to create subdirectory: %hs.\n", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - return false; - } - - if (InternalReload(pMsgWnd) != 0) - return false; - - return true; -} - - -/* - * =========================================================================== - * DiskArchive -- delete selection - * =========================================================================== - */ - -/*static*/ int DiskArchive::CompareDisplayNamesDesc(const void* ventry1, - const void* ventry2) -{ - const DiskEntry* pEntry1 = *((const DiskEntry**) ventry1); - const DiskEntry* pEntry2 = *((const DiskEntry**) ventry2); - - return wcsicmp(pEntry2->GetDisplayName(), pEntry1->GetDisplayName()); -} - -bool DiskArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - /* - * The DiskFS DeleteFile() function will not delete a subdirectory unless - * it is empty. This complicates matters somewhat for us, because the - * selection set isn't in any particular order. We need to sort on the - * pathname and then delete bottom-up. - * - * CiderPress does work to ensure that, if a subdir is selected, everything - * in that subdir is also selected. So if we just delete everything in the - * right order, we should be okay. - */ - CString errMsg; - SelectionEntry* pSelEntry; - DiskEntry* pEntry; - DIError dierr; - bool retVal = false; - - SET_PROGRESS_BEGIN(); - - /* - * Start by copying the DiskEntry pointers out of the selection set and - * into an array. The selection set was created such that there is one - * entry in the set for each file. (The file viewer likes to have one - * entry for each thread.) - */ - int numEntries = pSelSet->GetNumEntries(); - ASSERT(numEntries > 0); - DiskEntry** entryArray = new DiskEntry*[numEntries]; - int idx = 0; - - pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = (DiskEntry*) pSelEntry->GetEntry(); - ASSERT(pEntry != NULL); - - entryArray[idx++] = pEntry; - LOGI("Added 0x%08lx '%ls'", (long) entryArray[idx-1], - (LPCWSTR) entryArray[idx-1]->GetDisplayName()); - - pSelEntry = pSelSet->IterNext(); - } - ASSERT(idx == numEntries); - - /* - * Sort the file array by descending filename. - */ - ::qsort(entryArray, numEntries, sizeof(DiskEntry*), CompareDisplayNamesDesc); - - /* - * Run through the sorted list, deleting each entry. - */ - for (idx = 0; idx < numEntries; idx++) { - A2File* pFile; - - pEntry = entryArray[idx]; - pFile = pEntry->GetA2File(); - - /* - * We shouldn't be here at all if the main volume were opened - * read-only. However, it's possible that the main is read-write - * and our sub-volumes are read-only (probably because we don't - * support write access to the filesystem). - */ - if (!pFile->GetDiskFS()->GetReadWriteSupported()) { - errMsg.Format(L"Unable to delete '%ls' on '%hs': operation not supported.", - (LPCWSTR) pEntry->GetDisplayName(), - (LPCSTR) pFile->GetDiskFS()->GetVolumeName()); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - LOGI(" Deleting '%ls' from '%hs'", (LPCWSTR) pEntry->GetPathNameUNI(), - (LPCSTR) pFile->GetDiskFS()->GetVolumeName()); - // TODO: should be using display name for progress updater? - SET_PROGRESS_UPDATE2(0, pEntry->GetPathNameUNI(), NULL); - - /* - * Ask the DiskFS to delete the file. As soon as this completes, - * "pFile" is invalid and must not be dereferenced. - */ - dierr = pFile->GetDiskFS()->DeleteFile(pFile); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to delete '%ls' on '%hs': %hs.", - (LPCWSTR) pEntry->GetDisplayName(), - (LPCSTR) pFile->GetDiskFS()->GetVolumeName(), - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - SET_PROGRESS_UPDATE(100); - - /* - * Be paranoid and zap the pointer, on the off chance somebody - * tries to redraw the content list from the deleted data. - * - * In practice we don't work this way -- the stuff that gets drawn - * on the screen comes out of GenericEntry, not A2File. If this - * changes we'll need to raise the "reload" flag here, before the - * reload, to prevent the ContentList from chasing a bad pointer. - */ - pEntry->SetA2File(NULL); - } - - retVal = true; - -bail: - SET_PROGRESS_END(); - delete[] entryArray; - if (InternalReload(pMsgWnd) != 0) - retVal = false; - - return retVal; -} - -/* - * =========================================================================== - * DiskArchive -- rename files - * =========================================================================== - */ - -bool DiskArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - /* - * If we rename a subdirectory, it could affect the next thing we try to - * rename (because we show the full path). We have to reload our file - * list from the DiskFS after each renamed subdir. The trouble is that - * this invalidates the data displayed in the ContentList, and we won't - * redraw the screen correctly. We can work around the problem by getting - * the pathname directly from the DiskFS instead of from DiskEntry, though - * it's not immediately obvious which is less confusing. - */ - CString errMsg; - bool retVal = false; - - LOGI("Renaming %d entries", pSelSet->GetNumEntries()); - - /* - * For each item in the selection set, bring up the "rename" dialog, - * and ask the GenericEntry to process it. - * - * If they hit "cancel" or there's an error, we still flush the - * previous changes. This is so that we don't have to create the - * same sort of deferred-write feature when renaming things in other - * sorts of archives (e.g. disk archives). - */ - SelectionEntry* pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - RenameEntryDialog renameDlg(pMsgWnd); - DiskEntry* pEntry = (DiskEntry*) pSelEntry->GetEntry(); - - LOGI(" Renaming '%ls'", (LPCWSTR) pEntry->GetPathNameUNI()); - if (!SetRenameFields(pMsgWnd, pEntry, &renameDlg)) - break; - - int result; - if (pEntry->GetA2File()->IsVolumeDirectory()) - result = IDIGNORE; // don't allow rename of volume dir - else - result = renameDlg.DoModal(); - if (result == IDOK) { - DIError dierr; - DiskFS* pDiskFS; - A2File* pFile; - - pFile = pEntry->GetA2File(); - pDiskFS = pFile->GetDiskFS(); - CStringA newNameA(renameDlg.fNewName); - dierr = pDiskFS->RenameFile(pFile, newNameA); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to rename '%ls' to '%ls': %hs.", - (LPCWSTR) pEntry->GetPathNameUNI(), - (LPCWSTR) renameDlg.fNewName, - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - LOGD("Rename of '%ls' to '%ls' succeeded", - (LPCWSTR) pEntry->GetDisplayName(), - (LPCWSTR) renameDlg.fNewName); - } else if (result == IDCANCEL) { - LOGI("Canceling out of remaining renames"); - break; - } else { - /* 3rd possibility is IDIGNORE, i.e. skip this entry */ - LOGI("Skipping rename of '%ls'", (LPCWSTR) pEntry->GetDisplayName()); - } - - pSelEntry = pSelSet->IterNext(); - } - - /* reload GenericArchive from disk image */ - if (InternalReload(pMsgWnd) == kNuErrNone) - retVal = true; - -bail: - return retVal; -} - -bool DiskArchive::SetRenameFields(CWnd* pMsgWnd, DiskEntry* pEntry, - RenameEntryDialog* pDialog) -{ - DiskFS* pDiskFS; - - ASSERT(pEntry != NULL); - - /* - * Figure out if we're allowed to change the entire path. (This is - * doing it the hard way, but what the hell.) - */ - long cap = GetCapability(GenericArchive::kCapCanRenameFullPath); - bool renameFullPath = (cap != 0); - - // a bit round-about, but it works - pDiskFS = pEntry->GetA2File()->GetDiskFS(); - - /* - * Make sure rename is allowed. It's nice to do these *before* putting - * up the rename dialog, so that the user doesn't do a bunch of typing - * before being told that it's pointless. - */ - if (!pDiskFS->GetReadWriteSupported()) { - CString errMsg; - errMsg.Format(L"Unable to rename '%ls': operation not supported.", - (LPCWSTR) pEntry->GetPathNameUNI()); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - return false; - } - if (pDiskFS->GetFSDamaged()) { - CString errMsg; - errMsg.Format(L"Unable to rename '%ls': the disk it's on appears to be damaged.", - (LPCWSTR) pEntry->GetPathNameUNI()); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - return false; - } - - pDialog->SetCanRenameFullPath(renameFullPath); - pDialog->fOldName = pEntry->GetPathNameUNI(); - pDialog->fFssep = pEntry->GetFssep(); - pDialog->fpArchive = this; - pDialog->fpEntry = pEntry; - - return true; -} - -CString DiskArchive::TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, char newFssep) const -{ - const DiskEntry* pEntry = (DiskEntry*) pGenericEntry; - DiskImg::FSFormat format; - CString errMsg, pathName; - CStringA newNameA; - DiskFS* pDiskFS; - - if (basePath.IsEmpty()) { - pathName = newName; - } else { - pathName = basePath; - pathName += newFssep; - pathName += newName; - } - - pDiskFS = pEntry->GetA2File()->GetDiskFS(); - format = pDiskFS->GetDiskImg()->GetFSFormat(); - - /* look for an existing file, but don't compare against self */ - A2File* existingFile; - CStringA pathNameA(pathName); - existingFile = pDiskFS->GetFileByName(pathNameA); - if (existingFile != NULL && existingFile != pEntry->GetA2File()) { - errMsg = L"A file with that name already exists."; - goto bail; - } - - newNameA = newName; - switch (format) { - case DiskImg::kFormatProDOS: - if (!DiskFSProDOS::IsValidFileName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_FILENAME_PRODOS); - break; - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - if (!DiskFSDOS33::IsValidFileName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_FILENAME_DOS); - break; - case DiskImg::kFormatPascal: - if (!DiskFSPascal::IsValidFileName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_FILENAME_PASCAL); - break; - case DiskImg::kFormatMacHFS: - if (!DiskFSHFS::IsValidFileName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_FILENAME_HFS); - break; - default: - errMsg = L"Not supported by TestPathName!"; - } - -bail: - return errMsg; -} - - -/* - * =========================================================================== - * DiskArchive -- rename a volume - * =========================================================================== - */ - -bool DiskArchive::RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) -{ - DIError dierr; - CString errMsg; - bool retVal = true; - - CStringA newNameA(newName); - dierr = pDiskFS->RenameVolume(newNameA); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to rename volume: %hs.\n", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - retVal = false; - /* fall through to reload anyway */ - } - - /* reload GenericArchive from disk image */ - if (InternalReload(pMsgWnd) != 0) - retVal = false; - - return retVal; -} - -CString DiskArchive::TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const -{ - DiskImg::FSFormat format; - CString errMsg; - - ASSERT(pDiskFS != NULL); - ASSERT(newName != NULL); - - format = pDiskFS->GetDiskImg()->GetFSFormat(); - - CStringA newNameA(newName); - switch (format) { - case DiskImg::kFormatProDOS: - if (!DiskFSProDOS::IsValidVolumeName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_PRODOS); - break; - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - if (!DiskFSDOS33::IsValidVolumeName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_DOS); - break; - case DiskImg::kFormatPascal: - if (!DiskFSPascal::IsValidVolumeName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_PASCAL); - break; - case DiskImg::kFormatMacHFS: - if (!DiskFSHFS::IsValidVolumeName(newNameA)) - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_HFS); - break; - default: - errMsg = L"Not supported by TestVolumeName!"; - break; - } - - return errMsg; -} - - -/* - * =========================================================================== - * DiskArchive -- set file properties - * =========================================================================== - */ - -bool DiskArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pGenericEntry, - const FileProps* pProps) -{ - /* - * Technically we should reload the GenericArchive from the disk image, - * but the set of changes is pretty small, so we just make them here. - */ - DIError dierr; - DiskEntry* pEntry = (DiskEntry*) pGenericEntry; - A2File* pFile = pEntry->GetA2File(); - - dierr = pFile->GetDiskFS()->SetFileInfo(pFile, pProps->fileType, - pProps->auxType, pProps->access); - if (dierr != kDIErrNone) { - CString errMsg; - errMsg.Format(L"Unable to set file info: %hs.\n", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - return false; - } - - /* do this in lieu of reloading GenericArchive */ - pEntry->SetFileType(pFile->GetFileType()); - pEntry->SetAuxType(pFile->GetAuxType()); - pEntry->SetAccess(pFile->GetAccess()); - - /* DOS 3.2/3.3 may change these as well */ - DiskImg::FSFormat fsFormat; - fsFormat = pFile->GetDiskFS()->GetDiskImg()->GetFSFormat(); - if (fsFormat == DiskImg::kFormatDOS32 || fsFormat == DiskImg::kFormatDOS33) { - LOGD(" (reloading additional fields after DOS SFI)"); - pEntry->SetDataForkLen(pFile->GetDataLength()); - pEntry->SetCompressedLen(pFile->GetDataSparseLength()); - pEntry->SetSuspicious(pFile->GetQuality() == A2File::kQualitySuspicious); - } - - /* clear the dirty flag in trivial cases */ - (void) fpPrimaryDiskFS->Flush(DiskImg::kFlushFastOnly); - - return true; -} - - -/* - * =========================================================================== - * DiskArchive -- transfer files to another archive - * =========================================================================== - */ - -GenericArchive::XferStatus DiskArchive::XferSelection(CWnd* pMsgWnd, - SelectionSet* pSelSet, ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) -{ - /* - * We get the open archive pointer and some options from "pXferOpts". - * - * The selection set was created with the "any" selection criteria, which - * means there's only one entry for each file regardless of whether it's - * forked or not. - */ - LOGI("DiskArchive XferSelection!"); - uint8_t* dataBuf = NULL; - uint8_t* rsrcBuf = NULL; - LocalFileDetails fileDetails; - CString errMsg, extractErrMsg, cmpStr; - CString fixedPathName; - XferStatus retval = kXferFailed; - - pXferOpts->fTarget->XferPrepare(pXferOpts); - - SelectionEntry* pSelEntry = pSelSet->IterNext(); - for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) { - long dataLen=-1, rsrcLen=-1; - DiskEntry* pEntry = (DiskEntry*) pSelEntry->GetEntry(); - int typeOverride = -1; - int result; - - ASSERT(dataBuf == NULL); - ASSERT(rsrcBuf == NULL); - - if (pEntry->GetDamaged()) { - LOGI(" XFER skipping damaged entry '%ls'", - (LPCWSTR) pEntry->GetDisplayName()); - continue; - } - - /* - * Do a quick de-colonizing pass for non-ProDOS volumes, then prepend - * the subvolume name (if any). - */ - fixedPathName = pEntry->GetPathNameUNI(); - if (fixedPathName.IsEmpty()) - fixedPathName = L"(no filename)"; - if (pEntry->GetFSFormat() != DiskImg::kFormatProDOS) - fixedPathName.Replace(PathProposal::kDefaultStoredFssep, '.'); - if (!pEntry->GetSubVolName().IsEmpty()) { - CString tmpStr = pEntry->GetSubVolName(); - tmpStr += (char) PathProposal::kDefaultStoredFssep; - tmpStr += fixedPathName; - fixedPathName = tmpStr; - } - - if (pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) { - /* this is the volume dir */ - LOGD(" XFER not transferring volume dir '%ls'", - (LPCWSTR) fixedPathName); - continue; - } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) { - if (pXferOpts->fPreserveEmptyFolders) { - /* if this is an empty directory, create a fake entry */ - cmpStr = fixedPathName; - cmpStr += (char)PathProposal::kDefaultStoredFssep; - - if (pSelSet->CountMatchingPrefix(cmpStr) == 0) { - LOGD("FOUND empty dir '%ls'", (LPCWSTR) fixedPathName); - cmpStr += kEmptyFolderMarker; - dataBuf = new unsigned char[1]; - dataLen = 0; - fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork); - fileDetails.SetStrippedLocalPathName(cmpStr); - fileDetails.SetFileType(0); // NON - fileDetails.SetAccess( - pEntry->GetAccess() | GenericEntry::kAccessInvisible); - goto have_stuff2; - } else { - LOGD("NOT empty dir '%ls'", (LPCWSTR) fixedPathName); - } - } - - LOGD(" XFER not transferring directory '%ls'", - (LPCWSTR) fixedPathName); - continue; - } - - LOGI(" Xfer '%ls' (data=%d rsrc=%d)", - (LPCWSTR) fixedPathName, pEntry->GetHasDataFork(), - pEntry->GetHasRsrcFork()); - - dataBuf = NULL; - dataLen = 0; - result = pEntry->ExtractThreadToBuffer(GenericEntry::kDataThread, - (char**) &dataBuf, &dataLen, &extractErrMsg); - if (result == IDCANCEL) { - LOGI("Cancelled during data extract!"); - goto bail; /* abort anything that was pending */ - } else if (result != IDOK) { - errMsg.Format(L"Failed while extracting '%ls': %ls.", - (LPCWSTR) fixedPathName, (LPCWSTR) extractErrMsg); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - ASSERT(dataBuf != NULL); - ASSERT(dataLen >= 0); - -#if 0 - if (pXferOpts->fConvDOSText && - DiskImg::UsesDOSFileStructure(pEntry->GetFSFormat()) && - pEntry->GetFileType() == kFileTypeTXT) - { - /* don't need to convert EOL, so just strip in place */ - long len; - unsigned char* ucp; - - LOGI(" Converting DOS text in '%ls'", fixedPathName); - for (ucp = dataBuf, len = dataLen; len > 0; len--, ucp++) - *ucp = *ucp & 0x7f; - } -#endif - -#if 0 // annoying to invoke PTX reformatter from here... ReformatHolder, etc. - if (pXferOpts->fConvPascalText && - pEntry->GetFSFormat() == DiskImg::kFormatPascal && - pEntry->GetFileType() == kFileTypePTX) - { - LOGI("WOULD CONVERT ptx '%ls'", fixedPathName); - } -#endif - - if (pEntry->GetHasRsrcFork()) { - rsrcBuf = NULL; - rsrcLen = 0; - result = pEntry->ExtractThreadToBuffer(GenericEntry::kRsrcThread, - (char**) &rsrcBuf, &rsrcLen, &extractErrMsg); - if (result == IDCANCEL) { - LOGI("Cancelled during rsrc extract!"); - goto bail; /* abort anything that was pending */ - } else if (result != IDOK) { - errMsg.Format(L"Failed while extracting '%ls': %ls.", - (LPCWSTR) fixedPathName, (LPCWSTR) extractErrMsg); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - } else { - ASSERT(rsrcBuf == NULL); - } - - if (pEntry->GetHasDataFork() && pEntry->GetHasRsrcFork()) { - fileDetails.SetEntryKind(LocalFileDetails::kFileKindBothForks); - } else if (pEntry->GetHasDataFork()) { - fileDetails.SetEntryKind(LocalFileDetails::kFileKindDataFork); - } else if (pEntry->GetHasRsrcFork()) { - fileDetails.SetEntryKind(LocalFileDetails::kFileKindRsrcFork); - } else { - ASSERT(false); - fileDetails.SetEntryKind(LocalFileDetails::kFileKindUnknown); - } - - /* - * Set up the rest of the LocalFileDetails fields. - */ - fileDetails.SetStrippedLocalPathName(fixedPathName); - fileDetails.SetFileType(pEntry->GetFileType()); - fileDetails.SetAccess(pEntry->GetAccess()); -have_stuff2: - fileDetails.SetFileSysFmt(pEntry->GetSourceFS()); - fileDetails.SetFssep(PathProposal::kDefaultStoredFssep); - fileDetails.SetExtraType(pEntry->GetAuxType()); - fileDetails.SetStorageType(kNuStorageUnknown); // let NufxLib deal - - NuDateTime ndt; - time_t when; - when = time(NULL); - UNIXTimeToDateTime(&when, &ndt); - fileDetails.SetArchiveWhen(ndt); - when = pEntry->GetModWhen(); - UNIXTimeToDateTime(&when, &ndt); - fileDetails.SetModWhen(ndt); - when = pEntry->GetCreateWhen(); - UNIXTimeToDateTime(&when, &ndt); - fileDetails.SetCreateWhen(ndt); - - pActionProgress->SetArcName(fileDetails.GetStrippedLocalPathName()); - if (pActionProgress->SetProgress(0) == IDCANCEL) { - retval = kXferCancelled; - goto bail; - } - - errMsg = pXferOpts->fTarget->XferFile(&fileDetails, &dataBuf, dataLen, - &rsrcBuf, rsrcLen); - if (!errMsg.IsEmpty()) { - LOGI("XferFile failed!"); - errMsg.Format(L"Failed while transferring '%ls': %ls.", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - ASSERT(dataBuf == NULL); - ASSERT(rsrcBuf == NULL); - - if (pActionProgress->SetProgress(100) == IDCANCEL) { - retval = kXferCancelled; - goto bail; - } - } - - //MainWindow* pMainWin; - //pMainWin = (MainWindow*)::AfxGetMainWnd(); - //pMainWin->EventPause(1000); - - retval = kXferOK; - -bail: - if (retval != kXferOK) - pXferOpts->fTarget->XferAbort(pMsgWnd); - else - pXferOpts->fTarget->XferFinish(pMsgWnd); - delete[] dataBuf; - delete[] rsrcBuf; - return retval; -} - -void DiskArchive::XferPrepare(const XferFileOptions* pXferOpts) -{ - LOGI("DiskArchive::XferPrepare"); - - //fpPrimaryDiskFS->SetParameter(DiskFS::kParmProDOS_AllowLowerCase, - // pXferOpts->fAllowLowerCase); - //fpPrimaryDiskFS->SetParameter(DiskFS::kParmProDOS_AllocSparse, - // pXferOpts->fUseSparseBlocks); - fpPrimaryDiskFS->SetParameter(DiskFS::kParm_CreateUnique, true); - - //fXferStoragePrefix = pXferOpts->fStoragePrefix; - fpXferTargetFS = pXferOpts->fpTargetFS; -} - -CString DiskArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) -{ - DiskFS* pDiskFS; - CString errMsg; - DIError dierr = kDIErrNone; - - LOGI(" XFER: transfer '%ls' (dataLen=%ld rsrcLen=%ld)", - (LPCWSTR) pDetails->GetStrippedLocalPathName(), dataLen, rsrcLen); - - ASSERT(pDataBuf != NULL); - ASSERT(pRsrcBuf != NULL); - - const DiskFS::CreateParms& createParms = pDetails->GetCreateParms(); - - if (fpXferTargetFS == NULL) - pDiskFS = fpPrimaryDiskFS; - else - pDiskFS = fpXferTargetFS; - - /* - * Strip the high ASCII from DOS and RDOS text files, unless we're adding - * them to a DOS disk. Likewise, if we're adding non-DOS text files to - * a DOS disk, we need to add the high bit. - * - * DOS converts both TXT and SRC to 'T', so we have to handle both here. - * Ideally we'd just ask DOS, "do you think this is a text file?", but it's - * not worth adding a new interface just for that. - */ - bool srcIsDOS, dstIsDOS; - srcIsDOS = DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt()); - dstIsDOS = DiskImg::UsesDOSFileStructure(pDiskFS->GetDiskImg()->GetFSFormat()); - if (dataLen > 0 && - (pDetails->GetFileType() == kFileTypeTXT || - pDetails->GetFileType() == kFileTypeSRC)) - { - unsigned char* ucp = *pDataBuf; - long len = dataLen; - - if (srcIsDOS && !dstIsDOS) { - LOGD(" Stripping high ASCII from '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - - while (len--) - *ucp++ &= 0x7f; - } else if (!srcIsDOS && dstIsDOS) { - LOGD(" Adding high ASCII to '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - - while (len--) { - if (*ucp != '\0') - *ucp |= 0x80; - ucp++; - } - } else if (srcIsDOS && dstIsDOS) { - LOGD(" --- not altering DOS-to-DOS text '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - } else { - LOGD(" --- non-DOS transfer '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - } - } - - /* add a file with one or two forks */ - if (createParms.storageType == kNuStorageDirectory) { - ASSERT(dataLen < 0 && rsrcLen < 0); - } else { - ASSERT(dataLen >= 0 || rsrcLen >= 0); // at least one fork - } - - /* if we still have something to write, write it */ - dierr = AddForksToDisk(pDiskFS, &createParms, *pDataBuf, dataLen, - *pRsrcBuf, rsrcLen); - if (dierr != kDIErrNone) { - errMsg.Format(L"%hs", DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* clean up */ - delete[] *pDataBuf; - *pDataBuf = NULL; - delete[] *pRsrcBuf; - *pRsrcBuf = NULL; - -bail: - return errMsg; -} - -void DiskArchive::XferAbort(CWnd* pMsgWnd) -{ - // Can't undo previous actions. - LOGI("DiskArchive::XferAbort"); - InternalReload(pMsgWnd); -} - -void DiskArchive::XferFinish(CWnd* pMsgWnd) -{ - LOGI("DiskArchive::XferFinish"); - InternalReload(pMsgWnd); -} diff --git a/ciderpress/app/DiskArchive.h b/ciderpress/app/DiskArchive.h deleted file mode 100644 index 41ba8f5..0000000 --- a/ciderpress/app/DiskArchive.h +++ /dev/null @@ -1,416 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Disk image "archive" support. - */ -#ifndef APP_DISKARCHIVE_H -#define APP_DISKARCHIVE_H - -#include "GenericArchive.h" -#include "../diskimg/DiskImg.h" - -class RenameEntryDialog; - - -/* - * One file in a disk image. - */ -class DiskEntry : public GenericEntry { -public: - DiskEntry(A2File* pFile) : fpFile(pFile) - {} - virtual ~DiskEntry(void) {} - - virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const override; - virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const override; - - virtual long GetSelectionSerial(void) const override - { return -1; } // idea: T/S block number - - /* - * Figure out whether or not we're allowed to change a file's type and - * aux type. - */ - virtual bool GetFeatureFlag(Feature feature) const override; - - // return the underlying FS format for this file - virtual DiskImg::FSFormat GetFSFormat(void) const { - ASSERT(fpFile != NULL); - return fpFile->GetFSFormat(); - } - - A2File* GetA2File(void) const { return fpFile; } - void SetA2File(A2File* pFile) { fpFile = pFile; } - -private: - /* - * Copy data from the open A2File to outfp, possibly converting EOL along - * the way. - */ - DIError CopyData(A2FileDescr* pOpenFile, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pMsg) const; - - A2File* fpFile; -}; - - -/* - * Disk image add-ons to GenericArchive. - */ -class DiskArchive : public GenericArchive { -public: - DiskArchive(void) : fpPrimaryDiskFS(NULL), fIsReadOnly(false), - fpAddDataHead(NULL), fpAddDataTail(NULL) - {} - virtual ~DiskArchive(void) { (void) Close(); } - - /* pass this as the "options" value to the New() function */ - typedef struct { - DiskImgLib::DiskImg::FSFormat format; - DiskImgLib::DiskImg::SectorOrder sectorOrder; - } NewOptionsBase; - typedef union { - NewOptionsBase base; - - struct { - NewOptionsBase base; - long numBlocks; - } blank; - struct { - NewOptionsBase base; - const WCHAR* volName; - long numBlocks; - } prodos; - struct { - NewOptionsBase base; - const WCHAR* volName; - long numBlocks; - } pascalfs; // "pascal" is reserved token in MSVC++ - struct { - NewOptionsBase base; - const WCHAR* volName; - long numBlocks; - } hfs; - struct { - NewOptionsBase base; - int volumeNum; - long numTracks; - int numSectors; - bool allocDOSTracks; - } dos; - } NewOptions; - - /* - * Perform one-time initialization of the DiskLib library. - */ - static CString AppInit(void); - - /* - * Perform one-time cleanup of DiskImgLib at shutdown time. - */ - static void AppCleanup(void); - - /* - * Finish instantiating a DiskArchive object by opening an existing file. - */ - virtual OpenResult Open(const WCHAR* filename, bool readOnly, - CString* pErrMsg) override; - - /* - * Finish instantiating a DiskArchive object by creating a new archive. - * - * Returns an error string on failure, or "" on success. - */ - virtual CString New(const WCHAR* filename, const void* options) override; - - /* - * Flush the DiskArchive object. - * - * Most of the stuff we do with disk images goes straight through, but in - * the case of compressed disks we don't normally re-compress them until - * it's time to close them. This forces us to update the copy on disk. - * - * Returns an empty string on success, or an error message on failure. - */ - virtual CString Flush(void) override; - - /* - * Reload the stuff from the underlying DiskFS. - * - * This also does a "lite" flush of the disk data. For files that are - * essentially being written as we go, this does little more than clear - * the "dirty" flag. Files that need to be recompressed or have some - * other slow operation remain dirty. - * - * We don't need to do the flush as part of the reload -- we can load the - * contents with everything in a perfectly dirty state. We don't need to - * do it at all. We do it to keep the "dirty" flag clear when nothing is - * really dirty, and we do it here because almost all of our functions call - * "reload" after making changes, which makes it convenient to call from here. - */ - virtual CString Reload(void) override; - - /* - * Returns true if the archive has un-flushed modifications pending. - */ - virtual bool IsModified(void) const override; - - /* - * Return an description of the disk archive, suitable for display in the - * main title bar. - */ - virtual CString GetDescription() const override; - - virtual bool BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override; - virtual bool AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override - { ASSERT(false); return false; } - virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) override; - virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override - { ASSERT(false); return false; } - virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual CString TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, - char newFssep) const override; - virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) override; - virtual CString TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const override; - virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) override - { ASSERT(false); return false; } - virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry, - CString* pStr) override - { ASSERT(false); return false; } - virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry, - const CString& str) override - { ASSERT(false); return false; } - virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) override - { ASSERT(false); return false; } - virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) override; - - /* - * User has updated their preferences. Take note. - * - * Setting preferences in a DiskFS causes those prefs to be pushed down - * to all sub-volumes. - */ - virtual void PreferencesChanged(void) override; - - virtual long GetCapability(Capability cap) override; - virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) override; - - virtual bool IsReadOnly(void) const { return fIsReadOnly; } - const DiskImg* GetDiskImg(void) const { return &fDiskImg; } - DiskFS* GetDiskFS(void) const { return fpPrimaryDiskFS; } - - /* - * Progress update callback, called from DiskImgLib during read/write - * operations. - * - * Returns "true" if we should continue; - */ - static bool ProgressCallback(DiskImgLib::A2FileDescr* pFile, - DiskImgLib::di_off_t max, DiskImgLib::di_off_t current, void* state); - -private: - /* - * Close the DiskArchive ojbect. - */ - virtual CString Close(void); - - virtual void XferPrepare(const XferFileOptions* pXferOpts) override; - virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override; - virtual void XferAbort(CWnd* pMsgWnd) override; - virtual void XferFinish(CWnd* pMsgWnd) override; - - /* - * Progress update callback, called from DiskImgLib while scanning a volume - * during Open(). - * - * "str" must not contain a '%'. (TODO: fix that) - * - * Returns "true" if we should continue. - */ - static bool ScanProgressCallback(void* cookie, const char* str, - int count); - - - /* - * Internal class used to keep track of files we're adding. - */ - class FileAddData { - public: - FileAddData(const LocalFileDetails* pDetails, char* fsNormalPathMOR) { - fDetails = *pDetails; - - fFSNormalPathMOR = fsNormalPathMOR; - fpOtherFork = NULL; - fpNext = NULL; - } - virtual ~FileAddData(void) {} - - FileAddData* GetNext(void) const { return fpNext; } - void SetNext(FileAddData* pNext) { fpNext = pNext; } - FileAddData* GetOtherFork(void) const { return fpOtherFork; } - void SetOtherFork(FileAddData* pData) { fpOtherFork = pData; } - - const LocalFileDetails* GetDetails(void) const { return &fDetails; } - - /* - * Get the "FS-normal" path, i.e. exactly what we want to appear - * on the disk image. This has the result of any conversions, so - * we need to store it as a narrow Mac OS Roman string. - */ - const char* GetFSNormalPath(void) const { return fFSNormalPathMOR; } - - private: - LocalFileDetails fDetails; - - // The DiskFS-normalized version of the storage name. This is the - // name as it will appear on the Apple II disk image. - CStringA fFSNormalPathMOR; - - FileAddData* fpOtherFork; - FileAddData* fpNext; - }; - - virtual ArchiveKind GetArchiveKind(void) override { return kArchiveDiskImage; } - virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) override; - - /* - * Reload the contents of the archive, showing an error message if the - * reload fails. - * - * Returns 0 on success, -1 on failure. - */ - int InternalReload(CWnd* pMsgWnd); - - /* - * Compare DiskEntry display names in descending order (Z-A). - */ - static int CompareDisplayNamesDesc(const void* ventry1, const void* ventry2); - - /* - * Load the contents of a "disk archive". Returns 0 on success. - */ - int LoadContents(void); - - /* - * Load the contents of a DiskFS. - * - * Recursively handle sub-volumes. "volName" holds the name of the - * sub-volume as it should appear in the list. - */ - int LoadDiskFSContents(DiskFS* pDiskFS, const WCHAR* volName); - - void DowncaseSubstring(CString* pStr, int startPos, int endPos, - bool prevWasSpace); - - /* - * Handle a debug message from the DiskImg library. - */ - static void DebugMsgHandler(const char* file, int line, const char* msg); - - /* - * A file we're adding clashes with an existing file. Decide what to do - * about it. - * - * Returns one of the following: - * kNuOverwrite - overwrite the existing file - * kNuSkip - skip adding the existing file - * kNuRename - user wants to rename the file - * kNuAbort - cancel out of the entire add process - * - * Side effects: - * Sets fOverwriteExisting and fOverwriteNoAsk if a "to all" button is hit - * Replaces pDetails->storageName if the user elects to rename - */ - NuResult HandleReplaceExisting(const A2File* pExisting, - LocalFileDetails* pDetails); - - /* - * Process the list of pending file adds. - * - * This is where the rubber (finally!) meets the road. - */ - CString ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL); - - /* - * Load a file into a buffer, possibly converting EOL markers and setting - * "high ASCII" along the way. - * - * Returns a pointer to a newly-allocated buffer (new[]) and the data length. - * If the file is empty, no buffer will be allocated. - * - * Returns an empty string on success, or an error message on failure. - */ - CString LoadFile(const WCHAR* pathName, uint8_t** pBuf, long* pLen, - GenericEntry::ConvertEOL conv, GenericEntry::ConvertHighASCII convHA) const; - - /* - * Add a file with the supplied data to the disk image. - * - * Forks that exist but are empty have a length of zero. Forks that don't - * exist have a length of -1. - * - * Called by XferFile and ProcessFileAddData. - */ - DIError AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms, - const uint8_t* dataBuf, long dataLen, - const uint8_t* rsrcBuf, long rsrcLen) const; - - /* - * Add an entry to the end of the FileAddData list. - * - * If "storageName" (the Windows filename with type goodies stripped, but - * without filesystem normalization) matches an entry already in the list, - * we check to see if these are forks of the same file. If they are - * different forks and we don't already have both forks, we put the - * pointer into the "fork pointer" of the existing file rather than adding - * it to the end of the list. - */ - void AddToAddDataList(FileAddData* pData); - - /* - * Free all entries in the FileAddData list. - */ - void FreeAddDataList(void); - - /* - * Set up a RenameEntryDialog for the entry in "*pEntry". - * - * Returns true on success, false on failure. - */ - bool SetRenameFields(CWnd* pMsgWnd, DiskEntry* pEntry, - RenameEntryDialog* pDialog); - - DiskImg fDiskImg; // DiskImg object for entire disk - DiskFS* fpPrimaryDiskFS; // outermost DiskFS - bool fIsReadOnly; - - /* active state while adding files */ - FileAddData* fpAddDataHead; - FileAddData* fpAddDataTail; - bool fOverwriteExisting; - bool fOverwriteNoAsk; - - /* state during xfer */ - //CString fXferStoragePrefix; - DiskFS* fpXferTargetFS; -}; - -#endif /*APP_DISKARCHIVE_H*/ diff --git a/ciderpress/app/DiskConvertDialog.cpp b/ciderpress/app/DiskConvertDialog.cpp deleted file mode 100644 index d9a04dd..0000000 --- a/ciderpress/app/DiskConvertDialog.cpp +++ /dev/null @@ -1,260 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for disk image conversion dialog - */ -#include "StdAfx.h" -#include "DiskConvertDialog.h" - -using namespace DiskImgLib; - -BEGIN_MESSAGE_MAP(DiskConvertDialog, CDialog) - ON_CONTROL_RANGE(BN_CLICKED, IDC_DISKCONV_DOS, IDC_DISKCONV_DDD, OnChangeRadio) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -void DiskConvertDialog::Init(const DiskImg* pDiskImg) -{ - ASSERT(pDiskImg != NULL); - const int kMagicNibbles = -1234; - bool hasBlocks = pDiskImg->GetHasBlocks(); - bool hasSectors = pDiskImg->GetHasSectors(); - bool hasNibbles = pDiskImg->GetHasNibbles(); - long diskSizeInSectors; - - ASSERT(fSizeInBlocks == -1); - if (hasBlocks) { - diskSizeInSectors = pDiskImg->GetNumBlocks() * 2; - fSizeInBlocks = diskSizeInSectors / 2; - } else if (hasSectors) { - diskSizeInSectors = - pDiskImg->GetNumTracks() * pDiskImg->GetNumSectPerTrack(); - if (pDiskImg->GetNumSectPerTrack() != 13) - fSizeInBlocks = diskSizeInSectors / 2; - } else { - ASSERT(hasNibbles); - diskSizeInSectors = kMagicNibbles; - } - - if (diskSizeInSectors >= 8388608) { - /* no conversions for files 2GB and larger except .PO */ - fDiskDescription.Format(IDS_CDESC_BLOCKS, diskSizeInSectors/4); - fAllowUnadornedProDOS = true; - fConvertIdx = kConvProDOSRaw; - } else if (diskSizeInSectors == 35*16) { - /* 140K disk image */ - CheckedLoadString(&fDiskDescription, IDS_CDESC_140K); - fAllowUnadornedDOS = true; - fAllowUnadornedProDOS = true; - fAllowProDOS2MG = true; - fAllowUnadornedNibble = true; - fAllowNuFX = true; - fAllowTrackStar = true; - fAllowSim2eHDV = true; - fAllowDDD = true; - if (hasNibbles) - fConvertIdx = kConvNibbleRaw; - else - fConvertIdx = kConvDOSRaw; - } else if (diskSizeInSectors == 40*16 && - (pDiskImg->GetFileFormat() == DiskImg::kFileFormatTrackStar || - pDiskImg->GetFileFormat() == DiskImg::kFileFormatFDI)) - { - /* 40-track TrackStar or FDI image; allow conversion to 35-track formats */ - CheckedLoadString(&fDiskDescription, IDS_CDESC_40TRACK); - ASSERT(pDiskImg->GetHasBlocks()); - fAllowUnadornedDOS = true; - fAllowUnadornedProDOS = true; - fAllowProDOS2MG = true; - fAllowUnadornedNibble = true; - fAllowNuFX = true; - fAllowSim2eHDV = true; - fAllowDDD = true; - fAllowTrackStar = true; - fConvertIdx = kConvDOSRaw; - } else if (diskSizeInSectors == 35*13) { - /* 13-sector 5.25" floppy */ - CheckedLoadString(&fDiskDescription, IDS_CDEC_140K_13); - fAllowUnadornedNibble = true; - fAllowD13 = true; - fConvertIdx = kConvNibbleRaw; - } else if (diskSizeInSectors == kMagicNibbles) { - /* blob of nibbles with no recognizable format; allow self-convert */ - CheckedLoadString(&fDiskDescription, IDS_CDEC_RAWNIB); - if (pDiskImg->GetPhysicalFormat() == DiskImg::kPhysicalFormatNib525_6656) - { - fAllowUnadornedNibble = true; - fConvertIdx = kConvNibbleRaw; - } else if (pDiskImg->GetPhysicalFormat() == DiskImg::kPhysicalFormatNib525_Var) - { - fAllowTrackStar = true; - fConvertIdx = kConvTrackStar; - } else if (pDiskImg->GetPhysicalFormat() == DiskImg::kPhysicalFormatNib525_6384) - { - /* don't currently allow .nb2 output */ - LOGI(" GLITCH: we don't allow self-convert of .nb2 files"); - ASSERT(false); - } else { - /* this should be impossible */ - ASSERT(false); - } - } else if (diskSizeInSectors == 3200) { - /* 800K disk image */ - CheckedLoadString(&fDiskDescription, IDS_CDESC_800K); - fAllowUnadornedDOS = true; - fAllowUnadornedProDOS = true; - fAllowProDOS2MG = true; - fAllowDiskCopy42 = true; - fAllowNuFX = true; - fAllowSim2eHDV = true; - fConvertIdx = kConvProDOS2MG; - } else { - /* odd-sized disk image; could allow DOS if hasSectors */ - fDiskDescription.Format(IDS_CDESC_BLOCKS, diskSizeInSectors/4); - fAllowUnadornedProDOS = true; - fAllowProDOS2MG = true; - fAllowNuFX = true; - fAllowSim2eHDV = true; - fConvertIdx = kConvProDOS2MG; - } -} - -void DiskConvertDialog::Init(int fileCount) -{ - /* allow everything */ - fAllowUnadornedDOS = fAllowUnadornedProDOS = fAllowProDOS2MG = - fAllowUnadornedNibble = fAllowD13 = fAllowDiskCopy42 = - fAllowNuFX = fAllowTrackStar = fAllowSim2eHDV = fAllowDDD = true; - fConvertIdx = kConvDOSRaw; // default choice == first in list - fBulkFileCount = fileCount; - fDiskDescription.Format(L"%d images selected", fBulkFileCount); -} - -BOOL DiskConvertDialog::OnInitDialog(void) -{ - CWnd* pWnd; - - ASSERT(fConvertIdx != -1); // must call Init before DoModal - - if (!fAllowUnadornedDOS) { - pWnd = GetDlgItem(IDC_DISKCONV_DOS); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_DISKCONV_DOS2MG); - pWnd->EnableWindow(FALSE); - } - if (!fAllowUnadornedProDOS) { - pWnd = GetDlgItem(IDC_DISKCONV_PRODOS); - pWnd->EnableWindow(FALSE); - } - if (!fAllowProDOS2MG) { - pWnd = GetDlgItem(IDC_DISKCONV_PRODOS2MG); - pWnd->EnableWindow(FALSE); - } - if (!fAllowUnadornedNibble) { - pWnd = GetDlgItem(IDC_DISKCONV_NIB); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_DISKCONV_NIB2MG); - pWnd->EnableWindow(FALSE); - } - if (!fAllowD13) { - pWnd = GetDlgItem(IDC_DISKCONV_D13); - pWnd->EnableWindow(FALSE); - } - if (!fAllowDiskCopy42) { - pWnd = GetDlgItem(IDC_DISKCONV_DC42); - pWnd->EnableWindow(FALSE); - } - if (!fAllowNuFX) { - pWnd = GetDlgItem(IDC_DISKCONV_SDK); - pWnd->EnableWindow(FALSE); - } - if (!fAllowTrackStar) { - pWnd = GetDlgItem(IDC_DISKCONV_TRACKSTAR); - pWnd->EnableWindow(FALSE); - } - if (!fAllowSim2eHDV) { - pWnd = GetDlgItem(IDC_DISKCONV_HDV); - pWnd->EnableWindow(FALSE); - } - if (!fAllowDDD) { - pWnd = GetDlgItem(IDC_DISKCONV_DDD); - pWnd->EnableWindow(FALSE); - } - - if (fBulkFileCount < 0) { - pWnd = GetDlgItem(IDC_IMAGE_TYPE); - pWnd->SetWindowText(fDiskDescription); - } else { - CRect rect; - int right; - pWnd = GetDlgItem(IDC_IMAGE_TYPE); - pWnd->GetWindowRect(&rect); - ScreenToClient(&rect); - right = rect.right; - pWnd->DestroyWindow(); - - pWnd = GetDlgItem(IDC_IMAGE_SIZE_TEXT); - pWnd->GetWindowRect(&rect); - ScreenToClient(&rect); - rect.right = right; - pWnd->MoveWindow(&rect); - pWnd->SetWindowText(fDiskDescription); - } - - OnChangeRadio(0); // set the gzip box - - CDialog::OnInitDialog(); - - return TRUE; -} - -void DiskConvertDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Check(pDX, IDC_DISKCONV_GZIP, fAddGzip); - DDX_Radio(pDX, IDC_DISKCONV_DOS, fConvertIdx); - - if (pDX->m_bSaveAndValidate) { - switch (fConvertIdx) { - case kConvDOSRaw: fExtension = L"do"; break; - case kConvDOS2MG: fExtension = L"2mg"; break; - case kConvProDOSRaw: fExtension = L"po"; break; - case kConvProDOS2MG: fExtension = L"2mg"; break; - case kConvNibbleRaw: fExtension = L"nib"; break; - case kConvNibble2MG: fExtension = L"2mg"; break; - case kConvD13: fExtension = L"d13"; break; - case kConvDiskCopy42: fExtension = L"dsk"; break; - case kConvNuFX: fExtension = L"sdk"; break; - case kConvTrackStar: fExtension = L"app"; break; - case kConvSim2eHDV: fExtension = L"hdv"; break; - case kConvDDD: fExtension = L"ddd"; break; - default: - fExtension = L"???"; - ASSERT(false); - break; - } - - if (fAddGzip && fConvertIdx != kConvNuFX) { - fExtension += L".gz"; - } - - LOGI(" DCD recommending extension '%ls'", (LPCWSTR) fExtension); - } -} - -void DiskConvertDialog::OnChangeRadio(UINT nID) -{ - CWnd* pGzip = GetDlgItem(IDC_DISKCONV_GZIP); - ASSERT(pGzip != NULL); - CButton* pNuFX = (CButton*)GetDlgItem(IDC_DISKCONV_SDK); - ASSERT(pNuFX != NULL); - - if (fSizeInBlocks > DiskImgLib::kGzipMax / 512) - pGzip->EnableWindow(FALSE); - else - pGzip->EnableWindow(pNuFX->GetCheck() == BST_UNCHECKED); -} diff --git a/ciderpress/app/DiskConvertDialog.h b/ciderpress/app/DiskConvertDialog.h deleted file mode 100644 index 6bf81f6..0000000 --- a/ciderpress/app/DiskConvertDialog.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Let the user choose how they want to convert a disk image. - */ -#ifndef APP_DISKCONVERTDIALOG_H -#define APP_DISKCONVERTDIALOG_H - -#include "resource.h" -#include "../diskimg/DiskImg.h" -#include "HelpTopics.h" - -/* - * The set of conversions available depends on the format of the source image. - */ -class DiskConvertDialog : public CDialog { -public: - DiskConvertDialog(CWnd* pParentWnd) : CDialog(IDD_DISKCONV, pParentWnd) - { - fAllowUnadornedDOS = fAllowUnadornedProDOS = fAllowProDOS2MG = - fAllowUnadornedNibble = fAllowD13 = fAllowDiskCopy42 = - fAllowNuFX = fAllowTrackStar = fAllowSim2eHDV = fAllowDDD = false; - fAddGzip = FALSE; - fConvertIdx = -1; - fBulkFileCount = -1; - fSizeInBlocks = -1; - } - virtual ~DiskConvertDialog(void) {} - - /* - * Initialize the set of available options based on the source image. - */ - void Init(const DiskImgLib::DiskImg* pDiskImg); - - /* - * Initialize options for a bulk transfer. - */ - void Init(int fileCount); - - /* must match up with dialog */ - enum { - kConvDOSRaw = 0, - kConvDOS2MG = 1, - kConvProDOSRaw = 2, - kConvProDOS2MG = 3, - kConvNibbleRaw = 4, - kConvNibble2MG = 5, - kConvD13 = 6, - kConvDiskCopy42 = 7, - kConvNuFX = 8, - kConvTrackStar = 9, - kConvSim2eHDV = 10, - kConvDDD = 11, - }; - int fConvertIdx; - - BOOL fAddGzip; - - // this is set to proper extension for the type chosen (e.g. "do") - CString fExtension; - -private: - BOOL OnInitDialog(void) override; - void DoDataExchange(CDataExchange* pDX) override; - - /* - * If the radio button selection changes, we may need to disable the gzip - * checkbox to show that NuFX can't be combined with gzip. - * - * If the underlying disk is over 32MB, disable gzip, because we won't be - * able to open the disk we create. - */ - afx_msg void OnChangeRadio(UINT nID); - - // User pressed the "Help" button. - afx_msg void OnHelp(void) { - if (fBulkFileCount < 0) - MyApp::HandleHelp(this, HELP_TOPIC_DISK_CONV); - else - MyApp::HandleHelp(this, HELP_TOPIC_BULK_DISK_CONV); - } - - // Context help request (question mark button). - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - CString fDiskDescription; - bool fAllowUnadornedDOS; - bool fAllowUnadornedProDOS; - bool fAllowProDOS2MG; - bool fAllowUnadornedNibble; - bool fAllowD13; - bool fAllowDiskCopy42; - bool fAllowNuFX; - bool fAllowTrackStar; - bool fAllowSim2eHDV; - bool fAllowDDD; - - int fBulkFileCount; - - long fSizeInBlocks; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_DISKCONVERTDIALOG_H*/ diff --git a/ciderpress/app/DiskEditDialog.cpp b/ciderpress/app/DiskEditDialog.cpp deleted file mode 100644 index 126c23f..0000000 --- a/ciderpress/app/DiskEditDialog.cpp +++ /dev/null @@ -1,1415 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Disk editor implementation. - * - * Note to self: it should be possible to open an image in "nibble" mode, - * switch to one of the various "sector" modes, and maybe even try out a - * "block" mode for a while. Locking the editor into one particular mode - * makes for a cumbersome interface when dealing with certain kinds of disks. - * It should also be possible to configure the "custom" NibbleDescr and then - * re-analyze the disk, possibly transferring the customization over to - * the main file listing. (Perhaps the customization affects a global slot? - * Probably want more than one set of custom nibble values, with the config - * dialog accessible from main menu and from within disk editor.) - * - * A track copier would be neat too. - */ -#include "stdafx.h" -#include "SubVolumeDialog.h" -#include "DEFileDialog.h" -#include "DiskEditDialog.h" -#include "../reformat/Charset.h" - - -/* - * =========================================================================== - * DiskEditDialog (base class) - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(DiskEditDialog, CDialog) - ON_BN_CLICKED(IDC_DISKEDIT_DONE, OnDone) - ON_BN_CLICKED(IDC_DISKEDIT_HEX, OnHexMode) - ON_BN_CLICKED(IDC_DISKEDIT_DOREAD, OnDoRead) - ON_BN_CLICKED(IDC_DISKEDIT_DOWRITE, OnDoWrite) - ON_BN_CLICKED(IDC_DISKEDIT_PREV, OnReadPrev) - ON_BN_CLICKED(IDC_DISKEDIT_NEXT, OnReadNext) - ON_BN_CLICKED(IDC_DISKEDIT_SUBVOLUME, OnSubVolume) - ON_BN_CLICKED(IDC_DISKEDIT_OPENFILE, OnOpenFile) - ON_BN_CLICKED(IDHELP, OnHelp) - ON_CBN_SELCHANGE(IDC_DISKEDIT_NIBBLE_PARMS, OnNibbleParms) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - -BOOL DiskEditDialog::OnInitDialog(void) -{ - ASSERT(!fFileName.IsEmpty()); - ASSERT(fpDiskFS != NULL); - - /* - * Disable the write button. - */ - if (fReadOnly) { - CButton* pButton = (CButton*) GetDlgItem(IDC_DISKEDIT_DOWRITE); - ASSERT(pButton != NULL); - pButton->EnableWindow(FALSE); - } - - /* - * Use modified spin controls so we're not limited to 16 bits. - */ - ReplaceSpinCtrl(&fTrackSpinner, IDC_DISKEDIT_TRACKSPIN, - IDC_DISKEDIT_TRACK); - ReplaceSpinCtrl(&fSectorSpinner, IDC_DISKEDIT_SECTORSPIN, - IDC_DISKEDIT_SECTOR); - - - - /* - * Configure the RichEdit control. - */ - CRichEditCtrl* pEdit = (CRichEditCtrl*) GetDlgItem(IDC_DISKEDIT_EDIT); - ASSERT(pEdit != NULL); - - /* set the font to 10-point Courier New */ - CHARFORMAT cf; - cf.cbSize = sizeof(CHARFORMAT); - cf.dwMask = CFM_FACE | CFM_SIZE; - wcscpy(cf.szFaceName, L"Courier New"); - cf.yHeight = 10 * 20; // point size in twips - BOOL cc = pEdit->SetDefaultCharFormat(cf); - if (cc == FALSE) { - LOGI("SetDefaultCharFormat failed?"); - ASSERT(FALSE); - } - - /* set to read-only */ - pEdit->SetReadOnly(); - /* retain the selection even if we lose focus [can't do this in OnInit] */ - pEdit->SetOptions(ECOOP_OR, ECO_SAVESEL); - - /* - * Disable the sub-volume and/or file open buttons if the DiskFS doesn't - * have the appropriate stuff inside. - */ - if (fpDiskFS->GetNextFile(NULL) == NULL) { - CWnd* pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); - pWnd->EnableWindow(FALSE); - } - if (fpDiskFS->GetNextSubVolume(NULL) == NULL) { - CWnd* pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); - pWnd->EnableWindow(FALSE); - } - - /* - * Configure the nibble parm drop-list in an appropriate fashion. - */ - InitNibbleParmList(); - - /* - * If this is a sub-volume edit window, pop us up slightly offset from the - * parent window so the user can see that there's more than one thing - * open. - */ - if (fPositionShift != 0) { - CRect rect; - GetWindowRect(&rect); - rect.top += fPositionShift; - rect.left += fPositionShift; - rect.bottom += fPositionShift; - rect.right += fPositionShift; - MoveWindow(&rect); - } - - /* - * Set the window title. - */ - CString title("Disk Viewer - "); - title += fFileName; - if (fpDiskFS->GetVolumeID() != NULL) { - title += " ("; - title += Charset::ConvertMORToUNI(fpDiskFS->GetVolumeID()); - title += ")"; - } - SetWindowText(title); - - return TRUE; -} - -void DiskEditDialog::InitNibbleParmList(void) -{ - ASSERT(fpDiskFS != NULL); - DiskImg* pDiskImg = fpDiskFS->GetDiskImg(); - CComboBox* pCombo; - - pCombo = (CComboBox*) GetDlgItem(IDC_DISKEDIT_NIBBLE_PARMS); - ASSERT(pCombo != NULL); - - if (pDiskImg->GetHasNibbles()) { - const DiskImg::NibbleDescr* pTable; - const DiskImg::NibbleDescr* pCurrent; - int i, count; - - pTable = pDiskImg->GetNibbleDescrTable(&count); - if (pTable == NULL || count <= 0) { - LOGI("WHOOPS: nibble parm got table=0x%08lx, count=%d", - (long) pTable, count); - return; - } - pCurrent = pDiskImg->GetNibbleDescr(); - - /* configure the selection to match the disk analysis */ - int dflt = -1; - if (pCurrent != NULL) { - for (i = 0; i < count; i++) { - if (memcmp(&pTable[i], pCurrent, sizeof(*pCurrent)) == 0) { - LOGI(" NibbleParm match on entry %d", i); - dflt = i; - break; - } - } - - if (dflt == -1) { - LOGI(" GLITCH: no match on nibble descr in table?!"); - dflt = 0; - } - } - - for (i = 0; i < count; i++) { - if (pTable[i].numSectors > 0) { - CString description(pTable[i].description); - pCombo->AddString(description); - } else { - /* only expecting this on the last, "custom" entry */ - ASSERT(i == count-1); - } - } - pCombo->SetCurSel(dflt); - } else { - pCombo->AddString(L"Nibble Parms"); - pCombo->SetCurSel(0); - pCombo->EnableWindow(FALSE); - } -} - -int DiskEditDialog::ReplaceSpinCtrl(MySpinCtrl* pNewSpin, int idSpin, int idEdit) -{ - CSpinButtonCtrl* pSpin; -// CRect rect; - DWORD style; - - pSpin = (CSpinButtonCtrl*)GetDlgItem(idSpin); - if (pSpin == NULL) - return -1; -// pSpin->GetWindowRect(&rect); -// ScreenToClient(&rect); - style = pSpin->GetStyle(); - style &= ~(UDS_SETBUDDYINT); - //style |= UDS_AUTOBUDDY; - ASSERT(!(style & UDS_AUTOBUDDY)); - pSpin->DestroyWindow(); - pNewSpin->Create(style, CRect(0,0,0,0), this, idSpin); - pNewSpin->SetBuddy(GetDlgItem(idEdit)); - - return 0; -} - -BOOL DiskEditDialog::PreTranslateMessage(MSG* pMsg) -{ - if (pMsg->message == WM_KEYDOWN && - pMsg->wParam == VK_RETURN) - { - //LOGI("RETURN!"); - LoadData(); - return TRUE; - } - - return CDialog::PreTranslateMessage(pMsg); -} - -void DiskEditDialog::OnDone(void) -{ - LOGI("DiskEditDialog OnDone"); - EndDialog(IDOK); -} - -void DiskEditDialog::OnHexMode(void) -{ - int base; - - CButton* pButton = (CButton*) GetDlgItem(IDC_DISKEDIT_HEX); - ASSERT(pButton != NULL); - - if (pButton->GetCheck() == 0) - base = 10; - else - base = 16; - - SetSpinMode(IDC_DISKEDIT_TRACKSPIN, base); - SetSpinMode(IDC_DISKEDIT_SECTORSPIN, base); -} - -void DiskEditDialog::OnSubVolume(void) -{ - SubVolumeDialog subv(this); - bool showAsBlocks; - - subv.Setup(fpDiskFS); - if (subv.DoModal() == IDOK) { - LOGI("SELECTED subv %d", subv.fListBoxIndex); - DiskFS::SubVolume* pSubVol = fpDiskFS->GetNextSubVolume(NULL); - if (pSubVol == NULL) - return; - - while (subv.fListBoxIndex-- > 0) { - pSubVol = fpDiskFS->GetNextSubVolume(pSubVol); - } - ASSERT(pSubVol != NULL); - - BlockEditDialog blockEdit; - SectorEditDialog sectorEdit; - DiskEditDialog* pEditDialog; - - showAsBlocks = pSubVol->GetDiskImg()->ShowAsBlocks(); - if (showAsBlocks) - pEditDialog = &blockEdit; - else - pEditDialog = §orEdit; - CString volumeId(Charset::ConvertMORToUNI(fpDiskFS->GetVolumeID())); - pEditDialog->Setup(pSubVol->GetDiskFS(), volumeId); - pEditDialog->SetPositionShift(8); - (void) pEditDialog->DoModal(); - } -} - -void DiskEditDialog::SetSpinMode(int id, int base) -{ - CString valStr; - - ASSERT(base == 10 || base == 16); - - MySpinCtrl* pSpin = (MySpinCtrl*) GetDlgItem(id); - if (pSpin == NULL) { - // expected behavior in "block" mode for sector button - LOGI("Couldn't find spin button %d", id); - return; - } - - long val = pSpin->GetPos(); - if (val & 0xff000000) { - LOGI("NOTE: hex transition while value is invalid"); - val = 0; - } - - if (base == 10) - valStr.Format(L"%d", val); - else - valStr.Format(L"%X", val); - - pSpin->SetBase(base); - pSpin->GetBuddy()->SetWindowText(valStr); - LOGI("Set spin button base to %d val=%d", base, val); -} - -int DiskEditDialog::ReadSpinner(int id, long* pVal) -{ - MySpinCtrl* pSpin = (MySpinCtrl*) GetDlgItem(id); - ASSERT(pSpin != NULL); - - long val = pSpin->GetPos(); - if (val & 0xff000000) { - /* error */ - CString msg, err; - - CheckedLoadString(&err, IDS_ERROR); - int lower, upper; - pSpin->GetRange32(lower, upper); - msg.Format(L"Please enter a value between %d and %d (0x%x and 0x%x).", - lower, upper, lower, upper); - MessageBox(msg, err, MB_OK|MB_ICONEXCLAMATION); - return -1; - } - - *pVal = val; - return 0; -} - -void DiskEditDialog::SetSpinner(int id, long val) -{ - MySpinCtrl* pSpin = (MySpinCtrl*) GetDlgItem(id); - ASSERT(pSpin != NULL); - - /* sanity check */ - int lower, upper; - pSpin->GetRange32(lower, upper); - ASSERT(val >= lower && val <= upper); - - pSpin->SetPos(val); -} - -void DiskEditDialog::DisplayData(const uint8_t* srcBuf, int size) -{ - WCHAR textBuf[80 * 16 * 2]; - WCHAR* cp; - int i, j; - - ASSERT(srcBuf != NULL); - ASSERT(size == kSectorSize || size == kBlockSize); - - CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); - ASSERT(pEdit != NULL); - - /* - * If we have an alert message, show that instead. - */ - if (!fAlertMsg.IsEmpty()) { - const int kWidth = 72; - int indent = (kWidth/2) - (fAlertMsg.GetLength() / 2); - if (indent < 0) - indent = 0; - - CString msg = L" " - L" "; - ASSERT(msg.GetLength() == kWidth); - msg = msg.Left(indent); - msg += fAlertMsg; - for (i = 0; i < (size / 16)-2; i += 2) { - textBuf[i] = '\r'; - textBuf[i+1] = '\n'; - } - wcscpy(&textBuf[i], msg); - pEdit->SetWindowText(textBuf); - - return; - } - - /* - * No alert, do the usual thing. - */ - cp = textBuf; - for (i = 0; i < size/16; i++) { - if (size == kSectorSize) { - /* two-nybble addr */ - wsprintf(cp, L" %02x: %02x %02x %02x %02x %02x %02x %02x %02x " - L"%02x %02x %02x %02x %02x %02x %02x %02x ", - i * 16, - srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], - srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], - srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], - srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); - } else { - /* three-nybble addr */ - wsprintf(cp, L"%03x: %02x %02x %02x %02x %02x %02x %02x %02x " - L"%02x %02x %02x %02x %02x %02x %02x %02x ", - i * 16, - srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], - srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], - srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], - srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); - } - ASSERT(wcslen(cp) == 54); - cp += 54; // strlen(cp) - for (j = 0; j < 16; j++) - *cp++ = PrintableChar(srcBuf[j]); - - *cp++ = '\r'; - *cp++ = '\n'; - - srcBuf += 16; - } - /* kill the last EOL, so the cursor doesn't move past that line */ - cp--; - *cp = '\0'; - - pEdit->SetWindowText(textBuf); -} - -void DiskEditDialog::DisplayNibbleData(const unsigned char* srcBuf, int size) -{ - ASSERT(srcBuf != NULL); - ASSERT(size > 0); - ASSERT(fAlertMsg.IsEmpty()); - - int bufSize = ((size+15) / 16) * 80; - WCHAR* textBuf = new WCHAR[bufSize]; - WCHAR* cp; - int i; - - if (textBuf == NULL) - return; - - cp = textBuf; - for (i = 0; size > 0; i++) { - if (size >= 16) { - wsprintf(cp, L"%04x: %02x %02x %02x %02x %02x %02x %02x %02x " - L"%02x %02x %02x %02x %02x %02x %02x %02x", - i * 16, - srcBuf[0], srcBuf[1], srcBuf[2], srcBuf[3], - srcBuf[4], srcBuf[5], srcBuf[6], srcBuf[7], - srcBuf[8], srcBuf[9], srcBuf[10], srcBuf[11], - srcBuf[12], srcBuf[13], srcBuf[14], srcBuf[15]); - ASSERT(wcslen(cp) == 53); - cp += 53; // strlen(cp) - } else { - wsprintf(cp, L"%04x:", i * 16); - cp += 5; - for (int j = 0; j < size; j++) { - wsprintf(cp, L" %02x", srcBuf[j]); - cp += 3; - } - } - - *cp++ = '\r'; - *cp++ = '\n'; - - srcBuf += 16; - size -= 16; - } - /* kill the last EOL, so the cursor doesn't move past that line */ - cp--; - *cp = '\0'; - - CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); - ASSERT(pEdit != NULL); - pEdit->SetWindowText(textBuf); - - /* - * Handle resize of edit box. We have to do this late or the scroll bar - * won't appear under Win98. (Whatever.) - */ - if (fFirstResize) { - fFirstResize = false; - - const int kStretchHeight = 249; - CRect rect; - - GetWindowRect(&rect); - - CRect inner; - pEdit->GetRect(&inner); - inner.bottom += kStretchHeight; - pEdit->GetWindowRect(&rect); - ScreenToClient(&rect); - rect.bottom += kStretchHeight; - pEdit->MoveWindow(&rect); - pEdit->SetRect(&inner); - } - - delete[] textBuf; -} - -DIError DiskEditDialog::OpenFile(const WCHAR* fileName, bool openRsrc, - A2File** ppFile, A2FileDescr** ppOpenFile) -{ - A2File* pFile; - A2FileDescr* pOpenFile = NULL; - - LOGI(" OpenFile '%ls' rsrc=%d", fileName, openRsrc); - CStringA fileNameA(fileName); - pFile = fpDiskFS->GetFileByName(fileNameA); - if (pFile == NULL) { - CString msg, failed; - - msg.Format(IDS_DEFILE_FIND_FAILED, fileName); - CheckedLoadString(&failed, IDS_FAILED); - MessageBox(msg, failed, MB_OK | MB_ICONSTOP); - return kDIErrFileNotFound; - } - - DIError dierr; - dierr = pFile->Open(&pOpenFile, true, openRsrc); - if (dierr != kDIErrNone) { - CString msg, failed; - - msg.Format(IDS_DEFILE_OPEN_FAILED, fileName, - DiskImgLib::DIStrError(dierr)); - CheckedLoadString(&failed, IDS_FAILED); - MessageBox(msg, failed, MB_OK | MB_ICONSTOP); - return dierr; - } - - *ppFile = pFile; - *ppOpenFile = pOpenFile; - - return kDIErrNone; -} - -void DiskEditDialog::OnNibbleParms(void) -{ - DiskImg* pDiskImg = fpDiskFS->GetDiskImg(); - CComboBox* pCombo; - int sel; - - ASSERT(pDiskImg != NULL); - ASSERT(pDiskImg->GetHasNibbles()); - - pCombo = (CComboBox*) GetDlgItem(IDC_DISKEDIT_NIBBLE_PARMS); - ASSERT(pCombo != NULL); - - sel = pCombo->GetCurSel(); - if (sel == CB_ERR) - return; - - LOGI(" OnNibbleParms: entry %d now selected", sel); - const DiskImg::NibbleDescr* pNibbleTable; - int count; - pNibbleTable = pDiskImg->GetNibbleDescrTable(&count); - ASSERT(sel < count); - pDiskImg->SetNibbleDescr(sel); - - LoadData(); -} - - -#if 0 -/* - * Make a "sparse" block in a file obvious by filling it with the word - * "sparse". - */ -void -DiskEditDialog::FillWithPattern(unsigned char* buf, int size, - const char* pattern) -{ - const char* cp; - unsigned char* ucp; - - ucp = buf; - cp = pattern; - while (ucp < buf+size) { - *ucp++ = *cp++; - if (*cp == '\0') - cp = pattern; - } -} -#endif - - -/* - * =========================================================================== - * SectorEditDialog - * =========================================================================== - */ - -BOOL SectorEditDialog::OnInitDialog(void) -{ - /* - * Do base-class construction. - */ - DiskEditDialog::OnInitDialog(); - - /* - * Change track/sector text. - */ - CString trackStr; - CWnd* pWnd; - trackStr.Format(L"Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); - pWnd = GetDlgItem(IDC_STEXT_TRACK); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(trackStr); - trackStr.Format(L"Sector (%d):", fpDiskFS->GetDiskImg()->GetNumSectPerTrack()); - pWnd = GetDlgItem(IDC_STEXT_SECTOR); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(trackStr); - - /* - * Configure the spin buttons. - */ - MySpinCtrl* pSpin; - pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_TRACKSPIN); - ASSERT(pSpin != NULL); - pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumTracks()-1); - pSpin->SetPos(0); - pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_SECTORSPIN); - ASSERT(pSpin != NULL); - pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumSectPerTrack()-1); - pSpin->SetPos(0); - - /* give us something to look at */ - LoadData(); - - return TRUE; -} - -int SectorEditDialog::LoadData(void) -{ - //LOGI("SED LoadData"); - ASSERT(fpDiskFS != NULL); - ASSERT(fpDiskFS->GetDiskImg() != NULL); - - if (ReadSpinner(IDC_DISKEDIT_TRACKSPIN, &fTrack) != 0) - return -1; - if (ReadSpinner(IDC_DISKEDIT_SECTORSPIN, &fSector) != 0) - return -1; - - LOGI("LoadData reading t=%d s=%d", fTrack, fSector); - - fAlertMsg = ""; - DIError dierr; - dierr = fpDiskFS->GetDiskImg()->ReadTrackSector(fTrack, fSector, fSectorData); - if (dierr != kDIErrNone) { - LOGI("SED sector read failed: %hs", DiskImgLib::DIStrError(dierr)); - //CString msg; - //CString err; - //err.LoadString(IDS_ERROR); - //msg.Format(IDS_DISKEDIT_NOREADTS, fTrack, fSector); - //MessageBox(msg, err, MB_OK|MB_ICONSTOP); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_BADSECTOR); - //return -1; - } - - DisplayData(); - - return 0; -} - -void SectorEditDialog::OnDoRead(void) -{ - LoadData(); -} - -void SectorEditDialog::OnDoWrite(void) -{ - MessageBox(L"Write!"); -} - -void SectorEditDialog::OnReadPrev(void) -{ - if (fTrack == 0 && fSector == 0) - return; - - if (fSector == 0) { - fSector = fpDiskFS->GetDiskImg()->GetNumSectPerTrack() -1; - fTrack--; - } else { - fSector--; - } - - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); - SetSpinner(IDC_DISKEDIT_SECTORSPIN, fSector); - - LoadData(); -} - -void SectorEditDialog::OnReadNext(void) -{ - int numTracks = fpDiskFS->GetDiskImg()->GetNumTracks(); - int numSects = fpDiskFS->GetDiskImg()->GetNumSectPerTrack(); - - if (fTrack == numTracks-1 && fSector == numSects-1) - return; - - if (fSector == numSects-1) { - fSector = 0; - fTrack++; - } else { - fSector++; - } - - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); - SetSpinner(IDC_DISKEDIT_SECTORSPIN, fSector); - - LoadData(); -} - -void SectorEditDialog::OnOpenFile(void) -{ - DEFileDialog fileDialog(this); - - if (fileDialog.DoModal() == IDOK) { - SectorFileEditDialog fileEdit(this, this); - A2File* pFile; - A2FileDescr* pOpenFile = NULL; - DIError dierr; - - dierr = OpenFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, - &pFile, &pOpenFile); - if (dierr != kDIErrNone) - return; - - fileEdit.SetupFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, - pFile, pOpenFile); - fileEdit.SetPositionShift(8); - (void) fileEdit.DoModal(); - - pOpenFile->Close(); - } -} - - -/* - * =========================================================================== - * SectorFileEditDialog - * =========================================================================== - */ - -BOOL SectorFileEditDialog::OnInitDialog(void) -{ - BOOL retval; - - /* do base class first */ - retval = SectorEditDialog::OnInitDialog(); - - /* disable direct entry of tracks and sectors */ - CWnd* pWnd; - pWnd = GetDlgItem(IDC_DISKEDIT_TRACKSPIN); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_DISKEDIT_SECTORSPIN); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(FALSE); - - /* disallow opening of sub-volumes and files */ - pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); - pWnd->EnableWindow(FALSE); - - CEdit* pEdit; - pEdit = (CEdit*) GetDlgItem(IDC_DISKEDIT_TRACK); - ASSERT(pEdit != NULL); - pEdit->SetReadOnly(TRUE); - pEdit = (CEdit*) GetDlgItem(IDC_DISKEDIT_SECTOR); - ASSERT(pEdit != NULL); - pEdit->SetReadOnly(TRUE); - - /* set the window title */ - CString title; - CString rsrcIndic; - CheckedLoadString(&rsrcIndic, IDS_INDIC_RSRC); - title.Format(L"Disk Viewer - %hs%ls (%I64d bytes)", - (LPCSTR) fpFile->GetPathName(), // use fpFile version to get case - fOpenRsrcFork ? (LPCWSTR)rsrcIndic : L"", (LONGLONG) fLength); - SetWindowText(title); - - return retval; -} - -int SectorFileEditDialog::LoadData(void) -{ - ASSERT(fpDiskFS != NULL); - ASSERT(fpDiskFS->GetDiskImg() != NULL); - - DIError dierr; - LOGI("SFED LoadData reading index=%d", fSectorIdx); - -#if 0 - LOGI("LoadData reading offset=%d", fOffset); - size_t actual = 0; - dierr = fpFile->Seek(fOffset, EmbeddedFD::kSeekSet); - if (dierr == kDIErrNone) { - dierr = fpFile->Read(fSectorData, 1 /*kSectorSize*/, &actual); - } - if (dierr != kDIErrNone) { - CString msg, failed; - failed.LoadString(IDS_FAILED); - msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImg::DIStrError(dierr)); - MessageBox(msg, failed, MB_OK); - // TO DO: mark contents as invalid, so editing fails - return -1; - } - - if (actual != kSectorSize) { - LOGI(" SFED partial read of %d bytes", actual); - ASSERT(actual < kSectorSize && actual >= 0); - } - - /* - * We've read the data, but we can't use it. We're a sector editor, - * not a file editor, and we need to get the actual sector data without - * EOF trimming or CP/M 0xe5 removal. - */ - fpFile->GetLastLocationRead(&fTrack, &fSector); - if (fTrack == A2File::kLastWasSparse && fSector == A2File::kLastWasSparse) - - ; -#endif - - fAlertMsg = ""; - - dierr = fpOpenFile->GetStorage(fSectorIdx, &fTrack, &fSector); - if (dierr == kDIErrInvalidIndex && fSectorIdx == 0) { - // no first sector; should only happen on CP/M - //FillWithPattern(fSectorData, sizeof(fSectorData), _T("EMPTY ")); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_EMPTY); - } else if (dierr != kDIErrNone) { - CString msg, failed; - CheckedLoadString(&failed, IDS_FAILED); - msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImgLib::DIStrError(dierr)); - MessageBox(msg, failed, MB_OK); - CheckedLoadString(&fAlertMsg, IDS_FAILED); - // TO DO: mark contents as invalid, so editing fails - return -1; - } else { - if (fTrack == 0 && fSector == 0) { - LOGI("LoadData Sparse sector"); - //FillWithPattern(fSectorData, sizeof(fSectorData), _T("SPARSE ")); - fAlertMsg.Format(IDS_DISKEDITMSG_SPARSE, fSectorIdx); - } else { - LOGI("LoadData reading T=%d S=%d", fTrack, fSector); - - dierr = fpDiskFS->GetDiskImg()->ReadTrackSector(fTrack, fSector, - fSectorData); - if (dierr != kDIErrNone) { - //CString msg; - //CString err; - //err.LoadString(IDS_ERROR); - //msg.Format(IDS_DISKEDIT_NOREADTS, fTrack, fSector); - //MessageBox(msg, err, MB_OK|MB_ICONSTOP); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_BADSECTOR); - //return -1; - } - } - } - - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); - SetSpinner(IDC_DISKEDIT_SECTORSPIN, fSector); - - CWnd* pWnd; - pWnd = GetDlgItem(IDC_DISKEDIT_PREV); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(fSectorIdx > 0); - if (!pWnd->IsWindowEnabled() && GetFocus() == NULL) - GetDlgItem(IDC_DISKEDIT_NEXT)->SetFocus(); - - pWnd = GetDlgItem(IDC_DISKEDIT_NEXT); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(fSectorIdx+1 < fpOpenFile->GetSectorCount()); - if (!pWnd->IsWindowEnabled() && GetFocus() == NULL) - GetDlgItem(IDC_DISKEDIT_PREV)->SetFocus(); - - DisplayData(); - - return 0; -} - -void SectorFileEditDialog::OnReadPrev(void) -{ - if (fSectorIdx == 0) - return; - - fSectorIdx--; - ASSERT(fSectorIdx >= 0); - LoadData(); -} - -void SectorFileEditDialog::OnReadNext(void) -{ - if (fSectorIdx+1 >= fpOpenFile->GetSectorCount()) - return; - - fSectorIdx++; - ASSERT(fSectorIdx < fpOpenFile->GetSectorCount()); - LoadData(); -} - - -/* - * =========================================================================== - * BlockEditDialog - * =========================================================================== - */ - -/* - * Rearrange the DiskEdit dialog (which defaults to SectorEdit mode) to - * accommodate block editing. - */ -BOOL BlockEditDialog::OnInitDialog(void) -{ - /* - * Get rid of the "sector" input item, and change the "track" input - * item to accept blocks instead. - */ - CWnd* pWnd; - - pWnd = GetDlgItem(IDC_STEXT_SECTOR); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DISKEDIT_SECTOR); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DISKEDIT_SECTORSPIN); - pWnd->DestroyWindow(); - - CString blockStr; - //blockStr.LoadString(IDS_BLOCK); - blockStr.Format(L"Block (%d):", fpDiskFS->GetDiskImg()->GetNumBlocks()); - pWnd = GetDlgItem(IDC_STEXT_TRACK); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(blockStr); - - /* - * Increase the size of the window to accommodate the larger block size. - */ - const int kStretchHeight = 250; - CRect rect; - - GetWindowRect(&rect); - rect.bottom += kStretchHeight; - MoveWindow(&rect); - - CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); - ASSERT(pEdit != NULL); - CRect inner; - pEdit->GetRect(&inner); - inner.bottom += kStretchHeight; - pEdit->GetWindowRect(&rect); - ScreenToClient(&rect); - rect.bottom += kStretchHeight; - pEdit->MoveWindow(&rect); - pEdit->SetRect(&inner); - - MoveControl(this, IDC_DISKEDIT_DONE, 0, kStretchHeight); - MoveControl(this, IDC_DISKEDIT_OPENFILE, 0, kStretchHeight); - MoveControl(this, IDC_DISKEDIT_SUBVOLUME, 0, kStretchHeight); - MoveControl(this, IDHELP, 0, kStretchHeight); - MoveControl(this, IDC_DISKEDIT_NIBBLE_PARMS, 0, kStretchHeight); - - /* - * Do base-class construction. - */ - DiskEditDialog::OnInitDialog(); - - /* - * Configure the spin button. We use the "track" spin button for blocks. - */ - MySpinCtrl* pSpin; - pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_TRACKSPIN); - ASSERT(pSpin != NULL); - pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumBlocks()-1); - pSpin->SetPos(0); - - /* give us something to look at */ - if (LoadData() != 0) { - LOGI("WHOOPS: LoadData() failed, but we're in OnInitDialog"); - } - - return TRUE; -} - -#if 0 -/* - * Move a control so it maintains its same position relative to the bottom - * and right edges. - */ -void -BlockEditDialog::MoveControl(int id, int deltaX, int deltaY) -{ - CWnd* pWnd; - CRect rect; - - pWnd = GetDlgItem(id); - ASSERT(pWnd != NULL); - - pWnd->GetWindowRect(&rect); - ScreenToClient(&rect); - rect.left += deltaX; - rect.right += deltaX; - rect.top += deltaY; - rect.bottom += deltaY; - pWnd->MoveWindow(&rect, TRUE); -} -#endif - - -int BlockEditDialog::LoadData(void) -{ - //LOGI("BED LoadData"); - ASSERT(fpDiskFS != NULL); - ASSERT(fpDiskFS->GetDiskImg() != NULL); - - if (ReadSpinner(IDC_DISKEDIT_TRACKSPIN, &fBlock) != 0) - return -1; - - LOGI("LoadData reading block=%d", fBlock); - - fAlertMsg = ""; - DIError dierr; - dierr = fpDiskFS->GetDiskImg()->ReadBlock(fBlock, fBlockData); - if (dierr != kDIErrNone) { - LOGI("BED block read failed: %hs", DiskImgLib::DIStrError(dierr)); - //CString msg; - //CString err; - //err.LoadString(IDS_ERROR); - //msg.Format(IDS_DISKEDIT_NOREADBLOCK, fBlock); - //MessageBox(msg, err, MB_OK|MB_ICONSTOP); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_BADBLOCK); - //return -1; - } - - DisplayData(); - - return 0; -} - -void BlockEditDialog::OnDoRead(void) -{ - LoadData(); -} - -void BlockEditDialog::OnDoWrite(void) -{ - MessageBox(L"Write!"); -} - -void BlockEditDialog::OnReadPrev(void) -{ - if (fBlock == 0) - return; - - fBlock--; - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fBlock); - LoadData(); -} - -void -BlockEditDialog::OnReadNext(void) -{ - ASSERT(fpDiskFS != NULL); - if (fBlock == fpDiskFS->GetDiskImg()->GetNumBlocks() - 1) - return; - - fBlock++; - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fBlock); - LoadData(); -} - -void BlockEditDialog::OnOpenFile(void) -{ - DEFileDialog fileDialog(this); - - if (fileDialog.DoModal() == IDOK) { - BlockFileEditDialog fileEdit(this, this); - A2File* pFile; - A2FileDescr* pOpenFile = NULL; - DIError dierr; - - dierr = OpenFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, - &pFile, &pOpenFile); - if (dierr != kDIErrNone) - return; - - fileEdit.SetupFile(fileDialog.fName, fileDialog.fOpenRsrcFork != 0, - pFile, pOpenFile); - fileEdit.SetPositionShift(8); - (void) fileEdit.DoModal(); - - pOpenFile->Close(); - } -} - - -/* - * =========================================================================== - * BlockFileEditDialog - * =========================================================================== - */ - -BOOL BlockFileEditDialog::OnInitDialog(void) -{ - BOOL retval; - - /* do base class first */ - retval = BlockEditDialog::OnInitDialog(); - - /* disable direct entry of tracks and Blocks */ - CWnd* pWnd; - pWnd = GetDlgItem(IDC_DISKEDIT_TRACKSPIN); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(FALSE); - - /* disallow opening of sub-volumes and files */ - pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); - pWnd->EnableWindow(FALSE); - - CEdit* pEdit; - pEdit = (CEdit*) GetDlgItem(IDC_DISKEDIT_TRACK); - ASSERT(pEdit != NULL); - pEdit->SetReadOnly(TRUE); - - /* set the window title */ - CString title; - CString rsrcIndic; - CheckedLoadString(&rsrcIndic, IDS_INDIC_RSRC); - title.Format(L"Disk Viewer - %hs%ls (%I64d bytes)", - (LPCSTR) fpFile->GetPathName(), // use fpFile version to get case - fOpenRsrcFork ? (LPCWSTR)rsrcIndic : L"", (LONGLONG) fLength); - SetWindowText(title); - - return retval; -} - -int BlockFileEditDialog::LoadData(void) -{ - ASSERT(fpDiskFS != NULL); - ASSERT(fpDiskFS->GetDiskImg() != NULL); - - DIError dierr; - LOGI("BFED LoadData reading index=%d", fBlockIdx); - -#if 0 - LOGI("LoadData reading offset=%d", fOffset); - size_t actual = 0; - dierr = fpFile->Seek(fOffset, EmbeddedFD::kSeekSet); - if (dierr == kDIErrNone) { - dierr = fpFile->Read(fBlockData, 1 /*kBlockSize*/, &actual); - } - if (dierr != kDIErrNone) { - CString msg, failed; - failed.LoadString(IDS_FAILED); - msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImg::DIStrError(dierr)); - MessageBox(msg, failed, MB_OK); - // TO DO: mark contents as invalid, so editing fails - return -1; - } - - if (actual != kBlockSize) { - LOGI(" BFED partial read of %d bytes", actual); - ASSERT(actual < kBlockSize && actual >= 0); - } - - /* - * We've read the data, but we can't use it. We're a Block editor, - * not a file editor, and we need to get the actual Block data without - * EOF trimming or CP/M 0xe5 removal. - */ - fpFile->GetLastLocationRead(&fBlock); - if (fBlock == A2File::kLastWasSparse) - ; -#endif - - fAlertMsg = ""; - - dierr = fpOpenFile->GetStorage(fBlockIdx, &fBlock); - if (dierr == kDIErrInvalidIndex && fBlockIdx == 0) { - // no first sector; should only happen on CP/M - //FillWithPattern(fBlockData, sizeof(fBlockData), _T("EMPTY ")); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_EMPTY); - } else if (dierr != kDIErrNone) { - CString msg, failed; - CheckedLoadString(&failed, IDS_FAILED); - msg.Format(IDS_DISKEDIT_FIRDFAILED, DiskImgLib::DIStrError(dierr)); - MessageBox(msg, failed, MB_OK); - CheckedLoadString(&fAlertMsg, IDS_FAILED); - // TO DO: mark contents as invalid, so editing fails - return -1; - } else { - if (fBlock == 0) { - LOGI("LoadData Sparse block"); - //FillWithPattern(fBlockData, sizeof(fBlockData), _T("SPARSE ")); - fAlertMsg.Format(IDS_DISKEDITMSG_SPARSE, fBlockIdx); - } else { - LOGI("LoadData reading B=%d", fBlock); - - dierr = fpDiskFS->GetDiskImg()->ReadBlock(fBlock, fBlockData); - if (dierr != kDIErrNone) { - //CString msg; - //CString err; - //err.LoadString(IDS_ERROR); - //msg.Format(IDS_DISKEDIT_NOREADBLOCK, fBlock); - //MessageBox(msg, err, MB_OK|MB_ICONSTOP); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_BADBLOCK); - //return -1; - } - } - } - - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fBlock); - - CWnd* pWnd; - pWnd = GetDlgItem(IDC_DISKEDIT_PREV); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(fBlockIdx > 0); - if (!pWnd->IsWindowEnabled() && GetFocus() == NULL) - GetDlgItem(IDC_DISKEDIT_NEXT)->SetFocus(); - - pWnd = GetDlgItem(IDC_DISKEDIT_NEXT); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(fBlockIdx+1 < fpOpenFile->GetBlockCount()); - if (!pWnd->IsWindowEnabled() && GetFocus() == NULL) - GetDlgItem(IDC_DISKEDIT_PREV)->SetFocus(); - - DisplayData(); - - return 0; -} - -void BlockFileEditDialog::OnReadPrev(void) -{ - if (fBlockIdx == 0) - return; - - fBlockIdx--; - ASSERT(fBlockIdx >= 0); - LoadData(); -} - -void BlockFileEditDialog::OnReadNext(void) -{ - if (fBlockIdx+1 >= fpOpenFile->GetBlockCount()) - return; - - fBlockIdx++; - ASSERT(fBlockIdx < fpOpenFile->GetBlockCount()); - LoadData(); -} - - -/* - * =========================================================================== - * NibbleEditDialog - * =========================================================================== - */ - -BOOL NibbleEditDialog::OnInitDialog(void) -{ - /* - * Get rid of the "sector" input item. - */ - CWnd* pWnd; - - pWnd = GetDlgItem(IDC_STEXT_SECTOR); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DISKEDIT_SECTOR); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DISKEDIT_SECTORSPIN); - pWnd->DestroyWindow(); - - CString trackStr; - trackStr.Format(L"Track (%d):", fpDiskFS->GetDiskImg()->GetNumTracks()); - pWnd = GetDlgItem(IDC_STEXT_TRACK); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(trackStr); - - /* - * Increase the size of the window so it's the same height as blocks. - * - * NOTE: using a pixel constant is probably bad. We want to use something - * like GetTextMetrics, but I'm not sure how to get that without a - * device context. - */ - CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_DISKEDIT_EDIT); - ASSERT(pEdit != NULL); - const int kStretchHeight = 249; - CRect rect; - - GetWindowRect(&rect); - rect.bottom += kStretchHeight; - MoveWindow(&rect); - - /* - * Must postpone resize of edit ctrl until after data has been loaded, or - * scroll bars fail to appear under Win98. Makes no sense whatsoever, but - * that's Windows for you. - */ -#if 0 - CRect inner; - pEdit->GetRect(&inner); - inner.bottom += kStretchHeight; - pEdit->GetWindowRect(&rect); - ScreenToClient(&rect); - rect.bottom += kStretchHeight; - pEdit->MoveWindow(&rect); - pEdit->SetRect(&inner); -#endif - - /* show the scroll bar */ - pEdit->ShowScrollBar(SB_VERT); - - MoveControl(this, IDC_DISKEDIT_DONE, 0, kStretchHeight); - MoveControl(this, IDC_DISKEDIT_OPENFILE, 0, kStretchHeight); - MoveControl(this, IDC_DISKEDIT_SUBVOLUME, 0, kStretchHeight); - MoveControl(this, IDHELP, 0, kStretchHeight); - MoveControl(this, IDC_DISKEDIT_NIBBLE_PARMS, 0, kStretchHeight); - - /* disable opening of files and sub-volumes */ - pWnd = GetDlgItem(IDC_DISKEDIT_OPENFILE); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_DISKEDIT_SUBVOLUME); - pWnd->EnableWindow(FALSE); - - /* - * Do base-class construction. - */ - DiskEditDialog::OnInitDialog(); - - /* - * This currently has no effect on the nibble editor. Someday we may - * want to highlight and/or decode address fields. - */ - pWnd = GetDlgItem(IDC_DISKEDIT_NIBBLE_PARMS); - pWnd->EnableWindow(FALSE); - - /* - * Configure the track spin button. - */ - MySpinCtrl* pSpin; - pSpin = (MySpinCtrl*)GetDlgItem(IDC_DISKEDIT_TRACKSPIN); - ASSERT(pSpin != NULL); - pSpin->SetRange32(0, fpDiskFS->GetDiskImg()->GetNumTracks()-1); - pSpin->SetPos(0); - - /* give us something to look at */ - LoadData(); - - return TRUE; -} - -int NibbleEditDialog::LoadData(void) -{ - //LOGI("BED LoadData"); - ASSERT(fpDiskFS != NULL); - ASSERT(fpDiskFS->GetDiskImg() != NULL); - - if (ReadSpinner(IDC_DISKEDIT_TRACKSPIN, &fTrack) != 0) - return -1; - - LOGI("LoadData reading track=%d", fTrack); - - fAlertMsg = ""; - DIError dierr; - dierr = fpDiskFS->GetDiskImg()->ReadNibbleTrack(fTrack, fNibbleData, - &fNibbleDataLen); - if (dierr != kDIErrNone) { - LOGI("NED track read failed: %hs", DiskImgLib::DIStrError(dierr)); - CheckedLoadString(&fAlertMsg, IDS_DISKEDITMSG_BADTRACK); - } - - DisplayData(); - - return 0; -} - -void NibbleEditDialog::OnDoRead(void) -{ - LoadData(); -} - -void NibbleEditDialog::OnDoWrite(void) -{ - MessageBox(L"Write!"); -} - -void NibbleEditDialog::OnReadPrev(void) -{ - if (fTrack == 0) - return; - - fTrack--; - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); - LoadData(); -} - -void NibbleEditDialog::OnReadNext(void) -{ - ASSERT(fpDiskFS != NULL); - if (fTrack == fpDiskFS->GetDiskImg()->GetNumTracks() - 1) - return; - - fTrack++; - SetSpinner(IDC_DISKEDIT_TRACKSPIN, fTrack); - LoadData(); -} diff --git a/ciderpress/app/DiskEditDialog.h b/ciderpress/app/DiskEditDialog.h deleted file mode 100644 index 0e00f74..0000000 --- a/ciderpress/app/DiskEditDialog.h +++ /dev/null @@ -1,412 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Class definition for DiskEdit dialog. - */ -#ifndef APP_DISKEDITDIALOG_H -#define APP_DISKEDITDIALOG_H - -#include "../diskimg/DiskImg.h" -#include "../util/UtilLib.h" -#include "resource.h" - -/* - * An abstract base class to support "sector editing" and "block editing" - * dialogs, which differ chiefly in how much data they present at a time. - * - * NOTE: override OnCancel to insert an "are you sure" message when the - * block is dirty. - * - * NOTE to self: if the initial block/sector read fails, we can be left - * with invalid stuff in the buffer. Keep that in mind if editing is - * enabled. - */ -class DiskEditDialog : public CDialog { -public: - DiskEditDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL) : - CDialog(nIDTemplate, pParentWnd) - { - fReadOnly = true; - fpDiskFS = NULL; - fFileName = ""; - fPositionShift = 0; - fFirstResize = true; - } - virtual ~DiskEditDialog() {} - - void Setup(DiskFS* pDiskFS, const WCHAR* fileName) { - ASSERT(pDiskFS != NULL); - ASSERT(fileName != NULL); - fpDiskFS = pDiskFS; - fFileName = fileName; - } - - enum { kSectorSize=256, kBlockSize=512 }; - - virtual int LoadData(void) = 0; - - virtual void DisplayData(void) = 0; - - /* - * Convert a chunk of data into a hex dump, and stuff it into the edit control. - */ - virtual void DisplayData(const uint8_t* buf, int size); - - /* - * Display a track full of nibble data. - */ - virtual void DisplayNibbleData(const uint8_t* srcBuf, int size); - - bool GetReadOnly(void) const { return fReadOnly; } - void SetReadOnly(bool val) { fReadOnly = val; } - int GetPositionShift(void) const { return fPositionShift; } - void SetPositionShift(int val) { fPositionShift = val; } - DiskFS* GetDiskFS(void) const { return fpDiskFS; } - const WCHAR* GetFileName(void) const { return fFileName; } - -protected: - // return a low-ASCII character so we can read high-ASCII files - inline char PrintableChar(unsigned char ch) { - if (ch < 0x20) - return '.'; - else if (ch < 0x80) - return ch; - else if (ch < 0xa0 || ch == 0xff) // 0xff becomes 0x7f - return '.'; - else - return ch & 0x7f; - } - - virtual BOOL OnInitDialog(void) override; - - // catch key - virtual BOOL PreTranslateMessage(MSG* pMsg) override; - - /* - * Handle the "Done" button. We don't use IDOK because we don't want - * to bail out of the dialog. - */ - afx_msg virtual void OnDone(void); - - /* - * Toggle the spin button / edit controls. - */ - afx_msg virtual void OnHexMode(void); - - afx_msg virtual void OnDoRead(void) = 0; - afx_msg virtual void OnDoWrite(void) = 0; - afx_msg virtual void OnReadPrev(void) = 0; - afx_msg virtual void OnReadNext(void) = 0; - - /* - * Create a new instance of the disk edit dialog, for a sub-volume. - */ - afx_msg virtual void OnSubVolume(void); - - afx_msg virtual void OnOpenFile(void) = 0; - - /* - * Change the nibble parms. - * - * Assumes the parm list is linear and unbroken. - */ - afx_msg virtual void OnNibbleParms(void); - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_DISKEDIT); - } - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - /* - * Change the mode of a spin button. The Windows control doesn't - * immediately update with a hex display, so we do it manually. (Our - * replacement class does this correctly, but I'm leaving the code alone - * for now.) - */ - void SetSpinMode(int id, int base); - - /* - * Read a value from a spin control. - * - * Returns 0 on success, -1 if the return value from the spin control was - * invalid. In the latter case, an error dialog will be displayed. - */ - int ReadSpinner(int id, long* pVal); - - /* - * Set the value of a spin control. - */ - void SetSpinner(int id, long val); - - /* - * Open a file in a disk image. - * - * Returns a pointer to the A2File and A2FileDescr structures on success, NULL - * on failure. The pointer placed in "ppOpenFile" must be freed by invoking - * its Close function. - */ - DIError OpenFile(const WCHAR* fileName, bool openRsrc, A2File** ppFile, - A2FileDescr** ppOpenFile); - - DiskFS* fpDiskFS; - CString fFileName; - CString fAlertMsg; - bool fReadOnly; - int fPositionShift; - -private: - /* - * Initialize the nibble parm drop-list. - */ - void InitNibbleParmList(void); - - /* - * Replace a spin button with our improved version. - */ - int ReplaceSpinCtrl(MySpinCtrl* pNewSpin, int idSpin, int idEdit); - - MySpinCtrl fTrackSpinner; - MySpinCtrl fSectorSpinner; - bool fFirstResize; - - //afx_msg void OnPaint(); - DECLARE_MESSAGE_MAP() -}; - - -/* - * The "sector edit" dialog, which displays 256 bytes at a time, and - * accesses a disk by track/sector. - */ -class SectorEditDialog : public DiskEditDialog { -public: - SectorEditDialog(CWnd* pParentWnd = NULL) : - DiskEditDialog(IDD_DISKEDIT, pParentWnd) - { - fTrack = 0; - fSector = 0; - } - virtual ~SectorEditDialog() {} - - virtual int LoadData(void) override; // load the current track/sector - virtual void DisplayData(void) override { - DiskEditDialog::DisplayData(fSectorData, kSectorSize); - } - - //void SetTrack(int val) { fTrack = val; } - //void SetSector(int val) { fSector = val; } - -protected: - virtual BOOL OnInitDialog(void) override; - - afx_msg virtual void OnDoRead(void); - afx_msg virtual void OnDoWrite(void); - - /* - * Back up to the previous track/sector. - */ - afx_msg virtual void OnReadPrev(void); - - /* - * Advance to the next track/sector. - */ - afx_msg virtual void OnReadNext(void); - - /* - * Open a file on the disk image. If successful, open a new edit dialog - * that's in "file follow" mode. - */ - afx_msg virtual void OnOpenFile(void); - - long fTrack; - long fSector; - uint8_t fSectorData[kSectorSize]; -}; - -/* - * Edit a file sector-by-sector. - */ -class SectorFileEditDialog : public SectorEditDialog { -public: - SectorFileEditDialog(SectorEditDialog* pSectEdit, CWnd* pParentWnd = NULL): - SectorEditDialog(pParentWnd) - { - DiskEditDialog::Setup(pSectEdit->GetDiskFS(), - pSectEdit->GetFileName()); - fSectorIdx = 0; - } - virtual ~SectorFileEditDialog() {} - - /* we do NOT own pOpenFile, and should not delete it */ - void SetupFile(const WCHAR* fileName, bool rsrcFork, A2File* pFile, - A2FileDescr* pOpenFile) - { - fOpenFileName = fileName; - fOpenRsrcFork = rsrcFork; - fpFile = pFile; - fpOpenFile = pOpenFile; - fLength = 0; - if (fpOpenFile->Seek(0, DiskImgLib::kSeekEnd) == kDIErrNone) - fLength = fpOpenFile->Tell(); - } - - virtual int LoadData(void); // load data from the current offset - -private: - // overrides - virtual BOOL OnInitDialog(void); - - afx_msg virtual void OnReadPrev(void); - afx_msg virtual void OnReadNext(void); - - CString fOpenFileName; - bool fOpenRsrcFork; - A2File* fpFile; - A2FileDescr* fpOpenFile; - //long fOffset; - long fSectorIdx; - di_off_t fLength; -}; - - -/* - * The "block edit" dialog, which displays 512 bytes at a time, and - * accesses a disk by linear block number. - */ -class BlockEditDialog : public DiskEditDialog { -public: - BlockEditDialog(CWnd* pParentWnd = NULL) : - DiskEditDialog(IDD_DISKEDIT, pParentWnd) - { - fBlock = 0; - } - virtual ~BlockEditDialog() {} - - virtual int LoadData(void) override; // load the current block - virtual void DisplayData(void) override { - DiskEditDialog::DisplayData(fBlockData, kBlockSize); - } - -protected: - virtual BOOL OnInitDialog(void) override; - - afx_msg virtual void OnDoRead(void); - afx_msg virtual void OnDoWrite(void); - - /* - * Back up to the previous track/sector, or (in follow-file mode) to the - * previous sector in the file. - */ - afx_msg virtual void OnReadPrev(void); - - /* - * Same as OnReadPrev, but moving forward. - */ - afx_msg virtual void OnReadNext(void); - - /* - * Open a file on the disk image. If successful, open a new edit dialog - * that's in "file follow" mode. - */ - afx_msg virtual void OnOpenFile(void); - - long fBlock; - uint8_t fBlockData[kBlockSize]; -}; - - -/* - * Edit a file block-by-block. - */ -class BlockFileEditDialog : public BlockEditDialog { -public: - BlockFileEditDialog(BlockEditDialog* pBlockEdit, CWnd* pParentWnd = NULL) : - BlockEditDialog(pParentWnd) - { - DiskEditDialog::Setup(pBlockEdit->GetDiskFS(), - pBlockEdit->GetFileName()); - fBlockIdx = 0; - } - virtual ~BlockFileEditDialog() {} - - /* we do NOT own pOpenFile, and should not delete it */ - void SetupFile(const WCHAR* fileName, bool rsrcFork, A2File* pFile, - A2FileDescr* pOpenFile) - { - fOpenFileName = fileName; - fOpenRsrcFork = rsrcFork; - fpFile = pFile; - fpOpenFile = pOpenFile; - fLength = 0; - if (fpOpenFile->Seek(0, DiskImgLib::kSeekEnd) == kDIErrNone) - fLength = fpOpenFile->Tell(); - } - - virtual int LoadData(void); // load data from the current offset - -private: - // overrides - virtual BOOL OnInitDialog(void); - - /* - * Move to the previous Block in the file. - */ - afx_msg virtual void OnReadPrev(void); - - /* - * Move to the next Block in the file. - */ - afx_msg virtual void OnReadNext(void); - - CString fOpenFileName; - bool fOpenRsrcFork; - A2File* fpFile; - A2FileDescr* fpOpenFile; - //long fOffset; - long fBlockIdx; - di_off_t fLength; -}; - -/* - * The "sector edit" dialog, which displays 256 bytes at a time, and - * accesses a disk by track/sector. - */ -class NibbleEditDialog : public DiskEditDialog { -public: - NibbleEditDialog(CWnd* pParentWnd = NULL) : - DiskEditDialog(IDD_DISKEDIT, pParentWnd) - { - fTrack = 0; - } - virtual ~NibbleEditDialog() {} - - virtual int LoadData(void) override; // load the current track/sector - virtual void DisplayData(void) override { - DiskEditDialog::DisplayNibbleData(fNibbleData, fNibbleDataLen); - } - -protected: - /* - * Rearrange the DiskEdit dialog (which defaults to SectorEdit mode) to - * accommodate nibble editing. - */ - virtual BOOL OnInitDialog(void) override; - - afx_msg virtual void OnDoRead(void); - afx_msg virtual void OnDoWrite(void); - afx_msg virtual void OnReadPrev(void); - afx_msg virtual void OnReadNext(void); - afx_msg virtual void OnOpenFile(void) { ASSERT(false); } - afx_msg virtual void OnNibbleParms(void) { ASSERT(false); } - - long fTrack; - uint8_t fNibbleData[DiskImgLib::kTrackAllocSize]; - long fNibbleDataLen; -}; - -#endif /*APP_DISKEDITDIALOG_H*/ diff --git a/ciderpress/app/DiskEditOpenDialog.cpp b/ciderpress/app/DiskEditOpenDialog.cpp deleted file mode 100644 index f5e5ded..0000000 --- a/ciderpress/app/DiskEditOpenDialog.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Simple dialog class that returns when any of its buttons are hit. - */ -#include "StdAfx.h" -#include "DiskEditOpenDialog.h" - -BEGIN_MESSAGE_MAP(DiskEditOpenDialog, CDialog) - ON_BN_CLICKED(IDC_DEOW_FILE, OnButtonFile) - ON_BN_CLICKED(IDC_DEOW_VOLUME, OnButtonVolume) - ON_BN_CLICKED(IDC_DEOW_CURRENT, OnButtonCurrent) -END_MESSAGE_MAP() - - -BOOL DiskEditOpenDialog::OnInitDialog(void) -{ - if (!fArchiveOpen) { - CButton* pButton = (CButton*) GetDlgItem(IDC_DEOW_CURRENT); - ASSERT(pButton != NULL); - pButton->EnableWindow(FALSE); - } - - return CDialog::OnInitDialog(); -} - -void DiskEditOpenDialog::OnButtonFile(void) -{ - fOpenWhat = kOpenFile; - OnOK(); -} - -void DiskEditOpenDialog::OnButtonVolume(void) -{ - fOpenWhat = kOpenVolume; - OnOK(); -} - -void DiskEditOpenDialog::OnButtonCurrent(void) -{ - fOpenWhat = kOpenCurrent; - OnOK(); -} diff --git a/ciderpress/app/DiskEditOpenDialog.h b/ciderpress/app/DiskEditOpenDialog.h deleted file mode 100644 index 613242e..0000000 --- a/ciderpress/app/DiskEditOpenDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Decide how to open the disk editor. - */ -#ifndef APP_DISKEDITOPENDIALOG_H -#define APP_DISKEDITOPENDIALOG_H - -#include -#include "resource.h" - -/* - * Very simple dialog class with three buttons (plus "cancel"). - * - * The button chosen will be returned in "fOpenWhat". - */ -class DiskEditOpenDialog : public CDialog { -public: - typedef enum { - kOpenUnknown = 0, - kOpenFile, - kOpenVolume, - kOpenCurrent, - } OpenWhat; - - DiskEditOpenDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_DISKEDIT_OPENWHICH, pParentWnd), - fArchiveOpen(false), fOpenWhat(kOpenUnknown) - {} - - // set this if the main content list has a file open - bool fArchiveOpen; - // return value -- which button was hit - OpenWhat fOpenWhat; - -private: - virtual BOOL OnInitDialog(void) override; - - afx_msg void OnButtonFile(void); - afx_msg void OnButtonVolume(void); - afx_msg void OnButtonCurrent(void); - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_DISKEDITOPENDIALOG_H*/ diff --git a/ciderpress/app/DiskFSTree.cpp b/ciderpress/app/DiskFSTree.cpp deleted file mode 100644 index d0c6184..0000000 --- a/ciderpress/app/DiskFSTree.cpp +++ /dev/null @@ -1,223 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "StdAfx.h" -#include "DiskFSTree.h" -#include "ChooseAddTargetDialog.h" -#include "../reformat/Charset.h" - -using namespace DiskImgLib; - -bool DiskFSTree::BuildTree(DiskFS* pDiskFS, CTreeCtrl* pTree) -{ - ASSERT(pDiskFS != NULL); - ASSERT(pTree != NULL); - - pTree->SetImageList(&fTreeImageList, TVSIL_NORMAL); - return AddDiskFS(pTree, TVI_ROOT, pDiskFS, 1); -} - -bool DiskFSTree::AddDiskFS(CTreeCtrl* pTree, HTREEITEM parent, - DiskImgLib::DiskFS* pDiskFS, int depth) -{ - const DiskFS::SubVolume* pSubVol; - TargetData* pTarget; - HTREEITEM hLocalRoot; - TVITEM tvi; - TVINSERTSTRUCT tvins; - - /* - * Insert an entry for the current item. - * - * The TVITEM struct wants a pointer to WCHAR* storage for the item - * text. The DiskFS only provides narrow chars, so we need to create - * some local storage for the widened version. Some calls that take a - * TVITEM allow the text to be edited, so the field is LPWSTR rather - * than LPCWSTR, but our uses don't allow editing, so we're okay - * passing it a pointer to storage inside a CString. - */ - pTarget = AllocTargetData(); - pTarget->kind = kTargetDiskFS; - pTarget->pDiskFS = pDiskFS; - pTarget->pFile = NULL; // could also use volume dir for ProDOS - tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - CString volumeId(Charset::ConvertMORToUNI(pDiskFS->GetVolumeID())); - int index = fStringHolder.Add(volumeId); - tvi.pszText = (LPWSTR)(LPCWSTR) fStringHolder.GetAt(index); - tvi.cchTextMax = 0; // not needed for insertitem -// tvi.iImage = kTreeImageFolderClosed; -// tvi.iSelectedImage = kTreeImageFolderOpen; - if (pDiskFS->GetReadWriteSupported() && !pDiskFS->GetFSDamaged()) { - tvi.iImage = kTreeImageHardDriveRW; - pTarget->selectable = true; - } else { - tvi.iImage = kTreeImageHardDriveRO; - pTarget->selectable = false; - } - tvi.iSelectedImage = tvi.iImage; - tvi.lParam = (LPARAM) pTarget; - tvins.item = tvi; - tvins.hInsertAfter = parent; - tvins.hParent = parent; - hLocalRoot = pTree->InsertItem(&tvins); - if (hLocalRoot == NULL) { - LOGW("Tree root InsertItem failed"); - return false; - } - - /* - * Scan for and handle all sub-volumes. - */ - pSubVol = pDiskFS->GetNextSubVolume(NULL); - while (pSubVol != NULL) { - if (!AddDiskFS(pTree, hLocalRoot, pSubVol->GetDiskFS(), depth+1)) - return false; - - pSubVol = pDiskFS->GetNextSubVolume(pSubVol); - } - - /* - * If this volume has sub-directories, and is read-write, add the subdirs - * to the tree. - * - * We use "depth" rather than "depth+1" because the first subdir entry - * (the volume dir) doesn't get its own entry. We use the disk entry - * to represent the disk's volume dir. - */ - if (fIncludeSubdirs && pDiskFS->GetReadWriteSupported() && - !pDiskFS->GetFSDamaged()) - { - AddSubdir(pTree, hLocalRoot, pDiskFS, NULL, depth); - } - - /* - * If we're above the max expansion depth, expand the node. - */ - if (fExpandDepth == -1 || depth <= fExpandDepth) - pTree->Expand(hLocalRoot, TVE_EXPAND); - - /* - * Finally, if this is the root node, select it. - */ - if (parent == TVI_ROOT) { - pTree->Select(hLocalRoot, TVGN_CARET); - } - - return true; -} - -DiskImgLib::A2File* DiskFSTree::AddSubdir(CTreeCtrl* pTree, HTREEITEM parent, - DiskImgLib::DiskFS* pDiskFS, DiskImgLib::A2File* pParentFile, - int depth) -{ - A2File* pFile; - TargetData* pTarget; - HTREEITEM hLocalRoot; - TVITEM tvi; - TVINSERTSTRUCT tvins; - - pFile = pDiskFS->GetNextFile(pParentFile); - if (pFile == NULL && pParentFile == NULL) { - /* this can happen on an empty DOS 3.3 disk; under ProDOS, we always - have the volume entry */ - /* note pFile will be NULL if this happens to be a subdirectory - positioned as the very last file on the disk */ - return NULL; - } - - if (pParentFile == NULL) { - /* - * This is the root of the disk. We already have a DiskFS entry for - * it, so don't add a new tree item here. - * - * Check to see if this disk has a volume directory entry. - */ - if (pFile->IsVolumeDirectory()) { - pParentFile = pFile; - pFile = pDiskFS->GetNextFile(pFile); - } - hLocalRoot = parent; - } else { - /* - * Add an entry for this subdir (the "parent" entry). - * - * This has the same wide-vs-narrow storage issues as AddDiskFS(). - */ - pTarget = AllocTargetData(); - pTarget->kind = kTargetSubdir; - pTarget->selectable = true; - pTarget->pDiskFS = pDiskFS; - pTarget->pFile = pParentFile; - tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; - CString fileNameW(pParentFile->GetFileName()); - int index = fStringHolder.Add(fileNameW); - tvi.pszText = (LPWSTR)(LPCWSTR) fStringHolder.GetAt(index); - tvi.cchTextMax = 0; // not needed for insertitem - tvi.iImage = kTreeImageFolderClosed; - tvi.iSelectedImage = kTreeImageFolderOpen; - tvi.lParam = (LPARAM) pTarget; - tvins.item = tvi; - tvins.hInsertAfter = parent; - tvins.hParent = parent; - hLocalRoot = pTree->InsertItem(&tvins); - if (hLocalRoot == NULL) { - LOGW("Tree insert '%ls' failed", tvi.pszText); - return NULL; - } - } - - while (pFile != NULL) { - if (pFile->IsDirectory()) { - ASSERT(!pFile->IsVolumeDirectory()); - - if (pFile->GetParent() == pParentFile) { - /* this is a subdir of us */ - pFile = AddSubdir(pTree, hLocalRoot, pDiskFS, pFile, depth+1); - if (pFile == NULL) - break; // out of while -- disk is done - } else { - /* not one of our subdirs; pop up a level */ - break; // out of while -- subdir is done - } - } else { - pFile = pDiskFS->GetNextFile(pFile); - } - } - - /* expand as appropriate */ - if (fExpandDepth == -1 || depth <= fExpandDepth) - pTree->Expand(hLocalRoot, TVE_EXPAND); - - return pFile; -} - -DiskFSTree::TargetData* DiskFSTree::AllocTargetData(void) -{ - TargetData* pNew = new TargetData; - - if (pNew == NULL) - return NULL; - - /* insert it at the head of the list, and update the head pointer */ - pNew->pNext = fpTargetData; - fpTargetData = pNew; - - return pNew; -} - -void DiskFSTree::FreeAllTargetData(void) -{ - TargetData* pTarget; - TargetData* pNext; - - pTarget = fpTargetData; - while (pTarget != NULL) { - pNext = pTarget->pNext; - delete pTarget; - pTarget = pNext; - } - - fpTargetData = NULL; -} diff --git a/ciderpress/app/DiskFSTree.h b/ciderpress/app/DiskFSTree.h deleted file mode 100644 index 809498d..0000000 --- a/ciderpress/app/DiskFSTree.h +++ /dev/null @@ -1,126 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Fill out a CTreeCtrl with the results of a tree search through a DiskFS and - * its sub-volumes. - */ -#ifndef APP_DISKFSTREE_H -#define APP_DISKFSTREE_H - -#include "resource.h" -#include "../diskimg/DiskImg.h" - -/* - * Utility class for extracting a directory hierarchy from a DiskFS and - * adding it to a CTreeCtrl. - * - * The storage for some of the strings provided to the tree control is - * managed by this class, so delete this object after the CTreeCtrl is - * deleted. (Generally, this should be paired with a CTreeCtrl in a dialog - * object.) - */ -class DiskFSTree { -public: - DiskFSTree(void) { - fIncludeSubdirs = false; - fExpandDepth = 0; - - fpDiskFS = NULL; - fpTargetData = NULL; - LoadTreeImages(); - } - virtual ~DiskFSTree(void) { FreeAllTargetData(); } - - /* - * Create the contents of the tree control. - */ - bool BuildTree(DiskImgLib::DiskFS* pDiskFS, CTreeCtrl* pTree); - - /* if set, includes folders as well as disks */ - bool fIncludeSubdirs; - - /* start with the tree expanded to this depth (0=none, -1=all) */ - int fExpandDepth; - - typedef enum { - kTargetUnknown = 0, kTargetDiskFS, kTargetSubdir - } TargetKind; - struct TargetData { - TargetData() - : kind(kTargetUnknown), selectable(false), pDiskFS(NULL), - pFile(NULL), pNext(NULL) - {} - TargetKind kind; - bool selectable; - DiskImgLib::DiskFS* pDiskFS; - DiskImgLib::A2File* pFile; - - // easier to keep a list than to chase through the tree - struct TargetData* pNext; - }; - -private: - /* - * Load the specified DiskFS into the tree, recursively adding any - * sub-volumes. Pass in an initial depth of 1. - * - * Returns true on success. - */ - bool AddDiskFS(CTreeCtrl* pTree, HTREEITEM root, - DiskImgLib::DiskFS* pDiskFS, int depth); - - /* - * Add the subdir and all of the subdirectories of the current subdir. - * - * The files are held in a linear list in the DiskFS, so we have to - * reconstruct the hierarchy from the path names. Pass in NULL for the - * root volume. - * - * Returns a pointer to the next A2File in the list (i.e. the first one - * that we couldn't digest). This assumes that the contents of a - * subdirectory are grouped together in the linear list, so that we can - * immediately bail when the first misfit is encountered. - */ - DiskImgLib::A2File* AddSubdir(CTreeCtrl* pTree, HTREEITEM parent, - DiskImgLib::DiskFS* pDiskFS, DiskImgLib::A2File* pFile, - int depth); - - /* - * Allocate a new TargetData struct, and add it to our list. - */ - TargetData* AllocTargetData(void); - - /* - * Free up the TargetData structures we created. - */ - void FreeAllTargetData(void); - - /* - * Load bitmaps used in the tree control. - */ - void LoadTreeImages(void) { - if (!fTreeImageList.Create(IDB_TREE_PICS, 16, 1, CLR_DEFAULT)) { - LOGW("GLITCH: list image create failed"); - } - fTreeImageList.SetBkColor(::GetSysColor(COLOR_WINDOW)); - } - - enum { // defs for IDB_TREE_PICS - kTreeImageFolderClosed = 0, - kTreeImageFolderOpen = 1, - kTreeImageHardDriveRW = 2, - kTreeImageHardDriveRO = 3, - }; - CImageList fTreeImageList; - - DiskImgLib::DiskFS* fpDiskFS; - TargetData* fpTargetData; - - // Storage for wide strings that were converted from DiskFS narrow strings. - CStringArray fStringHolder; -}; - -#endif /*APP_DISKFSTREE_H*/ diff --git a/ciderpress/app/DoneOpenDialog.h b/ciderpress/app/DoneOpenDialog.h deleted file mode 100644 index eb97dc9..0000000 --- a/ciderpress/app/DoneOpenDialog.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Simple dialog to offer the opportunity to open the file we just created. - */ -#ifndef APP_DONEOPENDIALOG_H -#define APP_DONEOPENDIALOG_H - -#include "resource.h" - -class DoneOpenDialog : public CDialog { -public: - DoneOpenDialog(CWnd* pParentWnd = NULL) : CDialog(IDD_DONEOPEN, pParentWnd) - {} - virtual ~DoneOpenDialog(void) {} -}; - -#endif /*APP_DONEOPENDIALOG_H*/ diff --git a/ciderpress/app/EOLScanDialog.cpp b/ciderpress/app/EOLScanDialog.cpp deleted file mode 100644 index 3cba9c2..0000000 --- a/ciderpress/app/EOLScanDialog.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Trivial implementation of EOLScanDialog. - * - * I'd stuff the whole thing in the header, but I need the "help" button to - * work, and it's easiest to do that through a message map. - */ -#include "StdAfx.h" -#include "EOLScanDialog.h" - -BEGIN_MESSAGE_MAP(EOLScanDialog, CDialog) - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - -BOOL EOLScanDialog::OnInitDialog(void) -{ - CWnd* pWnd; - CString fmt; - - fmt.Format(L"%ld", fCountChars); - pWnd = GetDlgItem(IDC_EOLSCAN_CHARS); - pWnd->SetWindowText(fmt); - - fmt.Format(L"%ld", fCountCR); - pWnd = GetDlgItem(IDC_EOLSCAN_CR); - pWnd->SetWindowText(fmt); - - fmt.Format(L"%ld", fCountLF); - pWnd = GetDlgItem(IDC_EOLSCAN_LF); - pWnd->SetWindowText(fmt); - - fmt.Format(L"%ld", fCountCRLF); - pWnd = GetDlgItem(IDC_EOLSCAN_CRLF); - pWnd->SetWindowText(fmt); - - fmt.Format(L"%ld", fCountHighASCII); - pWnd = GetDlgItem(IDC_EOLSCAN_HIGHASCII); - pWnd->SetWindowText(fmt); - - return CDialog::OnInitDialog(); -} diff --git a/ciderpress/app/EOLScanDialog.h b/ciderpress/app/EOLScanDialog.h deleted file mode 100644 index 8484287..0000000 --- a/ciderpress/app/EOLScanDialog.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * A simple dialog to display the results of an EOL scan. - */ -#ifndef APP_EOLSCANDIALOG_H -#define APP_EOLSCANDIALOG_H - -#include "resource.h" - -/* - * Entire class is here. - */ -class EOLScanDialog : public CDialog { -public: - EOLScanDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_EOLSCAN, pParentWnd) - {} - virtual ~EOLScanDialog(void) {} - - long fCountChars; - long fCountCR; - long fCountLF; - long fCountCRLF; - long fCountHighASCII; - -private: - BOOL OnInitDialog(void) override; - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_EOL_SCAN); - } - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_EOLSCANDIALOG_H*/ diff --git a/ciderpress/app/EditAssocDialog.cpp b/ciderpress/app/EditAssocDialog.cpp deleted file mode 100644 index 699dcb0..0000000 --- a/ciderpress/app/EditAssocDialog.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#ifdef CAN_UPDATE_FILE_ASSOC -#include "EditAssocDialog.h" -#include "MyApp.h" -#include "Registry.h" - -BEGIN_MESSAGE_MAP(EditAssocDialog, CDialog) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - -/* this comes from VC++6.0 MSDN help */ -#ifndef ListView_SetCheckState - #define ListView_SetCheckState(hwndLV, i, fCheck) \ - ListView_SetItemState(hwndLV, i, \ - INDEXTOSTATEIMAGEMASK((fCheck)+1), LVIS_STATEIMAGEMASK) -#endif - -BOOL EditAssocDialog::OnInitDialog(void) -{ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_ASSOCIATION_LIST); - - ASSERT(pListView != NULL); - //pListView->ModifyStyleEx(0, LVS_EX_CHECKBOXES); - ListView_SetExtendedListViewStyleEx(pListView->m_hWnd, - LVS_EX_CHECKBOXES, LVS_EX_CHECKBOXES); - - /* move it over slightly so we see some overlap */ - CRect rect; - GetWindowRect(&rect); - rect.left += 10; - rect.right += 10; - MoveWindow(&rect); - - - /* - * Initialize this before DDX stuff happens. If the caller didn't - * provide a set, load our own. - */ - if (fOurAssociations == NULL) { - fOurAssociations = new bool[gMyApp.fRegistry.GetNumFileAssocs()]; - Setup(true); - } else { - Setup(false); - } - - return CDialog::OnInitDialog(); -} - -void EditAssocDialog::Setup(bool loadAssoc) -{ - LOGD("Setup!"); - - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_ASSOCIATION_LIST); - ASSERT(pListView != NULL); - - ASSERT(fOurAssociations != NULL); - - /* two columns */ - CRect rect; - pListView->GetClientRect(&rect); - int width; - - width = pListView->GetStringWidth(L"XXExtensionXX"); - pListView->InsertColumn(0, L"Extension", LVCFMT_LEFT, width); - pListView->InsertColumn(1, L"Association", LVCFMT_LEFT, - rect.Width() - width); - - int num = gMyApp.fRegistry.GetNumFileAssocs(); - int idx = 0; - while (num--) { - CString ext, handler; - CString dispStr; - bool ours; - - gMyApp.fRegistry.GetFileAssoc(idx, &ext, &handler, &ours); - if (handler.IsEmpty()) { - handler = L"(no association)"; - } - - pListView->InsertItem(idx, ext); - pListView->SetItemText(idx, 1, handler); - - if (loadAssoc) - fOurAssociations[idx] = ours; - idx++; - } - - //DeleteAllItems(); // for Reload case -} - -void EditAssocDialog::DoDataExchange(CDataExchange* pDX) -{ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_ASSOCIATION_LIST); - - ASSERT(fOurAssociations != NULL); - if (fOurAssociations == NULL) - return; - - int num = gMyApp.fRegistry.GetNumFileAssocs(); - - if (!pDX->m_bSaveAndValidate) { - /* load fixed set of file associations */ - int idx = 0; - while (num--) { - ListView_SetCheckState(pListView->m_hWnd, idx, - fOurAssociations[idx]); - idx++; - } - } else { - /* copy the checkboxes out */ - int idx = 0; - while (num--) { - fOurAssociations[idx] = - (ListView_GetCheckState(pListView->m_hWnd, idx) != 0); - idx++; - } - } -} - -#endif \ No newline at end of file diff --git a/ciderpress/app/EditAssocDialog.h b/ciderpress/app/EditAssocDialog.h deleted file mode 100644 index fd0bf8b..0000000 --- a/ciderpress/app/EditAssocDialog.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * File associations edit dialog. - */ -#ifndef APP_EDITASSOCDIALOG_H -#define APP_EDITASSOCDIALOG_H -#ifdef CAN_UPDATE_FILE_ASSOC -#include "resource.h" - -/* - * Edit whatever associations our registry class cares about. - */ -class EditAssocDialog : public CDialog { -public: - EditAssocDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_ASSOCIATIONS, pParentWnd), - fOurAssociations(NULL) - {} - virtual ~EditAssocDialog() { - delete[] fOurAssociations; - } - - // Which associations are ours. This should be left uninitialized; - // Setup() takes care of that. The caller may "steal" the array - // afterward, freeing it with delete[]. - bool* fOurAssociations; - -protected: - virtual BOOL OnInitDialog(void) override; - void DoDataExchange(CDataExchange* pDX) override; - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_EDIT_ASSOC); - } - - /* - * Load the list view control. - * - * This list isn't sorted, so we don't need to stuff anything into lParam to - * keep the list and source data tied. - * - * If "loadAssoc" is true, we also populate the fOurAssocations table. - */ - void Setup(bool loadAssoc); - - DECLARE_MESSAGE_MAP() -}; - -#endif -#endif /*APP_EDITASSOCDIALOG_H*/ diff --git a/ciderpress/app/EditCommentDialog.cpp b/ciderpress/app/EditCommentDialog.cpp deleted file mode 100644 index 6340f72..0000000 --- a/ciderpress/app/EditCommentDialog.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for EditCommentDialog. - */ -#include "stdafx.h" -#include "EditCommentDialog.h" - -BEGIN_MESSAGE_MAP(EditCommentDialog, CDialog) - ON_BN_CLICKED(IDC_COMMENT_DELETE, OnDelete) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -BOOL EditCommentDialog::OnInitDialog(void) -{ - /* - * If this is a new comment, don't show the delete button. - */ - if (fNewComment) { - CWnd* pWnd = GetDlgItem(IDC_COMMENT_DELETE); - pWnd->EnableWindow(FALSE); - } - - return CDialog::OnInitDialog(); -} - -void EditCommentDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Text(pDX, IDC_COMMENT_EDIT, fComment); -} - -void EditCommentDialog::OnDelete(void) -{ - CString question, title; - int result; - - CheckedLoadString(&title, IDS_EDIT_COMMENT); - CheckedLoadString(&question, IDS_DEL_COMMENT_OK); - result = MessageBox(question, title, MB_OKCANCEL | MB_ICONQUESTION); - if (result == IDCANCEL) - return; - - EndDialog(kDeleteCommentID); -} diff --git a/ciderpress/app/EditCommentDialog.h b/ciderpress/app/EditCommentDialog.h deleted file mode 100644 index 00efb4d..0000000 --- a/ciderpress/app/EditCommentDialog.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Edit a comment. - */ -#ifndef APP_EDITCOMMENTDIALOG_H -#define APP_EDITCOMMENTDIALOG_H - -#include "GenericArchive.h" -#include "resource.h" - -/* - * Edit a comment. We don't currently put a length limit on the comment - * field. - */ -class EditCommentDialog : public CDialog { -public: - EditCommentDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_COMMENT_EDIT, pParentWnd) - { - fNewComment = false; - } - virtual ~EditCommentDialog(void) {} - - enum { kDeleteCommentID = IDC_COMMENT_DELETE }; - - CString fComment; - bool fNewComment; // entry doesn't already have one - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_EDIT_COMMENT); - } - - /* - * User wants to delete the comment. Verify first. - */ - afx_msg void OnDelete(void); - -private: - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_EDITCOMMENTDIALOG_H*/ diff --git a/ciderpress/app/EditPropsDialog.cpp b/ciderpress/app/EditPropsDialog.cpp deleted file mode 100644 index ca59cd5..0000000 --- a/ciderpress/app/EditPropsDialog.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "StdAfx.h" -#include "EditPropsDialog.h" -#include "FileNameConv.h" - -using namespace DiskImgLib; - -BEGIN_MESSAGE_MAP(EditPropsDialog, CDialog) - ON_BN_CLICKED(IDC_PROPS_ACCESS_W, UpdateSimpleAccess) - ON_BN_CLICKED(IDC_PROPS_HFS_MODE, UpdateHFSMode) - ON_CBN_SELCHANGE(IDC_PROPS_FILETYPE, OnTypeChange) - ON_EN_CHANGE(IDC_PROPS_AUXTYPE, OnTypeChange) - ON_EN_CHANGE(IDC_PROPS_HFS_FILETYPE, OnHFSTypeChange) - ON_EN_CHANGE(IDC_PROPS_HFS_AUXTYPE, OnHFSTypeChange) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -void EditPropsDialog::InitProps(GenericEntry* pEntry) -{ - fPathName = pEntry->GetPathNameUNI(); - fProps.fileType = pEntry->GetFileType(); - fProps.auxType = pEntry->GetAuxType(); - fProps.access = pEntry->GetAccess(); - fProps.createWhen = pEntry->GetCreateWhen(); - fProps.modWhen = pEntry->GetModWhen(); - - if (!pEntry->GetFeatureFlag(GenericEntry::kFeatureCanChangeType)) - fAllowedTypes = kAllowedNone; - else if (pEntry->GetFeatureFlag(GenericEntry::kFeaturePascalTypes)) - fAllowedTypes = kAllowedPascal; - else if (pEntry->GetFeatureFlag(GenericEntry::kFeatureDOSTypes)) - fAllowedTypes = kAllowedDOS; - else if (pEntry->GetFeatureFlag(GenericEntry::kFeatureHFSTypes)) - fAllowedTypes = kAllowedHFS; // for HFS disks and ShrinkIt archives - else - fAllowedTypes = kAllowedProDOS; - if (!pEntry->GetFeatureFlag(GenericEntry::kFeatureHasFullAccess)) { - if (pEntry->GetFeatureFlag(GenericEntry::kFeatureHasSimpleAccess)) - fSimpleAccess = true; - else - fNoChangeAccess = true; - } - if (pEntry->GetFeatureFlag(GenericEntry::kFeatureHasInvisibleFlag)) - fAllowInvis = true; -} - - -/* - * Set up the control. We need to load the drop list with the file type - * info, and configure any controls that aren't set by DoDataExchange. - * - * If this is a disk archive, we might want to make the aux type read-only, - * though this would provide a way for users to fix badly-formed archives. - */ -BOOL EditPropsDialog::OnInitDialog(void) -{ - static const int kPascalTypes[] = { - 0x00 /*NON*/, 0x01 /*BAD*/, 0x02 /*PCD*/, 0x03 /*PTX*/, - 0xf3 /*$F3*/, 0x05 /*PDA*/, 0xf4 /*$F4*/, 0x08 /*FOT*/, - 0xf5 /*$f5*/ - }; - static const int kDOSTypes[] = { - 0x04 /*TXT*/, 0x06 /*BIN*/, 0xf2 /*$F2*/, 0xf3 /*$F3*/, - 0xf4 /*$F4*/, 0xfa /*INT*/, 0xfc /*BAS*/, 0xfe /*REL*/ - }; - CComboBox* pCombo; - CWnd* pWnd; - int comboIdx; - - pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE); - ASSERT(pCombo != NULL); - - pCombo->InitStorage(256, 256 * 8); - - for (int type = 0; type < 256; type++) { - const WCHAR* str; - WCHAR buf[10]; - - if (fAllowedTypes == kAllowedPascal) { - /* not the most efficient way, but it'll do */ - int j; - for (j = 0; j < NELEM(kPascalTypes); j++) { - if (kPascalTypes[j] == type) - break; - } - if (j == NELEM(kPascalTypes)) - continue; - } else if (fAllowedTypes == kAllowedDOS) { - int j; - for (j = 0; j < NELEM(kDOSTypes); j++) { - if (kDOSTypes[j] == type) - break; - } - if (j == NELEM(kDOSTypes)) - continue; - } - - str = PathProposal::FileTypeString(type); - if (str[0] == '$') - wsprintf(buf, L"??? $%02X", type); - else - wsprintf(buf, L"%ls $%02X", str, type); - comboIdx = pCombo->AddString(buf); - pCombo->SetItemData(comboIdx, type); - - if ((int) fProps.fileType == type) - pCombo->SetCurSel(comboIdx); - } - if (fProps.fileType >= 256) { - if (fAllowedTypes == kAllowedHFS) { - pCombo->SetCurSel(0); - } else { - // unexpected -- bogus data out of DiskFS? - comboIdx = pCombo->AddString(L"???"); - pCombo->SetCurSel(comboIdx); - pCombo->SetItemData(comboIdx, 256); - } - } - - CString dateStr; - pWnd = GetDlgItem(IDC_PROPS_CREATEWHEN); - ASSERT(pWnd != NULL); - FormatDate(fProps.createWhen, &dateStr); - pWnd->SetWindowText(dateStr); - - pWnd = GetDlgItem(IDC_PROPS_MODWHEN); - ASSERT(pWnd != NULL); - FormatDate(fProps.modWhen, &dateStr); - pWnd->SetWindowText(dateStr); - //LOGI("USING DATE '%ls' from 0x%08lx", dateStr, fProps.modWhen); - - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_PROPS_AUXTYPE); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(4); // max len of aux type str - pEdit = (CEdit*) GetDlgItem(IDC_PROPS_HFS_FILETYPE); - pEdit->SetLimitText(4); - pEdit = (CEdit*) GetDlgItem(IDC_PROPS_HFS_AUXTYPE); - pEdit->SetLimitText(4); - - if (fReadOnly || fAllowedTypes == kAllowedNone) { - pWnd = GetDlgItem(IDC_PROPS_FILETYPE); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_PROPS_AUXTYPE); - pWnd->EnableWindow(FALSE); - } else if (fAllowedTypes == kAllowedPascal) { - pWnd = GetDlgItem(IDC_PROPS_AUXTYPE); - pWnd->EnableWindow(FALSE); - } - if (fReadOnly || fSimpleAccess || fNoChangeAccess) { - pWnd = GetDlgItem(IDC_PROPS_ACCESS_R); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_PROPS_ACCESS_B); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_PROPS_ACCESS_N); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_PROPS_ACCESS_D); - pWnd->EnableWindow(FALSE); - } - if (fReadOnly || !fAllowInvis) { - pWnd = GetDlgItem(IDC_PROPS_ACCESS_I); - pWnd->EnableWindow(FALSE); - } - if (fReadOnly || fNoChangeAccess) { - pWnd = GetDlgItem(IDC_PROPS_ACCESS_W); - pWnd->EnableWindow(FALSE); - } - if (fReadOnly) { - pWnd = GetDlgItem(IDOK); - pWnd->EnableWindow(FALSE); - - CString title; - GetWindowText(/*ref*/ title); - title = title + " (read only)"; - SetWindowText(title); - } - - if (fAllowedTypes != kAllowedHFS) { - CButton* pButton = (CButton*) GetDlgItem(IDC_PROPS_HFS_MODE); - pButton->EnableWindow(FALSE); - } - - return CDialog::OnInitDialog(); -} - -void EditPropsDialog::DoDataExchange(CDataExchange* pDX) -{ - int fileTypeIdx; - BOOL accessR, accessW, accessI, accessB, accessN, accessD; - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE); - - if (pDX->m_bSaveAndValidate) { - CString appName; - CButton *pButton; - bool typeChanged = false; - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - pButton = (CButton*) GetDlgItem(IDC_PROPS_HFS_MODE); - if (pButton->GetCheck() == BST_CHECKED) { - /* HFS mode */ - CString type, creator; - DDX_Text(pDX, IDC_PROPS_HFS_FILETYPE, type); - DDX_Text(pDX, IDC_PROPS_HFS_AUXTYPE, creator); - if (type.GetLength() != 4 || creator.GetLength() != 4) { - MessageBox(L"The file and creator types must be exactly" - L" 4 characters each.", - appName, MB_OK); - pDX->Fail(); - return; - } - fProps.fileType = ((unsigned char) type[0]) << 24 | - ((unsigned char) type[1]) << 16 | - ((unsigned char) type[2]) << 8 | - ((unsigned char) type[3]); - fProps.auxType = ((unsigned char) creator[0]) << 24 | - ((unsigned char) creator[1]) << 16 | - ((unsigned char) creator[2]) << 8 | - ((unsigned char) creator[3]); - } else { - /* ProDOS mode */ - if (GetAuxType() < 0) { - MessageBox(L"The AuxType field must be a valid 4-digit" - L" hexadecimal number.", - appName, MB_OK); - pDX->Fail(); - return; - } - fProps.auxType = GetAuxType(); - - /* pull the file type out, but don't disturb >= 256 */ - DDX_CBIndex(pDX, IDC_PROPS_FILETYPE, fileTypeIdx); - if (fileTypeIdx != 256) { - unsigned long oldType = fProps.fileType; - fProps.fileType = pCombo->GetItemData(fileTypeIdx); - if (fProps.fileType != oldType) - typeChanged = true; - } - } - - DDX_Check(pDX, IDC_PROPS_ACCESS_R, accessR); - DDX_Check(pDX, IDC_PROPS_ACCESS_W, accessW); - DDX_Check(pDX, IDC_PROPS_ACCESS_I, accessI); - DDX_Check(pDX, IDC_PROPS_ACCESS_B, accessB); - DDX_Check(pDX, IDC_PROPS_ACCESS_N, accessN); - DDX_Check(pDX, IDC_PROPS_ACCESS_D, accessD); - fProps.access = (accessR ? GenericEntry::kAccessRead : 0) | - (accessW ? GenericEntry::kAccessWrite : 0) | - (accessI ? GenericEntry::kAccessInvisible : 0) | - (accessB ? GenericEntry::kAccessBackup : 0) | - (accessN ? GenericEntry::kAccessRename : 0) | - (accessD ? GenericEntry::kAccessDelete : 0); - - if (fAllowedTypes == kAllowedDOS && typeChanged && - (fProps.fileType == kFileTypeBIN || - fProps.fileType == kFileTypeINT || - fProps.fileType == kFileTypeBAS)) - { - CString msg; - int result; - - CheckedLoadString(&msg, IDS_PROPS_DOS_TYPE_CHANGE); - result = MessageBox(msg, appName, MB_ICONQUESTION|MB_OKCANCEL); - if (result != IDOK) { - pDX->Fail(); - return; - } - } - } else { - accessR = (fProps.access & GenericEntry::kAccessRead) != 0; - accessW = (fProps.access & GenericEntry::kAccessWrite) != 0; - accessI = (fProps.access & GenericEntry::kAccessInvisible) != 0; - accessB = (fProps.access & GenericEntry::kAccessBackup) != 0; - accessN = (fProps.access & GenericEntry::kAccessRename) != 0; - accessD = (fProps.access & GenericEntry::kAccessDelete) != 0; - DDX_Check(pDX, IDC_PROPS_ACCESS_R, accessR); - DDX_Check(pDX, IDC_PROPS_ACCESS_W, accessW); - DDX_Check(pDX, IDC_PROPS_ACCESS_I, accessI); - DDX_Check(pDX, IDC_PROPS_ACCESS_B, accessB); - DDX_Check(pDX, IDC_PROPS_ACCESS_N, accessN); - DDX_Check(pDX, IDC_PROPS_ACCESS_D, accessD); - - if (fAllowedTypes == kAllowedHFS && - (fProps.fileType > 0xff || fProps.auxType > 0xffff)) - { - char type[5], creator[5]; - - type[0] = (unsigned char) (fProps.fileType >> 24); - type[1] = (unsigned char) (fProps.fileType >> 16); - type[2] = (unsigned char) (fProps.fileType >> 8); - type[3] = (unsigned char) fProps.fileType; - type[4] = '\0'; - creator[0] = (unsigned char) (fProps.auxType >> 24); - creator[1] = (unsigned char) (fProps.auxType >> 16); - creator[2] = (unsigned char) (fProps.auxType >> 8); - creator[3] = (unsigned char) fProps.auxType; - creator[4] = '\0'; - - CString tmpStr; - tmpStr = type; - DDX_Text(pDX, IDC_PROPS_HFS_FILETYPE, tmpStr); - tmpStr = creator; - DDX_Text(pDX, IDC_PROPS_HFS_AUXTYPE, tmpStr); - tmpStr = L"0000"; - DDX_Text(pDX, IDC_PROPS_AUXTYPE, tmpStr); - - CButton* pButton = (CButton*) GetDlgItem(IDC_PROPS_HFS_MODE); - pButton->SetCheck(BST_CHECKED); - } else { - //fileTypeIdx = fProps.fileType; - //if (fileTypeIdx > 256) - // fileTypeIdx = 256; - //DDX_CBIndex(pDX, IDC_PROPS_FILETYPE, fileTypeIdx); - - /* write the aux type as a hex string */ - fAuxType.Format(L"%04X", fProps.auxType); - DDX_Text(pDX, IDC_PROPS_AUXTYPE, fAuxType); - } - OnTypeChange(); // set the description field - UpdateHFSMode(); // set up fields - UpdateSimpleAccess(); // coordinate N/D with W - } - - DDX_Text(pDX, IDC_PROPS_PATHNAME, fPathName); -} - -void EditPropsDialog::OnTypeChange(void) -{ - static const WCHAR kUnknownFileType[] = L"Unknown file type"; - CComboBox* pCombo; - CWnd* pWnd; - int fileType, fileTypeIdx; - long auxType; - const WCHAR* descr = NULL; - - pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE); - ASSERT(pCombo != NULL); - - fileTypeIdx = pCombo->GetCurSel(); - fileType = pCombo->GetItemData(fileTypeIdx); - if (fileType >= 256) { - descr = kUnknownFileType; - } else { - auxType = GetAuxType(); - if (auxType < 0) - auxType = 0; - descr = PathProposal::FileTypeDescription(fileType, auxType); - if (descr == NULL) - descr = kUnknownFileType; - } - - pWnd = GetDlgItem(IDC_PROPS_TYPEDESCR); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(descr); - - /* DOS aux type only applies to BIN */ - if (!fReadOnly && fAllowedTypes == kAllowedDOS) { - pWnd = GetDlgItem(IDC_PROPS_AUXTYPE); - pWnd->EnableWindow(fileType == kFileTypeBIN); - } -} - -void EditPropsDialog::OnHFSTypeChange(void) -{ - assert(fAllowedTypes == kAllowedHFS); -} - -void EditPropsDialog::UpdateHFSMode(void) -{ - CButton* pButton = (CButton*) GetDlgItem(IDC_PROPS_HFS_MODE); - CComboBox* pCombo; - CWnd* pWnd; - - if (pButton->GetCheck() == BST_CHECKED) { - /* switch to HFS mode */ - LOGI("Switching to HFS mode"); - //fHFSMode = true; - - if (!fReadOnly) { - pWnd = GetDlgItem(IDC_PROPS_HFS_FILETYPE); - pWnd->EnableWindow(TRUE); - pWnd = GetDlgItem(IDC_PROPS_HFS_AUXTYPE); - pWnd->EnableWindow(TRUE); - pWnd = GetDlgItem(IDC_PROPS_HFS_LABEL); - pWnd->EnableWindow(TRUE); - } - - /* point the file type at something safe */ - pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE); - pCombo->EnableWindow(FALSE); - - pWnd = GetDlgItem(IDC_PROPS_AUXTYPE); - pWnd->EnableWindow(FALSE); - - pWnd = GetDlgItem(IDC_PROPS_TYPEDESCR); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(L"(HFS type)"); - OnHFSTypeChange(); - } else { - /* switch to ProDOS mode */ - LOGI("Switching to ProDOS mode"); - //fHFSMode = false; - if (!fReadOnly) { - pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE); - pCombo->EnableWindow(TRUE); - pWnd = GetDlgItem(IDC_PROPS_AUXTYPE); - pWnd->EnableWindow(TRUE); - } - - pWnd = GetDlgItem(IDC_PROPS_HFS_FILETYPE); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_PROPS_HFS_AUXTYPE); - pWnd->EnableWindow(FALSE); - pWnd = GetDlgItem(IDC_PROPS_HFS_LABEL); - pWnd->EnableWindow(FALSE); - OnTypeChange(); - } -} - -void EditPropsDialog::UpdateSimpleAccess(void) -{ - if (!fSimpleAccess) - return; - - CButton* pButton; - UINT checked; - - pButton = (CButton*) GetDlgItem(IDC_PROPS_ACCESS_W); - checked = pButton->GetCheck(); - - pButton = (CButton*) GetDlgItem(IDC_PROPS_ACCESS_N); - pButton->SetCheck(checked); - pButton = (CButton*) GetDlgItem(IDC_PROPS_ACCESS_D); - pButton->SetCheck(checked); -} - -long EditPropsDialog::GetAuxType(void) -{ - CWnd* pWnd = GetDlgItem(IDC_PROPS_AUXTYPE); - ASSERT(pWnd != NULL); - - CString aux; - pWnd->GetWindowText(aux); - - const WCHAR* str = aux; - WCHAR* end; - long val; - - if (str[0] == '\0') { - LOGI(" HEY: blank aux type, returning -1"); - return -1; - } - val = wcstoul(aux, &end, 16); - if (end != str + wcslen(str)) { - LOGI(" HEY: found some garbage in aux type '%ls', returning -1", - (LPCWSTR) aux); - return -1; - } - return val; -} diff --git a/ciderpress/app/EditPropsDialog.h b/ciderpress/app/EditPropsDialog.h deleted file mode 100644 index 6055525..0000000 --- a/ciderpress/app/EditPropsDialog.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Edit file properties. - */ -#ifndef APP_EDITPROPSDIALOG_H -#define APP_EDITPROPSDIALOG_H - -#include "GenericArchive.h" -#include "resource.h" - -/* - * Edit ProDOS file attributes, such as file type and auxtype. - */ -class EditPropsDialog : public CDialog { -public: - typedef enum AllowedTypes { - kAllowedUnknown = 0, - kAllowedProDOS, // 8-bit type, 16-bit aux - kAllowedHFS, // 32-bit type, 32-bit aux - kAllowedNone, // CP/M - kAllowedPascal, // UCSD Pascal - kAllowedDOS, // DOS 3.2/3.3 - } AllowedTypes; - - EditPropsDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_PROPS_EDIT, pParentWnd) - { - memset(&fProps, 0, sizeof(fProps)); - fReadOnly = false; - fAllowedTypes = kAllowedProDOS; - fSimpleAccess = false; - fNoChangeAccess = false; - fAllowInvis = false; - //fHFSMode = false; - fHFSComboIdx = -1; - } - ~EditPropsDialog(void) {} - - /* these get handed to GenericArchive */ - FileProps fProps; - - /* initialize fProps and other fields from pEntry */ - void InitProps(GenericEntry* pEntry); - - /* set this to disable editing of all fields */ - bool fReadOnly; - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * This is called when the file type selection changes or something is - * typed in the aux type box. - * - * We use this notification to configure the type description field. - * - * Typing in the ProDOS aux type box causes us to nuke the HFS values. - * If we were in "HFS mode" we reset the file type to zero. - */ - afx_msg void OnTypeChange(void); - - /* - * Called when something is typed in one of the HFS type boxes. - */ - afx_msg void OnHFSTypeChange(void); - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_EDIT_PROPS); - } - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - /* - * For "simple" access formats, i.e. DOS 3.2/3.3, the "write" button acts - * as a "locked" flag. We want the other rename/delete flags to track this - * one. - */ - void UpdateSimpleAccess(void); - - /* - * Called initially and when switching modes. - */ - void UpdateHFSMode(void); - - /* - * Get the aux type. - * - * Returns -1 if something was wrong with the string (e.g. empty or has - * invalid chars). - */ - long GetAuxType(void); - - /* what sort of type changes do we allow? */ - AllowedTypes fAllowedTypes; - /* set this to disable access to fields other than 'W' */ - bool fSimpleAccess; - /* set this to disable file access fields */ - bool fNoChangeAccess; - /* this enabled the 'I' flag, independent of other settings */ - bool fAllowInvis; - - /* are we in "ProDOS mode" or "HFS mode"? */ - //bool fHFSMode; - /* fake file type entry that says "(HFS)" */ - int fHFSComboIdx; - - /* these are displayed locally */ - CString fPathName; - CString fAuxType; // DDX doesn't do hex conversion - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_EDITPROPSDIALOG_H*/ diff --git a/ciderpress/app/EnterRegDialog.cpp b/ciderpress/app/EnterRegDialog.cpp deleted file mode 100644 index 465ef6f..0000000 --- a/ciderpress/app/EnterRegDialog.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#if 0 -/* - * Support for entering registration data. - */ -#include "stdafx.h" -#include "EnterRegDialog.h" -#include "MyApp.h" -#include "HelpTopics.h" - -BEGIN_MESSAGE_MAP(EnterRegDialog, CDialog) - ON_EN_CHANGE(IDC_REGENTER_USER, OnUserChange) - ON_EN_CHANGE(IDC_REGENTER_COMPANY, OnCompanyChange) - ON_EN_CHANGE(IDC_REGENTER_REG, OnRegChange) - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -/* - * Disable the "OK" button initially. - */ -BOOL EnterRegDialog::OnInitDialog(void) -{ - //CWnd* pWnd = GetDlgItem(IDOK); - //ASSERT(pWnd != NULL); - //pWnd->EnableWindow(false); - - fMyEdit.ReplaceDlgCtrl(this, IDC_REGENTER_REG); - fMyEdit.SetProperties(MyEdit::kCapsOnly | MyEdit::kNoWhiteSpace); - - /* place a reasonable cap on the field lengths, since these go - straight into the registry */ - CEdit* pEdit; - pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_USER); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(120); - pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_COMPANY); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(120); - pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_REG); - ASSERT(pEdit != NULL); - pEdit->SetLimitText(40); - - return CDialog::OnInitDialog(); -} - -/* - * Shuffle data in and out of the edit fields. We do an extra validation - * step on the registration key before accepting it. - */ -void EnterRegDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Text(pDX, IDC_REGENTER_USER, fUserName); - DDX_Text(pDX, IDC_REGENTER_COMPANY, fCompanyName); - DDX_Text(pDX, IDC_REGENTER_REG, fRegKey); - - /* validate the reg field */ - if (pDX->m_bSaveAndValidate) { - ASSERT(!fUserName.IsEmpty()); - ASSERT(!fRegKey.IsEmpty()); - - if (gMyApp.fRegistry.IsValidRegistrationKey(fUserName, fCompanyName, - fRegKey)) - { - LOGI("Correct key entered: '%ls' '%ls' '%ls'", - (LPCTSTR)fUserName, (LPCTSTR)fCompanyName, (LPCTSTR)fRegKey); - } else { - LOGI("Incorrect key entered, rejecting"); - CString appName, msg; - appName.LoadString(IDS_MB_APP_NAME); - msg.LoadString(IDS_REG_BAD_ENTRY); - MessageBox(msg, appName, MB_ICONWARNING|MB_OK); - pDX->Fail(); - } - } else { - OnUserChange(); - OnCompanyChange(); - OnRegChange(); - } -} - -void EnterRegDialog::HandleEditChange(int editID, int crcID) -{ - CString userStr, regStr; - CEdit* pEdit; - CWnd* pWnd; - - /* - * Update the CRC for the modified control. - */ - pEdit = (CEdit*) GetDlgItem(editID); - ASSERT(pEdit != NULL); - pEdit->GetWindowText(userStr); - unsigned short crc; - crc = gMyApp.fRegistry.ComputeStringCRC(userStr); - userStr.Format("%04X", crc); - pWnd = GetDlgItem(crcID); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(userStr); - - /* - * Update the OK button. - */ - pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_USER); - ASSERT(pEdit != NULL); - pEdit->GetWindowText(userStr); - - pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_REG); - ASSERT(pEdit != NULL); - pEdit->GetWindowText(regStr); - - pWnd = GetDlgItem(IDOK); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(!userStr.IsEmpty() && !regStr.IsEmpty()); -} - -void EnterRegDialog::OnUserChange(void) -{ - HandleEditChange(IDC_REGENTER_USER, IDC_REGENTER_USERCRC); -} - -void EnterRegDialog::OnCompanyChange(void) -{ - HandleEditChange(IDC_REGENTER_COMPANY, IDC_REGENTER_COMPCRC); -} - -void EnterRegDialog::OnRegChange(void) -{ - HandleEditChange(IDC_REGENTER_REG, IDC_REGENTER_REGCRC); -} - - -void EnterRegDialog::OnHelp(void) -{ - WinHelp(HELP_TOPIC_ENTER_REG_DATA, HELP_CONTEXT); -} - - -/*static*/ int EnterRegDialog::GetRegInfo(CWnd* pWnd) -{ - CString user, company, reg, versions, expire; - - /* - * Get current data (if any). This call only fails if the registry itself - * appears to be generally inaccessible. - */ - if (gMyApp.fRegistry.GetRegistration(&user, &company, ®, &versions, - &expire) != 0) - { - CString msg; - msg.LoadString(IDS_REG_FAILURE); - ShowFailureMsg(pWnd, msg, IDS_FAILED); - return -1; - } - - /* - * Post the dialog. - */ - EnterRegDialog dlg(pWnd); - int result = -1; - - if (dlg.DoModal() == IDOK) { - user = dlg.fUserName; - company = dlg.fCompanyName; - reg = dlg.fRegKey; - - /* data was validated by EnterRegDialog, so just save it to registry */ - if (gMyApp.fRegistry.SetRegistration(user, company, reg, versions, - expire) != 0) - { - CString msg; - msg.LoadString(IDS_REG_FAILURE); - ShowFailureMsg(pWnd, msg, IDS_FAILED); - } else { - result = 0; - } - } - - return result; -} - -#endif /*0*/ diff --git a/ciderpress/app/EnterRegDialog.h b/ciderpress/app/EnterRegDialog.h deleted file mode 100644 index 70549d1..0000000 --- a/ciderpress/app/EnterRegDialog.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Dialog allowing the user to enter registration data. - */ -#ifndef APP_ENTERREGDIALOG_H -#define APP_ENTERREGDIALOG_H - -#include "../util/UtilLib.h" -#include "resource.h" - -/* - * Straightforward dialog. We validate the registration key in the DDX - * function, so an IDOK is a guarantee that they have entered valid data. It - * is up to the caller to store the values in the registry. - * - * [ This was only used in the shareware product. ] - */ -class EnterRegDialog : public CDialog { -public: - EnterRegDialog(CWnd* pParent = NULL) : CDialog(IDD_REGISTRATION, pParent) - { fDepth = 0; } - virtual ~EnterRegDialog(void) {} - - CString fUserName; - CString fCompanyName; - CString fRegKey; - - /* - * Get registration info from the user. This is a static utility function - * that can be called from elsewhere in the app. - * - * Returns 0 on successful registration, nonzero on failure or if the user - * cancels out of the dialog. - */ - static int GetRegInfo(CWnd* pWnd); - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnUserChange(void); - afx_msg void OnCompanyChange(void); - afx_msg void OnRegChange(void); - afx_msg void OnHelp(void); - - /* - * Call this when the text in an edit field has changed. - * - * If there's nothing in the "user name" or "reg key" fields, dim the OK - * button. - */ - void HandleEditChange(int editID, int crcID); - - MyEdit fMyEdit; - int fDepth; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_ENTERREGDIALOG_H*/ diff --git a/ciderpress/app/ExtractOptionsDialog.cpp b/ciderpress/app/ExtractOptionsDialog.cpp deleted file mode 100644 index 2cf93a4..0000000 --- a/ciderpress/app/ExtractOptionsDialog.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "ExtractOptionsDialog.h" -#include "ChooseDirDialog.h" - -BEGIN_MESSAGE_MAP(ExtractOptionsDialog, CDialog) - ON_BN_CLICKED(IDC_EXT_CHOOSE_FOLDER, OnChooseFolder) - ON_BN_CLICKED(IDC_EXT_CONVEOLNONE, OnChangeTextConv) - ON_BN_CLICKED(IDC_EXT_CONVEOLTYPE, OnChangeTextConv) - ON_BN_CLICKED(IDC_EXT_CONVEOLTEXT, OnChangeTextConv) - ON_BN_CLICKED(IDC_EXT_CONVEOLALL, OnChangeTextConv) - ON_BN_CLICKED(IDC_EXT_CONFIG_PRESERVE, OnConfigPreserve) - ON_BN_CLICKED(IDC_EXT_CONFIG_CONVERT, OnConfigConvert) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -/* - * Set up the dialog that lets the user choose file extraction options. - * - * All we really need to do is update the string that indicates how many - * files have been selected. - */ -BOOL ExtractOptionsDialog::OnInitDialog(void) -{ - CString countFmt; - CString selStr; - CWnd* pWnd; - - /* grab the radio button with the selection count */ - pWnd = GetDlgItem(IDC_EXT_SELECTED); - ASSERT(pWnd != NULL); - - /* set the count string using a string table entry */ - if (fSelectedCount == 1) { - CheckedLoadString(&countFmt, IDS_EXT_SELECTED_COUNT); - pWnd->SetWindowText(countFmt); - } else { - CheckedLoadString(&countFmt, IDS_EXT_SELECTED_COUNTS_FMT); - selStr.Format((LPCWSTR) countFmt, fSelectedCount); - pWnd->SetWindowText(selStr); - - // disable "extract selection" when nothing is selected - if (fSelectedCount == 0) - pWnd->EnableWindow(FALSE); - } - - /* if "no convert" is selected, disable high ASCII button */ - if (fConvEOL == kConvEOLNone) { - pWnd = GetDlgItem(IDC_EXT_CONVHIGHASCII); - pWnd->EnableWindow(false); - } - - /* replace the existing button with one of our bitmap buttons */ - fChooseFolderButton.ReplaceDlgCtrl(this, IDC_EXT_CHOOSE_FOLDER); - fChooseFolderButton.SetBitmapID(IDB_CHOOSE_FOLDER); - - return CDialog::OnInitDialog(); - //return TRUE; // let Windows set the focus -} - -void ExtractOptionsDialog::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - - DDX_Text(pDX, IDC_EXT_PATH, fExtractPath); - - DDX_Radio(pDX, IDC_EXT_SELECTED, fFilesToExtract); - - DDX_Check(pDX, IDC_EXT_DATAFORK, fIncludeDataForks); - DDX_Check(pDX, IDC_EXT_RSRCFORK, fIncludeRsrcForks); - DDX_Check(pDX, IDC_EXT_DISKIMAGE, fIncludeDiskImages); - - DDX_Check(pDX, IDC_EXT_REFORMAT, fEnableReformat); - DDX_Check(pDX, IDC_EXT_DISK_2MG, fDiskTo2MG); - - DDX_Check(pDX, IDC_EXT_ADD_PRESERVE, fAddTypePreservation); - DDX_Check(pDX, IDC_EXT_ADD_EXTEN, fAddExtension); - DDX_Check(pDX, IDC_EXT_STRIP_FOLDER, fStripFolderNames); - - DDX_Radio(pDX, IDC_EXT_CONVEOLNONE, fConvEOL); - DDX_Check(pDX, IDC_EXT_CONVHIGHASCII, fConvHighASCII); - - DDX_Check(pDX, IDC_EXT_OVERWRITE_EXIST, fOverwriteExisting); - - if (pDX->m_bSaveAndValidate) { - if (!fIncludeDataForks && !fIncludeRsrcForks && !fIncludeDiskImages) { - ShowFailureMsg(this, IDS_NO_FORKS_SPECIFIED, IDS_MB_APP_NAME); - pDX->Fail(); - return; - } - } -} - -void ExtractOptionsDialog::OnConfigPreserve(void) -{ - // IDC_EXT_PATH, IDC_EXT_SELECTED - SetDlgButtonCheck(this, IDC_EXT_DATAFORK, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_RSRCFORK, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_DISKIMAGE, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_REFORMAT, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_DISK_2MG, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_ADD_PRESERVE, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_ADD_EXTEN, BST_UNCHECKED); - //SetDlgButtonCheck(this, IDC_EXT_STRIP_FOLDER, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLNONE, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLTYPE, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLTEXT, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLALL, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVHIGHASCII, BST_UNCHECKED); - //SetDlgButtonCheck(this, IDC_EXT_OVERWRITE_EXIST, BST_CHECKED); - - OnChangeTextConv(); -} - -void ExtractOptionsDialog::OnConfigConvert(void) -{ - // IDC_EXT_PATH, IDC_EXT_SELECTED - SetDlgButtonCheck(this, IDC_EXT_DATAFORK, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_RSRCFORK, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_DISKIMAGE, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_REFORMAT, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_DISK_2MG, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_ADD_PRESERVE, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_ADD_EXTEN, BST_CHECKED); - //SetDlgButtonCheck(this, IDC_EXT_STRIP_FOLDER, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLNONE, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLTYPE, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLTEXT, BST_CHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVEOLALL, BST_UNCHECKED); - SetDlgButtonCheck(this, IDC_EXT_CONVHIGHASCII, BST_CHECKED); - //SetDlgButtonCheck(this, IDC_EXT_OVERWRITE_EXIST, BST_CHECKED); - - OnChangeTextConv(); -} - -void ExtractOptionsDialog::OnChangeTextConv(void) -{ - CButton* pButton = (CButton*) GetDlgItem(IDC_EXT_CONVEOLNONE); - ASSERT(pButton != NULL); - bool convDisabled = (pButton->GetCheck() == BST_CHECKED); - - CWnd* pWnd = GetDlgItem(IDC_EXT_CONVHIGHASCII); - ASSERT(pWnd != NULL); - pWnd->EnableWindow(!convDisabled); -} - -void ExtractOptionsDialog::OnChooseFolder(void) -{ - ChooseDirDialog chooseDir(this); - CWnd* pEditWnd; - CString editPath; - - /* get the currently-showing text from the edit field */ - pEditWnd = GetDlgItem(IDC_EXT_PATH); - ASSERT(pEditWnd != NULL); - pEditWnd->GetWindowText(editPath); - - chooseDir.SetPathName(editPath); - if (chooseDir.DoModal() == IDOK) { - const WCHAR* ccp = chooseDir.GetPathName(); - LOGI("New extract path chosen = '%ls'", ccp); - - pEditWnd->SetWindowText(ccp); - } -} diff --git a/ciderpress/app/ExtractOptionsDialog.h b/ciderpress/app/ExtractOptionsDialog.h deleted file mode 100644 index 2425479..0000000 --- a/ciderpress/app/ExtractOptionsDialog.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Choose options related to file extraction. - */ -#ifndef APP_EXTRACTOPTIONSDIALOG_H -#define APP_EXTRACTOPTIONSDIALOG_H - -#include "../util/UtilLib.h" -#include "resource.h" -#include "HelpTopics.h" - -/* - * Our somewhat complicated extraction options dialog. - */ -class ExtractOptionsDialog : public CDialog { -public: - ExtractOptionsDialog(int selCount, CWnd* pParentWnd = NULL) : - CDialog(IDD_EXTRACT_FILES, pParentWnd), fSelectedCount(selCount) - { - // init values; these should be overridden before DoModal - fExtractPath = ""; - fFilesToExtract = 0; - fConvEOL = 0; - fConvHighASCII = FALSE; - fIncludeDataForks = fIncludeRsrcForks = fIncludeDiskImages = FALSE; - fEnableReformat = fDiskTo2MG = FALSE; - fAddTypePreservation = fAddExtension = fStripFolderNames = FALSE; - fOverwriteExisting = FALSE; - } - virtual ~ExtractOptionsDialog(void) { - //LOGI("~ExtractOptionsDialog()"); - } - - CString fExtractPath; - - enum { kExtractSelection = 0, kExtractAll = 1 }; - int fFilesToExtract; - -// enum { kPreserveNone = 0, kPreserveTypes, kPreserveAndExtend }; -// int fTypePreservation; - - // this must match tab order of radio buttons in dialog - enum { kConvEOLNone = 0, kConvEOLType, kConvEOLAuto, kConvEOLAll }; - int fConvEOL; - BOOL fConvHighASCII; - -// enum { kDiskImageNoExtract = 0, kDiskImageAsPO = 1, kDiskImageAs2MG }; -// int fDiskImageExtract; - - BOOL fIncludeDataForks; - BOOL fIncludeRsrcForks; - BOOL fIncludeDiskImages; - - BOOL fEnableReformat; - BOOL fDiskTo2MG; - - BOOL fAddTypePreservation; - BOOL fAddExtension; - BOOL fStripFolderNames; - - BOOL fOverwriteExisting; - - bool ShouldTryReformat(void) const { - return fEnableReformat != 0; - } - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * Reconfigure controls for best preservation of Apple II formats. - */ - afx_msg void OnConfigPreserve(void); - - /* - * Reconfigure controls for easiest viewing under Windows. - */ - afx_msg void OnConfigConvert(void); - - /* - * Enable or disable the "Convert high ASCII" button based on the current - * setting of the radio button above it. - */ - afx_msg void OnChangeTextConv(void); - - /* - * They want to choose the folder from a tree. - */ - afx_msg void OnChooseFolder(void); - - // Context help request (question mark button). - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - // User pressed the "Help" button. - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_EXT_OPTIONS); - } - - MyBitmapButton fChooseFolderButton; - int fSelectedCount; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_EXTRACTOPTIONSDIALOG_H*/ diff --git a/ciderpress/app/FileNameConv.cpp b/ciderpress/app/FileNameConv.cpp deleted file mode 100644 index 7a668d0..0000000 --- a/ciderpress/app/FileNameConv.cpp +++ /dev/null @@ -1,1373 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Filename manipulation, including file type preservation. This is - * substantially ripped from NuLib2. - */ -#include "stdafx.h" -#include "FileNameConv.h" -#include "GenericArchive.h" -#include "AddFilesDialog.h" -#include - - -#define WINDOWS_LIKE - -/* replace unsupported chars with '%xx' */ -#define kForeignIndic '%' - -/* convert single hex digit char to number */ -#define HexDigit(x) ( !isxdigit((int)(x)) ? -1 : \ - (x) <= '9' ? (x) - '0' : toupper(x) +10 - 'A' ) - -/* convert number from 0-15 to hex digit */ -#define HexConv(x) ( ((unsigned int)(x)) <= 15 ? \ - ( (x) <= 9 ? (x) + '0' : (x) -10 + 'A') : -1 ) - - -/* - * =========================================================================== - * Common definitions - * =========================================================================== - */ - -#define kPreserveIndic '#' /* use # rather than $ for hex indication */ -#define kFilenameExtDelim '.' /* separates extension from filename */ -#define kResourceFlag 'r' -#define kDiskImageFlag 'i' -#define kMaxExtLen 5 /* ".1234" */ -#define kResourceStr L"_rsrc_" - -/* must be longer then strlen(kResourceStr)... no problem there */ -#define kMaxPathGrowth (sizeof(L"#XXXXXXXXYYYYYYYYZ")-1 + kMaxExtLen+1) - - -/* ProDOS file type names; must be entirely in upper case */ -static const WCHAR gFileTypeNames[256][4] = { - L"NON", L"BAD", L"PCD", L"PTX", L"TXT", L"PDA", L"BIN", L"FNT", - L"FOT", L"BA3", L"DA3", L"WPF", L"SOS", L"$0D", L"$0E", L"DIR", - L"RPD", L"RPI", L"AFD", L"AFM", L"AFR", L"SCL", L"PFS", L"$17", - L"$18", L"ADB", L"AWP", L"ASP", L"$1C", L"$1D", L"$1E", L"$1F", - L"TDM", L"$21", L"$22", L"$23", L"$24", L"$25", L"$26", L"$27", - L"$28", L"$29", L"8SC", L"8OB", L"8IC", L"8LD", L"P8C", L"$2F", - L"$30", L"$31", L"$32", L"$33", L"$34", L"$35", L"$36", L"$37", - L"$38", L"$39", L"$3A", L"$3B", L"$3C", L"$3D", L"$3E", L"$3F", - L"DIC", L"OCR", L"FTD", L"$43", L"$44", L"$45", L"$46", L"$47", - L"$48", L"$49", L"$4A", L"$4B", L"$4C", L"$4D", L"$4E", L"$4F", - L"GWP", L"GSS", L"GDB", L"DRW", L"GDP", L"HMD", L"EDU", L"STN", - L"HLP", L"COM", L"CFG", L"ANM", L"MUM", L"ENT", L"DVU", L"FIN", - L"$60", L"$61", L"$62", L"$63", L"$64", L"$65", L"$66", L"$67", - L"$68", L"$69", L"$6A", L"BIO", L"$6C", L"TDR", L"PRE", L"HDV", - L"$70", L"$71", L"$72", L"$73", L"$74", L"$75", L"$76", L"$77", - L"$78", L"$79", L"$7A", L"$7B", L"$7C", L"$7D", L"$7E", L"$7F", - L"$80", L"$81", L"$82", L"$83", L"$84", L"$85", L"$86", L"$87", - L"$88", L"$89", L"$8A", L"$8B", L"$8C", L"$8D", L"$8E", L"$8F", - L"$90", L"$91", L"$92", L"$93", L"$94", L"$95", L"$96", L"$97", - L"$98", L"$99", L"$9A", L"$9B", L"$9C", L"$9D", L"$9E", L"$9F", - L"WP ", L"$A1", L"$A2", L"$A3", L"$A4", L"$A5", L"$A6", L"$A7", - L"$A8", L"$A9", L"$AA", L"GSB", L"TDF", L"BDF", L"$AE", L"$AF", - L"SRC", L"OBJ", L"LIB", L"S16", L"RTL", L"EXE", L"PIF", L"TIF", - L"NDA", L"CDA", L"TOL", L"DVR", L"LDF", L"FST", L"$BE", L"DOC", - L"PNT", L"PIC", L"ANI", L"PAL", L"$C4", L"OOG", L"SCR", L"CDV", - L"FON", L"FND", L"ICN", L"$CB", L"$CC", L"$CD", L"$CE", L"$CF", - L"$D0", L"$D1", L"$D2", L"$D3", L"$D4", L"MUS", L"INS", L"MDI", - L"SND", L"$D9", L"$DA", L"DBM", L"$DC", L"DDD", L"$DE", L"$DF", - L"LBR", L"$E1", L"ATK", L"$E3", L"$E4", L"$E5", L"$E6", L"$E7", - L"$E8", L"$E9", L"$EA", L"$EB", L"$EC", L"$ED", L"R16", L"PAS", - L"CMD", L"$F1", L"$F2", L"$F3", L"$F4", L"$F5", L"$F6", L"$F7", - L"$F8", L"OS ", L"INT", L"IVR", L"BAS", L"VAR", L"REL", L"SYS" -}; - -static const WCHAR kUnknownTypeStr[] = L"???"; - -/*static*/ const WCHAR* PathProposal::FileTypeString(uint32_t fileType) -{ - // Note to self: code down below tests first char for '?'. - if (fileType < NELEM(gFileTypeNames)) - return gFileTypeNames[fileType]; - else - return kUnknownTypeStr; -} - - -/* - * Some file extensions we recognize. When adding files with "extended" - * preservation mode, we try to assign types to files that weren't - * explicitly preserved, but nevertheless have a recognizeable type. - * - * geoff at gwlink.net pointed out that this really ought to be in an external - * file rather than a hard-coded table. Ought to fix that someday. - */ -static const struct { - const WCHAR* label; - uint8_t fileType; - uint16_t auxType; -} gRecognizedExtensions[] = { - { L"ASM", 0xb0, 0x0003 }, /* APW assembly source */ - { L"C", 0xb0, 0x000a }, /* APW C source */ - { L"H", 0xb0, 0x000a }, /* APW C header */ - { L"CPP", 0xb0, 0x0000 }, /* generic source file */ - { L"BNY", 0xe0, 0x8000 }, /* Binary II lib */ - { L"BQY", 0xe0, 0x8000 }, /* Binary II lib, w/ compress */ - { L"BXY", 0xe0, 0x8000 }, /* Binary II wrap around SHK */ - { L"BSE", 0xe0, 0x8000 }, /* Binary II wrap around SEA */ - { L"SEA", 0xb3, 0xdb07 }, /* GSHK SEA */ - { L"TEXT", 0x04, 0x0000 }, /* ASCII Text */ - { L"GIF", 0xc0, 0x8006 }, /* GIF image */ - { L"JPG", 0x06, 0x0000 }, /* JPEG (nicer than 'NON') */ - { L"JPEG", 0x06, 0x0000 }, /* JPEG (nicer than 'NON') */ - //{ L"ACU", 0xe0, 0x8001 }, /* ACU archive */ - { L"SHK", 0xe0, 0x8002 }, /* ShrinkIt archive */ -}; - -/* - * Description table. - * - * The first item that matches will be used, but the table is searched - * bottom-up, so it's important to have the most general entry first. - * - * In retrospect, it might have made sense to use the same format as the - * "FTD" file type description file that the IIgs Finder used. Might have - * made sense to just ship that and load it on startup (although copyright - * issues would have to be investigated). - * - * This list should be complete as of the May 1992 "about" note. - */ -static const struct { - uint8_t fileType; - uint16_t minAuxType; // start of range for which this applies - uint16_t maxAuxType; // end of range - const WCHAR* descr; -} gTypeDescriptions[] = { - /*NON*/ { 0x00, 0x0000, 0xffff, L"Untyped file" }, - /*BAD*/ { 0x01, 0x0000, 0xffff, L"Bad blocks" }, - /*PCD*/ { 0x02, 0x0000, 0xffff, L"Pascal code" }, - /*PTX*/ { 0x03, 0x0000, 0xffff, L"Pascal text" }, - /*TXT*/ { 0x04, 0x0000, 0xffff, L"ASCII text" }, - /*PDA*/ { 0x05, 0x0000, 0xffff, L"Pascal data" }, - /*BIN*/ { 0x06, 0x0000, 0xffff, L"Binary" }, - /*FNT*/ { 0x07, 0x0000, 0xffff, L"Apple /// font" }, - /*FOT*/ { 0x08, 0x0000, 0xffff, L"Apple II or /// graphics" }, - /* */ { 0x08, 0x0000, 0x3fff, L"Apple II graphics" }, - /* */ { 0x08, 0x4000, 0x4000, L"Packed hi-res image" }, - /* */ { 0x08, 0x4001, 0x4001, L"Packed double hi-res image" }, - /* */ { 0x08, 0x8001, 0x8001, L"Printographer packed HGR file" }, - /* */ { 0x08, 0x8002, 0x8002, L"Printographer packed DHGR file" }, - /* */ { 0x08, 0x8003, 0x8003, L"Softdisk hi-res image" }, - /* */ { 0x08, 0x8004, 0x8004, L"Softdisk double hi-res image" }, - /*BA3*/ { 0x09, 0x0000, 0xffff, L"Apple /// BASIC program" }, - /*DA3*/ { 0x0a, 0x0000, 0xffff, L"Apple /// BASIC data" }, - /*WPF*/ { 0x0b, 0x0000, 0xffff, L"Apple II or /// word processor" }, - /* */ { 0x0b, 0x8001, 0x8001, L"Write This Way document" }, - /* */ { 0x0b, 0x8002, 0x8002, L"Writing & Publishing document" }, - /*SOS*/ { 0x0c, 0x0000, 0xffff, L"Apple /// SOS system" }, - /*DIR*/ { 0x0f, 0x0000, 0xffff, L"Folder" }, - /*RPD*/ { 0x10, 0x0000, 0xffff, L"Apple /// RPS data" }, - /*RPI*/ { 0x11, 0x0000, 0xffff, L"Apple /// RPS index" }, - /*AFD*/ { 0x12, 0x0000, 0xffff, L"Apple /// AppleFile discard" }, - /*AFM*/ { 0x13, 0x0000, 0xffff, L"Apple /// AppleFile model" }, - /*AFR*/ { 0x14, 0x0000, 0xffff, L"Apple /// AppleFile report format" }, - /*SCL*/ { 0x15, 0x0000, 0xffff, L"Apple /// screen library" }, - /*PFS*/ { 0x16, 0x0000, 0xffff, L"PFS document" }, - /* */ { 0x16, 0x0001, 0x0001, L"PFS:File document" }, - /* */ { 0x16, 0x0002, 0x0002, L"PFS:Write document" }, - /* */ { 0x16, 0x0003, 0x0003, L"PFS:Graph document" }, - /* */ { 0x16, 0x0004, 0x0004, L"PFS:Plan document" }, - /* */ { 0x16, 0x0016, 0x0016, L"PFS internal data" }, - /*ADB*/ { 0x19, 0x0000, 0xffff, L"AppleWorks data base" }, - /*AWP*/ { 0x1a, 0x0000, 0xffff, L"AppleWorks word processor" }, - /*ASP*/ { 0x1b, 0x0000, 0xffff, L"AppleWorks spreadsheet" }, - /*TDM*/ { 0x20, 0x0000, 0xffff, L"Desktop Manager document" }, - /*???*/ { 0x21, 0x0000, 0xffff, L"Instant Pascal source" }, - /*???*/ { 0x22, 0x0000, 0xffff, L"UCSD Pascal volume" }, - /*???*/ { 0x29, 0x0000, 0xffff, L"Apple /// SOS dictionary" }, - /*8SC*/ { 0x2a, 0x0000, 0xffff, L"Apple II source code" }, - /* */ { 0x2a, 0x8001, 0x8001, L"EBBS command script" }, - /*8OB*/ { 0x2b, 0x0000, 0xffff, L"Apple II object code" }, - /* */ { 0x2b, 0x8001, 0x8001, L"GBBS Pro object Code" }, - /*8IC*/ { 0x2c, 0x0000, 0xffff, L"Apple II interpreted code" }, - /* */ { 0x2c, 0x8003, 0x8003, L"APEX Program File" }, - /* */ { 0x2c, 0x8005, 0x8005, L"EBBS tokenized command script" }, - /*8LD*/ { 0x2d, 0x0000, 0xffff, L"Apple II language data" }, - /* */ { 0x2d, 0x8006, 0x8005, L"EBBS message bundle" }, - /* */ { 0x2d, 0x8007, 0x8007, L"EBBS compressed message bundle" }, - /*P8C*/ { 0x2e, 0x0000, 0xffff, L"ProDOS 8 code module" }, - /* */ { 0x2e, 0x8001, 0x8001, L"Davex 8 Command" }, - /*PTP*/ { 0x2e, 0x8002, 0x8002, L"Point-to-Point drivers" }, - /*PTP*/ { 0x2e, 0x8003, 0x8003, L"Point-to-Point code" }, - /* */ { 0x2e, 0x8004, 0x8004, L"Softdisk printer driver" }, - /*DIC*/ { 0x40, 0x0000, 0xffff, L"Dictionary file" }, - /*???*/ { 0x41, 0x0000, 0xffff, L"OCR data" }, - /* */ { 0x41, 0x8001, 0x8001, L"InWords OCR font table" }, - /*FTD*/ { 0x42, 0x0000, 0xffff, L"File type names" }, - /*???*/ { 0x43, 0x0000, 0xffff, L"Peripheral data" }, - /* */ { 0x43, 0x8001, 0x8001, L"Express document" }, - /*???*/ { 0x44, 0x0000, 0xffff, L"Personal information" }, - /* */ { 0x44, 0x8001, 0x8001, L"ResuMaker personal information" }, - /* */ { 0x44, 0x8002, 0x8002, L"ResuMaker resume" }, - /* */ { 0x44, 0x8003, 0x8003, L"II Notes document" }, - /* */ { 0x44, 0x8004, 0x8004, L"Softdisk scrapbook document" }, - /* */ { 0x44, 0x8005, 0x8005, L"Don't Forget document" }, - /* */ { 0x44, 0x80ff, 0x80ff, L"What To Do data" }, - /* */ { 0x44, 0xbeef, 0xbeef, L"Table Scraps scrapbook" }, - /*???*/ { 0x45, 0x0000, 0xffff, L"Mathematical document" }, - /* */ { 0x45, 0x8001, 0x8001, L"GSymbolix 3D graph document" }, - /* */ { 0x45, 0x8002, 0x8002, L"GSymbolix formula document" }, - /*???*/ { 0x46, 0x0000, 0xffff, L"AutoSave profiles" }, - /* */ { 0x46, 0x8001, 0x8001, L"AutoSave profiles" }, - /*GWP*/ { 0x50, 0x0000, 0xffff, L"Apple IIgs Word Processor" }, - /* */ { 0x50, 0x8001, 0x8001, L"DeluxeWrite document" }, - /* */ { 0x50, 0x8003, 0x8003, L"Personal Journal document" }, - /* */ { 0x50, 0x8010, 0x8010, L"AppleWorks GS word processor" }, - /* */ { 0x50, 0x8011, 0x8011, L"Softdisk issue text" }, - /* */ { 0x50, 0x5445, 0x5445, L"Teach document" }, - /*GSS*/ { 0x51, 0x0000, 0xffff, L"Apple IIgs spreadsheet" }, - /* */ { 0x51, 0x8010, 0x8010, L"AppleWorks GS spreadsheet" }, - /* */ { 0x51, 0x2358, 0x2358, L"QC Calc spreadsheet " }, - /*GDB*/ { 0x52, 0x0000, 0xffff, L"Apple IIgs data base" }, - /* */ { 0x52, 0x8001, 0x8001, L"GTv database" }, - /* */ { 0x52, 0x8010, 0x8010, L"AppleWorks GS data base" }, - /* */ { 0x52, 0x8011, 0x8011, L"AppleWorks GS DB template" }, - /* */ { 0x52, 0x8013, 0x8013, L"GSAS database" }, - /* */ { 0x52, 0x8014, 0x8014, L"GSAS accounting journals" }, - /* */ { 0x52, 0x8015, 0x8015, L"Address Manager document" }, - /* */ { 0x52, 0x8016, 0x8016, L"Address Manager defaults" }, - /* */ { 0x52, 0x8017, 0x8017, L"Address Manager index" }, - /*DRW*/ { 0x53, 0x0000, 0xffff, L"Drawing" }, - /* */ { 0x53, 0x8002, 0x8002, L"Graphic Disk Labeler document" }, - /* */ { 0x53, 0x8010, 0x8010, L"AppleWorks GS graphics" }, - /*GDP*/ { 0x54, 0x0000, 0xffff, L"Desktop publishing" }, - /* */ { 0x54, 0x8002, 0x8002, L"GraphicWriter document" }, - /* */ { 0x54, 0x8003, 0x8003, L"Label It document" }, - /* */ { 0x54, 0x8010, 0x8010, L"AppleWorks GS Page Layout" }, - /* */ { 0x54, 0xdd3e, 0xdd3e, L"Medley document" }, - /*HMD*/ { 0x55, 0x0000, 0xffff, L"Hypermedia" }, - /* */ { 0x55, 0x0001, 0x0001, L"HyperCard IIgs stack" }, - /* */ { 0x55, 0x8001, 0x8001, L"Tutor-Tech document" }, - /* */ { 0x55, 0x8002, 0x8002, L"HyperStudio document" }, - /* */ { 0x55, 0x8003, 0x8003, L"Nexus document" }, - /* */ { 0x55, 0x8004, 0x8004, L"HyperSoft stack" }, - /* */ { 0x55, 0x8005, 0x8005, L"HyperSoft card" }, - /* */ { 0x55, 0x8006, 0x8006, L"HyperSoft external command" }, - /*EDU*/ { 0x56, 0x0000, 0xffff, L"Educational Data" }, - /* */ { 0x56, 0x8001, 0x8001, L"Tutor-Tech scores" }, - /* */ { 0x56, 0x8007, 0x8007, L"GradeBook data" }, - /*STN*/ { 0x57, 0x0000, 0xffff, L"Stationery" }, - /* */ { 0x57, 0x8003, 0x8003, L"Music Writer format" }, - /*HLP*/ { 0x58, 0x0000, 0xffff, L"Help file" }, - /* */ { 0x58, 0x8002, 0x8002, L"Davex 8 help file" }, - /* */ { 0x58, 0x8005, 0x8005, L"Micol Advanced Basic help file" }, - /* */ { 0x58, 0x8006, 0x8006, L"Locator help document" }, - /* */ { 0x58, 0x8007, 0x8007, L"Personal Journal help" }, - /* */ { 0x58, 0x8008, 0x8008, L"Home Refinancer help" }, - /* */ { 0x58, 0x8009, 0x8009, L"The Optimizer help" }, - /* */ { 0x58, 0x800a, 0x800a, L"Text Wizard help" }, - /* */ { 0x58, 0x800b, 0x800b, L"WordWorks Pro help system" }, - /* */ { 0x58, 0x800c, 0x800c, L"Sound Wizard help" }, - /* */ { 0x58, 0x800d, 0x800d, L"SeeHear help system" }, - /* */ { 0x58, 0x800e, 0x800e, L"QuickForms help system" }, - /* */ { 0x58, 0x800f, 0x800f, L"Don't Forget help system" }, - /*COM*/ { 0x59, 0x0000, 0xffff, L"Communications file" }, - /* */ { 0x59, 0x8002, 0x8002, L"AppleWorks GS communications" }, - /*CFG*/ { 0x5a, 0x0000, 0xffff, L"Configuration file" }, - /* */ { 0x5a, 0x0000, 0x0000, L"Sound settings files" }, - /* */ { 0x5a, 0x0002, 0x0002, L"Battery RAM configuration" }, - /* */ { 0x5a, 0x0003, 0x0003, L"AutoLaunch preferences" }, - /* */ { 0x5a, 0x0004, 0x0004, L"SetStart preferences" }, - /* */ { 0x5a, 0x0005, 0x0005, L"GSBug configuration" }, - /* */ { 0x5a, 0x0006, 0x0006, L"Archiver preferences" }, - /* */ { 0x5a, 0x0007, 0x0007, L"Archiver table of contents" }, - /* */ { 0x5a, 0x0008, 0x0008, L"Font Manager data" }, - /* */ { 0x5a, 0x0009, 0x0009, L"Print Manager data" }, - /* */ { 0x5a, 0x000a, 0x000a, L"IR preferences" }, - /* */ { 0x5a, 0x8001, 0x8001, L"Master Tracks Jr. preferences" }, - /* */ { 0x5a, 0x8002, 0x8002, L"GraphicWriter preferences" }, - /* */ { 0x5a, 0x8003, 0x8003, L"Z-Link configuration" }, - /* */ { 0x5a, 0x8004, 0x8004, L"JumpStart configuration" }, - /* */ { 0x5a, 0x8005, 0x8005, L"Davex 8 configuration" }, - /* */ { 0x5a, 0x8006, 0x8006, L"Nifty List configuration" }, - /* */ { 0x5a, 0x8007, 0x8007, L"GTv videodisc configuration" }, - /* */ { 0x5a, 0x8008, 0x8008, L"GTv Workshop configuration" }, - /*PTP*/ { 0x5a, 0x8009, 0x8009, L"Point-to-Point preferences" }, - /* */ { 0x5a, 0x800a, 0x800a, L"ORCA/Disassembler preferences" }, - /* */ { 0x5a, 0x800b, 0x800b, L"SnowTerm preferences" }, - /* */ { 0x5a, 0x800c, 0x800c, L"My Word! preferences" }, - /* */ { 0x5a, 0x800d, 0x800d, L"Chipmunk configuration" }, - /* */ { 0x5a, 0x8010, 0x8010, L"AppleWorks GS configuration" }, - /* */ { 0x5a, 0x8011, 0x8011, L"SDE Shell preferences" }, - /* */ { 0x5a, 0x8012, 0x8012, L"SDE Editor preferences" }, - /* */ { 0x5a, 0x8013, 0x8013, L"SDE system tab ruler" }, - /* */ { 0x5a, 0x8014, 0x8014, L"Nexus configuration" }, - /* */ { 0x5a, 0x8015, 0x8015, L"DesignMaster preferences" }, - /* */ { 0x5a, 0x801a, 0x801a, L"MAX/Edit keyboard template" }, - /* */ { 0x5a, 0x801b, 0x801b, L"MAX/Edit tab ruler set" }, - /* */ { 0x5a, 0x801c, 0x801c, L"Platinum Paint preferences" }, - /* */ { 0x5a, 0x801d, 0x801d, L"Sea Scan 1000" }, - /* */ { 0x5a, 0x801e, 0x801e, L"Allison preferences" }, - /* */ { 0x5a, 0x801f, 0x801f, L"Gold of the Americas options" }, - /* */ { 0x5a, 0x8021, 0x8021, L"GSAS accounting setup" }, - /* */ { 0x5a, 0x8022, 0x8022, L"GSAS accounting document" }, - /* */ { 0x5a, 0x8023, 0x8023, L"UtilityLaunch preferences" }, - /* */ { 0x5a, 0x8024, 0x8024, L"Softdisk configuration" }, - /* */ { 0x5a, 0x8025, 0x8025, L"Quit-To configuration" }, - /* */ { 0x5a, 0x8026, 0x8026, L"Big Edit Thing" }, - /* */ { 0x5a, 0x8027, 0x8027, L"ZMaker preferences" }, - /* */ { 0x5a, 0x8028, 0x8028, L"Minstrel configuration" }, - /* */ { 0x5a, 0x8029, 0x8029, L"WordWorks Pro preferences" }, - /* */ { 0x5a, 0x802b, 0x802b, L"Pointless preferences" }, - /* */ { 0x5a, 0x802c, 0x802c, L"Micol Advanced Basic config" }, - /* */ { 0x5a, 0x802e, 0x802e, L"Label It configuration" }, - /* */ { 0x5a, 0x802f, 0x802f, L"Cool Cursor document" }, - /* */ { 0x5a, 0x8030, 0x8030, L"Locator preferences" }, - /* */ { 0x5a, 0x8031, 0x8031, L"Replicator preferences" }, - /* */ { 0x5a, 0x8032, 0x8032, L"Kangaroo configuration" }, - /* */ { 0x5a, 0x8033, 0x8033, L"Kangaroo data" }, - /* */ { 0x5a, 0x8034, 0x8034, L"TransProg III configuration" }, - /* */ { 0x5a, 0x8035, 0x8035, L"Home Refinancer preferences" }, - /* */ { 0x5a, 0x8036, 0x8036, L"Easy Eyes settings" }, - /* */ { 0x5a, 0x8037, 0x8037, L"The Optimizer settings" }, - /* */ { 0x5a, 0x8038, 0x8038, L"Text Wizard settings" }, - /* */ { 0x5a, 0x803b, 0x803b, L"Disk Access II preferences" }, - /* */ { 0x5a, 0x803d, 0x803d, L"Quick DA configuration" }, - /* */ { 0x5a, 0x803e, 0x803e, L"Crazy 8s preferences" }, - /* */ { 0x5a, 0x803f, 0x803f, L"Sound Wizard settings" }, - /* */ { 0x5a, 0x8041, 0x8041, L"Quick Window configuration" }, - /* */ { 0x5a, 0x8044, 0x8044, L"Universe Master disk map" }, - /* */ { 0x5a, 0x8046, 0x8046, L"Autopilot configuration" }, - /* */ { 0x5a, 0x8047, 0x8047, L"EGOed preferences" }, - /* */ { 0x5a, 0x8049, 0x8049, L"Quick DA preferences" }, - /* */ { 0x5a, 0x804b, 0x804b, L"HardPressed volume preferences" }, - /* */ { 0x5a, 0x804c, 0x804c, L"HardPressed global preferences" }, - /* */ { 0x5a, 0x804d, 0x804d, L"HardPressed profile" }, - /* */ { 0x5a, 0x8050, 0x8050, L"Don't Forget settings" }, - /* */ { 0x5a, 0x8052, 0x8052, L"ProBOOT preferences" }, - /* */ { 0x5a, 0x8054, 0x8054, L"Battery Brain preferences" }, - /* */ { 0x5a, 0x8055, 0x8055, L"Rainbow configuration" }, - /* */ { 0x5a, 0x8061, 0x8061, L"TypeSet preferences" }, - /* */ { 0x5a, 0x8063, 0x8063, L"Cool Cursor preferences" }, - /* */ { 0x5a, 0x806e, 0x806e, L"Balloon preferences" }, - /* */ { 0x5a, 0x80fe, 0x80fe, L"Special Edition configuration" }, - /* */ { 0x5a, 0x80ff, 0x80ff, L"Sun Dial preferences" }, - /*ANM*/ { 0x5b, 0x0000, 0xffff, L"Animation file" }, - /* */ { 0x5b, 0x8001, 0x8001, L"Cartooners movie" }, - /* */ { 0x5b, 0x8002, 0x8002, L"Cartooners actors" }, - /* */ { 0x5b, 0x8005, 0x8005, L"Arcade King Super document" }, - /* */ { 0x5b, 0x8006, 0x8006, L"Arcade King DHRG document" }, - /* */ { 0x5b, 0x8007, 0x8007, L"DreamVision movie" }, - /*MUM*/ { 0x5c, 0x0000, 0xffff, L"Multimedia document" }, - /* */ { 0x5c, 0x8001, 0x8001, L"GTv multimedia playlist" }, - /*ENT*/ { 0x5d, 0x0000, 0xffff, L"Game/Entertainment document" }, - /* */ { 0x5d, 0x8001, 0x8001, L"Solitaire Royale document" }, - /* */ { 0x5d, 0x8002, 0x8002, L"BattleFront scenario" }, - /* */ { 0x5d, 0x8003, 0x8003, L"BattleFront saved game" }, - /* */ { 0x5d, 0x8004, 0x8004, L"Gold of the Americas game" }, - /* */ { 0x5d, 0x8006, 0x8006, L"Blackjack Tutor document" }, - /* */ { 0x5d, 0x8008, 0x8008, L"Canasta document" }, - /* */ { 0x5d, 0x800b, 0x800b, L"Word Search document" }, - /* */ { 0x5d, 0x800c, 0x800c, L"Tarot deal" }, - /* */ { 0x5d, 0x800d, 0x800d, L"Tarot tournament" }, - /* */ { 0x5d, 0x800e, 0x800e, L"Full Metal Planet game" }, - /* */ { 0x5d, 0x800f, 0x800f, L"Full Metal Planet player" }, - /* */ { 0x5d, 0x8010, 0x8010, L"Quizzical high scores" }, - /* */ { 0x5d, 0x8011, 0x8011, L"Meltdown high scores" }, - /* */ { 0x5d, 0x8012, 0x8012, L"BlockWords high scores" }, - /* */ { 0x5d, 0x8013, 0x8013, L"Lift-A-Gon scores" }, - /* */ { 0x5d, 0x8014, 0x8014, L"Softdisk Adventure" }, - /* */ { 0x5d, 0x8015, 0x8015, L"Blankety Blank document" }, - /* */ { 0x5d, 0x8016, 0x8016, L"Son of Star Axe champion" }, - /* */ { 0x5d, 0x8017, 0x8017, L"Digit Fidget high scores" }, - /* */ { 0x5d, 0x8018, 0x8018, L"Eddie map" }, - /* */ { 0x5d, 0x8019, 0x8019, L"Eddie tile set" }, - /* */ { 0x5d, 0x8122, 0x8122, L"Wolfenstein 3D scenario" }, - /* */ { 0x5d, 0x8123, 0x8123, L"Wolfenstein 3D saved game" }, - /*DVU*/ { 0x5e, 0x0000, 0xffff, L"Development utility document" }, - /* */ { 0x5e, 0x0001, 0x0001, L"Resource file" }, - /* */ { 0x5e, 0x8001, 0x8001, L"ORCA/Disassembler template" }, - /* */ { 0x5e, 0x8003, 0x8003, L"DesignMaster document" }, - /* */ { 0x5e, 0x8008, 0x8008, L"ORCA/C symbol file" }, - /*FIN*/ { 0x5f, 0x0000, 0xffff, L"Financial document" }, - /* */ { 0x5f, 0x8001, 0x8001, L"Your Money Matters document" }, - /* */ { 0x5f, 0x8002, 0x8002, L"Home Refinancer document" }, - /*BIO*/ { 0x6b, 0x0000, 0xffff, L"PC Transporter BIOS" }, - /*TDR*/ { 0x6d, 0x0000, 0xffff, L"PC Transporter driver" }, - /*PRE*/ { 0x6e, 0x0000, 0xffff, L"PC Transporter pre-boot" }, - /*HDV*/ { 0x6f, 0x0000, 0xffff, L"PC Transporter volume" }, - /*WP */ { 0xa0, 0x0000, 0xffff, L"WordPerfect document" }, - /*GSB*/ { 0xab, 0x0000, 0xffff, L"Apple IIgs BASIC program" }, - /*TDF*/ { 0xac, 0x0000, 0xffff, L"Apple IIgs BASIC TDF" }, - /*BDF*/ { 0xad, 0x0000, 0xffff, L"Apple IIgs BASIC data" }, - /*SRC*/ { 0xb0, 0x0000, 0xffff, L"Apple IIgs source code" }, - /* */ { 0xb0, 0x0001, 0x0001, L"APW Text file" }, - /* */ { 0xb0, 0x0003, 0x0003, L"APW 65816 Assembly source code" }, - /* */ { 0xb0, 0x0005, 0x0005, L"ORCA/Pascal source code" }, - /* */ { 0xb0, 0x0006, 0x0006, L"APW command file" }, - /* */ { 0xb0, 0x0008, 0x0008, L"ORCA/C source code" }, - /* */ { 0xb0, 0x0009, 0x0009, L"APW Linker command file" }, - /* */ { 0xb0, 0x000a, 0x000a, L"APW C source code" }, - /* */ { 0xb0, 0x000c, 0x000c, L"ORCA/Desktop command file" }, - /* */ { 0xb0, 0x0015, 0x0015, L"APW Rez source file" }, - /* */ { 0xb0, 0x0017, 0x0017, L"Installer script" }, - /* */ { 0xb0, 0x001e, 0x001e, L"TML Pascal source code" }, - /* */ { 0xb0, 0x0116, 0x0116, L"ORCA/Disassembler script" }, - /* */ { 0xb0, 0x0503, 0x0503, L"SDE Assembler source code" }, - /* */ { 0xb0, 0x0506, 0x0506, L"SDE command script" }, - /* */ { 0xb0, 0x0601, 0x0601, L"Nifty List data" }, - /* */ { 0xb0, 0x0719, 0x0719, L"PostScript file" }, - /*OBJ*/ { 0xb1, 0x0000, 0xffff, L"Apple IIgs object code" }, - /*LIB*/ { 0xb2, 0x0000, 0xffff, L"Apple IIgs Library file" }, - /*S16*/ { 0xb3, 0x0000, 0xffff, L"GS/OS application" }, - /*RTL*/ { 0xb4, 0x0000, 0xffff, L"GS/OS run-time library" }, - /*EXE*/ { 0xb5, 0x0000, 0xffff, L"GS/OS shell application" }, - /*PIF*/ { 0xb6, 0x0000, 0xffff, L"Permanent initialization file" }, - /*TIF*/ { 0xb7, 0x0000, 0xffff, L"Temporary initialization file" }, - /*NDA*/ { 0xb8, 0x0000, 0xffff, L"New desk accessory" }, - /*CDA*/ { 0xb9, 0x0000, 0xffff, L"Classic desk accessory" }, - /*TOL*/ { 0xba, 0x0000, 0xffff, L"Tool" }, - /*DVR*/ { 0xbb, 0x0000, 0xffff, L"Apple IIgs device driver file" }, - /* */ { 0xbb, 0x7e01, 0x7e01, L"GNO/ME terminal device driver" }, - /* */ { 0xbb, 0x7f01, 0x7f01, L"GTv videodisc serial driver" }, - /* */ { 0xbb, 0x7f02, 0x7f02, L"GTv videodisc game port driver" }, - /*LDF*/ { 0xbc, 0x0000, 0xffff, L"Load file (generic)" }, - /* */ { 0xbc, 0x4001, 0x4001, L"Nifty List module" }, - /* */ { 0xbc, 0xc001, 0xc001, L"Nifty List module" }, - /* */ { 0xbc, 0x4002, 0x4002, L"Super Info module" }, - /* */ { 0xbc, 0xc002, 0xc002, L"Super Info module" }, - /* */ { 0xbc, 0x4004, 0x4004, L"Twilight document" }, - /* */ { 0xbc, 0xc004, 0xc004, L"Twilight document" }, - /* */ { 0xbc, 0x4006, 0x4006, L"Foundation resource editor" }, - /* */ { 0xbc, 0xc006, 0xc006, L"Foundation resource editor" }, - /* */ { 0xbc, 0x4007, 0x4007, L"HyperStudio new button action" }, - /* */ { 0xbc, 0xc007, 0xc007, L"HyperStudio new button action" }, - /* */ { 0xbc, 0x4008, 0x4008, L"HyperStudio screen transition" }, - /* */ { 0xbc, 0xc008, 0xc008, L"HyperStudio screen transition" }, - /* */ { 0xbc, 0x4009, 0x4009, L"DreamGrafix module" }, - /* */ { 0xbc, 0xc009, 0xc009, L"DreamGrafix module" }, - /* */ { 0xbc, 0x400a, 0x400a, L"HyperStudio Extra utility" }, - /* */ { 0xbc, 0xc00a, 0xc00a, L"HyperStudio Extra utility" }, - /* */ { 0xbc, 0x400f, 0x400f, L"HardPressed module" }, - /* */ { 0xbc, 0xc00f, 0xc00f, L"HardPressed module" }, - /* */ { 0xbc, 0x4010, 0x4010, L"Graphic Exchange translator" }, - /* */ { 0xbc, 0xc010, 0xc010, L"Graphic Exchange translator" }, - /* */ { 0xbc, 0x4011, 0x4011, L"Desktop Enhancer blanker" }, - /* */ { 0xbc, 0xc011, 0xc011, L"Desktop Enhancer blanker" }, - /* */ { 0xbc, 0x4083, 0x4083, L"Marinetti link layer module" }, - /* */ { 0xbc, 0xc083, 0xc083, L"Marinetti link layer module" }, - /*FST*/ { 0xbd, 0x0000, 0xffff, L"GS/OS File System Translator" }, - /*DOC*/ { 0xbf, 0x0000, 0xffff, L"GS/OS document" }, - /*PNT*/ { 0xc0, 0x0000, 0xffff, L"Packed super hi-res picture" }, - /* */ { 0xc0, 0x0000, 0x0000, L"Paintworks packed picture" }, - /* */ { 0xc0, 0x0001, 0x0001, L"Packed super hi-res image" }, - /* */ { 0xc0, 0x0002, 0x0002, L"Apple Preferred Format picture" }, - /* */ { 0xc0, 0x0003, 0x0003, L"Packed QuickDraw II PICT file" }, - /* */ { 0xc0, 0x0080, 0x0080, L"TIFF document" }, - /* */ { 0xc0, 0x0081, 0x0081, L"JFIF (JPEG) document" }, - /* */ { 0xc0, 0x8001, 0x8001, L"GTv background image" }, - /* */ { 0xc0, 0x8005, 0x8005, L"DreamGrafix document" }, - /* */ { 0xc0, 0x8006, 0x8006, L"GIF document" }, - /*PIC*/ { 0xc1, 0x0000, 0xffff, L"Super hi-res picture" }, - /* */ { 0xc1, 0x0000, 0x0000, L"Super hi-res screen image" }, - /* */ { 0xc1, 0x0001, 0x0001, L"QuickDraw PICT file" }, - /* */ { 0xc1, 0x0002, 0x0002, L"Super hi-res 3200-color screen image" }, - /* */ { 0xc1, 0x8001, 0x8001, L"Allison raw image doc" }, - /* */ { 0xc1, 0x8002, 0x8002, L"ThunderScan image doc" }, - /* */ { 0xc1, 0x8003, 0x8003, L"DreamGrafix document" }, - /*ANI*/ { 0xc2, 0x0000, 0xffff, L"Paintworks animation" }, - /*PAL*/ { 0xc3, 0x0000, 0xffff, L"Paintworks palette" }, - /*OOG*/ { 0xc5, 0x0000, 0xffff, L"Object-oriented graphics" }, - /* */ { 0xc5, 0x8000, 0x8000, L"Draw Plus document" }, - /* */ { 0xc5, 0xc000, 0xc000, L"DYOH architecture doc" }, - /* */ { 0xc5, 0xc001, 0xc001, L"DYOH predrawn objects" }, - /* */ { 0xc5, 0xc002, 0xc002, L"DYOH custom objects" }, - /* */ { 0xc5, 0xc003, 0xc003, L"DYOH clipboard" }, - /* */ { 0xc5, 0xc004, 0xc004, L"DYOH interiors document" }, - /* */ { 0xc5, 0xc005, 0xc005, L"DYOH patterns" }, - /* */ { 0xc5, 0xc006, 0xc006, L"DYOH landscape document" }, - /* */ { 0xc5, 0xc007, 0xc007, L"PyWare Document" }, - /*SCR*/ { 0xc6, 0x0000, 0xffff, L"Script" }, - /* */ { 0xc6, 0x8001, 0x8001, L"Davex 8 script" }, - /* */ { 0xc6, 0x8002, 0x8002, L"Universe Master backup script" }, - /* */ { 0xc6, 0x8003, 0x8003, L"Universe Master Chain script" }, - /*CDV*/ { 0xc7, 0x0000, 0xffff, L"Control Panel document" }, - /*FON*/ { 0xc8, 0x0000, 0xffff, L"Font" }, - /* */ { 0xc8, 0x0000, 0x0000, L"Font (Standard Apple IIgs QuickDraw II Font)" }, - /* */ { 0xc8, 0x0001, 0x0001, L"TrueType font resource" }, - /* */ { 0xc8, 0x0008, 0x0008, L"Postscript font resource" }, - /* */ { 0xc8, 0x0081, 0x0081, L"TrueType font file" }, - /* */ { 0xc8, 0x0088, 0x0088, L"Postscript font file" }, - /*FND*/ { 0xc9, 0x0000, 0xffff, L"Finder data" }, - /*ICN*/ { 0xca, 0x0000, 0xffff, L"Icons" }, - /*MUS*/ { 0xd5, 0x0000, 0xffff, L"Music sequence" }, - /* */ { 0xd5, 0x0000, 0x0000, L"Music Construction Set song" }, - /* */ { 0xd5, 0x0001, 0x0001, L"MIDI Synth sequence" }, - /* */ { 0xd5, 0x0007, 0x0007, L"SoundSmith document" }, - /* */ { 0xd5, 0x8002, 0x8002, L"Diversi-Tune sequence" }, - /* */ { 0xd5, 0x8003, 0x8003, L"Master Tracks Jr. sequence" }, - /* */ { 0xd5, 0x8004, 0x8004, L"Music Writer document" }, - /* */ { 0xd5, 0x8005, 0x8005, L"Arcade King Super music" }, - /* */ { 0xd5, 0x8006, 0x8006, L"Music Composer file" }, - /*INS*/ { 0xd6, 0x0000, 0xffff, L"Instrument" }, - /* */ { 0xd6, 0x0000, 0x0000, L"Music Construction Set instrument" }, - /* */ { 0xd6, 0x0001, 0x0001, L"MIDI Synth instrument" }, - /* */ { 0xd6, 0x8002, 0x8002, L"Diversi-Tune instrument" }, - /*MDI*/ { 0xd7, 0x0000, 0xffff, L"MIDI data" }, - /* */ { 0xd7, 0x0000, 0x0000, L"MIDI standard data" }, - /* */ { 0xd7, 0x0080, 0x0080, L"MIDI System Exclusive data" }, - /* */ { 0xd7, 0x8001, 0x8001, L"MasterTracks Pro Sysex file" }, - /*SND*/ { 0xd8, 0x0000, 0xffff, L"Sampled sound" }, - /* */ { 0xd8, 0x0000, 0x0000, L"Audio IFF document" }, - /* */ { 0xd8, 0x0001, 0x0001, L"AIFF-C document" }, - /* */ { 0xd8, 0x0002, 0x0002, L"ASIF instrument" }, - /* */ { 0xd8, 0x0003, 0x0003, L"Sound resource file" }, - /* */ { 0xd8, 0x0004, 0x0004, L"MIDI Synth wave data" }, - /* */ { 0xd8, 0x8001, 0x8001, L"HyperStudio sound" }, - /* */ { 0xd8, 0x8002, 0x8002, L"Arcade King Super sound" }, - /* */ { 0xd8, 0x8003, 0x8003, L"SoundOff! sound bank" }, - /*DBM*/ { 0xdb, 0x0000, 0xffff, L"DB Master document" }, - /* */ { 0xdb, 0x0001, 0x0001, L"DB Master document" }, - /*???*/ { 0xdd, 0x0000, 0xffff, L"DDD Deluxe archive" }, // unofficial - /*LBR*/ { 0xe0, 0x0000, 0xffff, L"Archival library" }, - /* */ { 0xe0, 0x0000, 0x0000, L"ALU library" }, - /* */ { 0xe0, 0x0001, 0x0001, L"AppleSingle file" }, - /* */ { 0xe0, 0x0002, 0x0002, L"AppleDouble header file" }, - /* */ { 0xe0, 0x0003, 0x0003, L"AppleDouble data file" }, - /* */ { 0xe0, 0x0004, 0x0004, L"Archiver archive" }, - /* */ { 0xe0, 0x0005, 0x0005, L"DiskCopy 4.2 disk image" }, - /* */ { 0xe0, 0x0100, 0x0100, L"Apple 5.25 disk image" }, - /* */ { 0xe0, 0x0101, 0x0101, L"Profile 5MB disk image" }, - /* */ { 0xe0, 0x0102, 0x0102, L"Profile 10MB disk image" }, - /* */ { 0xe0, 0x0103, 0x0103, L"Apple 3.5 disk image" }, - /* */ { 0xe0, 0x0104, 0x0104, L"SCSI device image" }, - /* */ { 0xe0, 0x0105, 0x0105, L"SCSI hard disk image" }, - /* */ { 0xe0, 0x0106, 0x0106, L"SCSI tape image" }, - /* */ { 0xe0, 0x0107, 0x0107, L"SCSI CD-ROM image" }, - /* */ { 0xe0, 0x010e, 0x010e, L"RAM disk image" }, - /* */ { 0xe0, 0x010f, 0x010f, L"ROM disk image" }, - /* */ { 0xe0, 0x0110, 0x0110, L"File server image" }, - /* */ { 0xe0, 0x0113, 0x0113, L"Hard disk image" }, - /* */ { 0xe0, 0x0114, 0x0114, L"Floppy disk image" }, - /* */ { 0xe0, 0x0115, 0x0115, L"Tape image" }, - /* */ { 0xe0, 0x011e, 0x011e, L"AppleTalk file server image" }, - /* */ { 0xe0, 0x0120, 0x0120, L"DiskCopy 6 disk image" }, - /* */ { 0xe0, 0x0130, 0x0130, L"Universal Disk Image file" }, - /* */ { 0xe0, 0x8000, 0x8000, L"Binary II file" }, - /* */ { 0xe0, 0x8001, 0x8001, L"AppleLink ACU document" }, - /* */ { 0xe0, 0x8002, 0x8002, L"ShrinkIt (NuFX) document" }, - /* */ { 0xe0, 0x8003, 0x8003, L"Universal Disk Image file" }, - /* */ { 0xe0, 0x8004, 0x8004, L"Davex archived volume" }, - /* */ { 0xe0, 0x8006, 0x8006, L"EZ Backup Saveset doc" }, - /* */ { 0xe0, 0x8007, 0x8007, L"ELS DOS 3.3 volume" }, - /* */ { 0xe0, 0x8008, 0x8008, L"UtilityWorks document" }, - /* */ { 0xe0, 0x800a, 0x800a, L"Replicator document" }, - /* */ { 0xe0, 0x800b, 0x800b, L"AutoArk compressed document" }, - /* */ { 0xe0, 0x800d, 0x800d, L"HardPressed compressed data (data fork)" }, - /* */ { 0xe0, 0x800e, 0x800e, L"HardPressed compressed data (rsrc fork)" }, - /* */ { 0xe0, 0x800f, 0x800f, L"HardPressed compressed data (both forks)" }, - /* */ { 0xe0, 0x8010, 0x8010, L"LHA archive" }, - /*ATK*/ { 0xe2, 0x0000, 0xffff, L"AppleTalk data" }, - /* */ { 0xe2, 0xffff, 0xffff, L"EasyMount document" }, - /*R16*/ { 0xee, 0x0000, 0xffff, L"EDASM 816 relocatable file" }, - /*PAS*/ { 0xef, 0x0000, 0xffff, L"Pascal area" }, - /*CMD*/ { 0xf0, 0x0000, 0xffff, L"BASIC command" }, - /*???*/ { 0xf1, 0x0000, 0xffff, L"User type #1" }, - /*???*/ { 0xf2, 0x0000, 0xffff, L"User type #2" }, - /*???*/ { 0xf3, 0x0000, 0xffff, L"User type #3" }, - /*???*/ { 0xf4, 0x0000, 0xffff, L"User type #4" }, - /*???*/ { 0xf5, 0x0000, 0xffff, L"User type #5" }, - /*???*/ { 0xf6, 0x0000, 0xffff, L"User type #6" }, - /*???*/ { 0xf7, 0x0000, 0xffff, L"User type #7" }, - /*???*/ { 0xf8, 0x0000, 0xffff, L"User type #8" }, - /*OS */ { 0xf9, 0x0000, 0xffff, L"GS/OS system file" }, - /*OS */ { 0xfa, 0x0000, 0xffff, L"Integer BASIC program" }, - /*OS */ { 0xfb, 0x0000, 0xffff, L"Integer BASIC variables" }, - /*OS */ { 0xfc, 0x0000, 0xffff, L"AppleSoft BASIC program" }, - /*OS */ { 0xfd, 0x0000, 0xffff, L"AppleSoft BASIC variables" }, - /*OS */ { 0xfe, 0x0000, 0xffff, L"Relocatable code" }, - /*OS */ { 0xff, 0x0000, 0xffff, L"ProDOS 8 application" }, -}; - -/*static*/ const WCHAR* PathProposal::FileTypeDescription(long fileType, - long auxType) -{ - for (int i = NELEM(gTypeDescriptions)-1; i >= 0; i--) { - if (fileType == gTypeDescriptions[i].fileType && - auxType >= gTypeDescriptions[i].minAuxType && - auxType <= gTypeDescriptions[i].maxAuxType) - { - return gTypeDescriptions[i].descr; - } - } - - return NULL; -} - - -/* - * =========================================================================== - * Filename/filetype conversion - * =========================================================================== - */ - -void PathProposal::Init(GenericEntry* pEntry) -{ - // TODO(Unicode) - //fStoredPathName = Charset::ConvertMORToUNI(pEntry->GetPathNameMOR()); - // can't do this yet -- the rest of the extraction path isn't ready - fStoredPathName = pEntry->GetPathNameMOR(); - fStoredFssep = pEntry->GetFssep(); - //if (fStoredFssep == '\0') // e.g. embedded DOS 3.3 volume - // fStoredFssep = kDefaultStoredFssep; - fFileType = pEntry->GetFileType(); - fAuxType = pEntry->GetAuxType(); - //fThreadKind set from SelectionEntry - // reset the "output" fields - fLocalPathName = L":HOSED:"; - fLocalFssep = ']'; - // I expect these to be as-yet unset; check it - ASSERT(!fPreservation); - ASSERT(!fAddExtension); - ASSERT(!fJunkPaths); -} - -// init the "add to archive" side -void PathProposal::Init(const WCHAR* localPathName) { - //ASSERT(basePathName[strlen(basePathName)-1] != kLocalFssep); - //fLocalPathName = localPathName + strlen(basePathName)+1; - fLocalPathName = localPathName; - fLocalFssep = kLocalFssep; - // reset the "output" fields - fStoredPathName = L":HOSED:"; - fStoredFssep = '['; - fFileType = 0; - fAuxType = 0; - fThreadKind = GenericEntry::kDataThread; - // I expect these to be as-yet unset; check it - ASSERT(!fPreservation); - ASSERT(!fAddExtension); - ASSERT(!fJunkPaths); -} - -void PathProposal::ArchiveToLocal(void) -{ - WCHAR* pathBuf; - const WCHAR* startp; - const WCHAR* endp; - WCHAR* dstp; - int newBufLen; - - /* init output fields */ - fLocalFssep = kLocalFssep; - fLocalPathName = L""; - - /* - * Set up temporary buffer space. The maximum possible expansion - * requires converting all chars to '%' codes and adding the longest - * possible preservation string. - */ - newBufLen = fStoredPathName.GetLength()*3 + kMaxPathGrowth +1; - pathBuf = fLocalPathName.GetBuffer(newBufLen); - ASSERT(pathBuf != NULL); - - startp = fStoredPathName; - dstp = pathBuf; - while (*startp == fStoredFssep) { - /* ignore leading path sep; always extract to current dir */ - startp++; - } - - /* normalize all directory components and the filename component */ - while (startp != NULL) { - endp = NULL; - if (fStoredFssep != '\0') - endp = wcschr(startp, fStoredFssep); - if (endp != NULL && endp == startp) { - /* zero-length subdir component */ - LOGI("WARNING: zero-length subdir component in '%ls'", startp); - startp++; - continue; - } - if (endp != NULL) { - /* normalize directory component */ - NormalizeDirectoryName(startp, endp - startp, - fStoredFssep, &dstp, newBufLen); - - *dstp++ = fLocalFssep; - - startp = endp +1; - } else { - /* normalize filename component */ - NormalizeFileName(startp, wcslen(startp), - fStoredFssep, &dstp, newBufLen); - *dstp++ = '\0'; - - /* add/replace extension if necessary */ - WCHAR extBuf[kMaxPathGrowth +1] = L""; - if (fPreservation) { - AddPreservationString(pathBuf, extBuf); - } else if (fThreadKind == GenericEntry::kRsrcThread) { - /* add this in lieu of the preservation extension */ - wcscat(pathBuf, kResourceStr); - } - if (fAddExtension) { - AddTypeExtension(pathBuf, extBuf); - } - ASSERT(wcslen(extBuf) <= kMaxPathGrowth); - wcscat(pathBuf, extBuf); - - startp = NULL; /* we're done, break out of loop */ - } - } - - /* check for overflow */ - ASSERT(dstp - pathBuf <= newBufLen); - - /* - * If "junk paths" is set, drop everything but the last component. - */ - if (fJunkPaths) { - WCHAR* lastFssep; - lastFssep = wcsrchr(pathBuf, fLocalFssep); - if (lastFssep != NULL) { - ASSERT(*(lastFssep+1) != '\0'); /* should already have been caught*/ - memmove(pathBuf, lastFssep+1, - (wcslen(lastFssep+1)+1) * sizeof(WCHAR)); - } - } - - fLocalPathName.ReleaseBuffer(); -} - -#if defined(WINDOWS_LIKE) -/* - * You can't create files or directories with these names on a FAT filesystem, - * because they're MS-DOS "device special files". - * - * The list originally came from the Linux kernel's fs/msdos/namei.c; a - * better reference is "Naming Files, Paths, and Namespaces": - * http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx - * - * The trick is that the name can't start with any of these. That could mean - * that the name is just "aux", or it could be "aux.this.txt". - */ -static const WCHAR* gFatReservedNames3[] = { - L"CON", L"PRN", L"NUL", L"AUX", NULL -}; -static const WCHAR* gFatReservedNames4[] = { - L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9", - L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", - NULL -}; - -void PathProposal::Win32NormalizeFileName(const WCHAR* srcp, long srcLen, - char fssep, WCHAR** pDstp, long dstLen) -{ - /* - * TODO(Unicode): do proper conversion - * TODO: don't allow the filename to end with a space or period (Windows - * requirement) - */ - WCHAR* dstp = *pDstp; - const WCHAR* startp = srcp; - static const WCHAR* kInvalid = L"\\/:*?\"<>|"; - - /* match on "aux" or "aux.blah" */ - if (srcLen >= 3) { - const WCHAR** ppcch; - - for (ppcch = gFatReservedNames3; *ppcch != NULL; ppcch++) { - if (wcsnicmp(srcp, *ppcch, 3) == 0 && - (srcp[3] == '.' || srcLen == 3)) - { - LOGD("--- fixing '%ls'", *ppcch); - if (fPreservation) { - *dstp++ = kForeignIndic; - *dstp++ = '0'; - *dstp++ = '0'; - } else - *dstp++ = '_'; - break; - } - } - } - if (srcLen >= 4) { - const WCHAR** ppcch; - - for (ppcch = gFatReservedNames4; *ppcch != NULL; ppcch++) { - if (wcsnicmp(srcp, *ppcch, 4) == 0 && - (srcp[4] == '.' || srcLen == 4)) - { - LOGD("--- fixing '%ls'", *ppcch); - if (fPreservation) { - *dstp++ = kForeignIndic; - *dstp++ = '0'; - *dstp++ = '0'; - } else - *dstp++ = '_'; - break; - } - } - } - - - while (srcLen--) { /* don't go until null found! */ - ASSERT(*srcp != '\0'); - - if (*srcp == kForeignIndic) { - /* change '%' to "%%" */ - if (fPreservation) - *dstp++ = *srcp; - *dstp++ = *srcp++; - } else if (wcschr(kInvalid, *srcp) != NULL || - *srcp < 0x20 || *srcp >= 0x7f) - { - /* change invalid char to "%2f" or '_' */ - if (fPreservation) { - *dstp++ = kForeignIndic; - *dstp++ = HexConv(*srcp >> 4 & 0x0f); - *dstp++ = HexConv(*srcp & 0x0f); - } else { - *dstp++ = '_'; - } - srcp++; - } else { - /* no need to fiddle with it */ - *dstp++ = *srcp++; - } - } - - *dstp = '\0'; /* end the string, but don't advance past the null */ - ASSERT(*pDstp - dstp <= dstLen); /* make sure we didn't overflow */ - *pDstp = dstp; -} -#endif - -void PathProposal::NormalizeFileName(const WCHAR* srcp, long srcLen, - char fssep, WCHAR** pDstp, long dstLen) -{ - ASSERT(srcp != NULL); - ASSERT(srcLen > 0); - ASSERT(dstLen > srcLen); - ASSERT(pDstp != NULL); - ASSERT(*pDstp != NULL); - -#if defined(UNIX_LIKE) - UNIXNormalizeFileName(srcp, srcLen, fssep, pDstp, dstLen); -#elif defined(WINDOWS_LIKE) - Win32NormalizeFileName(srcp, srcLen, fssep, pDstp, dstLen); -#else - #error "port this" -#endif -} - -void PathProposal::NormalizeDirectoryName(const WCHAR* srcp, long srcLen, - char fssep, WCHAR** pDstp, long dstLen) -{ - /* in general, directories and filenames are the same */ - ASSERT(fssep > ' ' && fssep < 0x7f); - NormalizeFileName(srcp, srcLen, fssep, pDstp, dstLen); -} - -void PathProposal::AddPreservationString(const WCHAR* pathBuf, WCHAR* extBuf) -{ - WCHAR* cp; - - ASSERT(pathBuf != NULL); - ASSERT(extBuf != NULL); - ASSERT(fPreservation); - - cp = extBuf + wcslen(extBuf); - - /* - * Cons up a preservation string. On some platforms "sprintf" doesn't - * return the #of characters written, so we add it up manually. - */ - if (fFileType < 0x100 && fAuxType < 0x10000) { - wsprintf(cp, L"%c%02lx%04lx", kPreserveIndic, fFileType, fAuxType); - cp += 7; - } else { - wsprintf(cp, L"%c%08lx%08lx", kPreserveIndic, fFileType, fAuxType); - cp += 17; - } - - if (fThreadKind == GenericEntry::kRsrcThread) - *cp++ = kResourceFlag; - else if (fThreadKind == GenericEntry::kDiskImageThread) - *cp++ = kDiskImageFlag; - - - /* make sure it's terminated */ - *cp = '\0'; -} - -void PathProposal::AddTypeExtension(const WCHAR* pathBuf, WCHAR* extBuf) -{ - const WCHAR* pPathExt = NULL; - const WCHAR* pWantedExt = NULL; - const WCHAR* pTypeExt = NULL; - WCHAR* end; - WCHAR* cp; - - cp = extBuf + wcslen(extBuf); - - /* - * Find extension in the local filename that we've prepared so far. - * Note FindExtension guarantees there's at least one char after '.'. - */ - pPathExt = PathName::FindExtension(pathBuf, fLocalFssep); - if (pPathExt == NULL) { - /* - * There's no extension on the filename. Use the standard - * ProDOS type, if one exists for this entry. We don't use - * the table if it's NON, "???", or a hex value. - */ - if (fFileType) { - pTypeExt = FileTypeString(fFileType); - if (pTypeExt[0] == '?' || pTypeExt[0] == '$') - pTypeExt = NULL; - } - } else { - pPathExt++; // skip leading '.' - } - - /* - * Figure out what extension we want this file to have. Files of type - * text are *always* ".TXT", and our extracted disk images are always - * ".PO". If it's not one of these two, we either retain the file's - * original extension, or generate one for it from the ProDOS file type. - */ - if (fFileType == 0x04) - pWantedExt = L"TXT"; - else if (fThreadKind == GenericEntry::kDiskImageThread) - pWantedExt = L"PO"; - else { - /* - * We want to use the extension currently on the file, if it has one. - * If not, use the one from the file type. - */ - if (pPathExt != NULL) { - pWantedExt = pPathExt; - } else { - pWantedExt = pTypeExt; - } - } - /* pWantedExt != NULL unless we failed to find a pTypeExt */ - - - /* - * Now we know which one we want. Figure out if we want to add it. - */ - if (pWantedExt != NULL) { - if (extBuf[0] == '\0' && pPathExt != NULL && - wcsicmp(pPathExt, pWantedExt) == 0) - { - /* don't add an extension that's already there */ - pWantedExt = NULL; - goto know_ext; - } - - if (wcslen(pWantedExt) >= kMaxExtLen) { - /* too long, forget it */ - pWantedExt = NULL; - goto know_ext; - } - - /* if it's strictly decimal-numeric, don't use it (.1, .2, etc) */ - (void) wcstoul(pWantedExt, &end, 10); - if (*end == '\0') { - pWantedExt = NULL; - goto know_ext; - } - - /* if '#' appears in it, don't use it -- it'll confuse us */ - //LOGI("LOOKING FOR '%c' in '%ls'", kPreserveIndic, ccp); - const WCHAR* ccp = pWantedExt; - while (*ccp != '\0') { - if (*ccp == kPreserveIndic) { - pWantedExt = NULL; - goto know_ext; - } - ccp++; - } - } -know_ext: - - /* - * If pWantedExt is non-NULL, it points to a filename extension without - * the leading '.'. - */ - if (pWantedExt != NULL) { - *cp++ = kFilenameExtDelim; - wcscpy(cp, pWantedExt); - //cp += strlen(pWantedExt); - } -} - - -/* - * =========================================================================== - * File type restoration - * =========================================================================== - */ - -typedef bool Boolean; - -void PathProposal::LocalToArchive(const AddFilesDialog* pAddOpts) -{ - Boolean wasPreserved; - Boolean doJunk = false; - Boolean adjusted; - WCHAR slashDotDotSlash[5] = L"_.._"; - - fStoredPathName = fLocalPathName; - WCHAR* livePathStr = fStoredPathName.GetBuffer(1); - - fStoredFssep = kDefaultStoredFssep; - - /* convert '/' to '\' */ - ReplaceFssep(livePathStr, - kAltLocalFssep, //NState_GetAltSystemPathSeparator(pState), - kLocalFssep, //NState_GetSystemPathSeparator(pState), - kLocalFssep); //NState_GetSystemPathSeparator(pState)); - - /* - * Check for file type preservation info in the filename. If present, - * set the file type values and truncate the filename. - */ - wasPreserved = false; - if (pAddOpts->fTypePreservation == AddFilesDialog::kPreserveTypes || - pAddOpts->fTypePreservation == AddFilesDialog::kPreserveAndExtend) - { - wasPreserved = ExtractPreservationString(livePathStr); - } - - /* - * Do a "denormalization" pass, where we convert invalid chars (such - * as '/') from percent-codes back to 8-bit characters. The filename - * will always be the same size or smaller, so we can do it in place. - */ - if (wasPreserved) - DenormalizePath(livePathStr); - - /* - * If we're in "extended" mode, and the file wasn't preserved, take a - * guess at what the file type should be based on the file extension. - */ - if (!wasPreserved && - pAddOpts->fTypePreservation == AddFilesDialog::kPreserveAndExtend) - { - InterpretExtension(livePathStr); - } - - if (fStripDiskImageSuffix) - StripDiskImageSuffix(livePathStr); - - /* - * Strip bad chars off the front of the pathname. Every time we - * remove one thing we potentially expose another, so we have to - * loop until it's sanitized. - * - * The outer loop isn't really necessary under Win32, because you'd - * need to do something like ".\\foo", which isn't allowed. UNIX - * silently allows ".//foo", so this is a problem there. (We could - * probably do away with the inner loops, but those were already - * written when I saw the larger problem.) - */ - do { - adjusted = false; - - /* - * Check for other unpleasantness, such as a leading fssep. - */ - ASSERT(kLocalFssep != '\0'); - while (livePathStr[0] == kLocalFssep) { - /* slide it down, len is (strlen +1), -1 (dropping first char)*/ - memmove(livePathStr, livePathStr+1, - wcslen(livePathStr) * sizeof(WCHAR)); - adjusted = true; - } - - /* - * Remove leading "./". - */ - while (livePathStr[0] == '.' && livePathStr[1] == kLocalFssep) - { - /* slide it down, len is (strlen +1) -2 (dropping two chars) */ - memmove(livePathStr, livePathStr+2, - (wcslen(livePathStr)-1) * sizeof(WCHAR)); - adjusted = true; - } - } while (adjusted); - - /* - * If there's a "/../" present anywhere in the name, junk everything - * but the filename. - * - * This won't catch "foo/bar/..", but that should've been caught as - * a directory anyway. - */ - slashDotDotSlash[0] = kLocalFssep; - slashDotDotSlash[3] = kLocalFssep; - if ((livePathStr[0] == '.' && livePathStr[1] == '.') || - (wcsstr(livePathStr, slashDotDotSlash) != NULL)) - { - LOGD("Found dot dot in '%ls', keeping only filename", livePathStr); - doJunk = true; - } - - /* - * Scan for and remove "/./" and trailing "/.". They're filesystem - * no-ops that work just fine under Win32 and UNIX but could confuse - * a IIgs. (Of course, the user could just omit them from the pathname.) - */ - /* TO DO 20030208 */ - - /* - * If "junk paths" is set, drop everything before the last fssep char. - */ - if (pAddOpts->fStripFolderNames || doJunk) { - WCHAR* lastFssep; - lastFssep = wcsrchr(livePathStr, kLocalFssep); - if (lastFssep != NULL) { - ASSERT(*(lastFssep+1) != '\0'); /* should already have been caught*/ - memmove(livePathStr, lastFssep+1, - (wcslen(lastFssep+1)+1) * sizeof(WCHAR)); - } - } - - /* - * Finally, substitute our generally-accepted path separator in place of - * the local one, stomping on anything with a ':' in it as we do. The - * goal is to avoid having "subdir:foo/bar" turn into "subdir/foo/bar", - * so we change it to "subdirXfoo:bar". Were we a general-purpose - * archiver, this might be a mistake, but we're not. NuFX doesn't really - * give us a choice. - */ - ReplaceFssep(livePathStr, kLocalFssep, - PathProposal::kDefaultStoredFssep, 'X'); - - /* let the CString manage itself again */ - fStoredPathName.ReleaseBuffer(); -} - -void PathProposal::ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst) -{ - while (*str != '\0') { - if (*str == oldc) - *str = newc; - else if (*str == newc) - *str = newSubst; - str++; - } -} - -void PathProposal::LookupExtension(const WCHAR* ext) -{ - WCHAR uext3[4]; - int i, extLen; - - extLen = wcslen(ext); - ASSERT(extLen > 0); - - /* - * First step is to try to find it in the recognized types list. - */ - for (i = 0; i < NELEM(gRecognizedExtensions); i++) { - if (wcsicmp(ext, gRecognizedExtensions[i].label) == 0) { - fFileType = gRecognizedExtensions[i].fileType; - fAuxType = gRecognizedExtensions[i].auxType; - goto bail; - } - } - - /* - * Second step is to try to find it in the ProDOS types list. - * - * The extension is converted to upper case and padded with spaces. - * - * [do we want to obstruct matching on things like '$f7' here?] - */ - if (extLen <= 3) { - for (i = 2; i >= extLen; i--) - uext3[i] = ' '; - for ( ; i >= 0; i--) - uext3[i] = toupper(ext[i]); - uext3[3] = '\0'; - - for (i = 0; i < NELEM(gFileTypeNames); i++) { - if (wcscmp(uext3, gFileTypeNames[i]) == 0) { - fFileType = i; - goto bail; - } - } - } - -bail: - return; -} - -void PathProposal::InterpretExtension(const WCHAR* pathName) -{ - const WCHAR* pExt; - - ASSERT(pathName != NULL); - - pExt = PathName::FindExtension(pathName, fLocalFssep); - if (pExt != NULL) - LookupExtension(pExt+1); -} - -Boolean PathProposal::ExtractPreservationString(WCHAR* pathname) -{ - /* - * We have to be careful not to trip on false-positive occurrences of '#' - * in the filename. - */ - WCHAR numBuf[9]; - unsigned long fileType, auxType; - int threadMask; - WCHAR* pPreserve; - WCHAR* cp; - int digitCount; - - ASSERT(pathname != NULL); - - pPreserve = wcsrchr(pathname, kPreserveIndic); - if (pPreserve == NULL) - return false; - - /* count up the #of hex digits */ - digitCount = 0; - for (cp = pPreserve+1; *cp != '\0' && isxdigit((int)*cp); cp++) - digitCount++; - - /* extract the file and aux type */ - switch (digitCount) { - case 6: - /* ProDOS 1-byte type and 2-byte aux */ - memcpy(numBuf, pPreserve+1, 2 * sizeof(WCHAR)); - numBuf[2] = 0; - fileType = wcstoul(numBuf, &cp, 16); - ASSERT(cp == numBuf + 2); - - auxType = wcstoul(pPreserve+3, &cp, 16); - ASSERT(cp == pPreserve + 7); - break; - case 16: - /* HFS 4-byte type and 4-byte creator */ - memcpy(numBuf, pPreserve+1, 8 * sizeof(WCHAR)); - numBuf[8] = 0; - fileType = wcstoul(numBuf, &cp, 16); - ASSERT(cp == numBuf + 8); - - auxType = wcstoul(pPreserve+9, &cp, 16); - ASSERT(cp == pPreserve + 17); - break; - default: - /* not valid */ - return false; - } - - /* check for a threadID specifier */ - //threadID = kNuThreadIDDataFork; - threadMask = GenericEntry::kDataThread; - switch (*cp) { - case kResourceFlag: - //threadID = kNuThreadIDRsrcFork; - threadMask = GenericEntry::kRsrcThread; - cp++; - break; - case kDiskImageFlag: - //threadID = kNuThreadIDDiskImage; - threadMask = GenericEntry::kDiskImageThread; - cp++; - break; - default: - /* do nothing... yet */ - break; - } - - /* make sure we were the very last component */ - switch (*cp) { - case kFilenameExtDelim: /* redundant "-ee" extension */ - case '\0': /* end of string! */ - break; - default: - return false; - } - - /* truncate the original string, and return what we got */ - *pPreserve = '\0'; - fFileType = fileType; - fAuxType = auxType; - fThreadKind = threadMask; - //*pThreadID = threadID; - - return true; -} - -void PathProposal::DenormalizePath(WCHAR* pathBuf) -{ - const WCHAR* srcp; - WCHAR* dstp; - WCHAR ch; - - srcp = pathBuf; - dstp = pathBuf; - - while (*srcp != '\0') { - if (*srcp == kForeignIndic) { - srcp++; - if (*srcp == kForeignIndic) { - *dstp++ = kForeignIndic; - srcp++; - } else if (isxdigit((int)*srcp)) { - ch = HexDigit(*srcp) << 4; - srcp++; - if (isxdigit((int)*srcp)) { - /* valid, output char (unless it's a %00 place-holder) */ - ch += HexDigit(*srcp); - if (ch != '\0') { - *dstp++ = ch; - } - srcp++; - } else { - /* bogus '%' with trailing hex digit found! */ - *dstp++ = kForeignIndic; - *dstp++ = *(srcp-1); - } - } else { - /* bogus lone '%s' found! */ - *dstp++ = kForeignIndic; - } - - } else { - *dstp++ = *srcp++; - } - } - - *dstp = '\0'; - ASSERT(dstp <= srcp); -} - -void PathProposal::StripDiskImageSuffix(WCHAR* pathName) -{ - static const WCHAR diskExt[][4] = { - L"SHK", L"SDK", L"IMG", L"PO", L"DO", L"2MG", L"DSK" - }; - const WCHAR* pExt; - int i; - - pExt = PathName::FindExtension(pathName, fLocalFssep); - if (pExt == NULL || pExt == pathName) - return; - - for (i = 0; i < NELEM(diskExt); i++) { - if (wcsicmp(pExt+1, diskExt[i]) == 0) { - LOGI("Dropping '%ls' from '%ls'", pExt, pathName); - *const_cast(pExt) = '\0'; - return; - } - } -} diff --git a/ciderpress/app/FileNameConv.h b/ciderpress/app/FileNameConv.h deleted file mode 100644 index 6c8b6f2..0000000 --- a/ciderpress/app/FileNameConv.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * File name conversion. - * TODO: rename to PathProposal.h - */ -#ifndef APP_FILENAMECONV_H -#define APP_FILENAMECONV_H - -#include "GenericArchive.h" - - -/* - * Proposal for an output pathname, based on the contents of a GenericEntry. - */ -class PathProposal { -public: - typedef GenericEntry::RecordKind RecordKind; - enum { - kDefaultStoredFssep = ':', - kLocalFssep = '\\', // PATH_SEP - kAltLocalFssep = '/' // PATH_SEP2 - }; - - PathProposal(void) { - fStoredPathName = L":BOGUS:"; - fStoredFssep = '['; - fFileType = 256; - fAuxType = 65536; - fThreadKind = 0; - - fLocalPathName = L":HOSED:"; - fLocalFssep = ']'; - - fPreservation = false; - fAddExtension = false; - fJunkPaths = false; - fStripDiskImageSuffix = false; - } - virtual ~PathProposal(void) {} - - // init the "extract from archive" side from a GenericEntry struct - void Init(GenericEntry* pEntry); - - // init the "add to archive" side - void Init(const WCHAR* localPathName); - - /* - * Convert a pathname pulled out of an archive to something suitable for the - * local filesystem. - * - * The new pathname may be shorter (because characters were removed) or - * longer (if we add a "#XXYYYYZ" extension or replace chars with '%' codes). - */ - void ArchiveToLocal(void); - - /* - * Convert a local path into something suitable for storage in an archive. - * Type preservation strings are interpreted and stripped as appropriate. - * - * This does *not* do filesystem-specific normalization here. (It could, but - * it's better to leave that for later so we can do uniqueification.) - * - * In the current implementation, fStoredPathName will always get smaller, - * but it would be unwise to rely on that. - */ - void LocalToArchive(const AddFilesDialog* pAddOpts); - - /* - * Fields for the "archive" side. - */ - // pathname from record or full pathname from disk image - CString fStoredPathName; - // filesystem separator char (or '\0' for things like DOS 3.3) - char fStoredFssep; - // file type, aux type, and what piece of the file this is - uint32_t fFileType; - uint32_t fAuxType; - int fThreadKind; // GenericEntry, e.g. kDataThread - - /* - * Fields for the "local" Side. - */ - // relative path of file for local filesystem - CString fLocalPathName; - // filesystem separator char for new path (always '\\' for us) - char fLocalFssep; - - /* - * Flags. - */ - // filename/filetype preservation flags - bool fPreservation; - bool fAddExtension; - bool fJunkPaths; - bool fStripDiskImageSuffix; - - /* - * Return a pointer to the three-letter representation of the file type name. - */ - static const WCHAR* FileTypeString(uint32_t fileType); - - /* - * Find an entry in the type description table that matches both file type and - * aux type. If no match is found, NULL is returned. - */ - static const WCHAR* FileTypeDescription(long fileType, long auxType); - -private: - /* - * Filename normalization for Win32 filesystems. You can't use [ \/:*?"<>| ] - * or control characters, and we're currently avoiding high-ASCII stuff. - */ - void Win32NormalizeFileName(const WCHAR* srcp, long srcLen, - char fssep, WCHAR** pDstp, long dstLen); - - /* - * Normalize a file name to local filesystem conventions. The input - * is quite possibly *NOT* null-terminated, since it may represent a - * substring of a full pathname. Use "srcLen". - * - * The output filename is copied to *pDstp, which is advanced forward. - * - * The output buffer must be able to hold 3x the original string length. - */ - void NormalizeFileName(const WCHAR* srcp, long srcLen, - char fssep, WCHAR** pDstp, long dstLen); - - /* - * Normalize a directory name to local filesystem conventions. - */ - void NormalizeDirectoryName(const WCHAR* srcp, long srcLen, - char fssep, WCHAR** pDstp, long dstLen); - - /* - * Add a preservation string. - * - * "pathBuf" is assumed to have enough space to hold the current path - * plus kMaxPathGrowth more. It will be modified in place. - */ - void AddPreservationString(const WCHAR* pathBuf, WCHAR* extBuf); - - /* - * Add a ".type" extension to the filename. - * - * We either need to retain the existing extension (possibly obscured by file - * type preservation) or append an extension based on the ProDOS file type. - */ - void AddTypeExtension(const WCHAR* pathBuf, WCHAR* extBuf); - - /* - * Replace "oldc" with "newc". If we find an instance of "newc" already - * in the string, replace it with "newSubst". - */ - void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst); - - /* - * Try to figure out what file type is associated with a filename extension. - * - * This checks the standard list of ProDOS types (which should catch things - * like "TXT" and "BIN") and the separate list of recognized extensions. - */ - void LookupExtension(const WCHAR* ext); - - /* - * Try to associate some meaning with the file extension. - */ - void InterpretExtension(const WCHAR* pathName); - - /* - * Check to see if there's a preservation string on the filename. If so, - * set the filetype and auxtype information, and trim the preservation - * string off. - */ - bool ExtractPreservationString(WCHAR* pathName); - - /* - * Remove NuLib2's normalization magic (e.g. "%2f" for '/'). - * - * This always results in the filename staying the same length or getting - * smaller, so we can do it in place in the buffer. - */ - void DenormalizePath(WCHAR* pathBuf); - - /* - * Remove a disk image suffix. - * - * Useful when adding disk images directly from a .SDK or .2MG file. We - * don't want them to retain their original suffix. - */ - void StripDiskImageSuffix(WCHAR* pathName); -}; - -#endif /*APP_FILENAMECONV_H*/ diff --git a/ciderpress/app/GenericArchive.cpp b/ciderpress/app/GenericArchive.cpp deleted file mode 100644 index 96be02f..0000000 --- a/ciderpress/app/GenericArchive.cpp +++ /dev/null @@ -1,1191 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of GenericArchive and GenericEntry. - * - * These serve as abstract base classes for archive-specific classes. - */ -#include "stdafx.h" -#include "GenericArchive.h" -#include "NufxArchive.h" -#include "FileNameConv.h" -#include "ContentList.h" -#include "../reformat/ReformatBase.h" -#include "Main.h" -#include -#include - -/* - * For systems (e.g. Visual C++ 6.0) that don't have these standard values. - */ -#ifndef S_IRUSR -# define S_IRUSR 0400 -# define S_IWUSR 0200 -# define S_IXUSR 0100 -# define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) -# define S_IRGRP (S_IRUSR >> 3) -# define S_IWGRP (S_IWUSR >> 3) -# define S_IXGRP (S_IXUSR >> 3) -# define S_IRWXG (S_IRWXU >> 3) -# define S_IROTH (S_IRGRP >> 3) -# define S_IWOTH (S_IWGRP >> 3) -# define S_IXOTH (S_IXGRP >> 3) -# define S_IRWXO (S_IRWXG >> 3) -#endif -#ifndef S_ISREG -# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#endif - - -/* - * =========================================================================== - * GenericEntry - * =========================================================================== - */ - -GenericEntry::GenericEntry(void) - : fFssep('\0'), - fFileType(0), - fAuxType(0), - fAccess(0), - fCreateWhen(kDateNone), - fModWhen(kDateNone), - fRecordKind(kRecordKindUnknown), - fFormatStr(L"Unknown"), - fDataForkLen(0), - fRsrcForkLen(0), - fCompressedLen(0), - fSourceFS(DiskImg::kFormatUnknown), - fHasDataFork(false), - fHasRsrcFork(false), - fHasDiskImage(false), - fHasComment(false), - fHasNonEmptyComment(false), - fDamaged(false), - fSuspicious(false), - fIndex(-1), - fpPrev(NULL), - fpNext(NULL) -{ -} - -GenericEntry::~GenericEntry(void) {} - -void GenericEntry::SetPathNameMOR(const char* path) -{ - ASSERT(path != NULL && strlen(path) > 0); - fPathNameMOR = path; - // nuke the derived fields - fFileName = L""; - fFileNameExtension = L""; - fDisplayName = L""; - - /* - * Generate the Unicode representation from the Mac OS Roman source. - * For now, we just treat the input as CP-1252. - * - * TODO(Unicode) - */ - fPathNameUNI = fPathNameMOR; - - /* - * Warning: to be 100% pedantically correct here, we should NOT do this - * if the fssep char is '_'. However, that may not have been set by - * the time we got here, so to do this "correctly" we'd need to delay - * the underscorage until the first GetPathName call. - */ - const Preferences* pPreferences = GET_PREFERENCES(); - if (pPreferences->GetPrefBool(kPrSpacesToUnder)) { - SpacesToUnderscores(&fPathNameMOR); - } -} - -const CString& GenericEntry::GetFileName(void) -{ - ASSERT(!fPathNameMOR.IsEmpty()); - if (fFileName.IsEmpty()) { - fFileName = PathName::FilenameOnly(fPathNameUNI, fFssep); - } - return fFileName; -} - -const CString& GenericEntry::GetFileNameExtension(void) -{ - ASSERT(!fPathNameMOR.IsEmpty()); - if (fFileNameExtension.IsEmpty()) { - fFileNameExtension = PathName::FindExtension(fPathNameUNI, fFssep); - } - return fFileNameExtension; -} - -const CStringA& GenericEntry::GetFileNameExtensionMOR(void) -{ - ASSERT(!fPathNameMOR.IsEmpty()); - if (fFileNameExtensionMOR.IsEmpty()) { - CString str = PathName::FindExtension(fPathNameUNI, fFssep); - // TODO(Unicode): either get the extension from the MOR filename, - // or convert this properly from Unicode to MOR (not CP-1252). - fFileNameExtensionMOR = str; - } - return fFileNameExtensionMOR; -} - -void GenericEntry::SetSubVolName(const WCHAR* name) -{ - fSubVolName = name; -} - -const CString& GenericEntry::GetDisplayName(void) const -{ - ASSERT(!fPathNameMOR.IsEmpty()); - if (!fDisplayName.IsEmpty()) { - return fDisplayName; - } - - if (!fSubVolName.IsEmpty()) { - fDisplayName = fSubVolName + (WCHAR) DiskFS::kDIFssep; - } - fDisplayName += Charset::ConvertMORToUNI(fPathNameMOR); - return fDisplayName; -} - -const WCHAR* GenericEntry::GetFileTypeString(void) const -{ - return PathProposal::FileTypeString(fFileType); -} - -/*static*/ void GenericEntry::SpacesToUnderscores(CStringA* pStr) -{ - pStr->Replace(' ', '_'); -} - -/*static*/ bool GenericEntry::CheckHighASCII(const uint8_t* buffer, - size_t count) -{ - /* - * (Pulled from NufxLib Funnel.c.) - * - * Check to see if this is a high-ASCII file. To qualify, EVERY - * character must have its high bit set, except for spaces (0x20, - * courtesy Glen Bredon's "Merlin") and nulls (0x00, because of random- - * access text files). - * - * The test for 0x00 is actually useless in many circumstances because the - * NULLs will cause the text file auto-detector to flunk the file. It will, - * however, allow the user to select "convert ALL files" and still have the - * stripping enabled. - */ - bool isHighASCII; - - ASSERT(buffer != NULL); - ASSERT(count != 0); - - isHighASCII = true; - while (count--) { - if ((*buffer & 0x80) == 0 && *buffer != 0x20 && *buffer != 0x00) { - LOGI("Flunking CheckHighASCII on 0x%02x", *buffer); - isHighASCII = false; - break; - } - - buffer++; - } - - return isHighASCII; -} - -/* - * (Pulled from NufxLib Funnel.c.) - * - * Table determining what's a binary character and what isn't. It would - * possibly be more compact to generate this from a simple description, - * but I'm hoping static/const data will end up in the code segment and - * save space on the heap. - * - * This corresponds to less-316's ISO-latin1 "8bcccbcc18b95.33b.". This - * may be too loose by itself; we may want to require that the lower-ASCII - * values appear in higher proportions than the upper-ASCII values. - * Otherwise we run the risk of converting a binary file with specific - * properties. (Note that "upper-ASCII" refers to umlauts and other - * accented characters, not DOS 3.3 "high ASCII".) - * - * The auto-detect mechanism will never be perfect though, so there's not - * much point in tweaking it to death. - */ -static const char gIsBinary[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, /* ^@-^O */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P-^_ */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* - / */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - ? */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* P - _ */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - DEL */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 */ -}; - -static const int kNuMaxUpperASCII = 1; /* max #of binary chars per 100 bytes */ -static const int kMinConvThreshold = 40; /* min of 40 chars for auto-detect */ -static const char kCharLF = '\n'; -static const char kCharCR = '\r'; - -/*static*/ GenericEntry::ConvertEOL GenericEntry::DetermineConversion( - const uint8_t* buffer, size_t count, - EOLType* pSourceType, ConvertHighASCII* pConvHA) -{ - /* - * We need to decide if we are looking at text data, and if so, what kind - * of line terminator is in use. - * - * If we don't have enough data to make a determination, don't mess with it. - * (Thought for the day: add a "bias" flag, based on the NuRecord fileType, - * that causes us to handle borderline or sub-min-threshold cases more - * reasonably. If it's of type TXT, it's probably text.) - * - * We try to figure out whether it's CR, LF, or CRLF, so that we can - * skip the CPU-intensive conversion process if it isn't necessary. - * - * We will also investigate enabling a "high-ASCII" stripper if requested. - * This is only enabled when EOL conversions are enabled. Set "*pConvHA" - * to on/off/auto before calling. If it's initially set to "off", no - * attempt to evaluate high ASCII will be made. If "on" or "auto", the - * buffer will be scanned, and if the input appears to be high ASCII then - * it will be stripped *before* the EOL determination is made. - */ - ConvertHighASCII wantConvHA = *pConvHA; - size_t bufCount, numBinary, numLF, numCR; - bool isHighASCII; - uint8_t val; - - *pSourceType = kEOLUnknown; - *pConvHA = kConvertHAOff; - - if (count < kMinConvThreshold) - return kConvertEOLOff; - - /* - * Check to see if the buffer is all high-ASCII characters. If it is, - * we want to strip characters before we test them below. - * - * If high ASCII conversion is disabled, assume that any high-ASCII - * characters are not meant to be line terminators, i.e. 0x8d != 0x0d. - */ - if (wantConvHA == kConvertHAOn || wantConvHA == kConvertHAAuto) { - isHighASCII = CheckHighASCII(buffer, count); - LOGI(" +++ Determined isHighASCII=%d", isHighASCII); - } else { - isHighASCII = false; - LOGI(" +++ Not even checking isHighASCII"); - } - - bufCount = count; - numBinary = numLF = numCR = 0; - while (bufCount--) { - val = *buffer++; - if (isHighASCII) - val &= 0x7f; - if (gIsBinary[val]) - numBinary++; - if (val == kCharLF) - numLF++; - if (val == kCharCR) - numCR++; - } - - /* if #found is > #allowed, it's a binary file */ - if (count < 100) { - /* use simplified check on files between kNuMinConvThreshold and 100 */ - if (numBinary > kNuMaxUpperASCII) - return kConvertEOLOff; - } else if (numBinary > (count / 100) * kNuMaxUpperASCII) - return kConvertEOLOff; - - /* - * If our "convert to" setting is the same as what we're converting - * from, we can turn off the converter and speed things up. - * - * These are simplistic, but this is intended as an optimization. We - * will blow it if the input has lots of CRs and LFs scattered about, - * and they just happen to be in equal amounts, but it's not clear - * to me that an automatic EOL conversion makes sense on that sort - * of file anyway. - * - * None of this applies if we also need to do a high-ASCII conversion, - * because we can't bypass the processing. - */ - if (isHighASCII) { - *pConvHA = kConvertHAOn; - } else { - if (numLF && !numCR) - *pSourceType = kEOLLF; - else if (!numLF && numCR) - *pSourceType = kEOLCR; - else if (numLF && numLF == numCR) - *pSourceType = kEOLCRLF; - else - *pSourceType = kEOLUnknown; - } - - return kConvertEOLOn; -} - -/* - * Output CRLF. - */ -static inline void PutEOL(FILE* fp) -{ - putc(kCharCR, fp); - putc(kCharLF, fp); -} - -/*static*/ int GenericEntry::WriteConvert(FILE* fp, const char* buf, size_t len, - ConvertEOL* pConv, ConvertHighASCII* pConvHA, bool* pLastCR) -{ - int err = 0; - - LOGD("+++ WriteConvert conv=%d convHA=%d", *pConv, *pConvHA); - - if (len == 0) { - LOGI("WriteConvert asked to write 0 bytes; returning"); - return err; - } - - /* if we're in "auto" mode, scan the input for EOL and high ASCII */ - if (*pConv == kConvertEOLAuto) { - EOLType sourceType; - *pConv = DetermineConversion((uint8_t*)buf, len, &sourceType, - pConvHA); - if (*pConv == kConvertEOLOn && sourceType == kEOLCRLF) { - LOGI(" Auto-detected text conversion from CRLF; disabling"); - *pConv = kConvertEOLOff; - } - LOGI(" Auto-detected EOL conv=%d ha=%d", *pConv, *pConvHA); - } else if (*pConvHA == kConvertHAAuto) { - if (*pConv == kConvertEOLOn) { - /* definitely converting EOL, test for high ASCII */ - if (CheckHighASCII((uint8_t*)buf, len)) - *pConvHA = kConvertHAOn; - else - *pConvHA = kConvertHAOff; - } else { - /* not converting EOL, don't convert high ASCII */ - *pConvHA = kConvertHAOff; - } - } - LOGD("+++ After auto, conv=%d convHA=%d", *pConv, *pConvHA); - ASSERT(*pConv == kConvertEOLOn || *pConv == kConvertEOLOff); - ASSERT(*pConvHA == kConvertHAOn || *pConvHA == kConvertHAOff); - - /* write the output */ - if (*pConv == kConvertEOLOff) { - if (fwrite(buf, len, 1, fp) != 1) { - err = errno; - LOGE("WriteConvert failed, err=%d", errno); - } - } else { - ASSERT(*pConv == kConvertEOLOn); - bool lastCR = *pLastCR; - uint8_t uch; - int mask; - - if (*pConvHA == kConvertHAOn) - mask = 0x7f; - else - mask = 0xff; - - while (len--) { - uch = (*buf) & mask; - - if (uch == kCharCR) { - PutEOL(fp); - lastCR = true; - } else if (uch == kCharLF) { - if (!lastCR) - PutEOL(fp); - lastCR = false; - } else { - putc(uch, fp); - lastCR = false; - } - buf++; - } - *pLastCR = lastCR; - } - - return err; -} - - -/* - * =========================================================================== - * GenericArchive - * =========================================================================== - */ - -void GenericArchive::AddEntry(GenericEntry* pEntry) -{ - if (fEntryHead == NULL) { - ASSERT(fEntryTail == NULL); - fEntryHead = pEntry; - fEntryTail = pEntry; - ASSERT(pEntry->GetPrev() == NULL); - ASSERT(pEntry->GetNext() == NULL); - } else { - ASSERT(fEntryTail != NULL); - ASSERT(pEntry->GetPrev() == NULL); - pEntry->SetPrev(fEntryTail); - ASSERT(fEntryTail->GetNext() == NULL); - fEntryTail->SetNext(pEntry); - fEntryTail = pEntry; - } - - fNumEntries++; - - //if (fEntryIndex != NULL) { - // LOGI("Resetting fEntryIndex"); - // delete [] fEntryIndex; - // fEntryIndex = NULL; - //} -} - -void GenericArchive::DeleteEntries(void) -{ - GenericEntry* pEntry; - GenericEntry* pNext; - - LOGI("Deleting %d archive entries", fNumEntries); - - pEntry = GetEntries(); - while (pEntry != NULL) { - pNext = pEntry->GetNext(); - delete pEntry; - pEntry = pNext; - } - - //delete [] fEntryIndex; - fNumEntries = 0; - fEntryHead = fEntryTail = NULL; -} - -#if 0 -/* - * Create an index for fast access. - */ -void -GenericArchive::CreateIndex(void) -{ - GenericEntry* pEntry; - int num; - - LOGI("Creating entry index (%d entries)", fNumEntries); - - ASSERT(fNumEntries != 0); - - fEntryIndex = new GenericEntry*[fNumEntries]; - if (fEntryIndex == NULL) - return; - - pEntry = GetEntries(); - num = 0; - while (pEntry != NULL) { - fEntryIndex[num] = pEntry; - pEntry = pEntry->GetNext(); - num++; - } -} -#endif - -/*static*/ CString GenericArchive::GenDerivedTempName(const WCHAR* filename) -{ - /* - * The key is to come up with the name of a temp file in the same directory - * (or at least on the same disk volume) so that the temp file can be - * renamed on top of the original. - * - * Windows _mktemp does appear to test for the existence of the file, which - * is good. It doesn't actually open the file, which creates a small window - * in which bad things could happen, but it should be okay. - */ - static const WCHAR kTmpTemplate[] = L"CPtmp_XXXXXX"; - CString mangle(filename); - int idx, len; - - ASSERT(filename != NULL); - - len = mangle.GetLength(); - ASSERT(len > 0); - idx = mangle.ReverseFind('\\'); - if (idx < 0) { - /* generally shouldn't happen -- we're using full paths */ - return kTmpTemplate; - } else { - mangle.Delete(idx+1, len-(idx+1)); /* delete out to the end */ - mangle += kTmpTemplate; - } - LOGD("GenDerived: passed '%ls' returned '%ls'", filename, (LPCWSTR) mangle); - - return mangle; -} - -/*static*/ int GenericArchive::ComparePaths(const CString& name1, char fssep1, - const CString& name2, char fssep2) -{ - const WCHAR* cp1 = name1; - const WCHAR* cp2 = name2; - - while (*cp1 != '\0' && *cp2 != '\0') { - if (*cp1 == fssep1) { - if (*cp2 != fssep2) { - /* one fssep, one not, no match */ - if (*cp1 == *cp2) - return 1; - else - return *cp1 - *cp2; - } else { - /* both are fssep, it's a match even if ASCII is different */ - } - } else if (*cp2 == fssep2) { - /* one fssep, one not */ - if (*cp1 == *cp2) - return -1; - else - return *cp1 - *cp2; - } else if (tolower(*cp1) != tolower(*cp2)) { - /* mismatch */ - return tolower(*cp1) - tolower(*cp2); - } - - cp1++; - cp2++; - } - - return *cp1 - *cp2; -} - - -/* - * =========================================================================== - * GenericArchive -- "add files" stuff - * =========================================================================== - */ - -/* - * Much of this was adapted from NuLib2. - */ - -/*static*/ void GenericArchive::UNIXTimeToDateTime(const time_t* pWhen, - NuDateTime* pDateTime) -{ - struct tm* ptm; - - ASSERT(pWhen != NULL); - ASSERT(pDateTime != NULL); - - ptm = localtime(pWhen); - if (ptm == NULL) { - ASSERT(*pWhen == kDateNone || *pWhen == kDateInvalid); - memset(pDateTime, 0, sizeof(*pDateTime)); - return; - } - pDateTime->second = ptm->tm_sec; - pDateTime->minute = ptm->tm_min; - pDateTime->hour = ptm->tm_hour; - pDateTime->day = ptm->tm_mday -1; - pDateTime->month = ptm->tm_mon; - pDateTime->year = ptm->tm_year; - pDateTime->extra = 0; - pDateTime->weekDay = ptm->tm_wday +1; -} - -/* - * Directory structure and functions, based on zDIR in Info-Zip sources. - */ -typedef struct Win32dirent { - char d_attr; - WCHAR d_name[MAX_PATH]; - int d_first; - HANDLE d_hFindFile; -} Win32dirent; - -static const WCHAR kWildMatchAll[] = L"*.*"; - -Win32dirent* GenericArchive::OpenDir(const WCHAR* name) -{ - Win32dirent* dir = NULL; - WCHAR* tmpStr = NULL; - WCHAR* cp; - WIN32_FIND_DATA fnd; - - dir = (Win32dirent*) malloc(sizeof(*dir)); - tmpStr = (WCHAR*) malloc((wcslen(name) + 2 + wcslen(kWildMatchAll)) * sizeof(WCHAR)); - if (dir == NULL || tmpStr == NULL) - goto failed; - - wcscpy(tmpStr, name); - cp = tmpStr + wcslen(tmpStr); - - /* don't end in a colon (e.g. "C:") */ - if ((cp - tmpStr) > 0 && wcsrchr(tmpStr, ':') == (cp - 1)) - *cp++ = '.'; - /* must end in a slash */ - if ((cp - tmpStr) > 0 && - wcsrchr(tmpStr, PathProposal::kLocalFssep) != (cp - 1)) - *cp++ = PathProposal::kLocalFssep; - - wcscpy(cp, kWildMatchAll); - - dir->d_hFindFile = FindFirstFile(tmpStr, &fnd); - if (dir->d_hFindFile == INVALID_HANDLE_VALUE) - goto failed; - - wcscpy(dir->d_name, fnd.cFileName); - dir->d_attr = (unsigned char) fnd.dwFileAttributes; - dir->d_first = 1; - -bail: - free(tmpStr); - return dir; - -failed: - free(dir); - dir = NULL; - goto bail; -} - -Win32dirent* GenericArchive::ReadDir(Win32dirent* dir) -{ - if (dir->d_first) - dir->d_first = 0; - else { - WIN32_FIND_DATA fnd; - - if (!FindNextFile(dir->d_hFindFile, &fnd)) - return NULL; - wcscpy(dir->d_name, fnd.cFileName); - dir->d_attr = (unsigned char) fnd.dwFileAttributes; - } - - return dir; -} - -void GenericArchive::CloseDir(Win32dirent* dir) -{ - if (dir == NULL) - return; - - FindClose(dir->d_hFindFile); - free(dir); -} - -NuError GenericArchive::Win32AddDirectory(const AddFilesDialog* pAddOpts, - const WCHAR* dirName, CString* pErrMsg) -{ - NuError err = kNuErrNone; - Win32dirent* dirp = NULL; - Win32dirent* entry; - WCHAR nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ - char fssep; - int len; - - ASSERT(pAddOpts != NULL); - ASSERT(dirName != NULL); - - LOGI("+++ DESCEND: '%ls'", dirName); - - dirp = OpenDir(dirName); - if (dirp == NULL) { - if (errno == ENOTDIR) - err = kNuErrNotDir; - else - err = errno ? (NuError)errno : kNuErrOpenDir; - - pErrMsg->Format(L"Failed on '%ls': %hs.", dirName, NuStrError(err)); - goto bail; - } - - fssep = PathProposal::kLocalFssep; - - /* could use readdir_r, but we don't care about reentrancy here */ - while ((entry = ReadDir(dirp)) != NULL) { - /* skip the dotsies */ - if (wcscmp(entry->d_name, L".") == 0 || - wcscmp(entry->d_name, L"..") == 0) - { - continue; - } - - len = wcslen(dirName); - if (len + wcslen(entry->d_name) +2 > MAX_PATH) { - err = kNuErrInternal; - LOGE("ERROR: Filename exceeds %d bytes: %ls%c%ls", - MAX_PATH, dirName, fssep, entry->d_name); - goto bail; - } - - /* form the new name, inserting an fssep if needed */ - wcscpy(nbuf, dirName); - if (dirName[len-1] != fssep) - nbuf[len++] = fssep; - wcscpy(nbuf+len, entry->d_name); - - err = Win32AddFile(pAddOpts, nbuf, pErrMsg); - if (err != kNuErrNone) - goto bail; - } - -bail: - if (dirp != NULL) - (void)CloseDir(dirp); - return err; -} - -NuError GenericArchive::Win32AddFile(const AddFilesDialog* pAddOpts, - const WCHAR* pathname, CString* pErrMsg) -{ - NuError err = kNuErrNone; - bool exists, isDir, isReadable; - LocalFileDetails details; - struct _stat sb; - - ASSERT(pAddOpts != NULL); - ASSERT(pathname != NULL); - - PathName checkPath(pathname); - int ierr = checkPath.CheckFileStatus(&sb, &exists, &isReadable, &isDir); - if (ierr != 0) { - err = kNuErrGeneric; - pErrMsg->Format(L"Unexpected error while examining '%ls': %hs.", - pathname, NuStrError((NuError) ierr)); - goto bail; - } - - if (!exists) { - err = kNuErrFileNotFound; - pErrMsg->Format(L"Couldn't find '%ls'", pathname); - goto bail; - } - if (!isReadable) { - err = kNuErrFileNotReadable; - pErrMsg->Format(L"File '%ls' isn't readable.", pathname); - goto bail; - } - if (isDir) { - if (pAddOpts->fIncludeSubfolders) - err = Win32AddDirectory(pAddOpts, pathname, pErrMsg); - goto bail; - } - - /* - * We've found a file that we want to add. We need to decide what - * filetype and auxtype it has, and whether or not it's actually the - * resource fork of another file. - */ - LOGD("+++ ADD '%ls'", pathname); - - /* - * Fill out the "details" structure. - */ - err = details.SetFields(pAddOpts, pathname, &sb); - if (err != kNuErrNone) - goto bail; - - assert(wcscmp(pathname, details.GetLocalPathName()) == 0); - err = DoAddFile(pAddOpts, &details); - if (err == kNuErrSkipped) // ignore "skipped" result - err = kNuErrNone; - if (err != kNuErrNone) - goto bail; - -bail: - if (err != kNuErrNone && pErrMsg->IsEmpty()) { - pErrMsg->Format(L"Unable to add file '%ls': %hs.", - pathname, NuStrError(err)); - } - return err; -} - -NuError GenericArchive::AddFile(const AddFilesDialog* pAddOpts, - const WCHAR* pathname, CString* pErrMsg) -{ - *pErrMsg = L""; - return Win32AddFile(pAddOpts, pathname, pErrMsg); -} - - -/* - * =========================================================================== - * GenericArchive::FileDetails - * =========================================================================== - */ - -GenericArchive::LocalFileDetails::LocalFileDetails(void) - : fEntryKind(kFileKindUnknown), - fFileSysFmt(DiskImg::kFormatUnknown), - fFssep('\0'), - fFileType(0), - fExtraType(0), - fAccess(0), - fStorageType(0) -{ - memset(&fCreateWhen, 0, sizeof(fCreateWhen)); - memset(&fModWhen, 0, sizeof(fModWhen)); - memset(&fArchiveWhen, 0, sizeof(fArchiveWhen)); - - // set these for debugging - memset(&fNuFileDetails, 0xcc, sizeof(fNuFileDetails)); - memset(&fCreateParms, 0xcc, sizeof(&fCreateParms)); - fStoragePathNameMOR = "!INIT!"; -} - -NuError GenericArchive::LocalFileDetails::SetFields(const AddFilesDialog* pAddOpts, - const WCHAR* pathname, struct _stat* psb) -{ - time_t now; - - ASSERT(pAddOpts != NULL); - ASSERT(pathname != NULL); - - /* get adjusted filename, along with any preserved type info */ - PathProposal pathProp; - pathProp.Init(pathname); - pathProp.LocalToArchive(pAddOpts); - - /* set up the local and archived pathnames */ - fLocalPathName = pathname; - fStrippedLocalPathName = L""; - if (!pAddOpts->fStoragePrefix.IsEmpty()) { - fStrippedLocalPathName += pAddOpts->fStoragePrefix; - fStrippedLocalPathName += pathProp.fStoredFssep; - } - fStrippedLocalPathName += pathProp.fStoredPathName; - GenerateStoragePathName(); - - fFileSysFmt = DiskImg::kFormatUnknown; - fStorageType = kNuStorageUnknown; /* let NufxLib et.al. worry about it */ - if (psb->st_mode & S_IWUSR) - fAccess = kNuAccessUnlocked; - else - fAccess = kNuAccessLocked; - fEntryKind = LocalFileDetails::kFileKindDataFork; - fFssep = pathProp.fStoredFssep; - fFileType = pathProp.fFileType; - fExtraType = pathProp.fAuxType; - -#if 0 - /* if this is a disk image, fill in disk-specific fields */ - if (NState_GetModAddAsDisk(pState)) { - if ((psb->st_size & 0x1ff) != 0) { - /* reject anything whose size isn't a multiple of 512 bytes */ - printf("NOT storing odd-sized (%ld) file as disk image: %ls\n", - (long)psb->st_size, livePathStr); - } else { - /* set fields; note the "preserve" stuff can override this */ - pDetails->threadID = kNuThreadIDDiskImage; - pDetails->storageType = 512; - pDetails->extraType = psb->st_size / 512; - } - } -#endif - - now = time(NULL); - UNIXTimeToDateTime(&now, &fArchiveWhen); - UNIXTimeToDateTime(&psb->st_mtime, &fModWhen); - UNIXTimeToDateTime(&psb->st_ctime, &fCreateWhen); - - switch (pathProp.fThreadKind) { - case GenericEntry::kDataThread: - //pDetails->threadID = kNuThreadIDDataFork; - fEntryKind = LocalFileDetails::kFileKindDataFork; - break; - case GenericEntry::kRsrcThread: - //pDetails->threadID = kNuThreadIDRsrcFork; - fEntryKind = LocalFileDetails::kFileKindRsrcFork; - break; - case GenericEntry::kDiskImageThread: - //pDetails->threadID = kNuThreadIDDiskImage; - fEntryKind = LocalFileDetails::kFileKindDiskImage; - break; - default: - ASSERT(false); - // was initialized to default earlier - break; - } - -/*bail:*/ - return kNuErrNone; -} - -const NuFileDetails& GenericArchive::LocalFileDetails::GetNuFileDetails() -{ - //details.threadID = threadID; - switch (fEntryKind) { - case kFileKindDataFork: - fNuFileDetails.threadID = kNuThreadIDDataFork; - break; - case kFileKindBothForks: // not exactly supported, doesn't really matter - case kFileKindRsrcFork: - fNuFileDetails.threadID = kNuThreadIDRsrcFork; - break; - case kFileKindDiskImage: - fNuFileDetails.threadID = kNuThreadIDDiskImage; - break; - case kFileKindDirectory: - default: - LOGW("Invalid entryKind (%d) for NuFileDetails conversion", fEntryKind); - ASSERT(false); - fNuFileDetails.threadID = 0; // that makes it an old-style comment?! - break; - } - - fNuFileDetails.origName = (LPCWSTR) fLocalPathName; - fNuFileDetails.storageNameMOR = (LPCSTR) fStoragePathNameMOR; - fNuFileDetails.fileSysInfo = fFssep; - fNuFileDetails.access = fAccess; - fNuFileDetails.fileType = fFileType; - fNuFileDetails.extraType = fExtraType; - fNuFileDetails.storageType = fStorageType; - fNuFileDetails.createWhen = fCreateWhen; - fNuFileDetails.modWhen = fModWhen; - fNuFileDetails.archiveWhen = fArchiveWhen; - - switch (fFileSysFmt) { - case DiskImg::kFormatProDOS: - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - case DiskImg::kFormatPascal: - case DiskImg::kFormatMacHFS: - case DiskImg::kFormatMacMFS: - case DiskImg::kFormatLisa: - case DiskImg::kFormatCPM: - //kFormatCharFST - case DiskImg::kFormatMSDOS: - //kFormatHighSierra - case DiskImg::kFormatISO9660: - /* these map directly */ - fNuFileDetails.fileSysID = (enum NuFileSysID) fFileSysFmt; - break; - - case DiskImg::kFormatRDOS33: - case DiskImg::kFormatRDOS32: - case DiskImg::kFormatRDOS3: - /* these look like DOS33, e.g. text is high-ASCII */ - fNuFileDetails.fileSysID = kNuFileSysDOS33; - break; - - default: - fNuFileDetails.fileSysID = kNuFileSysUnknown; - break; - } - - return fNuFileDetails; -} - -const DiskFS::CreateParms& GenericArchive::LocalFileDetails::GetCreateParms() -{ - fCreateParms.pathName = (LPCSTR) fStoragePathNameMOR; - fCreateParms.fssep = fFssep; - fCreateParms.storageType = fStorageType; - fCreateParms.fileType = fFileType; - fCreateParms.auxType = fExtraType; - fCreateParms.access = fAccess; - fCreateParms.createWhen = NufxArchive::DateTimeToSeconds(&fCreateWhen); - fCreateParms.modWhen = NufxArchive::DateTimeToSeconds(&fModWhen); - return fCreateParms; -} - -void GenericArchive::LocalFileDetails::GenerateStoragePathName() -{ - // TODO(Unicode): generate MOR name from Unicode, instead of just - // doing a generic CP-1252 conversion. We need to do this on both - // sides though, so until we can extract MOR->Unicode we don't - // want to add Unicode->MOR. For this all to work well we need NufxLib - // and DiskImgLib to be able to handle UTF-16 filenames. - fStoragePathNameMOR = fStrippedLocalPathName; -} - - -/*static*/ void GenericArchive::LocalFileDetails::CopyFields(LocalFileDetails* pDst, - const LocalFileDetails* pSrc) -{ - // don't copy fNuFileDetails, fCreateParms - pDst->fEntryKind = pSrc->fEntryKind; - pDst->fLocalPathName = pSrc->fLocalPathName; - pDst->fStrippedLocalPathName = pSrc->fStrippedLocalPathName; - pDst->fStoragePathNameMOR = pSrc->fStoragePathNameMOR; - pDst->fFileSysFmt = pSrc->fFileSysFmt; - pDst->fFssep = pSrc->fFssep; - pDst->fAccess = pSrc->fAccess; - pDst->fFileType = pSrc->fFileType; - pDst->fExtraType = pSrc->fExtraType; - pDst->fStorageType = pSrc->fStorageType; - pDst->fCreateWhen = pSrc->fCreateWhen; - pDst->fModWhen = pSrc->fModWhen; - pDst->fArchiveWhen = pSrc->fArchiveWhen; -} - - -/* - * =========================================================================== - * SelectionSet - * =========================================================================== - */ - -void SelectionSet::CreateFromSelection(ContentList* pContentList, int threadMask) -{ - /* - * This grabs the items in the order in which they appear in the display - * (at least under Win2K), which is a good thing. It appears that, if you - * just grab indices 0..N, you will get them in order. - */ - LOGD("CreateFromSelection (threadMask=0x%02x)", threadMask); - - POSITION posn; - posn = pContentList->GetFirstSelectedItemPosition(); - ASSERT(posn != NULL); - if (posn == NULL) - return; - while (posn != NULL) { - int num = pContentList->GetNextSelectedItem(/*ref*/ posn); - GenericEntry* pEntry = (GenericEntry*) pContentList->GetItemData(num); - - AddToSet(pEntry, threadMask); - } -} - -void SelectionSet::CreateFromAll(ContentList* pContentList, int threadMask) -{ - LOGD("CreateFromAll (threadMask=0x%02x)", threadMask); - - int count = pContentList->GetItemCount(); - for (int idx = 0; idx < count; idx++) { - GenericEntry* pEntry = (GenericEntry*) pContentList->GetItemData(idx); - - AddToSet(pEntry, threadMask); - } -} - -void SelectionSet::AddToSet(GenericEntry* pEntry, int threadMask) -{ - SelectionEntry* pSelEntry; - - LOGV(" Sel '%ls'", (LPCWSTR) pEntry->GetPathNameUNI()); - - if (!(threadMask & GenericEntry::kAllowVolumeDir) && - pEntry->GetRecordKind() == GenericEntry::kRecordKindVolumeDir) - { - /* only include volume dir if specifically requested */ - //LOGI(" Excluding volume dir '%ls' from set", pEntry->GetPathName()); - return; - } - - if (!(threadMask & GenericEntry::kAllowDirectory) && - pEntry->GetRecordKind() == GenericEntry::kRecordKindDirectory) - { - /* only include directories if specifically requested */ - //LOGI(" Excluding folder '%ls' from set", pEntry->GetPathName()); - return; - } - - if (!(threadMask & GenericEntry::kAllowDamaged) && pEntry->GetDamaged()) - { - /* only include "damaged" files if specifically requested */ - return; - } - - bool doAdd = false; - - if (threadMask & GenericEntry::kAnyThread) - doAdd = true; - - if ((threadMask & GenericEntry::kCommentThread) && pEntry->GetHasComment()) - doAdd = true; - if ((threadMask & GenericEntry::kDataThread) && pEntry->GetHasDataFork()) - doAdd = true; - if ((threadMask & GenericEntry::kRsrcThread) && pEntry->GetHasRsrcFork()) - doAdd = true; - if ((threadMask & GenericEntry::kDiskImageThread) && pEntry->GetHasDiskImage()) - doAdd = true; - - if (doAdd) { - pSelEntry = new SelectionEntry(pEntry); - AddEntry(pSelEntry); - } -} - -void SelectionSet::AddEntry(SelectionEntry* pEntry) -{ - if (fEntryHead == NULL) { - ASSERT(fEntryTail == NULL); - fEntryHead = pEntry; - fEntryTail = pEntry; - ASSERT(pEntry->GetPrev() == NULL); - ASSERT(pEntry->GetNext() == NULL); - } else { - ASSERT(fEntryTail != NULL); - ASSERT(pEntry->GetPrev() == NULL); - pEntry->SetPrev(fEntryTail); - ASSERT(fEntryTail->GetNext() == NULL); - fEntryTail->SetNext(pEntry); - fEntryTail = pEntry; - } - - fNumEntries++; -} - -void SelectionSet::DeleteEntries(void) -{ - SelectionEntry* pEntry; - SelectionEntry* pNext; - - LOGD("Deleting selection entries"); - - pEntry = GetEntries(); - while (pEntry != NULL) { - pNext = pEntry->GetNext(); - delete pEntry; - pEntry = pNext; - } -} - -int SelectionSet::CountMatchingPrefix(const WCHAR* prefix) -{ - SelectionEntry* pEntry; - int count = 0; - int len = wcslen(prefix); - ASSERT(len > 0); - - pEntry = GetEntries(); - while (pEntry != NULL) { - GenericEntry* pGeneric = pEntry->GetEntry(); - - if (wcsnicmp(prefix, pGeneric->GetDisplayName(), len) == 0) - count++; - pEntry = pEntry->GetNext(); - } - - return count; -} - -void SelectionSet::Dump(void) -{ - const SelectionEntry* pEntry; - - LOGI("SelectionSet: %d entries", fNumEntries); - - pEntry = fEntryHead; - while (pEntry != NULL) { - LOGI(" : name='%ls'", (LPCWSTR) pEntry->GetEntry()->GetPathNameUNI()); - pEntry = pEntry->GetNext(); - } -} diff --git a/ciderpress/app/GenericArchive.h b/ciderpress/app/GenericArchive.h deleted file mode 100644 index d37fb6b..0000000 --- a/ciderpress/app/GenericArchive.h +++ /dev/null @@ -1,1026 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Generic Apple II archive handling. In the beginning we only handled - * NuFX archives, and the code continues to reflect that heritage. - * - * These are abstract base classes. - */ -#ifndef APP_GENERICARCHIVE_H -#define APP_GENERICARCHIVE_H - -#include "Preferences.h" -#include "../util/UtilLib.h" -#include "../diskimg/DiskImg.h" -#include "../nufxlib/NufxLib.h" -#include "../reformat/Reformat.h" -#include -#include - -// this shouldn't be in a header file, but in here it's probably okay -using namespace DiskImgLib; - -class ActionProgressDialog; -class AddFilesDialog; -class RecompressOptionsDialog; -class SelectionSet; -struct Win32dirent; -class GenericArchive; - -const int kFileTypeTXT = 0x04; -const int kFileTypeBIN = 0x06; -const int kFileTypeSRC = 0xb0; -const int kFileTypeINT = 0xfa; -const int kFileTypeBAS = 0xfc; - -/* - * Set of data allowed in file property "set file info" calls. - */ -struct FileProps { - uint32_t fileType; - uint32_t auxType; - uint32_t access; - time_t createWhen; - time_t modWhen; -}; - -/* - * Options for converting between file archives and disk archives. - */ -class XferFileOptions { -public: - XferFileOptions() : - fTarget(NULL), fPreserveEmptyFolders(false), fpTargetFS(NULL) - {} - ~XferFileOptions() {} - - /* where the stuff is going */ - GenericArchive* fTarget; - - /* these really only have meaning when converting disk to file */ - //bool fConvDOSText; - //bool fConvPascalText; - bool fPreserveEmptyFolders; - - /* only useful when converting files to a disk image */ - //CString fStoragePrefix; - DiskImgLib::DiskFS* fpTargetFS; -}; - - -/* - * Generic description of an Apple II file. - * - * Everything returned by the basic "get" calls for display in the ContentList - * must be held in local storage (i.e. not just pointers into DiskFS data). - * Otherwise, we run the risk of doing some DiskFS updates and having weird - * things happen in the ContentList. - */ -class GenericEntry { -public: - GenericEntry(); - virtual ~GenericEntry(); - - /* kinds of files found in archives */ - enum RecordKind { - kRecordKindUnknown = 0, - kRecordKindDisk, - kRecordKindFile, - kRecordKindForkedFile, - kRecordKindDirectory, - kRecordKindVolumeDir, - }; - /* - * Threads we will view or extract (threadMask). This is no longer used - * for viewing files, but still plays a role when extracting. - */ - enum { - // create one entry for each matching thread - kDataThread = 0x01, - kRsrcThread = 0x02, - kDiskImageThread = 0x04, - kCommentThread = 0x08, - - // grab any of the above threads - kAnyThread = 0x10, - - // set this if we allow matches on directory entries - kAllowDirectory = 0x20, - - // and volume directory entries - kAllowVolumeDir = 0x40, - - // set to include "damaged" files - kAllowDamaged = 0x80, - }; - /* EOL conversion mode for threads being extracted */ - enum ConvertEOL { - kConvertUnknown = 0, kConvertEOLOff, kConvertEOLOn, kConvertEOLAuto - }; - enum EOLType { - kEOLUnknown = 0, kEOLCR, kEOLLF, kEOLCRLF - }; - /* high ASCII conversion mode for threads being extracted */ - enum ConvertHighASCII { - kConvertHAUnknown = 0, kConvertHAOff, kConvertHAOn, kConvertHAAuto - }; - - /* ProDOS access flags, used for all filesystems */ - enum { - kAccessRead = 0x01, - kAccessWrite = 0x02, - kAccessInvisible = 0x04, - kAccessBackup = 0x20, - kAccessRename = 0x40, - kAccessDelete = 0x80 - }; - - /* - * Features supported by underlying archive. Primarily of interest - * to EditPropsDialog, which needs to know what sort of attributes can - * be altered in the file type and access flags. - */ - enum Feature { - kFeatureCanChangeType, - kFeaturePascalTypes, - kFeatureDOSTypes, - kFeatureHFSTypes, - kFeatureHasFullAccess, - kFeatureHasSimpleAccess, // mutually exclusive with FullAccess - kFeatureHasInvisibleFlag, - }; - - /* - * Extract data from an archive (NuFX, disk image, etc). - * - * If "*ppText" is non-NULL, the data will be read into the pointed-to buffer - * so long as it's shorter than *pLength bytes. The value in "*pLength" - * will be set to the actual length used. - * - * If "*ppText" is NULL, the uncompressed data will be placed into a buffer - * allocated with "new[]". - * - * Returns IDOK on success, IDCANCEL if the operation was cancelled by the - * user, and -1 value on failure. On failure, "*pErrMsg" holds an error - * message. - * - * "which" is an anonymous GenericArchive enum (e.g. "kDataThread"). - */ - virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const = 0; - - /* - * Extract data from a thread or disk file to a Windows file. Since we're - * not copying to a buffer we can't assume that we're able to hold the - * entire file in memory all at once. - * - * Returns IDOK on success, IDCANCEL if the operation was cancelled by the - * user, and -1 value on failure. On failure, "*pMsg" holds an - * error message. - */ - virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const = 0; - - // This helps us retain the ContentList selection across a Reload(). Only - // necessary for read-write archives, since those are the only ones that - // ever need to be reloaded. Value must be nonzero to be used. - virtual long GetSelectionSerial() const = 0; - - /* what operations are possible with this entry? */ - virtual bool GetFeatureFlag(Feature feature) const = 0; - - long GetIndex() const { return fIndex; } - void SetIndex(long idx) { fIndex = idx; } - - /* - * Set the pathname. This comes from a file archive or disk image, - * so it's always in Mac OS Roman format. - * - * Calling this will invalidate any strings previously returned by - * GetPathName*(), GetFileName*(), and GetDisplayName(). - */ - void SetPathNameMOR(const char* pathNameMOR); - - const CStringA& GetPathNameMOR() const { return fPathNameMOR; } - const CString& GetPathNameUNI() const { return fPathNameUNI; } - const CString& GetFileName(); - const CString& GetFileNameExtension(); // returns e.g. ".SHK" - const CStringA& GetFileNameExtensionMOR(); - /* - * Returns the "display" name. This is a combination of the sub-volume - * name and the path name. This string is intended for display only, - * and may include characters that aren't legal on the filesystem. - */ - const CString& GetDisplayName() const; - - void SetSubVolName(const WCHAR* name); - const CString& GetSubVolName() const { return fSubVolName; } - - char GetFssep() const { return fFssep; } - void SetFssep(char fssep) { fFssep = fssep; } - uint32_t GetFileType() const { return fFileType; } - void SetFileType(uint32_t type) { fFileType = type; } - uint32_t GetAuxType() const { return fAuxType; } - void SetAuxType(uint32_t type) { fAuxType = type; } - uint32_t GetAccess() const { return fAccess; } - void SetAccess(uint32_t access) { fAccess = access; } - time_t GetCreateWhen() const { return fCreateWhen; } - void SetCreateWhen(time_t when) { fCreateWhen = when; } - time_t GetModWhen() const { return fModWhen; } - void SetModWhen(time_t when) { fModWhen = when; } - RecordKind GetRecordKind() const { return fRecordKind; } - void SetRecordKind(RecordKind recordKind) { fRecordKind = recordKind; } - const WCHAR* GetFormatStr() const { return fFormatStr; } - void SetFormatStr(const WCHAR* str) { fFormatStr = str; } // arg not copied, must be static! - LONGLONG GetCompressedLen() const { return fCompressedLen; } - void SetCompressedLen(LONGLONG len) { fCompressedLen = len; } - LONGLONG GetUncompressedLen() const { - return fDataForkLen + fRsrcForkLen; - } - LONGLONG GetDataForkLen() const { return fDataForkLen; } - void SetDataForkLen(LONGLONG len) { fDataForkLen = len; } - LONGLONG GetRsrcForkLen() const { return fRsrcForkLen; } - void SetRsrcForkLen(LONGLONG len) { fRsrcForkLen = len; } - - DiskImg::FSFormat GetSourceFS() const { return fSourceFS; } - void SetSourceFS(DiskImg::FSFormat fmt) { fSourceFS = fmt; } - - bool GetHasDataFork() const { return fHasDataFork; } - void SetHasDataFork(bool val) { fHasDataFork = val; } - bool GetHasRsrcFork() const { return fHasRsrcFork; } - void SetHasRsrcFork(bool val) { fHasRsrcFork = val; } - bool GetHasDiskImage() const { return fHasDiskImage; } - void SetHasDiskImage(bool val) { fHasDiskImage = val; } - bool GetHasComment() const { return fHasComment; } - void SetHasComment(bool val) { fHasComment = val; } - bool GetHasNonEmptyComment() const { return fHasNonEmptyComment; } - void SetHasNonEmptyComment(bool val) { fHasNonEmptyComment = val; } - - bool GetDamaged() const { return fDamaged; } - void SetDamaged(bool val) { fDamaged = val; } - bool GetSuspicious() const { return fSuspicious; } - void SetSuspicious(bool val) { fSuspicious = val; } - - GenericEntry* GetPrev() const { return fpPrev; } - void SetPrev(GenericEntry* pEntry) { fpPrev = pEntry; } - GenericEntry* GetNext() const { return fpNext; } - void SetNext(GenericEntry* pEntry) { fpNext = pEntry; } - - /* - * Get a string for this entry's filetype. - */ - const WCHAR* GetFileTypeString() const; - - /* - * Check to see if this is a high-ASCII file. - */ - static bool CheckHighASCII(const uint8_t* buffer, size_t count); - - /* - * Decide, based on the contents of the buffer, whether we should do an - * EOL conversion on the data. - * - * Returns kConvEOLOff or kConvEOLOn. - */ - static ConvertEOL DetermineConversion(const uint8_t* buffer, - size_t count, EOLType* pSourceType, ConvertHighASCII* pConvHA); - - /* - * Write data to a file, possibly converting EOL markers to Windows CRLF - * and stripping high ASCII. - * - * If "*pConv" is kConvertEOLAuto, this will try to auto-detect whether - * the input is a text file or not by scanning the input buffer. - * - * Ditto for "*pConvHA". - * - * "fp" is the output file, "buf" is the input, "len" is the buffer length. - * "*pLastCR" should initially be "false", and carried across invocations. - * - * Returns 0 on success, or an errno value on error. - */ - static int GenericEntry::WriteConvert(FILE* fp, const char* buf, - size_t len, ConvertEOL* pConv, ConvertHighASCII* pConvHA, - bool* pLastCR); - -private: - /* - * Convert spaces to underscores, modifying the string. - */ - static void SpacesToUnderscores(CStringA* pStr); - -private: - /* - * This represents a file from an archive or disk image, so the Mac OS - * Roman representation is the "true" version. The Unicode version - * is how we will use it on Windows (e.g. for file extraction), so - * it will be a CP-1252 conversion until the various libraries - * support UTF-16 filenames. - * - * The "display name" is only used for display, and should do a proper - * MOR to Unicode conversion so the file name looks right. - */ - - CStringA fPathNameMOR; // original path name, Mac OS Roman chars - CString fPathNameUNI; // Unicode conversion - CString fFileName; // filename component of fPathNameUNI - CString fFileNameExtension; // filename extension from fPathNameUNI - CStringA fFileNameExtensionMOR; - char fFssep; - CString fSubVolName; // sub-volume prefix, or NULL if none - mutable CString fDisplayName; // combination of sub-vol and path - uint32_t fFileType; - uint32_t fAuxType; - uint32_t fAccess; - time_t fCreateWhen; - time_t fModWhen; - RecordKind fRecordKind; // forked file, disk image, ?? - const WCHAR* fFormatStr; // static str; compression or fs format - LONGLONG fDataForkLen; // also for disk images - LONGLONG fRsrcForkLen; // set to 0 when nonexistent - LONGLONG fCompressedLen; // data/disk + rsrc - - DiskImg::FSFormat fSourceFS; // if DOS3.3, text files have funky format - - bool fHasDataFork; - bool fHasRsrcFork; - bool fHasDiskImage; - bool fHasComment; - bool fHasNonEmptyComment; // set if fHasComment and it isn't empty - - bool fDamaged; // if set, don't try to open file - bool fSuspicious; // if set, file *might* be damaged - - long fIndex; // serial index, for sorting "unsorted" view - GenericEntry* fpPrev; - GenericEntry* fpNext; -}; - -/* - * Generic representation of a collection of Apple II files. - * - * This raises the "reload" flag whenever its data is reloaded. Any code that - * keeps pointers to stuff (e.g. local copies of pointers to GenericEntry - * objects) needs to check the "reload" flag before dereferencing them. - */ -class GenericArchive { -public: - GenericArchive() { - fPathName = NULL; - fNumEntries = 0; - fEntryHead = fEntryTail = NULL; - fReloadFlag = true; - //fEntryIndex = NULL; - } - virtual ~GenericArchive() { - //LOGI("Deleting GenericArchive"); - DeleteEntries(); - delete fPathName; - } - - virtual GenericEntry* GetEntries() const { - return fEntryHead; - } - virtual long GetNumEntries() const { - return fNumEntries; - } - - enum OpenResult { - kResultUnknown = 0, - kResultSuccess, // open succeeded - kResultFailure, // open failed - kResultCancel, // open was cancelled by user - kResultFileArchive, // found a file archive rather than disk image - }; - - // Open an archive and do fun things with the innards. - virtual OpenResult Open(const WCHAR* filename, bool readOnly, - CString* pErrMsg) = 0; - // Create a new archive with the specified name. - virtual CString New(const WCHAR* filename, const void* options) = 0; - // Flush any unwritten data to disk - virtual CString Flush() = 0; - // Force a re-read from the underlying storage. - virtual CString Reload() = 0; - // Do we allow modification? - virtual bool IsReadOnly() const = 0; - // Does the underlying storage have un-flushed modifications? - virtual bool IsModified() const = 0; - - virtual bool GetReloadFlag() { return fReloadFlag; } - virtual void ClearReloadFlag() { fReloadFlag = false; } - - // One of these for every sub-class. - enum ArchiveKind { - kArchiveUnknown = 0, - kArchiveNuFX, - kArchiveBNY, - kArchiveACU, - kArchiveAppleSingle, - kArchiveDiskImage, - }; - - // Returns the kind of archive this is (disk image, NuFX, BNY, etc). - virtual ArchiveKind GetArchiveKind() = 0; - - // Get a nice description for the title bar. - virtual CString GetDescription() const = 0; - - // Do a bulk add. - virtual bool BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) = 0; - - // Add a single disk to the archive. - virtual bool AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) = 0; - - // Create a subdirectory with name newName in pParentEntry. - virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) = 0; - - // Test a set of files. - virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0; - - // Delete a set of files. - virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0; - - // Rename a set of files. - virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0; - - // Verify that a name is suitable. Called by RenameEntryDialog and - // CreateSubdirDialog. - // - // Tests for context-specific syntax and checks for duplicates. - // - // Returns an empty string on success, or an error message on failure. - virtual CString TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, char newFssep) const = 0; - - // Rename a volume (or sub-volume) - virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) = 0; - virtual CString TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const = 0; - - // Recompress a set of files. - virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) = 0; - - // return result from XferSelection() - enum XferStatus { - kXferOK = 0, kXferFailed = 1, kXferCancelled = 2, kXferOutOfSpace = 3 - }; - - // Transfer selected files out of this archive and into another. - virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) = 0; - - // Extract a comment from the archive, converting line terminators to CRLF. - virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry, - CString* pStr) = 0; - - // Set a comment on an entry. - virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry, - const CString& str) = 0; - - // Delete the comment from the entry. - virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) = 0; - - // Set ProDOS file properties (file type, aux type, access flags). - virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) = 0; - - // Preferences have changed, update library state as needed. Also called - // the first time though. - virtual void PreferencesChanged() = 0; - - // Determine an archive's capabilities. This is specific to the object - // instance, so this must not be made a static function. - enum Capability { - kCapUnknown = 0, - - kCapCanTest, // NuFX, BNY - kCapCanRenameFullPath, // NuFX, BNY - kCapCanRecompress, // NuFX, BNY - kCapCanEditComment, // NuFX - kCapCanAddDisk, // NuFX - kCapCanConvEOLOnAdd, // Disk - kCapCanCreateSubdir, // Disk - kCapCanRenameVolume, // Disk - }; - virtual long GetCapability(Capability cap) = 0; - - // Get the pathname of the file we opened. - const WCHAR* GetPathName() const { return fPathName; } - - /* - * Generate a temp name from a file name. - */ - static CString GenDerivedTempName(const WCHAR* filename); - - /* - * Do a strcasecmp-like comparison, taking equivalent fssep chars into - * account. - * - * The tricky part is with files like "foo:bar" ':' -- "foo:bar" '/'. The - * names appear to match, but the fssep chars are different, so they don't. - * If we just return (char1 - char2), though, we'll be returning 0 because - * the ASCII values match even if the character *meanings* don't. - * - * This assumes that the fssep char is not affected by tolower(). - * - * [This may not sort correctly...haven't verified that I'm returning the - * right thing for ascending ASCII sort.] - */ - static int ComparePaths(const CString& name1, char fssep1, - const CString& name2, char fssep2); - - /* - * Add a new entry to the end of the list. - */ - void AddEntry(GenericEntry* pEntry); - - /* - * This class holds details about a file that we're adding from local disk. - */ - class LocalFileDetails { - public: - LocalFileDetails(); - virtual ~LocalFileDetails() {} - - /* - * Set the various fields, based on the pathname and characteristics - * of the file. - */ - NuError SetFields(const AddFilesDialog* pAddOpts, const WCHAR* pathname, - struct _stat* psb); - - /* - * What kind of file this is. Files being added to NuFX from Windows - * can't be "BothForks" because the forks are stored in - * separate files. However, files being transferred from a NuFX - * archive, a disk image, or in from the clipboard can be both. - * - * (NOTE: this gets embedded into clipboard data. If you change - * these values, update the version number in Clipboard.cpp.) - */ - enum FileKind { - kFileKindUnknown = 0, - kFileKindDataFork, - kFileKindRsrcFork, - kFileKindDiskImage, - kFileKindBothForks, - kFileKindDirectory, - }; - - /* - * Provide operator= and copy constructor. - */ - LocalFileDetails& operator=(const LocalFileDetails& src) { - if (&src != this) - CopyFields(this, &src); - return *this; - } - LocalFileDetails(const LocalFileDetails& src) { - CopyFields(this, &src); - } - - /* - * Returns a reference to a NuFileDetails structure with the contents - * of the LocalFileDetails. - * - * The returned structure may not be used after the LocalFileDetails - * is modified or released. - */ - const NuFileDetails& GetNuFileDetails(); - - /* - * Returns a reference to a NuFileDetails structure with the contents - * of the LocalFileDetails. - * - * The returned structure may not be used after the LocalFileDetails - * is modified or released. - */ - const DiskFS::CreateParms& GetCreateParms(); - - /* - * Returns the "local" pathname, i.e. the name of a Windows file. - */ - const CString& GetLocalPathName() const { - return fLocalPathName; - } - - void SetLocalPathName(const CString& newName) { - fLocalPathName = newName; - } - - /* - * This is the "local" pathname with any file type preservation - * strings removed. - */ - const CString& GetStrippedLocalPathName() const { - return fStrippedLocalPathName; - } - - /* - * Sets the "stripped" local path name, and updates the MOR version. - * Does not alter the full local path name. - */ - void SetStrippedLocalPathName(const CString& newName) { - fStrippedLocalPathName = newName; - GenerateStoragePathName(); - } - - /* - * Returns a copy of the stripped local pathname that has been - * converted to Mac OS Roman. - * - * The returned string will be invalid if SetStrippedLocalPathName - * is subsequently called. - */ - const CStringA& GetStoragePathNameMOR() const { - return fStoragePathNameMOR; - } - - FileKind GetEntryKind() const { return fEntryKind; } - void SetEntryKind(FileKind kind) { fEntryKind = kind; } - - DiskImg::FSFormat GetFileSysFmt() const { return fFileSysFmt; } - void SetFileSysFmt(DiskImg::FSFormat fmt) { fFileSysFmt = fmt; } - - char GetFssep() const { return fFssep; } - void SetFssep(char fssep) { fFssep = fssep; } - - uint32_t GetFileType() const { return fFileType; } - void SetFileType(uint32_t type) { fFileType = type; } - - uint32_t GetExtraType() const { return fExtraType; } - void SetExtraType(uint32_t type) { fExtraType = type; } - - uint32_t GetAccess() const { return fAccess; } - void SetAccess(uint32_t access) { fAccess = access; } - - uint16_t GetStorageType() const { return fStorageType; } - void SetStorageType(uint16_t type) { fStorageType = type; } - - const NuDateTime& GetArchiveWhen() const { return fArchiveWhen; } - void SetArchiveWhen(const NuDateTime& when) { fArchiveWhen = when; } - - const NuDateTime& GetModWhen() const { return fModWhen; } - void SetModWhen(const NuDateTime& when) { fModWhen = when; } - - const NuDateTime& GetCreateWhen() const { return fCreateWhen; } - void SetCreateWhen(const NuDateTime& when) { fCreateWhen = when; } - - private: - /* - * Copy the contents of our object to a new object, field by field, - * so the CStrings copy correctly. - * - * Useful for operator= and copy construction. - */ - static void CopyFields(LocalFileDetails* pDst, - const LocalFileDetails* pSrc); - - /* - * Generates fStoragePathNameMOR from fStrippedLocalPathName. - */ - void GenerateStoragePathName(); - - /* - * These are the structs that the libraries want, so we provide calls - * that populate them and return a reference. These are "hairy" - * structures, so we have to place limitations on their lifetime. - * - * Ideally these would either be proper objects with destructors, - * so we could create them and not worry about who will free up the - * hairy bits, or we would omit the hairy bits from the structure and - * pass them separately. - */ - NuFileDetails fNuFileDetails; - DiskFS::CreateParms fCreateParms; - - /* - * What does this entry represent? - */ - FileKind fEntryKind; - - /* - * Full pathname of the Windows file. - */ - CString fLocalPathName; // was origName - - /* - * "Stripped" pathname. This is the full path with any of our - * added bits removed (e.g. file type & fork identifiers). - * - * This is generated by PathProposal::LocalToArchive(). - */ - CString fStrippedLocalPathName; // was storageName - - /* - * The storage name, generated by converting strippedLocalPathName - * from Unicode to Mac OS Roman. This is what will be passed to - * DiskImg and NufxLib as the name of the file to create in the - * archive. For DiskImg there's an additional "normalization" pass - * make the filename suitable for use on the filesystem; that name - * is stored in a FileAddData object, not here. - */ - CStringA fStoragePathNameMOR; - - DiskImg::FSFormat fFileSysFmt; // only set for xfers? - uint8_t fFssep; - uint32_t fFileType; - uint32_t fExtraType; - uint32_t fAccess; - uint16_t fStorageType; // "Unknown" or disk block size - NuDateTime fCreateWhen; - NuDateTime fModWhen; - NuDateTime fArchiveWhen; - }; - - // Prepare for file transfers. - virtual void XferPrepare(const XferFileOptions* pXferOpts) = 0; - - // Transfer files, one at a time, into this archive from another. Called - // from XferSelection and clipboard "paste". - // - // "dataLen" and "rsrcLen" will be -1 if the corresponding fork doesn't exist. - // Returns 0 on success, nonzero on failure. - // - // On success, *pDataBuf and *pRsrcBuf are freed and set to NULL. (It's - // necessary for the interface to work this way because the NufxArchive - // version just tucks the pointers into NufxLib structures.) - virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) = 0; - - // Abort progress. Not all subclasses are capable of "undo". - virtual void XferAbort(CWnd* pMsgWnd) = 0; - - // Transfer is finished. - virtual void XferFinish(CWnd* pMsgWnd) = 0; - - /* - * Convert from time in seconds to Apple IIgs DateTime format. - */ - static void UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime); - - /* - * Reads a 16-bit big-endian value from a buffer. Does not guard - * against buffer overrun. - */ - static uint16_t Get16BE(const uint8_t* ptr) { - return ptr[1] | (ptr[0] << 8); - } - - /* - * Reads a 32-bit big-endian value from a buffer. Does not guard - * against buffer overrun. - */ - static uint32_t Get32BE(const uint8_t* ptr) { - return ptr[3] | (ptr[2] << 8) | (ptr[1] << 16) | (ptr[0] << 24); - } - - /* - * Reads a 16-bit little-endian value from a buffer. Does not guard - * against buffer overrun. - */ - static uint16_t Get16LE(const uint8_t* ptr) { - return ptr[0] | (ptr[1] << 8); - } - - /* - * Reads a 32-bit little-endian value from a buffer. Does not guard - * against buffer overrun. - */ - static uint32_t Get32LE(const uint8_t* ptr) { - return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); - } - -protected: - /* - * Delete the "entries" list. - */ - virtual void DeleteEntries(); - - void ReplaceFssep(WCHAR* str, char oldc, char newc, char newSubst); - - /* - * Prepare a directory for reading. - * - * Allocates a Win32dirent struct that must be freed by the caller. - */ - Win32dirent* OpenDir(const WCHAR* name); - - /* - * Get an entry from an open directory. - * - * Returns a NULL pointer after the last entry has been read. - */ - Win32dirent* ReadDir(Win32dirent* dir); - - /* - * Close a directory. - */ - void CloseDir(Win32dirent* dir); - - /* - * Win32 recursive directory descent. Scan the contents of a directory. - * If a subdirectory is found, follow it; otherwise, call Win32AddFile to - * add the file. - */ - NuError Win32AddDirectory(const AddFilesDialog* pAddOpts, - const WCHAR* dirName, CString* pErrMsg); - - /* - * Add a file to the list we're adding to the archive. If it's a directory, - * and the recursive descent feature is enabled, call Win32AddDirectory to - * add the contents of the dir. - * - * Returns with an error if the file doesn't exist or isn't readable. - */ - NuError Win32AddFile(const AddFilesDialog* pAddOpts, - const WCHAR* pathname, CString* pErrMsg); - - /* - * External entry point; just calls the system-specific version. - */ - NuError AddFile(const AddFilesDialog* pAddOpts, const WCHAR* pathname, - CString* pErrMsg); - - /* - * Each implementation must provide this. It's called from the generic - * AddFile function with the high-level add options, a partial pathname, - * and a FileDetails structure filled in using Win32 calls. - * - * One call to AddFile can result in multiple calls to DoAddFile if - * the subject of the AddFile call is a directory (and fIncludeSubdirs - * is set). - * - * DoAddFile is not called for subdirectories. The underlying code must - * create directories as needed. - * - * In some cases (such as renaming a file as it is being added) the - * information in "*pDetails" may be modified. - */ - virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) = 0; - - void SetPathName(const WCHAR* pathName) { - free(fPathName); - if (pathName != NULL) { - fPathName = _wcsdup(pathName); - } else { - fPathName = NULL; - } - } - - bool fReloadFlag; // set after Reload called - -private: - //virtual void CreateIndex(); - - //CString fNewPathHolder; - //CString fOrigPathHolder; - - WCHAR* fPathName; - long fNumEntries; - GenericEntry* fEntryHead; - GenericEntry* fEntryTail; - //GenericEntry** fEntryIndex; -}; - -/* - * One entry in a SelectionSet. - */ -class SelectionEntry { -public: - SelectionEntry(GenericEntry* pEntry) { - fpEntry = pEntry; - //fThreadKind = threadKind; - //fFilter = filter; - //fReformatName = ""; - fpPrev = fpNext = NULL; - } - ~SelectionEntry() {} - - int Reformat(ReformatHolder* pHolder); - - GenericEntry* GetEntry() const { return fpEntry; } - //int GetThreadKind() const { return fThreadKind; } - //int GetFilter() const { return fFilter; } - //const char* GetReformatName() const { return fReformatName; } - - SelectionEntry* GetPrev() const { return fpPrev; } - void SetPrev(SelectionEntry* pPrev) { fpPrev = pPrev; } - SelectionEntry* GetNext() const { return fpNext; } - void SetNext(SelectionEntry* pNext) { fpNext = pNext; } - -private: - GenericEntry* fpEntry; - //int fThreadKind; // data, rsrc, etc (threadMask) - //int fFilter; // fAllowedFilters, really - //const char* fReformatName; // name of formatting actually applied - - SelectionEntry* fpPrev; - SelectionEntry* fpNext; -}; - -class ContentList; - -/* - * A set of selected files. - * - * Each entry represents one item that can be displayed, such as a data - * fork, resource fork, or comment thread. Thus, a single file may have - * multiple entries in the set. - */ -class SelectionSet { -public: - SelectionSet() { - fNumEntries = 0; - fEntryHead = fEntryTail = fIterCurrent = NULL; - } - ~SelectionSet() { - DeleteEntries(); - } - - // create the set from the selected members of a ContentList - void CreateFromSelection(ContentList* pContentList, int threadMask); - // create the set from all members of a ContentList - void CreateFromAll(ContentList* pContentList, int threadMask); - - // get the head of the list - SelectionEntry* GetEntries() const { return fEntryHead; } - - void IterReset() { - fIterCurrent = NULL; - } - // move to the next or previous entry as part of iterating - SelectionEntry* IterPrev() { - if (fIterCurrent == NULL) - fIterCurrent = fEntryTail; - else - fIterCurrent = fIterCurrent->GetPrev(); - return fIterCurrent; - } - SelectionEntry* IterNext() { - if (fIterCurrent == NULL) - fIterCurrent = fEntryHead; - else - fIterCurrent = fIterCurrent->GetNext(); - return fIterCurrent; - } - SelectionEntry* IterCurrent() { - return fIterCurrent; - } - bool IterHasPrev() const { - if (fIterCurrent == NULL) - return fEntryTail != NULL; - else - return (fIterCurrent->GetPrev() != NULL); - } - bool IterHasNext() const { - if (fIterCurrent == NULL) - return fEntryHead != NULL; - else - return (fIterCurrent->GetNext() != NULL); - } - - int GetNumEntries() const { return fNumEntries; } - - // count the #of entries whose display name matches "prefix" - int CountMatchingPrefix(const WCHAR* prefix); - - // debug dump the contents of the selection set - void Dump(); - -private: - /* - * Add a GenericEntry to the set, but only if we can find a thread that - * matches the flags in "threadMask". - */ - void AddToSet(GenericEntry* pEntry, int threadMask); - - /* - * Add a new entry to the end of the list. - */ - void AddEntry(SelectionEntry* pEntry); - - /* - * Delete the "entries" list. - */ - void DeleteEntries(); - - int fNumEntries; - SelectionEntry* fIterCurrent; - - SelectionEntry* fEntryHead; - SelectionEntry* fEntryTail; -}; - -#endif /*APP_GENERICARCHIVE_H*/ diff --git a/ciderpress/app/Graphics/ChooseFolder.bmp b/ciderpress/app/Graphics/ChooseFolder.bmp deleted file mode 100644 index bbdaf69..0000000 Binary files a/ciderpress/app/Graphics/ChooseFolder.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/CiderPress.ico b/ciderpress/app/Graphics/CiderPress.ico deleted file mode 100644 index 1c5c03c..0000000 Binary files a/ciderpress/app/Graphics/CiderPress.ico and /dev/null differ diff --git a/ciderpress/app/Graphics/FileViewer.ico b/ciderpress/app/Graphics/FileViewer.ico deleted file mode 100644 index b7fcaea..0000000 Binary files a/ciderpress/app/Graphics/FileViewer.ico and /dev/null differ diff --git a/ciderpress/app/Graphics/NewFolder.bmp b/ciderpress/app/Graphics/NewFolder.bmp deleted file mode 100644 index 458e185..0000000 Binary files a/ciderpress/app/Graphics/NewFolder.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/binary2.ico b/ciderpress/app/Graphics/binary2.ico deleted file mode 100644 index bdc1a7f..0000000 Binary files a/ciderpress/app/Graphics/binary2.ico and /dev/null differ diff --git a/ciderpress/app/Graphics/diskimage.ico b/ciderpress/app/Graphics/diskimage.ico deleted file mode 100644 index 0f30d09..0000000 Binary files a/ciderpress/app/Graphics/diskimage.ico and /dev/null differ diff --git a/ciderpress/app/Graphics/fslogo.bmp b/ciderpress/app/Graphics/fslogo.bmp deleted file mode 100644 index df4b562..0000000 Binary files a/ciderpress/app/Graphics/fslogo.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/hdrbar.bmp b/ciderpress/app/Graphics/hdrbar.bmp deleted file mode 100644 index 73cc043..0000000 Binary files a/ciderpress/app/Graphics/hdrbar.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/list-pics.bmp b/ciderpress/app/Graphics/list-pics.bmp deleted file mode 100644 index 9e50a9d..0000000 Binary files a/ciderpress/app/Graphics/list-pics.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/nufx.ico b/ciderpress/app/Graphics/nufx.ico deleted file mode 100644 index 0bb7514..0000000 Binary files a/ciderpress/app/Graphics/nufx.ico and /dev/null differ diff --git a/ciderpress/app/Graphics/toolbar1.bmp b/ciderpress/app/Graphics/toolbar1.bmp deleted file mode 100644 index a0118cf..0000000 Binary files a/ciderpress/app/Graphics/toolbar1.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/tree_pics.bmp b/ciderpress/app/Graphics/tree_pics.bmp deleted file mode 100644 index 2551bf7..0000000 Binary files a/ciderpress/app/Graphics/tree_pics.bmp and /dev/null differ diff --git a/ciderpress/app/Graphics/vol_pics.bmp b/ciderpress/app/Graphics/vol_pics.bmp deleted file mode 100644 index e762eb2..0000000 Binary files a/ciderpress/app/Graphics/vol_pics.bmp and /dev/null differ diff --git a/ciderpress/app/Help/CiderPress.chm b/ciderpress/app/Help/CiderPress.chm deleted file mode 100644 index a37d8c1..0000000 Binary files a/ciderpress/app/Help/CiderPress.chm and /dev/null differ diff --git a/ciderpress/app/Help/CiderPress.hhc b/ciderpress/app/Help/CiderPress.hhc deleted file mode 100644 index f6bce89..0000000 --- a/ciderpress/app/Help/CiderPress.hhc +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - -
    -
  • - - -
      -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    -
  • - - -
      -
    • - - - -
    • - - -
        -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      -
    • - - -
        -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      -
    • - - -
        -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      • - - - -
      -
    -
  • - - -
      -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    • - - - -
    -
- diff --git a/ciderpress/app/Help/CiderPress.hhk b/ciderpress/app/Help/CiderPress.hhk deleted file mode 100644 index adaa5b3..0000000 --- a/ciderpress/app/Help/CiderPress.hhk +++ /dev/null @@ -1,564 +0,0 @@ - - - - - - - - -
    -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - - - - - - - -
  • - - - - - - - - - - -
  • - - - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - - - -
  • - - - -
  • - - - - - - - - - - - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - - - -
  • - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
    • - - - -
    -
  • - - - - - - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - - - - -
  • - - - - - - - - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
  • - - - -
- diff --git a/ciderpress/app/Help/CiderPress.hhp b/ciderpress/app/Help/CiderPress.hhp deleted file mode 100644 index 599515a..0000000 --- a/ciderpress/app/Help/CiderPress.hhp +++ /dev/null @@ -1,161 +0,0 @@ -; CiderPress Help -; Copyright (C) 2014 by CiderPress authors - -[OPTIONS] -Compatibility=1.1 or later -Compiled file=CiderPress.chm -Contents file=CiderPress.hhc -Default Window=main -Default topic=html\t10.htm -Display compile progress=No -Full-text search=Yes -Index file=CiderPress.hhk -Language=0x409 English (United States) -Title=CiderPress Help - -[WINDOWS] -main="CiderPress Help","CiderPress.hhc","CiderPress.hhk","html\t10.htm",,,,,,0x40520,,0x200e,[150,50,750,550],,,,,,,0 - - -[FILES] -html\t10.htm -html\t13.htm -html\t18.htm -html\t19.htm -html\t20.htm -html\t21.htm -html\t22.htm -html\t23.htm -html\t24.htm -html\t25.htm -html\t28.htm -html\t29.htm -html\t38.htm -html\t39.htm -html\t41.htm -html\t42.htm -html\t43.htm -html\t44.htm -html\t45.htm -html\t46.htm -html\t47.htm -html\t48.htm -html\t49.htm -html\t50.htm -html\t51.htm -html\t52.htm -html\t53.htm -html\t54.htm -html\t55.htm -html\t56.htm -html\t57.htm -html\t58.htm -html\t59.htm -html\t60.htm -html\t61.htm -html\t62.htm -html\t63.htm -html\t64.htm -html\t65.htm -html\t66.htm -html\t67.htm -html\t68.htm -html\t69.htm -html\t109.htm -html\t111.htm -html\t112.htm -html\t187.htm -html\t201.htm -html\t203.htm -html\t215.htm -html\t216.htm -html\t233.htm -html\t241.htm -html\t244.htm -html\t245.htm -html\t247.htm -html\t257.htm -html\t258.htm -html\t259.htm -html\t262.htm -html\t263.htm -html\t268.htm -html\t272.htm -html\t273.htm -html\t277.htm -html\t284.htm - -[MAP] -#define html\t10 10 -#define html\t109 109 -#define html\t111 111 -#define html\t112 112 -#define html\t13 13 -#define html\t18 18 -#define html\t187 187 -#define html\t19 19 -#define html\t20 20 -#define html\t201 201 -#define html\t203 203 -#define html\t21 21 -#define html\t215 215 -#define html\t216 216 -#define html\t22 22 -#define html\t23 23 -#define html\t233 233 -#define html\t24 24 -#define html\t241 241 -#define html\t244 244 -#define html\t245 245 -#define html\t247 247 -#define html\t25 25 -#define html\t257 257 -#define html\t258 258 -#define html\t259 259 -#define html\t262 262 -#define html\t263 263 -#define html\t268 268 -#define html\t272 272 -#define html\t273 273 -#define html\t277 277 -#define html\t28 28 -#define html\t284 284 -#define html\t29 29 -#define html\t38 38 -#define html\t39 39 -#define html\t41 41 -#define html\t42 42 -#define html\t43 43 -#define html\t44 44 -#define html\t45 45 -#define html\t46 46 -#define html\t47 47 -#define html\t48 48 -#define html\t49 49 -#define html\t50 50 -#define html\t51 51 -#define html\t52 52 -#define html\t53 53 -#define html\t54 54 -#define html\t55 55 -#define html\t56 56 -#define html\t57 57 -#define html\t58 58 -#define html\t59 59 -#define html\t60 60 -#define html\t61 61 -#define html\t62 62 -#define html\t63 63 -#define html\t64 64 -#define html\t65 65 -#define html\t66 66 -#define html\t67 67 -#define html\t68 68 -#define html\t69 69 - -[TEXT POPUPS] -PopUp.txt -PopUpIds.h - -[INFOTYPES] - diff --git a/ciderpress/app/Help/PopUp.txt b/ciderpress/app/Help/PopUp.txt deleted file mode 100644 index 185edea..0000000 --- a/ciderpress/app/Help/PopUp.txt +++ /dev/null @@ -1,682 +0,0 @@ -.topic IDH_IDOK -Click this to accept values and continue. - -.topic IDH_IDCANCEL -Click this to go back without doing anything. - -.topic IDH_IDHELP -Click this for help on this screen. - -.topic IDH_COL_PATHNAME -The pathname column may not be hidden. - -.topic IDH_COL_TYPE -Un-check to hide the "type" column. - -.topic IDH_COL_AUXTYPE -Un-check to hide the "aux type" column. - -.topic IDH_COL_MODDATE -Un-check to hide the "mod date" column. - -.topic IDH_COL_FORMAT -Un-check to hide the "format" column. - -.topic IDH_COL_SIZE -Un-check to hide the "size" column. - -.topic IDH_COL_RATIO -Un-check to hide the "ratio" column. - -.topic IDH_COL_PACKED -Un-check to hide the "packed" column. - -.topic IDH_COL_ACCESS -Un-check to hide the "access" column. - -.topic IDH_COL_DEFAULTS -Press this to reset the columns to default sizes. - -.topic IDH_DEFC_UNCOMPRESSED -Do not compress files. - -.topic IDH_DEFC_SQUEEZE -The "SQueeze" format was popularized under CP/M by the "sq" and "usq" commands, which created ".QQ" files. Commonly used as part of the Binary II application "BLU" to create .BQY files. Generally inferior to LZW, it was rarely used once ShrinkIt was released. Supported in NuFX archives by all versions of ShrinkIt, but due to program bugs only GS/ShrinkIt can actually unpack it. - -.topic IDH_DEFC_LZW1 -ShrinkIt's "Dynamic LZW/1" compression, part of the original ShrinkIt. A combination of RLE and 12-bit LZW. - -.topic IDH_DEFC_LZW2 -ShrinkIt's "Dynamic LZW/2" compression, first introduced in GS/ShrinkIt. A combination of RLE and 12-bit LZW. Slightly more efficient than LZW/1, this algorithm is well-supported on the Apple II. - -.topic IDH_DEFC_LZC12 -This creates the same output as UNIX "compress", one of the early LZW compressors, restricted to 12-bit codes. First introduced in the original NuLib utility, this format is supported by NuLib, NuLib2, and GS/ShrinkIt. - -.topic IDH_DEFC_LZC16 -Like LZC-12, but with 16-bit codes. Performs better than 12-bit LZW, but the improvement is only noticeable on larger files. - -.topic IDH_DEFC_DEFLATE -The "deflate" algorithm from zlib is used in ZIP archives, gzip ".gz" files, PNG graphics, and is defined by an Internet RFC. It is currently one of the most common ways to compress data. Its compression performance is considerably better than LZW, but support on the Apple II is limited. Its use was first defined as a NuFX extension in NuLib2 v1.1. - -.topic IDH_DEFC_BZIP2 -The "bzip2" program uses the Burrows-Wheeler Transform (BWT) compression method. It's slower and more memory-intensive than "deflate", but it does exceptionally well on large sets of text files or source code. Support on the Apple II is nonexistent. Its use was first defined as a NuFX extension in NuLib2 v1.1. - -.topic IDH_TOPIC1024 -If enabled, end-of-line characters in text files will be converted when viewing in "raw" mode. - -.topic IDH_PVIEW_NOWRAP_TEXT -This determines whether lines of text wrap when they reach the right edge of the file viewer, or run off the edge and are viewed with a scroll bar. - -.topic IDH_PVIEW_BOLD_HEXDUMP -If enabled, some columns in the hex dump display will be highlighted for easier viewing. - -.topic IDH_PVIEW_BOLD_BASIC -If enabled, Applesoft and Integer BASIC programs will be converted to Rich Text Format with colored highlights. - -.topic IDH_PVIEW_DISASM_ONEBYTEBRKCOP -The IIgs monitor disassembles BRK and COP instructions as two bytes, which can make some disassemblies harder to read. When this option is enabled, BRK and COP are shown as single-byte operations. - -.topic IDH_PVIEW_HIRES_BW -If enabled, hi-res graphics are converted to black & white images by default. - -.topic IDH_PVIEW_DHR_CONV_COMBO -Selects the preferred conversion algorithm for double-hi-res graphics. - -.topic IDH_PVIEW_MOUSETEXT_TO_ASCII -Converts MouseText to ASCII instead of Unicode symbols. - -.topic IDH_PVIEW_HITEXT -Enables the text converter, which strips "high ASCII" text and converts end-of-line characters. - -.topic IDH_PVIEW_PASCALTEXT -Enables the Pascal text (PTX) converter. - -.topic IDH_PVIEW_APPLESOFT -Enables the Applesoft BASIC converter. - -.topic IDH_PVIEW_INTEGER -Enables the Integer BASIC converter. - -.topic IDH_PVIEW_HIRES -Enables the hi-res graphics converter. - -.topic IDH_PVIEW_DHR -Enables the double-hi-res graphics converter. - -.topic IDH_PVIEW_SHR -Enables the super-hi-res graphics converter. - -.topic IDH_PVIEW_AWP -Enables the AppleWorks Word Processor document converter. - -.topic IDH_PVIEW_PRODOSFOLDER -Display ProDOS folders in a format similar to the ProDOS 8 BASIC.System 80-column "catalog" command. (Folders are never extracted, so this only affects the way folders are viewed.) - -.topic IDH_PVIEW_RESOURCES -View and extract resource forks by breaking them down into individual resources, which are displayed as hex dumps. - -.topic IDH_PVIEW_RELAX_GFX -Relaxes strict type-checking requirements for graphics files. Useful for hi-res and double-hi-res images, which often are stored with the generic "BIN" type. - -.topic IDH_PVIEW_ADB -Enables the AppleWorks Database document converter. - -.topic IDH_PVIEW_SCASSEM -Enables conversion of assembly source files (S-C Assembler, LISA, Merlin) to text. - -.topic IDH_PVIEW_ASP -Enables the AppleWorks Spreadsheet document converter. - -.topic IDH_PVIEW_MACPAINT -Enables conversion of MacPaint 'PNTG' graphics files. These are monochrome 576x720 images from the original MacPaint program. The newer 'PICT' format is not supported. - -.topic IDH_PVIEW_PASCALCODE -Enables conversion of UCSD Pascal code files to a partially analyzed hex dump format. - -.topic IDH_PVIEW_CPMTEXT -Enables the CP/M text converter. Only applies to files that appear to contain text and reside on CP/M disks. - -.topic IDH_PVIEW_GWP -Enables IIgs word processor file conversion, including AWGS and Teach. - -.topic IDH_PVIEW_DISASM -Enables code disassembly, including //e and IIgs monitor output. - -.topic IDH_PVIEW_PRINTSHOP -Enable conversion of Print Shop and Print Shop GS clip art. - -.topic IDH_PVIEW_TEXT8 -Convert 8-bit word processor documents, such as Magic Window. - -.topic IDH_PVIEW_SIZE_EDIT -Set the size of the largest file that the file viewer will open, in 1K increments. - -.topic IDH_PVIEW_SIZE_SPIN -Set the size of the largest file that the file viewer will open, in 1K increments. - -.topic IDH_DISKEDIT_DOREAD -Read the block or sector specified. - -.topic IDH_DISKEDIT_DOWRITE -Click here to write the current block or sector. This is disabled for disks open in read-only mode. - -.topic IDH_DISKEDIT_TRACK -Enter a track/sector or block number. - -.topic IDH_DISKEDIT_OPENFILE -Open a file. This option is only available when the disk's filesystem is recognized. - -.topic IDH_DISKEDIT_EDIT -The contents of the block or sector are displayed here. If you are following a file and reach a sparse block, "SPARSE" will be displayed instead. - -.topic IDH_DISKEDIT_PREV -When following a file, move to the previous block or sector. - -.topic IDH_DISKEDIT_NEXT -When following a file, move to the next block or sector. - -.topic IDH_DISKEDIT_DONE -Closes the window. - -.topic IDH_DISKEDIT_HEX -Display and enter track/sector or block numbers in hexadecimal instead of decimal. - -.topic IDH_DISKEDIT_SUBVOLUME -Open a sub-volume. This option is not available if the disk image doesn't have any sub-volumes. - -.topic IDH_TOPIC1082 -Click for help on this screen. - -.topic IDH_TOPIC1089 -This determines whether the disk is accessed as 256-byte sectors or 512-byte blocks. - -.topic IDH_DECONF_FSFORMAT -The filesystem used on the disk image, i.e. how the disk was formatted on the Apple II. - -.topic IDH_DECONF_SECTORORDER -The order in which the sectors were written into the image file, defined in terms of the operating system used to write them. - -.topic IDH_DECONF_PHYSICAL -The physical format of the image, usually sectors or nibbles. This setting may not be changed. - -.topic IDH_DECONF_FILEFORMAT -The image file format. This setting may not be altered. - -.topic IDH_DECONF_SOURCE -The disk image file being opened. - -.topic IDH_FVIEW_EDITBOX -The contents of the current file. - -.topic IDH_SELECTED_COUNT -The number of entries you have selected. - -.topic IDH_TOPIC1103 -If enabled, data forks of selected files will be displayed. - -.topic IDH_TOPIC1105 -If enabled, comments attached to selected files will be displayed. - -.topic IDH_DECONF_HELP -Click this for help on this screen. - -.topic IDH_SUBV_LIST -Choose the sub-volume you want to open from the list. - -.topic IDH_DEFILE_FILENAME -Enter the name of the file to open exactly as it appears in the CiderPress file listing. This means including all path names, separated by ':', and converting ':' to '_' when it's used in a filename. - -If the file is in a DOS 3.3 sub-volume (e.g. it starts with "_DOS001:"), you will need to open the sub-volume first and then open the file from there. - -.topic IDH_DEFILE_RSRC -If checked, the resource fork of the file will be opened instead of the data fork. - -.topic IDH_PREF_TEMP_FOLDER -Enter the name of the folder to use for temporary files. - -.topic IDH_CHOOSEDIR_TREE -This is the "folder tree". Click on '+' to expand folders to see their contents, '-' to collapse them. - -.topic IDH_CHOOSEDIR_PATHEDIT -To jump directly to a folder, type the full pathname here and then click "Expand Tree". - -.topic IDH_CHOOSEDIR_EXPAND_TREE -Click this to expand the tree to show the folder name typed in the box on the left. - -.topic IDH_CHOOSEDIR_PATH -The full pathname of the folder currently selected in the tree. - -.topic IDH_CHOOSEDIR_NEW_FOLDER -Click this to open the "New Folder" dialog. Folders will be created in the folder currently selected in the tree. - -.topic IDH_PREF_CHOOSE_TEMP_FOLDER -Click here to choose the temporary folder from a list. - -.topic IDH_FVIEW_FONT -Change the font used when displaying text. - -.topic IDH_FVIEW_NEXT -Go to the next selected file. This button is disabled when there are no more files. - -.topic IDH_FVIEW_PREV -Go to the previous selected file. This button is disabled when there are no earlier files. - -.topic IDH_NEWFOLDER_CURDIR -The currently selected folder. The new folder will be created inside this folder. - -.topic IDH_NEWFOLDER_NAME -Enter the filename of the folder you want to create. - -.topic IDH_EXT_PATH -The full pathname of the folder to extract to. - -.topic IDH_EXT_CONVEOLTEXT -Select this to automatically determine which files are text files, and convert them. - -.topic IDH_EXT_CONVEOLALL -Select to treat all files as text files. (Archived disk images are NOT included.) - -.topic IDH_EXT_STRIP_FOLDER -If enabled, folder names will be removed before the files are extracted, effectively dumping all files into the extraction folder. - -.topic IDH_EXT_OVERWRITE_EXIST -If enabled, existing files will be overwritten without confirmation. - -.topic IDH_EXT_SELECTED -Click this to extract only the files selected in the file list. - -.topic IDH_EXT_ALL -Click this to extract all files in the archive or disk image. - -.topic IDH_EXT_REFORMAT -If enabled, extracted files will be translated using the file format converters. - -.topic IDH_EXT_DATAFORK -If enabled, data forks of selected files will be extracted. - -.topic IDH_EXT_RSRCFORK -If enabled, resource forks of selected files will be extracted. - -.topic IDH_EXT_CONVEOLNONE -Select this to disable conversion of text files. - -.topic IDH_EXT_CHOOSE_FOLDER -Click this to choose the folder to extract to from a list. - -.topic IDH_OVWR_YES -Click to overwrite the existing file with the new one. - -.topic IDH_OVWR_YESALL -Click to overwrite all existing files with the new ones. - -.topic IDH_OVWR_NO -Click this if you want to keep the existing file. - -.topic IDH_OVWR_NOALL -Click this if you want to leave all existing files alone. - -.topic IDH_OVWR_NEW_INFO -The modification date of the new file. - -.topic IDH_OVWR_RENAME -Click to rename the new file. The existing file will be left untouched. - -.topic IDH_OVWR_EXIST_NAME -The name of the existing file. - -.topic IDH_OVWR_EXIST_INFO -The modification date of the existing file. - -.topic IDH_OVWR_NEW_NAME -The name of the new file. - -.topic IDH_RENOVWR_SOURCE_NAME -The name of the file as it appears in the archive. - -.topic IDH_RENOVWR_ORIG_NAME -The full pathname of the file as CiderPress tried to extract it. - -.topic IDH_RENOVWR_NEW_NAME -Type the full pathname of the file to extract to. - -.topic IDH_SELECT_ACCEPT -Click this to accept the currently selected files and folders. - -.topic IDH_ADDFILES_PREFIX -Enter a path prefix here. This will be prepended to all files added to the archive. - -.topic IDH_ADDFILES_INCLUDE_SUBFOLDERS -If enabled, files in sub-folders will be added. If not, only the files in the current directory will be included. - -.topic IDH_ADDFILES_STRIP_FOLDER -If enabled, folder names will be stripped from all files being added. This effectively piles them into the "root" of the archive. - -.topic IDH_ADDFILES_NOPRESERVE -Select this to ignore file attribute preservation sequences in filenames. - -.topic IDH_ADDFILES_PRESERVE -Select this to look for and make use of file attribute sequences. - -.topic IDH_ADDFILES_PRESERVEPLUS -Select this to make use of file attribute preservation sequences, and to "guess" ProDOS file types from file extensions for files that don't have preservation data. - -.topic IDH_ADDFILES_OVERWRITE -If enabled, existing files will be overwritten without asking for confirmation. - -.topic IDH_PREF_SHRINKIT_COMPAT -When enabled, certain GS/ShrinkIt quirks are emulated. - -.topic IDH_USE_SELECTED -Apply only to selected entries. - -.topic IDH_USE_ALL -Apply to all entries. - -.topic IDH_RENAME_OLD -The current name of the file, possibly modified from its original form. - -.topic IDH_RENAME_NEW -Enter the new name for the entry. The names of disk images stored in ShrinkIt archives must be simple filenames. - -.topic IDH_RENAME_PATHSEP -The pathname separator character, usually ':' or '/'. This separates the folder names in a pathname. - -.topic IDH_COMMENT_EDIT -Enter the text of the comment here. - -.topic IDH_COMMENT_DELETE -Click here to delete the comment. This option will be disabled if you just now added the comment. - -.topic IDH_RECOMP_COMP -Select the compression method that the files will be re-compressed with. Algorithms not supported by the currently-installed copy of NufxLib will not be shown. - -.topic IDH_PREF_ASSOCIATIONS -Click this to open the Edit File Associations dialog. - -.topic IDH_ASSOCIATION_LIST -Place a checkmark next to the file extensions that you want to associate with CiderPress. - -.topic IDH_REGENTER_USER -Enter the user name shown on the registration letter. - -.topic IDH_REGENTER_COMPANY -Enter the company name shown on the registration letter. - -.topic IDH_REGENTER_REG -Enter the registration key shown on the registration letter. - -.topic IDH_EXT_CONVHIGHASCII -If enabled, "high ASCII" text files will be converted. This is only done to files identified as text. - -.topic IDH_EXT_DISKIMAGE -If enabled, archived disk images will be extracted. - -.topic IDH_EXT_DISK_2MG -If enabled, disks will be extracted as .2MG files. If not enabled, disks will be extracted into .PO files. - -.topic IDH_EXT_ADD_PRESERVE -If enabled, file attribute preservation sequences are inserted into filenames, and illegal characters are converted to "%xx". - -.topic IDH_EXT_ADD_EXTEN -If enabled, a three-letter extension will be added to extracted files. This can make them easier to use in Windows. - -.topic IDH_EXT_CONFIG_PRESERVE -Click this to modify the options on this screen so that extracted files retain their original structure and attributes. - -.topic IDH_EXT_CONFIG_CONVERT -Click this to modify the options on this screen so that extracted files are most readily usable under Windows. - -.topic IDH_PREF_COERCE_DOS -If enabled, DOS 3.3 filenames will be converted to a mix of upper and lower case for improved readability. - -.topic IDH_PREF_SPACES_TO_UNDER -If enabled, all spaces in filenames will be converted to underscores ('_'). Useful for extracting files that will be distributed by a web server. - -.topic IDH_REGENTER_USERCRC -The checksum of the text entered in the Name field. - -.topic IDH_REGENTER_COMPCRC -The checksum of the text entered in the Company field. - -.topic IDH_REGENTER_REGCRC -The checksum of the text entered in the Registration Key field. - -.topic IDH_RENAME_SKIP -Click this to leave the name alone and move on to the next selected entry. - -.topic IDH_DECONF_VIEWASBLOCKS -View the disk as a series of 512-byte blocks. Recommended for ProDOS, Pascal, and CP/M. Not available on 13-sector nibble images. - -.topic IDH_DECONF_VIEWASSECTORS -View the disk as a collection of tracks and 256-byte sectors. Recommended for DOS 3.2 and 3.3. Not available on odd-sized volumes, e.g. some ProDOS images. - -.topic IDH_DECONF_OUTERFORMAT -The outer format of the disk image, e.g. Zip archive. This setting may not be -changed. - -.topic IDH_IMAGE_TYPE -Basic information about the image. - -.topic IDH_DISKCONV_DOS -Create a plain DOS-ordered image (.DO). This is the most common format for 5.25" disk images. It is not available for large volumes or 13-sector disks. - -.topic IDH_DISKCONV_DOS2MG -Create a DOS-ordered image with a 2MG header. Not available for large volumes or 13-sector disks. - -.topic IDH_DISKCONV_PRODOS -Create a plain ProDOS-ordered image (.PO). Not available for 13-sector disks. - -.topic IDH_DISKCONV_PRODOS2MG -Create a ProDOS-ordered image with a 2MG header. Not available for 13-sector disks. - -.topic IDH_DISKCONV_NIB -Create a plain nibble image. Only available for 5.25" disk images. Tracks have 6656 bytes. - -.topic IDH_DISKCONV_NIB2MG -Create a nibble image with a 2MG header. Only available for 5.25" disk images. Tracks have 6656 bytes. - -.topic IDH_DISKCONV_D13 -Create an unadorned 13-sector disk image. Only available for 5.25" disk images with 13 sectors per track. - -.topic IDH_DISKCONV_DC42 -Create a DiskCopy 4.2 image file. Only available for 3.5" disk images. - -.topic IDH_DISKCONV_SDK -Create a ShrinkIt disk image. The configured default compression will be used. Not available for 13-sector disks. - -.topic IDH_DISKCONV_TRACKSTAR -Create a 40-track TrackStar image. Tracks are variable length and stored in nibble format. Only available for 5.25" disk images. - -.topic IDH_DISKCONV_HDV -Create a Sim //e "virtual hard drive" image. Not available for 13-sector disks. Some applications may have difficulty with the image if it does not contain a ProDOS filesystem. - -.topic IDH_DISKCONV_DDD -Create a DDD compressed disk image, compatible with DDD Pro. Only available for 5.25" disk images. - -.topic IDH_DISKCONV_GZIP -Compress the file with "gzip" compression, and add ".gz" to the filename extension. This setting is ignored when creating ShrinkIt disk images. - -.topic IDH_DISKEDIT_NIBBLE_PARMS -When viewing a nibble disk image as blocks or sectors, this lets you choose the sector parameters. The "Patched" versions will allow you to read sectors from mildly copy-protected disks. - -.topic IDH_PROPS_PATHNAME -The pathname of the entry being edited. - -.topic IDH_PROPS_FILETYPE -The file type, displayed as its hexadecimal code and the common three-letter mnemonic. Not all types have a mnemonic. - -Tip: clicking in the file type box and then typing a letter takes you to the next entry whose mnemonic begins with that letter. This can make it easier to find an entry by "name". - -.topic IDH_PROPS_AUXTYPE -The file's auxilliary type. Sometimes used to indicate information about the file (such as a 'BIN' load address), sometimes used to distinguish between files with the same file type (such as the various application-specific 'CFG' config file formats). - -Enter a four-digit hexadecimal number. - -.topic IDH_PROPS_ACCESS_R -Enable or disable the ability to read the file. - -.topic IDH_PROPS_ACCESS_W -Enable or disable the ability to write the file. On a DOS 3.2/3.3 file, this locks or unlocks the file. - -.topic IDH_PROPS_ACCESS_N -Enable or disable the ability to rename the file. - -.topic IDH_PROPS_ACCESS_D -Enable or disable the ability to delete the file. - -.topic IDH_PROPS_ACCESS_I -If set, the file won't be displayed by the IIgs Finder and some other utilities. - -.topic IDH_PROPS_ACCESS_B -Set or clear the "backup needed" flag. - -.topic IDH_PROPS_MODWHEN -Date when the file was modified. - -.topic IDH_PROPS_TYPEDESCR -A brief description of the file's type. This is based on both the file type and the aux type. - -.topic IDH_TOPIC1269 -Select this to convert DOS text files to ProDOS format. This involves converting the "high ASCII" text to standard ASCII. Conversion is recommended, because if you don't do this the files may be difficult to read. - -.topic IDH_CONVFILE_PRESERVEDIR -Select this to preserve empty folders on ProDOS volumes. ShrinkIt archives don't provide a good way to store empty folders, so CiderPress will create an empty ".$$EmptyFolder" file in the archive. This will be removed automatically if the archive is converted to a ProDOS disk image. This option should only be enabled if you plan to convert the archive back to a disk image. - -.topic IDH_CONVDISK_140K -Create a 140K image, equivalent to a 5.25" floppy disk. - -.topic IDH_CONVDISK_800K -Create an 800K image, equivalent to a 3.5" floppy disk. - -.topic IDH_CONVDISK_1440K -Create a 1440K image, equivalent to a 3.5" floppy disk on a PC. - -.topic IDH_CONVDISK_5MB -Create a 5MB image. - -.topic IDH_CONVDISK_16MB -Create a 16MB image. - -.topic IDH_CONVDISK_20MB -Create a 20MB image, equivalent to a 20MB floptical disk. - -.topic IDH_CONVDISK_32MB -Create a 32MB image, the largest possible ProDOS volume. (Technically, it's one 512-byte block short of 32MB.) - -.topic IDH_CONVDISK_SPECIFY -Specify the size in 512-byte blocks. The largest possible ProDOS volume has 65535 blocks (though 65536 is allowed for compatibility). The smallest CiderPress allows is 16 blocks. - -.topic IDH_PREF_EXTVIEWER_EXTS -List file extensions that should be opened in an external viewer when double-clicked from the file list. For example, if you want to view GIF or JPEG images stored in a ShrinkIt archive or disk image, you should put "gif; jpg; jpeg" here. - -.topic IDH_CONVDISK_SPECIFY_EDIT -Specify the size in 512-byte blocks. The largest possible ProDOS volume has 65535 blocks (though 65536 is allowed for compatibility). The smallest CiderPress allows is 16 blocks. - -.topic IDH_CONVDISK_COMPUTE -Click this to compute the size required with the currently-specified options. The computation is performed by creating a 32MB image and examining the number of blocks used. - -.topic IDH_CONVDISK_SPACEREQ -The space required by the selected set of files with the current options. Click the "Compute" button to calculate this. - -.topic IDH_DEOW_CURRENT -If selected, ProDOS files are allowed to have lower case letters and spaces in them. This can confuse older versions of ProDOS 8 (pre-v1.8), so only select this if you're using a recent version of ProDOS. - -.topic IDH_TOPIC1306 -If selected, empty blocks won't actually be stored. Instead, the ProDOS "sparse" mechanism is used to indicate the existence of the empty space. You should generally leave this enabled, as it can save considerable space. - -.topic IDH_CONVDISK_VOLNAME -Enter the desired ProDOS volume name here. It must start with a letter, and contain nothing but letters, numbers, and dots ('.'). - -.topic IDH_CREATEFS_DOS32 -Format the disk for DOS 3.2. - -.topic IDH_CREATEFS_DOS33 -Format the disk for DOS 3.3. - -.topic IDH_CREATEFS_PRODOS -Format the disk for ProDOS. - -.topic IDH_CREATEFS_PASCAL -Format the image with the UCSD Pascal filesystem. - -.topic IDH_TOPIC1320 -Create an image file but leave it completely blank. - -.topic IDH_CREATEFSDOS_ALLOCDOS -If checked, the first three tracks of the disk are marked "in-use" for a DOS image. If unchecked, two tracks are freed up but the image can't be booted. - -.topic IDH_CREATEFSDOS_VOLNUM -DOS volume number (usually 254). - -.topic IDH_CREATEFSPRODOS_VOLNAME -Enter the desired ProDOS volume name here. It must start with a letter, and contain nothing but letters, numbers, and dots ('.'). - -.topic IDH_CREATEFSPASCAL_VOLNAME -Enter the desired Pascal volume name here. You may use upper-case letters, numbers, and symbols other than "$=?,[#:". - -.topic IDH_PREF_SUCCESS_BEEP -If set, CiderPress will play a sound when operations complete successfully. - -.topic IDH_PDISK_CONFIRM_FORMAT -When enabled, the Disk Image Characteristics dialog is displayed whenever a disk image is opened. - -.topic IDH_PDISK_PRODOS_ALLOWLOWER -If enabled, files added to ProDOS disk images will have lower case letters in their names when viewed with GS/OS. ProDOS 8 older than v1.8 can't handle lower case names, so disable this option if you're using an old version of ProDOS. - -.topic IDH_PDISK_PRODOS_USESPARSE -If enabled, files with empty blocks (all zeroes) will be stored more efficiently (recommended.) - -.topic IDH_CREATESUBDIR_BASE -This is the name of the directory in which the new subdirectory will be created. - -.topic IDH_CREATESUBDIR_NEW -Enter the name of the directory to create here. - -.topic IDH_RENAMEVOL_TREE -Select the volume to rename. (In most cases you will only have one to choose from.) - -.topic IDH_RENAMEVOL_NEW -Enter the new volume name here. For DOS disks, enter the new volume number. - -.topic IDH_ADDFILES_CONVEOLNONE -Select this to disable conversion of text files. - -.topic IDH_ADDFILES_CONVEOLTEXT -Select this to automatically determine which files are text files, and convert them. - -.topic IDH_ADDFILES_CONVEOLALL -Select to treat all files as text files. - -.topic IDH_PREF_PASTE_JUNKPATHS -If enabled, pathnames will be stripped off of files before they are pasted into an archive. If disabled, the original directory hierarchy will be preserved. - -.topic IDH_EXT_CONVEOLTYPE -Select this to treat all files of type TXT or SRC as text files. - -.topic IDH_ADDFILES_CONVEOLTYPE -Select this to treat all files of type TXT or SRC as text files. - -.topic IDH_TWOIMG_LOCKED -Indicates whether the "locked" flag is set. Some emulators will use this flag to decide whether or not to treat the disk as write protected. - -.topic IDH_TWOIMG_DOSVOLSET -If checked, a volume number is specified in the header. If not checked, no number is specified, and the volume number edit field will be greyed out. - -.topic IDH_TWOIMG_DOSVOLNUM -Volume number (1-254). Some emulators use this field to emulate the sector-address-header volume number field. Rarely useful. - -.topic IDH_TWOIMG_COMMENT -The comment field is stored in the 2MG file, and can contain anything you want. - -.topic IDH_PDISK_OPENVOL_RO -If enabled, the "read only" box will be checked by default on all Open Volume requests. - -.topic IDH_PREF_REDUCE_SHK_ERROR_CHECKS -Disable some error checking on ShrinkIt archives. Specifically, this ignores bad LZW/2 lengths (usually found in "bad Mac" archives) and bad data CRCs. Leaving this enabled is a bad idea, because you may not discover corrupted data right away. - -.topic IDH_PDISK_OPENVOL_PHYS0 -If enabled, you will be allowed to open physical disk 0 for writing. This is usually your Windows boot disk, so setting this is not recommended. - -.topic IDH_PREF_SHK_BAD_MAC -If set, this attempts to recognize "bad Mac" archives, which have bad pathname separators ('?' instead of ':') and some bad data in the LZW area. - diff --git a/ciderpress/app/Help/PopUpIds.h b/ciderpress/app/Help/PopUpIds.h deleted file mode 100644 index 2219b7c..0000000 --- a/ciderpress/app/Help/PopUpIds.h +++ /dev/null @@ -1,332 +0,0 @@ -#define IDH_IDOK 1 -#define IDH_IDCANCEL 2 -#define IDH_IDHELP 9 -#define IDH_NUFXLIB_VERS_TEXT 1001 -#define IDH_CONTENT_LIST 1002 -#define IDH_COL_PATHNAME 1005 -#define IDH_COL_TYPE 1006 -#define IDH_COL_AUXTYPE 1007 -#define IDH_COL_MODDATE 1008 -#define IDH_COL_FORMAT 1009 -#define IDH_COL_SIZE 1010 -#define IDH_COL_RATIO 1011 -#define IDH_COL_PACKED 1012 -#define IDH_COL_ACCESS 1013 -#define IDH_COL_DEFAULTS 1014 -#define IDH_DEFC_UNCOMPRESSED 1016 -#define IDH_DEFC_SQUEEZE 1017 -#define IDH_DEFC_LZW1 1018 -#define IDH_DEFC_LZW2 1019 -#define IDH_DEFC_LZC12 1020 -#define IDH_DEFC_LZC16 1021 -#define IDH_DEFC_DEFLATE 1022 -#define IDH_DEFC_BZIP2 1023 -#define IDH_TOPIC1024 1024 -#define IDH_PVIEW_NOWRAP_TEXT 1025 -#define IDH_PVIEW_BOLD_HEXDUMP 1026 -#define IDH_PVIEW_BOLD_BASIC 1027 -#define IDH_PVIEW_DISASM_ONEBYTEBRKCOP 1028 -#define IDH_PVIEW_HIRES_BW 1029 -#define IDH_PVIEW_DHR_CONV_COMBO 1030 -#define IDH_PVIEW_MOUSETEXT_TO_ASCII 1031 -#define IDH_PVIEW_HITEXT 1036 -#define IDH_PVIEW_PASCALTEXT 1037 -#define IDH_PVIEW_APPLESOFT 1038 -#define IDH_PVIEW_INTEGER 1039 -#define IDH_PVIEW_HIRES 1040 -#define IDH_PVIEW_DHR 1041 -#define IDH_PVIEW_SHR 1042 -#define IDH_PVIEW_AWP 1043 -#define IDH_PVIEW_PRODOSFOLDER 1044 -#define IDH_PVIEW_RESOURCES 1045 -#define IDH_PVIEW_RELAX_GFX 1046 -#define IDH_PVIEW_ADB 1047 -#define IDH_PVIEW_SCASSEM 1048 -#define IDH_PVIEW_ASP 1049 -#define IDH_PVIEW_MACPAINT 1050 -#define IDH_PVIEW_PASCALCODE 1051 -#define IDH_PVIEW_CPMTEXT 1052 -#define IDH_PVIEW_GWP 1053 -#define IDH_PVIEW_DISASM 1054 -#define IDH_PVIEW_PRINTSHOP 1055 -#define IDH_PVIEW_TEXT8 1056 -#define IDH_PVIEW_SIZE_EDIT 1060 -#define IDH_PVIEW_SIZE_SPIN 1061 -#define IDH_DISKEDIT_DOREAD 1063 -#define IDH_DISKEDIT_DOWRITE 1064 -#define IDH_DISKEDIT_TRACK 1065 -#define IDH_DISKEDIT_TRACKSPIN 1066 -#define IDH_DISKEDIT_SECTOR 1067 -#define IDH_DISKEDIT_SECTORSPIN 1068 -#define IDH_DISKEDIT_OPENFILE 1069 -#define IDH_DISKEDIT_EDIT 1070 -#define IDH_DISKEDIT_PREV 1071 -#define IDH_DISKEDIT_NEXT 1072 -#define IDH_STEXT_SECTOR 1073 -#define IDH_STEXT_TRACK 1074 -#define IDH_DISKEDIT_DONE 1077 -#define IDH_DISKEDIT_HEX 1078 -#define IDH_DISKEDIT_SUBVOLUME 1081 -#define IDH_TOPIC1082 1082 -#define IDH_TOPIC1089 1089 -#define IDH_DECONF_FSFORMAT 1090 -#define IDH_DECONF_SECTORORDER 1091 -#define IDH_DECONF_PHYSICAL 1092 -#define IDH_DECONF_FILEFORMAT 1093 -#define IDH_DECONF_SOURCE 1094 -#define IDH_DISKIMG_VERS_TEXT 1095 -#define IDH_FVIEW_EDITBOX 1101 -#define IDH_SELECTED_COUNT 1102 -#define IDH_TOPIC1103 1103 -#define IDH_TOPIC1105 1105 -#define IDH_DECONF_HELP 1112 -#define IDH_SUBV_LIST 1114 -#define IDH_DEFILE_FILENAME 1115 -#define IDH_DEFILE_RSRC 1116 -#define IDH_CIDERPRESS_VERS_TEXT 1117 -#define IDH_PREF_TEMP_FOLDER 1118 -#define IDH_CHOOSEDIR_TREE 1121 -#define IDH_CHOOSEDIR_PATHEDIT 1123 -#define IDH_CHOOSEDIR_EXPAND_TREE 1124 -#define IDH_CHOOSEDIR_PATH 1125 -#define IDH_CHOOSEDIR_NEW_FOLDER 1127 -#define IDH_PREF_CHOOSE_TEMP_FOLDER 1128 -#define IDH_FVIEW_FONT 1129 -#define IDH_FVIEW_NEXT 1130 -#define IDH_FVIEW_PREV 1131 -#define IDH_NEWFOLDER_CURDIR 1132 -#define IDH_NEWFOLDER_NAME 1133 -#define IDH_EXT_PATH 1136 -#define IDH_EXT_CONVEOLTEXT 1137 -#define IDH_EXT_CONVEOLALL 1138 -#define IDH_EXT_STRIP_FOLDER 1142 -#define IDH_EXT_OVERWRITE_EXIST 1143 -#define IDH_EXT_SELECTED 1144 -#define IDH_EXT_ALL 1145 -#define IDH_EXT_REFORMAT 1147 -#define IDH_EXT_DATAFORK 1148 -#define IDH_EXT_RSRCFORK 1149 -#define IDH_EXT_CONVEOLNONE 1151 -#define IDH_EXT_CHOOSE_FOLDER 1152 -#define IDH_PROG_ARC_NAME 1153 -#define IDH_PROG_FILE_NAME 1154 -#define IDH_PROG_VERB 1155 -#define IDH_PROG_TOFROM 1156 -#define IDH_PROG_PROGRESS 1157 -#define IDH_OVWR_YES 1161 -#define IDH_OVWR_YESALL 1162 -#define IDH_OVWR_NO 1163 -#define IDH_OVWR_NOALL 1164 -#define IDH_OVWR_NEW_INFO 1166 -#define IDH_OVWR_RENAME 1167 -#define IDH_OVWR_EXIST_NAME 1168 -#define IDH_OVWR_EXIST_INFO 1169 -#define IDH_OVWR_NEW_NAME 1170 -#define IDH_RENOVWR_SOURCE_NAME 1171 -#define IDH_RENOVWR_ORIG_NAME 1172 -#define IDH_RENOVWR_NEW_NAME 1173 -#define IDH_SELECT_ACCEPT 1175 -#define IDH_ADDFILES_PREFIX 1177 -#define IDH_ADDFILES_INCLUDE_SUBFOLDERS 1180 -#define IDH_ADDFILES_STRIP_FOLDER 1181 -#define IDH_ADDFILES_NOPRESERVE 1182 -#define IDH_ADDFILES_PRESERVE 1183 -#define IDH_ADDFILES_PRESERVEPLUS 1184 -#define IDH_ADDFILES_STATIC1 1186 -#define IDH_ADDFILES_STATIC2 1187 -#define IDH_ADDFILES_STATIC3 1188 -#define IDH_ADDFILES_OVERWRITE 1189 -#define IDH_PREF_SHRINKIT_COMPAT 1190 -#define IDH_USE_SELECTED 1192 -#define IDH_USE_ALL 1193 -#define IDH_RENAME_OLD 1194 -#define IDH_RENAME_NEW 1195 -#define IDH_RENAME_PATHSEP 1196 -#define IDH_COMMENT_EDIT 1198 -#define IDH_COMMENT_DELETE 1199 -#define IDH_RECOMP_COMP 1201 -#define IDH_PREF_ASSOCIATIONS 1202 -#define IDH_ASSOCIATION_LIST 1209 -#define IDH_REG_COMPANY_NAME 1210 -#define IDH_REG_EXPIRES 1211 -#define IDH_ABOUT_ENTER_REG 1212 -#define IDH_REGENTER_USER 1213 -#define IDH_REGENTER_COMPANY 1214 -#define IDH_REGENTER_REG 1215 -#define IDH_REG_USER_NAME 1216 -#define IDH_ZLIB_VERS_TEXT 1218 -#define IDH_EXT_CONVHIGHASCII 1219 -#define IDH_EXT_DISKIMAGE 1220 -#define IDH_EXT_DISK_2MG 1221 -#define IDH_EXT_ADD_PRESERVE 1222 -#define IDH_EXT_ADD_EXTEN 1223 -#define IDH_EXT_CONFIG_PRESERVE 1224 -#define IDH_EXT_CONFIG_CONVERT 1225 -#define IDH_PREF_COERCE_DOS 1226 -#define IDH_PREF_SPACES_TO_UNDER 1227 -#define IDH_REGENTER_USERCRC 1228 -#define IDH_REGENTER_COMPCRC 1229 -#define IDH_REGENTER_REGCRC 1230 -#define IDH_RENAME_SKIP 1232 -#define IDH_DECONF_VIEWASBLOCKS 1233 -#define IDH_DECONF_VIEWASSECTORS 1234 -#define IDH_DECONF_VIEWASNIBBLES 1235 -#define IDH_DECONF_OUTERFORMAT 1236 -#define IDH_DECONF_VIEWAS 1237 -#define IDH_IMAGE_TYPE 1238 -#define IDH_DISKCONV_DOS 1239 -#define IDH_DISKCONV_DOS2MG 1240 -#define IDH_DISKCONV_PRODOS 1241 -#define IDH_DISKCONV_PRODOS2MG 1242 -#define IDH_DISKCONV_NIB 1243 -#define IDH_DISKCONV_NIB2MG 1244 -#define IDH_DISKCONV_D13 1245 -#define IDH_DISKCONV_DC42 1246 -#define IDH_DISKCONV_SDK 1247 -#define IDH_DISKCONV_TRACKSTAR 1248 -#define IDH_DISKCONV_HDV 1249 -#define IDH_DISKCONV_DDD 1250 -#define IDH_DISKCONV_GZIP 1251 -#define IDH_DISKEDIT_NIBBLE_PARMS 1252 -#define IDH_PROPS_PATHNAME 1255 -#define IDH_PROPS_FILETYPE 1256 -#define IDH_PROPS_AUXTYPE 1257 -#define IDH_PROPS_ACCESS_R 1258 -#define IDH_PROPS_ACCESS_W 1259 -#define IDH_PROPS_ACCESS_N 1260 -#define IDH_PROPS_ACCESS_D 1261 -#define IDH_PROPS_ACCESS_I 1262 -#define IDH_PROPS_ACCESS_B 1263 -#define IDH_PROPS_MODWHEN 1266 -#define IDH_PROPS_TYPEDESCR 1267 -#define IDH_TOPIC1269 1269 -#define IDH_CONVFILE_PRESERVEDIR 1270 -#define IDH_CONVDISK_140K 1271 -#define IDH_CONVDISK_800K 1273 -#define IDH_CONVDISK_1440K 1274 -#define IDH_CONVDISK_5MB 1275 -#define IDH_CONVDISK_16MB 1276 -#define IDH_CONVDISK_20MB 1277 -#define IDH_CONVDISK_32MB 1278 -#define IDH_CONVDISK_SPECIFY 1279 -#define IDH_IMAGE_SIZE_TEXT 1289 -#define IDH_BULKCONV_PATHNAME 1290 -#define IDH_PREF_EXTVIEWER_EXTS 1292 -#define IDH_VOLUME_LIST 1295 -#define IDH_OPENVOL_READONLY 1296 -#define IDH_VOLUMECOPYPROG_FROM 1297 -#define IDH_VOLUMECOPYPROG_TO 1298 -#define IDH_VOLUMECOPYPROG_PROGRESS 1299 -#define IDH_CONVDISK_SPECIFY_EDIT 1302 -#define IDH_CONVDISK_COMPUTE 1303 -#define IDH_DEOW_FILE 1303 -#define IDH_CONVDISK_SPACEREQ 1304 -#define IDH_DEOW_VOLUME 1304 -#define IDH_DEOW_CURRENT 1305 -#define IDH_TOPIC1306 1306 -#define IDH_CONVDISK_VOLNAME 1307 -#define IDH_VOLUME_FILTER 1307 -#define IDH_VOLUMECOPYSEL_LIST 1309 -#define IDH_VOLUEMCOPYSEL_TOFILE 1310 -#define IDH_VOLUEMCOPYSEL_FROMFILE 1311 -#define IDH_CREATEFS_DOS32 1312 -#define IDH_CREATEFS_DOS33 1313 -#define IDH_CREATEFS_PRODOS 1314 -#define IDH_CREATEFS_PASCAL 1315 -#define IDH_CREATEFS_HFS 1316 -#define IDH_CREATEFS_BLANK 1317 -#define IDH_TOPIC1320 1320 -#define IDH_CREATEFSDOS_ALLOCDOS 1321 -#define IDH_CREATEFSDOS_VOLNUM 1322 -#define IDH_CREATEFSPRODOS_VOLNAME 1323 -#define IDH_CREATEFSPASCAL_VOLNAME 1324 -#define IDH_ASPI_VERS_TEXT 1330 -#define IDH_PREF_SUCCESS_BEEP 1331 -#define IDH_ADD_TARGET_TREE 1333 -#define IDH_AIDISK_SUBVOLSEL 1334 -#define IDH_AIDISK_NOTES 1335 -#define IDH_AI_FILENAME 1336 -#define IDH_AIBNY_RECORDS 1337 -#define IDH_AINUFX_FORMAT 1338 -#define IDH_AINUFX_RECORDS 1339 -#define IDH_AINUFX_MASTERVERSION 1340 -#define IDH_AINUFX_CREATEWHEN 1341 -#define IDH_AINUFX_MODIFYWHEN 1342 -#define IDH_AINUFX_JUNKSKIPPED 1343 -#define IDH_AIDISK_OUTERFORMAT 1344 -#define IDH_AIDISK_FILEFORMAT 1345 -#define IDH_AIDISK_PHYSICALFORMAT 1346 -#define IDH_AIDISK_SECTORORDER 1347 -#define IDH_AIDISK_FSFORMAT 1348 -#define IDH_AIDISK_FILECOUNT 1349 -#define IDH_AIDISK_CAPACITY 1350 -#define IDH_AIDISK_FREESPACE 1351 -#define IDH_AIDISK_DAMAGED 1352 -#define IDH_AIDISK_WRITEABLE 1354 -#define IDH_PDISK_CONFIRM_FORMAT 1359 -#define IDH_PDISK_PRODOS_ALLOWLOWER 1360 -#define IDH_PDISK_PRODOS_USESPARSE 1361 -#define IDH_FVIEW_PRINT 1363 -#define IDH_CREATESUBDIR_BASE 1364 -#define IDH_CREATESUBDIR_NEW 1365 -#define IDH_RENAMEVOL_TREE 1366 -#define IDH_RENAMEVOL_NEW 1367 -#define IDH_ADDFILES_CONVEOLNONE 1368 -#define IDH_ADDFILES_CONVEOLTEXT 1369 -#define IDH_ADDFILES_CONVEOLALL 1370 -#define IDH_ADDFILES_STATIC4 1371 -#define IDH_PROPS_CREATEWHEN 1372 -#define IDH_EOLSCAN_CR 1374 -#define IDH_EOLSCAN_LF 1375 -#define IDH_EOLSCAN_CRLF 1376 -#define IDH_EOLSCAN_CHARS 1377 -#define IDH_PREF_PASTE_JUNKPATHS 1378 -#define IDH_EXT_CONVEOLTYPE 1379 -#define IDH_ADDFILES_CONVEOLTYPE 1380 -#define IDH_TWOIMG_LOCKED 1381 -#define IDH_TWOIMG_DOSVOLSET 1382 -#define IDH_TWOIMG_DOSVOLNUM 1383 -#define IDH_TWOIMG_COMMENT 1384 -#define IDH_TWOIMG_CREATOR 1385 -#define IDH_TWOIMG_VERSION 1386 -#define IDH_TWOIMG_FORMAT 1387 -#define IDH_TWOIMG_BLOCKS 1388 -#define IDH_FVIEW_DATA 1395 -#define IDH_FVIEW_RSRC 1396 -#define IDH_FVIEW_CMMT 1397 -#define IDH_FVIEW_FORMATSEL 1399 -#define IDH_FVIEW_FMT_HEX 1400 -#define IDH_FVIEW_FMT_RAW 1401 -#define IDH_FVIEW_FMT_BEST 1403 -#define IDH_PDISK_OPENVOL_RO 1405 -#define IDH_EOLSCAN_HIGHASCII 1407 -#define IDH_CASSETTE_LIST 1414 -#define IDH_IMPORT_CHUNK 1416 -#define IDH_CASSETTE_ALG 1418 -#define IDH_CASSETTE_INPUT 1419 -#define IDH_CASSIMPTARG_FILENAME 1420 -#define IDH_CASSIMPTARG_BAS 1421 -#define IDH_CASSIMPTARG_INT 1422 -#define IDH_CASSIMPTARG_BIN 1423 -#define IDH_CASSIMPTARG_BINADDR 1424 -#define IDH_CASSIMPTARG_RANGE 1425 -#define IDH_CLASH_RENAME 1426 -#define IDH_CLASH_SKIP 1427 -#define IDH_CLASH_WINNAME 1428 -#define IDH_CLASH_STORAGENAME 1429 -#define IDH_PREF_REDUCE_SHK_ERROR_CHECKS 1430 -#define IDH_IMPORT_BAS_RESULTS 1431 -#define IDH_IMPORT_BAS_SAVEAS 1432 -#define IDH_FVIEW_FIND 1438 -#define IDH_CREATEFSHFS_VOLNAME 1440 -#define IDH_PROPS_HFS_FILETYPE 1441 -#define IDH_PROPS_HFS_AUXTYPE 1442 -#define IDH_PROPS_HFS_MODE 1444 -#define IDH_PROPS_HFS_LABEL 1445 -#define IDH_PASTE_SPECIAL_COUNT 1446 -#define IDH_PASTE_SPECIAL_PATHS 1447 -#define IDH_PASTE_SPECIAL_NOPATHS 1448 -#define IDH_PROGRESS_COUNTER_COUNT 1449 -#define IDH_PROGRESS_COUNTER_DESC 1450 -#define IDH_PDISK_OPENVOL_PHYS0 1451 -#define IDH_PREF_SHK_BAD_MAC 1453 diff --git a/ciderpress/app/Help/README.txt b/ciderpress/app/Help/README.txt deleted file mode 100644 index 5e3b454..0000000 --- a/ciderpress/app/Help/README.txt +++ /dev/null @@ -1,15 +0,0 @@ -Updating Help -============= - -Make any edits, then: - -1. Launch HTML Help Workshop -2. Open CiderPress.hhp -3. File > Compile - -This should update the CiderPress.chm in the app/Help directory. You can -copy it into the CiderPress directory, or just rebuild CiderPress to have -it copied automatically. - -NOTE: pop-up text must be added to PopUp.txt, PopUpIds.h, and the -PopUpHelpIds table in MyApp.cpp. diff --git a/ciderpress/app/Help/html/t10.htm b/ciderpress/app/Help/html/t10.htm deleted file mode 100644 index 3ce8a87..0000000 --- a/ciderpress/app/Help/html/t10.htm +++ /dev/null @@ -1,28 +0,0 @@ - - - Welcome to CiderPress - - - - - - - - -

Welcome to CiderPress

-

 

-

CiderPress is the culmination of a journey that began in May 1989 with the first release of "NuView".  That program, which quickly evolved into NuLib and eventually became NuLib2, was the first to access ShrinkIt archives on computers other than Apple IIs.  CiderPress continues the tradition by becoming the first Windows GUI application to support ShrinkIt archives.

-

 

-

CiderPress is not "toy" software.  It is a fully-functional Windows file archiver, with all of the gizmos and gadgets you'll find in applications for "mainstream" formats.  The only difference is an emphasis on preservation and conversion of Apple II file formats.

-

 

-

Some pages that will get you started:

-

 

-

Quick overview of CiderPress features.

-

How to use CiderPress.

-

Getting help.

-

Credits.

-

 

-

If you prefer a tutorial to a reference manual, check out the tutorial on the web site. - -

- diff --git a/ciderpress/app/Help/html/t109.htm b/ciderpress/app/Help/html/t109.htm deleted file mode 100644 index 4bce141..0000000 --- a/ciderpress/app/Help/html/t109.htm +++ /dev/null @@ -1,145 +0,0 @@ - - - Appendix - Disassembly Notes - - - - - - - - - - -

Disassembly Notes

-

 

-

A great deal of Apple II source code is written in 6502 or 65816 assembly language.  The simplest way to disassemble raw data into something understandable on a real Apple II is to load the program, enter the monitor with "CALL -151", and list the data with the 'L' command ("2000L").  The output seen depends on which model of Apple II you have.

-

 

-

The CiderPress file viewer allows you to create monitor-style code disassemblies that closely match the original.  The disassembler is selected automatically for appropriate files.

-

 

-

All files are ranked in a priority scheme by file type.  "High" priority means that disassembly is the preferred method for viewing the file.  "Medium" means use it if nothing better comes along (e.g. an 8192-byte BIN file will be decoded as a hi-res image rather than disassembled, but both options will be available in the file viewer).  "Low" means the option to disassemble will be available, but will be prioritized below converted text or "raw" display.

-

 

-

The "extract files" feature when used in "easy access in Windows" mode will write the converted data to a text file.  This is an easy way to dump a monitor listing of a file to disk.

-

 

-

Apple ][, ][+, //e monitor

-

 

-

The classic Apple IIs come with the monitor built into the F8 ROM.  It's able to display all 6502 operations as three-letter mnemonic operations and hexadecimal values.

-

 

-

CiderPress emulates this output as closely as possible, but adds two features for convenience.  First, the new operations introduced with the 65C02 CPU are properly displayed.  Second, annotations have been added to the following:

-
  • ProDOS 8 MLI calls (the "inline" calls are broken out onto separate lines) -
  • Softswitches and F8 ROM routines
-

 

-

Files of type SYS, CMD, 8OB, and P8C are categorized "high priority".

-

Files of type BIN are categorized "medium priority".

-

Files of type NON and $F1 through $F8 are categorized "low priority".

-

 

-

Apple IIgs monitor

-

 

-

The Apple IIgs monitor introduced a number of new features.  The 65816 CPU added several new instructions as well as 24-bit addresses and variable-width registers.

-

 

-

The output of the disassembler closely matches the IIgs output.  Annotations have been added to the following:

-
  • ProDOS 8 MLI calls (the "inline" calls are broken out onto separate lines) -
  • ProDOS 16 and GS/OS calls (again, "inline" calls are broken out) -
  • IIgs toolbox calls -
  • E1/xxxx vectors -
  • E0/xxxx vectors -
  • 01/xxxx vectors -
  • Softswitches and F8 ROM routines
-

 

-

Disassembly of 65816 code is tricky because the width of the registers can change, which affects the size of instructions with "immediate" mode addressing.  CiderPress gives you the option of listing a file with "long" (16-bit) or "short" (8-bit) registers, but does not try to track the mode.  Obtaining a complete disassembly of a chunk of code may require listing it twice and merging pieces together.

-

 

-

There is also a preference to control whether BRK and COP commands are displayed as two-byte operations ("BRK 00") or single-byte ("BRK").  The former matches the behavior of the IIgs monitor, but the latter can make the disassembly easier to read in some circumstances, e.g. instructions following an odd number of zeroes.

-

 

-

The IIgs monitor listing mode includes limited OMF (Object Module Format) support.  OMF files are broken into segments, and the disassembly restarts at the top of each segment.

-

 

-

Files of type OBJ, LIB, S16, RTL, EXE, PIF, TIF, NDA, CDA, TOL, DVR, LDF, FST and OS are categorized "high priority".

-

Files of type BIN, SYS, CMD, 8OB, P8C, and NON are categorized "low priority".

-

 

-

Notes

-

 

-

If you want to see a disassembly of a file with the wrong type, e.g. you want to view assembly language code embedded in a BAS or INT file, change the file type to "BIN".

-

 

-

If a disassembly is starting at the wrong address, or you want to change the start address because the code relocates itself early on (fairly common for SYS files), change the file type to BIN and set the aux type to the desired start address.

-

 

-

The annotations on memory locations and OS calls come from a file called NList.Data, which can be found in the CiderPress installation directory.  The data file was developed by Dave Lyons as part of the Nifty List desk accessory, and is included with permission of the author.  If you want to add additional locations (perhaps Applesoft entry points) or expanded commentary, the file format is pretty self-explanatory.

-

 

-

The format of the Nifty List annotations may not be immediately obvious.  The following is an excerpt from the Nifty List v3.4 manual.

-

 

-

------------------------------------

-

Interpreting the parameter summaries

-

------------------------------------

-

Some tools take no parameters and return no information.  These

-

appear with an empty pair of parentheses after the tool name:

-

 

-

   GrafOff()

-

   SystemTask()

-

 

-

 

-

Tools that take one or more parameters and return no information are

-

listed like this:

-

 

-

   SetPort(@Port)

-

   WriteBParam(Data,Parm#)

-

 

-

An "@" in front of a parameter means it is a pointer and takes 4

-

bytes (2 words).  All parameters not specially marked take 2 bytes

-

(1 word).

-

 

-

 

-

Tools that take no parameters but return one are listed like this:

-

 

-

   GetPort():@Port

-

   FreeMem():FreeBytes/4

-

 

-

A "/" and a digit after a parmeter means it takes the specified number

-

of bytes.  (When making a tool call, you must push space on the stack

-

for any result values *before* pushing the input values.)

-

 

-

 

-

A few tools return more than one value.  In these cases, the

-

results are listed in the order they have been pushed onto the

-

stack (so that the first value PULLED is the last one listed):

-

 

-

   GetMouseClamp():Xmn,Xmx,Ymn,Ymx

-

 

-

Each of these values takes 2 bytes (1 word), since there is no

-

indication of a different size.

-

 

-

 

-

Tools that take and return values are listed like this, where a

-

trailing "H" indicates a Handle (4 bytes):

-

 

-

   EqualRgn(Rgn1H,Rgn2H):Flag

-

 

-

-----

-

Review of parameter sizes:

-

 

-

   Leading  "@"     4-byte pointer

-

   Trailing "H"     Handle (4 bytes)

-

   Trailing "/n"    n bytes

-

 

-

   All other values are 2 bytes long

-

 

-

-----

-

For ProDOS calls, the parameters are shown in parentheses even

-

though they actually belong in a parameter block.  For ProDOS 8,

-

the first item in the list is the parameter count, which should

-

be in the first byte of the parameter block.

-

 

-

   P8:RENAME(2:pn1,pn2)

-

 

-

ProDOS 16 calls (also called class-0 GS/OS calls) do not have a

-

parameter count.

-

 

-

   P16:CHANGE_PATH(@Path1,@Path2)

-

 

-

Class-1 GS/OS calls have parameter blocks beginning with a parameter

-

count word.  Some calls allow a range of values for the parameter

-

count (like Create, which can take from 1 to 7 parameters), and some

-

(like Destroy) have a single acceptable value:

-

 

-

   GS/OS:Create(1-7:@P,Acc,Typ,Aux/4,Stg,EOF/4,rEOF/4)

-

   GS/OS:Destroy(1:@P)

-

-

- diff --git a/ciderpress/app/Help/html/t111.htm b/ciderpress/app/Help/html/t111.htm deleted file mode 100644 index d227336..0000000 --- a/ciderpress/app/Help/html/t111.htm +++ /dev/null @@ -1,104 +0,0 @@ - - - Import From Cassette - - - - - - - - - - -

Import From Cassette

-

 

-

Back in the early days of the Apple II, the most popular way to distribute commercial software was on audio cassettes.  Cassette tapes are slow, transferring data at an average of 1350 bits per second (better than a 1200 baud modem, but about a thousand times slower than a 1x CD-ROM).  If stored properly, however, they can last a long time.

-

 

-

Technical Background

-

 

-

The basic layout of Apple II data on a cassette looks like this:

-

(1) Ten seconds of 770Hz tone.

-

(2) Short pulse indicating start of data.

-

(3) Data (one cycle at 1000Hz for a '1', one cycle at 2000Hz for a '0').

-

 

-

The last byte of data is a checksum that can be used to verify that the previous data read correctly.  The data length is not stored, and there is no explicit "end" marker, so it's necessary to either know the length ahead of time or guess the position of the end based on the absence of data.  Because of the way the code works, you're limited to 64K of data, though in practice you'd be limited to less than 48K on a real Apple II unless you were using custom read routines.

-

 

-

Applesoft and Integer BASIC programs write two consecutive blocks of data.  The first block contains the length of the second block.  CiderPress doesn't really need the first block, because the end of the recording is usually pretty obvious, but the existence of the first block makes it easier to guess what the second block contains.

-

 

-

Most cassettes include more than one copy of a program.  In some cases (such as Adventure International's "Asteroid") they are slightly different implementations, while in others it's the same program repeated.  Sometimes the program is repeated on the back side of the tape.  Magnetic tapes wear out if you play them too much, so redundancy was common.

-

 

-

Getting the Audio Data

-

 

-

You will need to capture the audio from the cassette tape on your computer.  This requires connecting a tape player to your PC while running audio capture software.  Using a sound editor is recommended.  Some examples:

-

 

-

Adobe Audition (www.adobe.com; expensive)

-

GoldWave (www.goldwave.com; shareware)

-

Audacity (audacity.sourceforge.net; freeware)

-

 

-

It isn't necessary to record each section of data from the cassette into its own WAV file.  CiderPress will try to find every chunk of data in a WAV file.

-

 

-

If you have a "line out" on your tape player and a "line in" on your PC sound card, use those.  If not, you can use the "microphone" input and the "headphone" out, though you will have to set the volume levels correctly.  In the "speaker" or "multimedia" control panel, set the microphone input gain to 50%.  Start up your sound editor.  If you have an input level meter (e.g. hit F10 in Adobe Audition), turn that on.  Play the cassette tape out loud until you hear a tone, then plug it into the computer and watch the input level.  You want to set the volume so that the input is as high as you can get it without exceeding the limit (this causes "clipping", which is a lot like a square wave but probably isn't going to help us here).  In Adobe Audition, make sure it stays above -3dB.

-

 

-

Once you have the volume level figured out, back the tape up to the start of the tone.  Hit "record" in your software and "play" on your tape player.  Record at 22.05KHz with 8-bit samples.  (Recording at CD quality -- 44.1KHz with 16-bit samples -- doesn't help and requires 4x the space.)  If your software shows an input meter while recording, continue to record until the volume level drops and stays low for at least 10 seconds.  If you can't monitor the input, you will either need to time the cassette, or just record for a long time and perhaps trim the excess off in the sound editor.  Make sure you get all of the data from the tape.  When you think you're done, pull the audio plug out of the tape player and keep listening for a little bit.

-

 

-

Tip: CiderPress only needs to see about a second of the lead-in, so it's okay to fiddle with the volume while the initial tone is playing.

-

Tip: in some cases, setting the volume a little too high can be beneficial.  It's better to clip some samples than have too little signal.  If at first you don't succeed, crank up the volume a notch and try again.

-

 

-

Save the WAV file, launch CiderPress, and open a ShrinkIt archive or disk image.  Files extracted from WAV files are added to the currently open archive.  Select the "import file from WAV" option in the Actions menu, and open the WAV file you created.

-

 

-

Decoding the Data

-

 

-

The display shows the name of an algorithm, and a box with six columns:

-

Index: relative position of the program on the tape.

-

Format: best guess at the nature of the data (Applesoft, Integer BASIC, etc).

-

Length: decoded length, in bytes, not including the checksum.

-

Checksum: the checksum, with "Good" if the checksum matched, "BAD" if it didn't.

-

Start sample: sample number (not byte offset) where the data began.  Useful when examining the data in a sound editor.

-

End sample: last good sample in the data.  If the data is bad, this is probably where things went wrong.

-

 

-

The text in the "format" column is a guess at the contents, based on the length of the current and previous segment.  For BASIC headers, the contents (e.g. length of program that follows) are shown in hexadecimal.  Applesoft also has an "auto-run" flag, set to either $55 or $D5.

-

 

-

The output of the Apple II is a blocky "square wave" rather than a smooth "sine wave".  Because of limitations in how quickly voltage levels can change, the output isn't perfectly square.  Because of the physical properties of and variations in magnetic media, the not-quite-square wave is rather rounded and wiggly.  After being stored in less-than-perfect conditions for 25-30 years, what you read back from an Apple II tape is pretty crazy.  Deciphering it can be tricky.

-

 

-

CiderPress provides different algorithms that you can apply:

-

"Zero Crossing" works very much like the Apple II does, and measures the distance between points where the signal changes from positive to negative.  This is usually a good place to start.

-

"Peak to Peak Width (Sharp)" measure the distance between peaks, which works a little better when the input has been affected by a DC bias (i.e. the entire signal has been shifted up or down, in some cases so far that it no longer crosses zero).

-

"Peak to Peak Width (Round)" is the same as "Sharp" but with different parameters.

-

"Peak to Peak Width (Shallow)" is another peak-width variant with different parameters.

-

 

-

Usually one will work better than the others.  In some cases, different algorithms may pull out different copies of a program from the same tape.  You can use the "start offset" column in the display to see which copy has been found.  (Note the start offset may vary by a couple of samples for different algorithms.)

-

 

-

Most cassettes can be recovered, even those that will no longer play on an Apple II.  If you find one that can't, you may want to keep the WAV recording anyway, on the off chance that in the future an improved algorithm can be developed that will decode it.

-

 

-

When CiderPress encounters data that it can't interpret, it stops trying to read from that section of the WAV file.  For this reason, damaged entries will usually be shorter than undamaged ones.  If a file appears to have the correct length but the checksum still doesn't match, it means the signal was sufficiently distorted to make a '0' bit look like a '1' bit, which is actually pretty hard to do.  In most cases the decoder will either make an accurate determination or will conclude that the signal is too distorted to process.  So far only one case has been found where the checksum was deliberately altered, as part of a copy protection scheme (Sargon II).

-

 

-

If the tape has more than one program on it, you can usually tell if it's multiple copies of the same thing by comparing lengths and checksums.  If the checkums say "good" but have different values, you probably have two different programs, or two slightly different versions of the same program.

-

 

-

Saving the Data

-

 

-

The best way to save cassette programs is to a DOS 3.3 disk image, because it's difficult to run Integer BASIC programs under ProDOS.  If you're only extracting Applesoft or binary programs, the choice of disk format doesn't really matter.

-

 

-

Click on the file to save and then click "import".  This brings up the import options screen.  You can change the file name, file type, and for binary files you can alter the start address.  The file name you enter will be altered as needed for compatibility with the currently open archive or disk image.  If it's the same as an existing file, numbers will be appended to the end of the name.

-

 

-

For binary files, a range of addresses should be shown on the cassette tape or on instructions included with it.  It should say something like "800.1A6FR", which is the Apple II system monitor command to read data from the tape and store it in memory (for this example, in locations $0800 through $1A6F, inclusive).  CiderPress is able to determine the length accurately, so when you supply the start address CiderPress fills out the end address for you.

-

 

-

You can import files with bad checksums.  They will almost certainly be damaged or, more likely, incomplete.

-

 

-

Once the file has been imported, you will be able to view it as you would any other file.  Because loading machine language subroutines from disk wasn't an option, many BASIC programs included embedded routines.  It's not uncommon for Integer BASIC programs to appear to have only one or two lines of POKEs and HIMEM: statements until they're run for the first time.  In some cases, the entire program was written in assembly language and then wrapped in BASIC so that the tape could be loaded with "LOAD" instead of from the monitor.  Such programs may not be all that interesting to look at when listed with the BASIC file viewer.

-

 

-

There are three cases where CiderPress currently guesses the file type incorrectly:

-
  • Applesoft BASIC arrays stored on tape.  These have a 3-byte header section like Applesoft programs, but just contain data.  The "RECALL" statement is used to load these. -
  • Shape tables stored on tape.  These have a 2-byte header section like Integer BASIC programs, but contain shape table data.  The "SHLOAD" statement is used to load these. -
  • Custom data load routines.  For example, the Integer BASIC program "Starfleet Orion" calls the F8 ROM tape read routines directly, so no BASIC header is present.
-

 

-

A quick examination of the file with the CiderPress file viewer should reveal whether or not it is not a BASIC program.  Anything that isn't should be saved as a binary file.  Actually making use of them is left as an exercise for the reader -- start with the Applesoft BASIC reference manual, or find an Apple II emulator that emulates the cassette I/O port.

-

 

-

Other Notes

-

 

-

You may be tempted to store copies of the WAV file in MP3 format.  This is not recommended.  CiderPress cannot decode MP3s, and the decoded MP3 file is less likely to work than the original.  However, experiments with converting the sound files in and out of MP3 format suggest that "healthy" files are unharmed at reasonable compression ratios.

-

 

-

You don't need fancy equipment.  Connecting the headphone jack of a 15-year-old "boom box" to the microphone jack of a low-cost PC with on-motherboard audio works just fine.

-

-

- diff --git a/ciderpress/app/Help/html/t112.htm b/ciderpress/app/Help/html/t112.htm deleted file mode 100644 index c0138d4..0000000 --- a/ciderpress/app/Help/html/t112.htm +++ /dev/null @@ -1,37 +0,0 @@ - - - Import BASIC Program - - - - - - - - - -

Import BASIC Program

-

 

-

This feature allows you to import an Applesoft BASIC program from a text file.  The text file must contain an Applesoft program listing, with one program line per line of text file.

-

 

-

CiderPress makes no attempt to verify the correctness of the program.  It's possible to import code that can't possibly work.  (The same holds true for loading a program from a text file with the "exec" command on an Apple II.)

-

 

-

Converting BASIC to Text

-

 

-

Going the other direction -- converting a BASIC program to a text file -- is easy.  Just extract the BAS program to windows, selecting "Configure for easy access in Windows".  The conversion is automatic.  Note that you must uncheck "Prefer syntax highlighting on BASIC programs" in the file viewer preferences, or it will be saved as a .RTF file to preserve the colored text.

-

 

-

You can also just open the BASIC program in the file viewer, select the text with Ctrl-A, copy it with Ctrl-C, and then paste it into a text editor such as Windows Notepad.

-

 

-

If you want to convert a program to a text file from within an Apple II emulator, add the following lines to the start of the file:

-

 

-

1 GOTO 4

-

2 D$=CHR$(4) : PRINT D$"OPEN LISTING" : PRINT D$"WRITE LISTING"

-

3 POKE 33,33 : LIST : PRINT D$"CLOSE" : TEXT : END

-

4 REM CONTINUE

-

 

-

To generate a listing, delete line 1 and run the program.  The "POKE 33,33" prevents Applesoft from splitting each program line into multiple output lines.  This will work under both DOS and ProDOS.

-

 

-

It is possible for carriage returns embedded in string constants or REM statements to be lost.  Fortunately these are rare.

-

-

- diff --git a/ciderpress/app/Help/html/t13.htm b/ciderpress/app/Help/html/t13.htm deleted file mode 100644 index c9b7c74..0000000 --- a/ciderpress/app/Help/html/t13.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - Tool - Disk Editor - - - - - - - - -

Disk Viewer

-

 

-

The Disk Viewer provides an interface similar to "disk zap" utilities on the Apple II.  You can view the contents of a disk as a collection of 256-byte tracks and sectors, as a series of 512-byte blocks, or (for nibble images) raw track data.  The current version of CiderPress does not allow sector or nibble editing, so the "write" button is always disabled.

-

 

-

Start the viewer by selecting it from the menu or by clicking on the toolbar icon that looks like a 5.25" floppy.  Select whether you want to open a disk image file or a Windows volume.  (The 3rd choice opens the disk image you currently have open in the file viewer.  If nothing is open, or you've opened a file archive, the 3rd button will be greyed out.)  Choose a disk image or volume to open.  In most cases, the viewer opens immediately.

-

 

-

If CiderPress can't identify the filesystem, or if you have "Confirm disk image format" selected in Disk Image Preferences, you will be allowed to review and change the disk image characteristics before the image is opened.  If the disk doesn't have a filesystem that CiderPress recognizes, you can use one of the "generic" entries to specify how the sector ordering should be handled.  (If it's a 140K disk, you probably want "Generic DOS"; if it's an 800K disk, you probably want "Generic ProDOS" and "View data as blocks".)  CiderPress may have been able to guess at the image sector ordering from the filename or file format, so if it's not set to "unknown" it's probably best to leave it alone.

-

 

-

CiderPress will automatically choose between displaying 512-byte blocks or 256-byte sectors based on the filesystem of the image you're opening (sectors for DOS disks, blocks for everything else).  If CiderPress can't identify any sectors on a nibble image, only the track nibble viewer will be available.  You can override the setting from the "Confirm disk image format" dialog described above.

-

 

-

The viewer opens on track 0/sector 0 or block 0.  Data is read into the buffer and displayed as a hex dump.  The ASCII on the right half of the window represents the data after a "High ASCII" conversion (technically: the high bits have been stripped off).

-

 

-

You can move to another location on the disk by adjusting the track/sector/block numbers and clicking on the "Read" button.  You can also use the "Read Next" and "Read Prev" buttons to move forward or backward.  If you prefer your numbers in hex, just hit the "Hex" checkbox to change how they are displayed and interpreted.

-

 

-

The disk viewer will allow you to see every block on the disk, including those that are part of sub-volumes.  If you want to open a sub-volume as a disk, click on the "Sub Volume" button and choose the volume from the list.  (If a disk has no sub-volumes, the button will be disabled.)  Learn more about DOS sub-volumes.

-

 

-

You can also choose to open and follow a file.  Click on the "Open File" button to bring up the file selection box.  Type the full pathname of the file as it appears when you open the disk with the CiderPress "Open..." menu item.  For a ProDOS disk, this means typing the full path with subdirectory names separated by colons (':').  If you want to try to open a resource fork, click the "Open resource fork" checkbox.  (NOTE: this feature does not currently work for HFS disks.)

-

 

-

Move through the file with the "Read Next" and "Read Prev" buttons.  The track/sector or block number will update automatically as you move.

-

 

-

You can't open files stored in embedded sub-volumes this way.  Instead, you need to open the sub-volume, and then open the file.

-

 

-

Bear in mind that what you are seeing is the actual sector or block data from the disk, not just part of a file.  In some cases you will see junk after the end of the file.  Also, attempting to view files on a DOS 3.3 disk while in 512-byte block edit mode will produce strange results, because two adjacent 256-byte sections of a file might be in completely different parts of the disk.  You will be shown the block that holds the first half of the 512-byte section of the file.  (If this doesn't make sense, don't worry about it -- just leave the default settings alone and view DOS disks as 256-byte sectors.)

-

 

-

DOS and ProDOS disks can hold "sparse" files, where some blocks filled with zeroes aren't actually stored on the disk.  If you move into a "sparse" block, the sector display will be replaced with a status message and the block number will be set to zero.  You will see one of these for every sparse block in the file.  On CP/M disks, empty files are stored without any block allocation, so if you try to follow an empty CP/M file you will see an appropriate message.

-

 

-

RDOS uses a sequential sector format, similar in structure to Pascal, but it's unusual in that it does not know how to "give up" storage.  In some cases you may find a few sectors allocated past the actual end of file, notably in some BASIC programs that apparently had some debugging statements deleted before the disks were shipped.  For "RDOS 3" disks, only the first 13 of 16 sectors on each track are used.

-

 

-

Nibble images are simply raw track data.  Identifying sectors on a disk requires interpreting that data.  CiderPress takes its best guess at the format of the disk, but if it can't find a reasonable number of readable sectors it gives up and only shows nibble data.  For cases where valid sectors were found, you will be able to choose between several sector formats in the sector or block display from a drop list in the lower right corner.  The "Patched" versions of DOS 3.2 and DOS 3.3 are like "standard" but they ignore epilog bytes and address field checksums, so using these should allow you to read disks with mild forms of copy protection.

-

 

-

Not all sectors on a nibble image will be readable.  Those that aren't will show an error message.  In some cases, changing the sector parameters with the drop-down list at the bottom right of the dialog will let you see more.  (One interesting example: floppy disks that were bootable under both DOS 3.2 and DOS 3.3 have two different copies of track 0 sector 0.  Switch from "DOS 3.3" to "DOS 3.2" and back again to see what's there.)

-

 

-

Click on the "Done" button to close the editor.

-

 

-

Some interesting places to visit:

-

 

-

DOS: catalog usually starts at track 17, sector 15, and goes down to track 17, sector 1.

-

ProDOS: volume directory in blocks 2 through 5.

-

Pascal: volume directory in blocks 2 through 5.

-

CP/M: volume directory in blocks 24 through 27.

-

RDOS: catalog starts on track 1 sector 0.

-

 

-

RDOS actually includes all of tracks 0 and 1 in a file called "RDOS 2.1 COPYRIGHT ...".  On nibble images of 13-sector disks, this file may be partially unreadable.

-

-

- diff --git a/ciderpress/app/Help/html/t18.htm b/ciderpress/app/Help/html/t18.htm deleted file mode 100644 index d94c222..0000000 --- a/ciderpress/app/Help/html/t18.htm +++ /dev/null @@ -1,130 +0,0 @@ - - - Appendix - About Disk Images - - - - - - - - - - -

About Disk Images

-

 

-

Disk images are copies of entire Apple II floppy disks stored in a single file.  There are several different ways to generate and store them, some designed for uploading to web pages, others for use with Apple II emulators.  There are also a number of different ways of storing files on an Apple II disk, the most prominent being the DOS 3.3 and ProDOS filesystems, so opening an image file and getting at the files inside can be tricky.

-

 

-

CiderPress takes most of the pain out of opening disk images by automatically determining the storage format and filesystem for many common formats.  You can view the format for an open disk image with the Archive Info feature, and you can attempt to override the automatic settings by enabling "Confirm disk image format" in Disk Image Preferences.

-

 

-

There are five attributes that must be determined before a disk image can be opened: outer format, image file format, physical format, sector ordering, and file system format.

-

 

-

Outer Format

-

 

-

Sometimes disk images are compressed with gzip or another program to reduce their size.  If CiderPress sees a ".gz" or ".zip" extension, it will automatically uncompress the file when it is opened, and re-compress it if any changes are made.

-

 

-

CiderPress supports Zip/gzip compression in conjunction with all formats except ShrinkIt archives, so ".sdk.gz" and ".sdk.zip" won't open.  Generally speaking, archives compressed with ShrinkIt don't compress much, so this is uncommon.

-

 

-

Image File Format

-

 

-

This is the format of the file that you have on your PC.  The format is often indicated by the filename extension, e.g. ".sdk" for a disk packed with ShrinkIt.  The disk image file formats that CiderPress handles, with their most common extensions, are:

-

 

-

 

-

Universal Disk Image .2MG, .2IMG  

-

 

-

DiskCopy 4.2 .DSK, .DC, .DC6  

-

 

-

Sim //e HDV .HDV  

-

 

-

Dalton's Disk Disintegrator (DDD) .DDD  

-

 

-

Formatted Disk Image (FDI) .FDI  

-

 

-

Unadorned sectors .DSK, .DO, .PO, .D13, .HDV, .IMG, .RAW, .ISO  

-

 

-

Unadorned nibbles .NIB, .NB2  

-

 

-

NuFX (ShrinkIt) .SHK, .SDK  

-

 

-

TrackStar .APP  

-

  

-

 

-

The "unadorned" format refers to files that are nothing but disk data.  The other formats have headers that must be removed before the data can be accessed.  For some formats, such as NuFX and DDD, that data may need to be uncompressed before it can be used.

-

 

-

The Image File Format is reliably detected by CiderPress, and can't be overridden.  If you think CiderPress has got it wrong, you can usually override its choice by changing the file extension.

-

 

-

Identifying DDD archives is tricky, because there is little information in the file format to distinguish it from an ordinary file.  The only way to tell is to try to unpack the file and see if you run out of compressed data at roughly the same time as you output 140K of uncompressed data.  Further complicating matters is that DOS DDD archives are stored as binary files but with a length of zero, so CiderPress has to guess that it's a DDD archive and then modify the length.  CiderPress will adjust a DOS 'B' file if it has an address and length of zero, is at least 8 sectors long, and has angle brackets (<>) in the filename.

-

 

-

Please do not use DDD v2.0 under DOS; it has a bug that can cause the last sector on the disk to be corrupted.  Because the DDD file format lacks any sort of checksum, the corruption can go undetected.  Use DDD v2.1 or later under DOS.  Better yet, use ShrinkIt to create your disk images.

-

 

-

TrackStar images are created and used by the TrackStar Apple II emulator hardware.  This board was developed many years ago, and is rare today.  The images are stored in nibble format, but have a variable length with a maximum of 6525 bytes per track.  Standard ".nib" images use 6656 bytes per track, which means that converting from ".app" to ".nib" requires adding extra bytes in inter-sector gaps, and converting the other way requires finding and removing unnecessary bytes.  Neither of these is 100% reliable, especially on copy-protected disks, so converting to and from ".app" only works well for unprotected disks.

-

 

-

The TrackStar's image maker creates 40-track and 80-track images.  On a typical 40-track image, the last 5 tracks are empty, but some forms of copy protection used an additional track at the end of the disk.  You can view the contents, if any, with the Disk Viewer.  When converting to or from standard 35-track disk images, the last 5 tracks are dropped or zero-filled.  80-track images are like 40-track images but with "half tracks" stored as well.  No other disk image format supports half-tracking, so CiderPress just skips every other track.

-

 

-

FDI images, created by "Disk2fdi", are similar to TrackStar in that they can have more than 35 tracks and use a variable-length track format.  The "raw" images contain an image of the magnetic pulses captured from a PC floppy drive while reading an Apple II disk.  FDI images may also contain nibble images of 3.5" disks, something no other format handles.  The images are converted internally to variable-length nibble format, which means they can be converted to TrackStar format without losing data, but not to .NIB.

-

 

-

A ".ISO" image of a CD-ROM is essentially equivalent to ".PO".  ".ISO" images of Macintosh-partitioned or HFS CD-ROMs can be accessed directly.

-

 

-

Physical Format

-

 

-

Once the headers are stripped and the data is uncompressed, you're left with one of two things: a stream of raw "nibble" data, stored exactly as it appeared when read from a 5.25" Apple II disk drive, or a stream of 256-byte sectors.  This value indicates which of the two formats is used.

-

 

-

The most common nibble image format (.nib) has 6656 bytes per track, but occasionally you will find a ".nb2" image with 6384, or a ".app" TrackStar image with variable-length tracks.  CiderPress supports all formats.

-

 

-

Sector Ordering

-

 

-

Apple II floppies typically have 16 sectors.  The various Apple II operating systems used different "sector interleave" values for performance reasons, resulting in some confusion about where exactly a given track and sector resides in a sector-based disk image.  Nibble images include the sector addresses, so there is no ambiguity.

-

 

-

There are four common ways to order the sectors, defined in terms of the operating system used to create the disk image:

-

 

-

Physical order: no interleave is used.  Nibbles images always use this, and the Copy ][+ ".IMG" format is an "unadorned" file with this ordering.

-

DOS order: "-2" interleave is used.  Programs commonly used for transferring 5.25" disk images will read the disk as DOS sectors, resulting in a DOS-ordered ".DO" image.

-

ProDOS order: "+2" interleave is used.  ShrinkIt reads disks as a series of ProDOS blocks, and once extracted the disk is a ProDOS-ordered ".PO" image.

-

CP/M order: "+3" interleave is used.  This would only be generated by a disk image creator running under CP/M, so you're unlikely to find an image in this order.

-

 

-

The sector order reflects the sequence in which the sectors were written into the image file.  It allows us to convert between "physical" order and the stored order, which is important for proper handling of the "filesystem" attribute (described next).

-

 

-

It is possible to override the sector ordering value.  If you do, CiderPress takes it as a recommendation, and will try that order first.  If it is unable to process the image using the order you requested, it will go ahead and try Physical, DOS, and ProDOS.

-

 

-

13-sector ".d13" images are always stored in ascending sector order.

-

 

-

Filesystem Format

-

 

-

This is the key.  It tells CiderPress how it should go about looking for files inside the disk image.  The attribute defines not only how the files are arranged, but also how sectors should be pulled out of the image.

-

 

-

The supported filesystems are as follows:

-

 

-

 

-

Filesystem Sector Order  

-

 

-

ProDOS ProDOS  

-

 

-

HFS ProDOS  

-

 

-

UCSD Pascal ProDOS  

-

 

-

DOS 3.2/3.3 DOS  

-

 

-

CP/M CP/M  

-

 

-

RDOS 3.2 Physical  

-

 

-

RDOS 3.3 ProDOS  

-

 

-

UNIDOS/AmDOS DOS  

-

 

-

OzDOS DOS  

-

 

-

 

-

CiderPress also supports CFFA-style fixed partitions and Macintosh-style partition maps.

-

 

-

If the sector order for the filesystem format matches the Sector Ordering attribute, then reading the correct sector from the disk image is easy, because the image was written in exactly the same way that the filesystem needs to be read.  If they don't match, then some amount of "sector swapping" is required to read the right sector from the right place.

-

 

-

If a disk doesn't actually have a filesystem -- perhaps it's an Apple II game that has its own disk format -- you will not be able to open it with CiderPress and view it as a collection of files.  You can, however, open it with the Disk Sector Viewer.  To provide the proper translation of sector numbers, you can use one of the "generic" filesystem formats.  These don't define a file structure, but do let you describe the ordering of the disk.  For example, if you use the "Generic DOS" entries, you will be able to read the disk exactly as you would with a "disk zap" utility.  If you select the "Generic ProDOS" entry, you will get results equivalent to a ProDOS "block zap" utility.

-

 

-

The "generic" entries are only available when opening disk images with the Disk Editor.

-

 

-

Some disks have embedded DOS "sub-volumes", e.g. DOS 3.3 embedded in a ProDOS disk.  See the embedded DOS volume section for more information.  CFFA cards, partitioned hard drives, and CD-ROMs are handled similarly.

-

-

- diff --git a/ciderpress/app/Help/html/t187.htm b/ciderpress/app/Help/html/t187.htm deleted file mode 100644 index 0e57312..0000000 --- a/ciderpress/app/Help/html/t187.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - Tool - Disk Image Converter - - - - - - - - - -

Disk Image Converter

-

 

-

This handy tool converts disk images from one format to another.  In this case the word "format" refers to the image file format (2MG, SDK, etc.) rather than the disk filesystem format (DOS, ProDOS).  With this utility you can add and remove 2MG headers, change DOS-order disks to ProDOS-order, convert 5.25" floppies to nibblized formats, and so on.  Read more about disk images here.

-

 

-

Start by selecting the image to open.  If the sector ordering cannot be determined, or if you have "Confirm disk image format" enabled in Disk Image preferences, you will be prompted to specify it.  (You will also be able to set the filesystem format, but that actually has no effect on the conversion process.  If it couldn't be determined, the dialog will show "Generic ProDOS blocks".)

-

 

-

After the image has been opened and examined, you will be asked to select the format of the new image.  One or more of the choices may be unavailable depending on the source image format.  For example, only 5.25" floppy images can be converted to nibble format, and only 3.5" 800K floppies can be converted to DiskCopy 4.2.

-

 

-

You can also choose to compress the file with gzip.  This makes the file smaller and adds a ".gz" extension to the filename.  ShrinkIt archives are already compressed, so the gzip flag is ignored.  A few emulators and utilities can handle DOS-order images with gzip (.do.gz), but generally speaking you should only add gzip compression if you're planning to store the images rather than use them.  The "gzip" checkbox is disabled for images larger than 32MB, because CiderPress doesn't open compressed images larger than that.

-

 

-

Once you have selected the format, click "OK".  You will be prompted for the name of the new archive.  If you don't specify an extension, the correct one will be added for you.  Click "Save" to write the image.

-

 

-

Most disk images are copied as a series of blocks, either ProDOS blocks or pairs of sectors on a 16-sector floppy.  If you are copying from a nibble format to a sector format, any unreadable blocks will be skipped.  A warning dialog will let you know how many had to be skipped over.  If you copy from a nibble format to a new image with the same nibble format (e.g. you're just adding or removing a 2MG header), the data is copied as nibble tracks, and any errors or copy protection are left undisturbed.  If the format changes, e.g. you're copying from ".nib" to ".nb2" or ".app", it may not be possible to copy the data directly because the formats are incompatible.  In such cases the image will be copied as formatted blocks, converting in and out of nibbles as appropriate.

-

 

-

You will be offered the opportunity to open the image in CiderPress to test it.  As with all disk images, this only works if the image has a recognizable filesystem.

-

 

-

The recommended image formats are:

-

5.25" disks: DOS-order .DO, or (if necessary) .NIB

-

3.5" disks: ProDOS-order .PO or .2MG

-

anything else: ProDOS-order .PO or .2MG

-

 

-

These will work with the broadest set of emulators and utilities.  Disk images that are 2GB or larger must be stored in .PO format.

-

 

-

Nibble and 2MG formats can store a volume number that DOS uses.  CiderPress takes care to preserve this value when converting between formats.

-

 

-

Comments in 2MG files are currently lost when images are converted.  This may be fixed in a future version of CiderPress. - -

- diff --git a/ciderpress/app/Help/html/t19.htm b/ciderpress/app/Help/html/t19.htm deleted file mode 100644 index d7a19e8..0000000 --- a/ciderpress/app/Help/html/t19.htm +++ /dev/null @@ -1,57 +0,0 @@ - - - Preferences - General - - - - - - - - - -

General Preferences

-

 

-

You can access General Preferences by selecting "Preferences..." from the "Edit" menu.

-

 

-

This screen is divided into a number of different sections.  Click on the tabs near the top to change to a different set of preferences.  Click on "OK" to accept the changes and close the dialog, "Apply" to accept the changes without closing the dialog, and "Cancel" to throw away any changes you have made since the last "Apply".

-

 

-

Columns

-

 

-

Place check marks next to the columns you want to see on the screen, and remove them from those you don't.  ("Disabled" columns are actually just zero pixels wide, and can be hidden or restored by clicking on the dividers in the column headers and dragging.)

-

 

-

Clicking on the "Defaults" button restores the displayed columns to the default set, and resizes all columns appropriately for the current screen dimensions and font choices.

-

 

-

NuFX (ShrinkIt) archives

-

 

-

Options that only affect ShrinkIt archives.  If you select "Mimic ShrinkIt quirks", ShrinkIt compatibility mode will be enabled in NufxLib.  This has the following effects:

-

 

-
  • The first file added to an archive will have an empty 200-byte comment attached. -
  • All files compressed with Dynamic LZW/1 or /2 will be one byte longer. -
  • Files smaller than 512 bytes are never compressed. -
  • Archives with SEA wrappers have an extra byte added to the end.
-

 

-

There's really no value in enabling any of these, but the archives that are created very closely resemble those created by GS/ShrinkIt.  In theory, a poorly-written application might depend on these quirks, and have compatibility problems with CiderPress if they weren't enabled.  In practice, everything tested so far works just fine without needing to have these enabled.

-

 

-

If you select "Reduce error checking", CiderPress will ignore bad CRCs and some forms of damage to compressed data.  Enabling this is generally a bad idea, because it means you will not be notified when corrupted data is found.  In some rare circumstances, however, it can allow you to recover some data from damaged or poorly-formed archives.

-

 

-

See the NufxLib API documentation on www.nulib.com for more details.

-

 

-

Filename munging

-

 

-

Enabling "Lower-case DOS 3.3 filenames" causes the names of files pulled from DOS 3.3 disks to be converted to a mix of upper and lower case, as if they were book titles.  The mixed-case names will be used when the files are extracted, so this is best used when preserving the original names is not important.  The flag does not, however, affect the names of files added to DOS disks (which are always upper-case-only).

-

 

-

"Show spaces as underscores" does exactly what it says: all spaces in all files are converted to underscores.  This is similar to the way MP3 files are often distributed, and is useful for files that will be placed directly on a web server.

-

 

-

File type associations

-

 

-

Clicking this button brings up the Edit File Associations dialog.

-

 

-

Miscellaneous

-

 

-

The "Strip pathnames when pasting files" flag determines how the paths of ProDOS files are handled when pasting files from the clipboard.  If set, the leading path is removed before the files are pasted.  You should set this flag if you just want to copy files around, but clear it if you're moving entire subdirectories and want to retain the hierarchy.  The flag affects pasting, not copying, so you don't need to re-copy files after changing this flag.

-

 

-

If "beep when operations complete successfully" is checked, CiderPress plays a sound when various operations (e.g. adding or extracting files) complete successfully.

-

-

- diff --git a/ciderpress/app/Help/html/t20.htm b/ciderpress/app/Help/html/t20.htm deleted file mode 100644 index 2e405ee..0000000 --- a/ciderpress/app/Help/html/t20.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - Disk Image Characteristics - - - - - - - - - -

Disk Image Characteristics

-

 

-

This comes up when you are opening a disk image and have either checked the "Confirm disk image format" box in Disk Image Preferences or are opening a disk that CiderPress does not fully recognize.

-

 

-

If CiderPress doesn't recognize the disk, you will probably need to choose a filesystem format, and may need to select the sector ordering.  For more information about these fields, see the detailed disk image discussion.

-

 

-

When opening an image with the disk sector viewer, you can select one of the "generic" formats if you don't know what format the disk is in.  The format you choose is combined with the sector ordering value to determine how blocks or sectors are read from the disk.  If you're trying to open the image in the file list, the "generic" formats will not be available.

-

-

- diff --git a/ciderpress/app/Help/html/t201.htm b/ciderpress/app/Help/html/t201.htm deleted file mode 100644 index 9547a8c..0000000 --- a/ciderpress/app/Help/html/t201.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - Tool - SST Image Merge - - - - - - - - - - -

SST Image Merge

-

 

-

This tool is used to combine two disk images created by the "SST" utility into a single ".nib" disk image.

-

 

-

SST is "Saltine's Super Transcopy", a nibble copier modified to read raw nibble data from disks and write it to two floppy disks.  The usual approach to creating .nib files is to run SST on a real Apple II, create images of the two disks, copy them to a PC, and then run SST in an emulator to copy the two sides onto a "blank.nib" file.  The advantage of this approach over creating ShrinkIt or ".do" files is that in many cases the copy protection can be left more or less intact.

-

 

-

CiderPress can't make the first half of the process easier, but it can simplify the second.  Instead of running an emulated SST to combine the images, just use the "SST Image Merge" function from the Tools menu.  You will be asked to locate the first side, then the second side, then asked for the name of the output file.  The pieces are merged immediately.  You are given the option of opening the newly-created disk in CiderPress, but as with all disk images this will only work if the disk has a recognizable filesystem.  The NIB format is generally used for disks without filesystems, so you may have to test the image by booting it in an Apple II emulator.

-

 

-

If CiderPress notices anything unusual about the images, you will be notified.  A warning will be shown if they don't appear to be images of SST-created disks, or if they were specified in the wrong order.

-

 

-

You can transfer the SST disk images from your Apple II in any format that CiderPress supports.  The most popular are unadorned DOS-order files (.DO) or ShrinkIt (.SHK/.SDK).  Since the disks don't have a recognizable filesystem, CiderPress' automatic sector order determination algorithms may not work, so it is important to use the appropriate filename extension on unadorned formats.  If CiderPress can't figure it out, or if you have "Confirm disk image format" enabled, you will be prompted to enter it.  (The "filesystem format" will usually show "Generic DOS sectors"; this is normal, and can be left alone.)

-

 

-

SST has some fancy features (nibble counting, track synchronization) that CiderPress does not try to emulate.  If you need special parameters in SST, you will need to use the original method.

-

-

- diff --git a/ciderpress/app/Help/html/t203.htm b/ciderpress/app/Help/html/t203.htm deleted file mode 100644 index 97eeaef..0000000 --- a/ciderpress/app/Help/html/t203.htm +++ /dev/null @@ -1,43 +0,0 @@ - - - Edit File Attributes - - - - - - - - - - -

Edit File Attributes

-

 

-

To edit an entry's attributes, select the entry from the list., and activate the "Edit attributes..." command.  You may only select one entry at a time.

-

 

-

The pathname and modification date will be displayed but may not be changed.  (To change the pathname, use the rename feature.)  The file type will be shown in the drop-down box, and may be changed by selecting a new entry.  Tip: if you click in the drop box and type a letter, you will move to the next entry that begins with that letter.  This can make it easier to find a file type by its three-letter abbreviation.

-

 

-

The auxilliary type (usually called "aux type") is shown as a 4-digit hexadecimal number, and may be edited freely.

-

 

-

The type description is based on both the file type and the aux type.  For example, type "LBR $E0" with aux type "8002" is listed as a "ShrinkIt (NuFX) document" in the Apple File Type Notes.

-

 

-

The access flags may be enabled or disabled individually with the checkboxes.  Most files have "Read" enabled and possibly the "Backup needed" flag.  Checking the bottom three boxes results in a "locked" file, while unchecking them indicates an "unlocked" file.

-

 

-

Not all formats support all possible values.  For example, on DOS 3.3 disks, checking the "write enabled" flag unlocks the file, while un-checking it locks the file.  DOS 3.3 only supports a few file types, and only BIN has a meaningful aux type.  HFS directories don't have file types at all.

-

 

-

Changing the file type on a DOS 3.2/3.3 disk should be done with caution.  BASIC programs and binary files have the file length embedded in the first sector of the file, and binary files have a start address as well.  CiderPress does not add or remove these values when changing the file type.  If, for example, you change a file of type TXT to type BIN, you will find that the first four bytes of the text file have mysteriously vanished, and the start address and length are strange.  If you change the aux type, and then change it back to TXT, you will find that the first two characters in the text file are different.

-

 

-

WARNING: if you change a DOS file to BAS, INT, or BIN, there's a good chance that the length value pulled out of the first sector will be larger than the file.  This may not be immediately apparent because CiderPress truncates the length for safety.  The next time CiderPress opens the disk, it will mark the file as "suspicious", and mark the disk read-only.  The only way to change the file type back will be to use a sector editor in an emulator.  If you want a file from another source (Windows text file, ProDOS disk, etc) to have a specific file type, add it to a ShrinkIt archive or ProDOS disk image, change the file type, and then copy it to a DOS volume.

-

 

-

This behavior is by design.  You can take advantage of it when copying certain types of files around.  Cracked games with short DOS loaders can be copied by first changing the file type to 'S' ($F2), copying the file, pasting it into the new disk, and then changing the type back to BIN.  This works because 'S' has no embedded length, and CiderPress does not alter the embedded aux type unless you explicitly change it.

-

 

-

When changing the file type to BIN, you will have the opportunity to change the aux type.  If you leave it alone, the aux type will be pulled out of the first sector of the file.  If you change the value in the edit box, the new value will replace the old.

-

 

-

DOS 3.2/3.3 disks only support eight file types.  The most common are 'B' (BIN), 'A' (BAS), 'I' (INT), and 'T' (TXT).  Less common are 'R' (REL) and 'S' ($F2), and you will rarely see the alternate 'A' ($F3) and 'B' ($F4).  The hex-valued types, like $F2, are used because there is no direct mapping between the DOS type and a ProDOS type.

-

 

-

Pascal disks are similar.  Three types are commonly used: PDA (generic data), PTX (Pascal text editor format), and PCD (Pascal code format).  The others are "untyped" (NON), "bad blocks" (BAD), "info" ($F3), "graffile" ($F4), "foto" (FOT), and "securdir" ($F5).

-

 

-

HFS uses 4-character values for the file type and "creator type".  Apple defined special handling for files with a creator type of "pdos".  CiderPress automatically recognizes those values, and presents them as standard ProDOS file types.  You can choose between ProDOS and HFS types on HFS disks and ShrinkIt archives.

-

-

- diff --git a/ciderpress/app/Help/html/t21.htm b/ciderpress/app/Help/html/t21.htm deleted file mode 100644 index d896c7f..0000000 --- a/ciderpress/app/Help/html/t21.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - Appendix - Embedded DOS Volumes - - - - - - - - - - - -

Embedded DOS Volumes

-

 

-

The DOS 3.3 file format is wonderful for 140K 5.25" floppies, but requires some modifications before it will work on other kinds of media.  A number of authors came up with ways of putting one or more DOS 3.3 volumes onto an 800K floppy.  CiderPress recognizes most of them automatically.

-

 

-

Apple shipped two 3.5" drives, the AppleDisk 3.5 and the UniDisk 3.5.  The latter, combined with an interface card, would work on an Apple //e, and is responsible for the suffix used in some of the product names.

-

 

-

ProSel Uni-DOS (Glen Bredon)

-

 

-

ProSel-8 and -16 shipped with Uni-DOS, a way to format an 800K disk with 600K of space for ProDOS and 200K of space for DOS 3.3.  The embedded volume had 50 16-sector tracks.

-

 

-

DOS Master (Glen Bredon)

-

 

-

Essentially an enhanced version of Uni-DOS, DOS Master allowed placement of multiple DOS volumes on a single disk.  You could, for example, put five 140K DOS 3.3 disks on one 800K disk, and switch between them with the ",v" (volume) parameter.  The volumes could also be placed on a hard drive.

-

 

-

AmDOS 3.5 (Gary Little)

-

 

-

The "Amateur Disk Operating System version 3.5" software allowed storing two 400K DOS volumes on one 800K floppy.  Each DOS volume used 50 tracks of 32 sectors, which is the largest you can get without making significant alterations to DOS.  The change from 16 to 32 sectors broke compatibility with some programs (such as the "FID" file copying utility), but was manageable with a few well-placed patches.

-

 

-

UNIDOS (Unknown)

-

 

-

The disk format is identical to AmDOS.  It's unclear what the heritage of this is.

-

 

-

OzDOS (Richard Bennett)

-

 

-

This is another two-400K-disks-on-one-800K scheme, but the author decided to do something a little different.  Instead of putting the volumes one after the other, he stretched them out across the entire disk, using odd sectors for one volume and even sectors for the other.  For a device that reads 512-byte blocks, this makes a certain kind of sense.

-

-

- diff --git a/ciderpress/app/Help/html/t215.htm b/ciderpress/app/Help/html/t215.htm deleted file mode 100644 index 965512b..0000000 --- a/ciderpress/app/Help/html/t215.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - Convert Disk Image to File Archive - - - - - - - - - - -

Convert Disk Image to File Archive

-

 

-

This feature extracts every file it can find in a disk image and stores it in a ShrinkIt archive.  This is similar to extracting all files with "preservation mode" enabled and re-adding them, but is faster and easier.  It can be used in conjunction with "convert file archive to disk image" to resize a ProDOS volume.

-

 

-

Open a disk image and highlight the files you want to convert.  Items will be converted in the order that they appear, so if you want to leave the order undisturbed make sure you have them sorted in the original order (more information on this).  Select "Convert to file archive..." from the Actions menu.

-

 

-

The "Preserve empty folders" feature is useful when you plan to convert the file archive back to a disk archive, perhaps after modifying some files or just choosing a different disk size.  The ShrinkIt archive format describes a way to store empty folders, but it has never been used by any ShrinkIt utility and could cause some software to break.  Instead, CiderPress creates standard zero-length file entries with the name ".$$EmptyFolder".  These are automatically dropped when converting to a disk archive, but will appear as normal entries in the archive.  This option should therefore be enabled only when you plan to convert back to a disk image.

-

 

-

The selected default compression will be used when creating the ShrinkIt archive.

-

 

-

Text files on DOS 3.2/3.3 and RDOS disks (file type 'T') will be converted to ProDOS format.  This involves stripping "high ASCII" text from the file.

-

 

-

For performance reasons this feature holds most of the files in memory during the operation, making this somewhat memory-intensive.  If you are working with 32MB hard drive partitions, performance on systems with insufficient RAM may suffer.

-

 

-

Files in embedded volumes, such as a DOS volume inside an 800K ProDOS disk, can be converted.  However, they become just like any other file, and if the file archive is converted back to a disk image they will no longer be stored in an embedded volume.

-

-

- diff --git a/ciderpress/app/Help/html/t216.htm b/ciderpress/app/Help/html/t216.htm deleted file mode 100644 index bf9ecba..0000000 --- a/ciderpress/app/Help/html/t216.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - Convert File Archive to Disk Image - - - - - - - - - - -

Convert File Archive to Disk Image

-

 

-

With this feature, you can copy part or all of a ShrinkIt file archive to a new ProDOS volume in a single step.  When combined with the "convert disk image to file archive" feature, it's possible to resize ProDOS disks, converting 140K floppies to 800K floppies or even 32MB hard drive images.

-

 

-

The images created are always unadorned ProDOS-order (.PO) files.  If you need to have it in a different format, you can use the Disk Image Converter to change it.

-

 

-

Open a file archive and highlight the files you want to convert.  Items will be converted in the order that they appear, so if you want to leave the order undisturbed make sure you have them sorted in the original order (more information on this).  Select "Convert to disk archive..." from the Actions menu.

-

 

-

Choose the size of the disk to create from the set shown.  Standard removable media and hard drive sizes are available.  If you're not sure how much space the files require, click the "Compute" button.  The size requirements will be determined by creating a temporary 32MB volume with the selected files and adjusting for system overhead.  Any size options that won't work will be dimmed.

-

 

-

Filenames on ProDOS disks must start with a letter, be composed entirely of letters, numbers, and dots ('.'), and can be at most 15 characters long.  Any invalid characters will be stripped, and long filenames will be truncated.  CiderPress will try to preserve filename extensions (e.g. ".jpg"), and will make filenames that reduce to the same thing unique.  For example, "my&&file" and "my__file" both convert to "myfile", so the first is stored as "myfile" and the second as "myfile1".

-

 

-

ProDOS stores filenames using upper case letters only.  A modification added for the benefit of the GS/OS ProDOS FST allows mixed-case names with spaces in them, but the method used to store the case information can confuse older versions of ProDOS 8.  The "Allow lower-case ProDOS names" option in Disk Image Preferences lets you tell CiderPress whether to create lower-case names on ProDOS disks.  If you're planning to use the disk with a pre-v1.8 version of ProDOS 8, you should not enable the feature, or your files may be inaccessible.

-

 

-

The volume directory on a ProDOS disk holds at most 51 files.  If you need to put more than 51 files onto a ProDOS disk, make sure some or all of them are in folders, which do not have a limit.

-

 

-

If you are converting a ShrinkIt archive that contains a disk image, the disk image will be converted as a ProDOS-order (.PO) file.

-

 

-

For performance reasons this feature holds most of the files in memory during the operation, making this somewhat memory-intensive.  If you are working with 32MB hard drive partitions, performance on systems with insufficient RAM may suffer.

-

-

- diff --git a/ciderpress/app/Help/html/t22.htm b/ciderpress/app/Help/html/t22.htm deleted file mode 100644 index 7b14fdd..0000000 --- a/ciderpress/app/Help/html/t22.htm +++ /dev/null @@ -1,159 +0,0 @@ - - - Appendix - File Format Converters - - - - - - - - - - - -

File Format Converters

-

 

-

CiderPress can convert a variety of Apple II files into formats more easily accessible on a PC.  BASIC programs can be converted into text program listings, AppleWorks word-processing documents become RTF (Rich Text Format) documents, and graphics become Windows BMPs.

-

 

-

The end-of-line (EOL) marker on text files changes from system to system.  Macintosh and the Apple II use carriage returns (CR), UNIX systems use linefeeds (LF), and MS-DOS uses one of each (CRLF).  Windows programs vary in their ability to handle text files with different EOL formats, but they all handle CRLF.  CiderPress can convert all text files to Windows format if you desire.

-

 

-

DOS High ASCII (TXT)

-

 

-

Apple DOS stores text files with the high bit of every 8-bit byte set.  This causes most other operating systems to display characters from an "extended" character set (accents, tildes, etc.) instead of the intended characters.  This is usually undesirable, so all files from DOS disks with file type 'T' should be run through this converter to strip the high bit off.  This will also convert the end-of-line character to CRLF.

-

 

-

CP/M Text (NON)

-

 

-

CP/M text files use CRLF line terminators, but also follow a convention that has fallen out of use under Windows: a Ctrl-Z marks the end of the file.  This converter identifies text files on CP/M disks, and drops everything after the first Ctrl-Z.  Files with unusual control characters embedded in them may not be identified as text.

-

 

-

UCSD Pascal Text (PTX)

-

 

-

UCSD Pascal disks have text files with an unusual format (done so for the benefit of the editor).  These can be converted to text in a format that mimics the on-screen and printed output of the original.

-

 

-

UCSD Pascal Code (PCD)

-

 

-

The executable code files can be broken into segments, each of which is named and has a data type.  The converter breaks the file into segments, and displays the segment header along with a hex dump of the contents.

-

 

-

Applesoft BASIC (BAS)

-

 

-

Applesoft programs are stored in a "tokenized" format, meaning that BASIC keywords like "FOR" and "NEXT" are stored as a single byte rather than a text string.  This reduces program size and improves execution speed, but makes them hard to read on a PC.  The Applesoft converter produces output identical to what you would see if you loaded the program and typed "LIST".  If the appropriate preference is enabled, BASIC keywords, comments, and quoted text will be highlighted in color by default.

-

 

-

Integer BASIC (INT)

-

 

-

This is similar to the Applesoft converter, but for the older Integer BASIC format.  Again, the output is identical to the "LIST" command.

-

 

-

It was not uncommon to stash machine-language code snippets at the start or end of an Integer BASIC program, resulting in a collection of junk.  Usually some "LOMEM:" and "HIMEM:" commands were used to rearrange things so that the code gets hidden and only the BASIC program remains ("APPLEVISION", from the DOS 3.3 system master disk, is a classic example).  The converted output of the machine-language parts may not match what "LIST" does, which is a good thing -- in some cases LIST would loop forever.

-

 

-

Asm source (TXT, INT, and $F4)

-

 

-

The S-C Assembler used the DOS 'I' file type, but doesn't use the Integer BASIC file format (it's a line-oriented compressed text file).  Fortunately it's easy to tell the difference between S-C files and Integer programs.  This converts the file to plain text.

-

 

-

The DOS versions of LISA (Lazerware's Interactive Symbolic Assembler), v2.5 and earlier, used a mildly tokenized format stored in DOS 'B' files.  (This is the alternate type 'B', not the standard binary format; it shows up as type $F4 in CiderPress.)  The ProDOS and GS/OS versions used the ProDOS INT type reserved for Integer BASIC, with the source written in a compressed format.  The output is plain text, and matches the converted output of the original when written to a text file with the "write" command.

-

 

-

The file formats for LISA versions 2, 3, and 4 are different.  All three are handled.  Version 5 is equivalent to version 4, and the format for version 1 is unknown.

-

 

-

Merlin and Merlin-16 used a text format that crunched out excess spaces.  The Merlin converter puts the spaces back in, so the output resembles what you'd see in the Merlin-16 full-screen editor.  Merlin source files usually have filenames ending in ".S".

-

 

-

Disassembly (various)

-

 

-

Two modes of disassembly are currently supported:

-
  • Apple //e monitor listing.  This produces output identical to the monitor 'L' command on an Apple ][+ or //e, with two changes: 65C02 operands are supported, and NiftyList-style annotations are added. -
  • Apple IIgs monitor listing.  Output is identical to the monitor 'L' command on an Apple IIgs, with the addition of NiftyList-style annotations.  You may choose "short" (8-bit) or "long" (16-bit) registers.
-

 

-

See the Disassembly Notes section for more information.

-

 

-

8-bit Word Processor (various)

-

 

-

Currently supports Magic Window / Magic Window II "formatted" documents.  These have file type BIN and end in ".MW".

-

 

-

AppleWorks Word Processor (AWP)

-

 

-

CiderPress does a fairly good job of converting AWP documents to Rich Text Format (RTF) files that can be edited with WordPad or Microsoft Word (the former is a Windows "accessory", and should be available on all systems).  Most text styles are supported (bold, italic, underline, subscript, superscript), as well as paragraph formatting (left/right justified, centering) and some page layout features (left/right margins, page breaks).

-

 

-

AppleWorks Database (ADB)

-

 

-

Database files in AppleWorks are two-dimensional, with a fixed set of columns and one row for each entry.  This converts easily to CSV (Comma-Separated Value) format, which can be imported into Microsoft Excel or other applications.  The file viewer will show the data in CSV form, which isn't ideal but is much easier to read than the raw unconverted format.  The first row of data holds the column titles.

-

 

-

AppleWorks Spreadsheet (ASP)

-

 

-

Spreadsheets tend to be tied to a specific application, and AppleWorks ASP files are no exception.  Microsoft Excel does a pretty good job of converting formulas over, but you can expect to do some amount of hand-tweaking after conversion.  Most formulas will transfer, but some (like @AVG) don't, and multi-cell labels will be chopped up.  As with database files, these are converted to CSV format, which should be accepted by just about any modern spreadsheet application.

-

 

-

Apple IIgs Word Processor (GWP)

-

 

-

There are actually three formats here.  All of them convert common symbols and accented characters from the IIgs fonts to Windows fonts.  Not all of the symbols have equivalents, but many of them do.  Text written in languages other than English should convert correctly.

-

 

-

The supported formats are:

-
  • Teach document (GWP $5445).  The "Teach" application on the Apple IIgs created these, which have text in the data fork and formatting information in the resource fork.  Font size and style changes are supported, accented characters are converted, and an attempt is made to convert the typeface to something similar to the original.  The results are ususally pretty good. -
  • AppleWorks GS Word Processor (GWP $8010).  Same basic features as "Teach", plus some basic formatting features like centered and justified paragraphs.  The "header" and "footer" sections are displayed at the top of the converted document. -
  • Generic (GWP, any aux type except the two above).  Does a IIgs text conversion without any other reformatting.  If you have a text file that uses symbols or accented characters from the IIgs font values, you can change its file type to GWP to enable this converter.
-

 

-

ProDOS Folders (DIR)

-

 

-

If you select a folder from a ProDOS disk for viewing, this converter will display it in a format similar to the 80-column output produced by the ProDOS "catalog" command.  The active set of files are shown, followed by any deleted files that can be identified.

-

 

-

Enabling or disabling this converter has no effect on file extraction, because CiderPress doesn't explicitly extract folders.  (It simply creates them when needed.)

-

 

-

Resources (resource fork of any file)

-

 

-

Resource forks have a well-defined structure.  Each fork is a series of resources whose formats are defined by the user or by the system.  This converter separates the fork into individual resources and displays their contents as hex dumps.  Any recognized system-defined resources will have a type description displayed next to the resource type.

-

 

-

Hi-Res Graphics (FOT or 8K BIN, 280x192, six colors)

-

 

-

The classic Apple II graphics mode is "hi-res", featuring a resolution of 280x192.  In some respects it's 140x192 (because two pixels are combined to form one color), in others it's 560x192 (because the Apple II display hardware used a half-pixel shift to get colors on a television).

-

 

-

The Hi-Res converter produces a 16-color, 560x384 bitmap file (BMP) that takes into account half-pixel shifting and other oddities.  The results are usually identical to what you would see on an Apple IIgs RGB monitor.  (If you set just the right pixels you can get yellow and brown on the hi-res screen of a real Apple II; in practice, this never really came up, so it isn't emulated here.)

-

 

-

If you select the black & white output mode, you will get a 16-color 560x384 image using only black and white.  The image matches the Apple IIgs RGB monitor output except that the half-pixel shifting is left enabled.  This was done to match the display of the IIgs composite output and other members of the Apple II line, which don't disable half-pixel shifting in monochrome mode by default.

-

 

-

Double Hi-Res Graphics (FOT or 16K BIN, 560x192, sixteen colors)

-

 

-

This graphics mode, first introduced on the enhanced Apple //e, can also be treated as 140x192, because four pixels are combined to form each color.  In monochrome mode, the output is fully 560x192.

-

 

-

The Double Hi-Res converter produces a 16-color, 560x384 bitmap file (BMP) that attempts to recreate the output from an Apple IIgs RGB or composite output.  Because of various weirdnesses in the Apple II display hardware, this is harder than you might think.  As a result, there are actually four different ways to process the file.  You can choose between them in the file viewer, or pick a default from the "Double Hi-Res mode" setting in the File Viewer tab of Preferences.

-

 

-

Black & White: output is in black and white only, full 560 pixels across.  Output matches Apple IIgs RGB monitor display exactly.

-

Simple 140: produces 560x384 output as if the input were 140x384.  The simplest and most "obvious" approach, it produces inferior results because the Apple II video hardware doesn't work this way.

-

Sliding window: converts the picture the way Apple IIe Tech Note #3 describes, using a 4-bit sliding window.  The result closely matches the composite output on an NTSC device (monitor or television), but is much more blurry than the output of an RGB monitor because most transitions have a lot of color "fringes".

-

Latched color: a variation on "sliding window", this tries to reduce the fringes around transitions to and from black and white.  The result renders the colors fairly well while sharpening up text.

-

 

-

Super Hi-Res Graphics (PIC/PNT or 32K BIN, 320/640x200)

-

 

-

First introduced on the Apple IIgs, Super-Res was the first mode largely devoid of video idiosyncracies.  When you set pixels to certain colors, the output on an RGB monitor was exactly what you expected.  The resolution, which could be changed on every line, was 200 lines of either 320 pixels across with 4 bits of color per pixel, or 640 pixels across with 2 bits of color per pixel.  The way colors in the file were translated to colors on screen involves some minor color palette gymnastics.  The output of the converter is a 256-color 640x400 BMP.

-

 

-

Super-Res images were also the first to be regularly compressed, which isn't surprising since they're 4x as large as standard hi-res.  CiderPress can convert images in the following formats:

-

 

-

Unpacked ("PIC" $c1/0000)

-

Activision Paintworks ("PNT" $c0/0000)

-

Packed with PackBytes ("PNT" $c0/0001)

-

Packed Apple Preferred Format ("PNT" $c0/0002)

-

 

-

The Apple Preferred Format allows for images of arbitrary dimensions.  CiderPress supports up to 1280x1024.  Paintworks images are 320x396, and convert to a 640x792 BMP.

-

 

-

3200-Color Super Hi-Res Graphics

-

 

-

A clever fellow named John Brooks figured out that, if you changed palettes at just the right time, you could get more colors on the screen than would be otherwise possible.  The results were sufficiently compelling to cause a number of GIF and JPEG converters to spring up, as well as one full-fledged 3200-color paint program.  The files are always 320x200, and the output from the file converter is a 24-bit 640x400 BMP.

-

 

-

Most 3200-color pictures were stored in BIN files and given names like ".3200".  Later on, official filetypes were specified, and some additional formats were developed.  CiderPress can convert the following:

-

 

-

Unpacked ($c1/0002 or BIN ".3200")

-

Packed with PackBytes (BIN ".3201")

-

Packed Apple Preferred Format ($c0/0002 with "MULTIPAL" block)

-

 

-

For Apple Preferred Format, only 320x200 images are handled.

-

 

-

Print Shop Clip Art

-

 

-

Clip art from Print Shop "classic" usually comes on DOS 3.3 disks as a 576-byte 'B' file with a load address (aux type) of $4800, $5800, $6800, or $7800.  The images unpack to 88x52 black & white BMP files.

-

 

-

Clip art files for Print Shop GS are 1716 bytes long, and use ProDOS file type $F8 with aux type $C323.  The images unpack to 4-bit 88x52 BMP files.

-

 

-

The Print Shop GS editor doubles the width and triples the height, displaying a nearly-square 176x156 image.  CiderPress does not magnify the image because that would make it awkward to manipulate the original pixels in an image editor.

-

 

-

MacPaint Graphics (*.mac)

-

 

-

The original Macintosh Paint program created monochrome 576x720 images.  These represented a full printed page on a 72dpi ImageWriter printer.  While not really an Apple II format, these were commonly found on BBS systems in the years when Apple IIs were popular.

-

 

-

CiderPress will attempt to identify MacPaint files that don't end in ".mac" by looking for a 128-byte MacBinary header with a 'PNTG' file type.  MacPaint files transferred to other systems usually had this header.

-

-

- diff --git a/ciderpress/app/Help/html/t23.htm b/ciderpress/app/Help/html/t23.htm deleted file mode 100644 index c0d4c3c..0000000 --- a/ciderpress/app/Help/html/t23.htm +++ /dev/null @@ -1,42 +0,0 @@ - - - Preferences - File Viewer - - - - - - - - - - - -

File Viewer Preferences

-

 

-

You can access the File Viewer Preferences by selecting "Preferences..." from the "Edit" menu, and then clicking on the "File Viewer" tab.

-

 

-

The options on this screen control the operation of the file viewer.  In most cases they also alter the way files are extracted when "Convert to non-Apple II formats" is enabled.

-

 

-

Converters

-

 

-

Use these checkboxes to enable or disable conversion of specific formats.  The converters are described in more detail under File Format Converters.

-

 

-

If you select "Relax type-checking on graphics", CiderPress will be a little more aggressive when deciding if a file is a graphic image or not.  For example, not all hi-res and double-hi-res graphics have reliable file types, so any binary file that looks to be about the right size is displayed as a graphic.

-

 

-

Conversion Options

-

 

-

Most of these are described on the File Format Converters page.

-

 

-

If "Scroll horizontally instead of wrapping words" is set, the file viewer will add a horizontal scroll bar, and lines of text will not wrap when they reach the right edge of the viewer.

-

 

-

"Highlight hex dump columns" puts alternating 4-byte columns in bold for easier reading.  This is only done for small files, because loading the hex dump with Rich Text Format notations is considerably slower.

-

 

-

"Disassemble BRK/COP as a single-byte instructions" affects IIgs monitor disassembly output.

-

 

-

File Viewer Size Limit

-

 

-

This is the size of the largest file that the file viewer will open.  Large files may place a strain on Windows systems with limited memory, and in many cases there's little value in examining truly huge files with the file viewer.  The upper limit can be set in 1K increments from 1K to 32MB (32768K).

-

-

- diff --git a/ciderpress/app/Help/html/t233.htm b/ciderpress/app/Help/html/t233.htm deleted file mode 100644 index 428b608..0000000 --- a/ciderpress/app/Help/html/t233.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - Tool - Bulk Disk Image Converter - - - - - - - - - - -

Bulk Disk Image Converter

-

 

-

The bulk converter works much like the single-image converter, but it can process a large number of disk images with a few clicks.  It's probably best to become familiar with the single-image converter before using this feature.

-

 

-

After you select this feature from the "Tools" menu, you will be asked to select the disk images to convert.  All images must reside in the same folder.  Select multiple files with the mouse, using shift-clicks to select a range of files or ctrl-clicks to add or remove individual files.  You can select all files in the current folder by clicking on one and then hitting Ctrl-A.

-

 

-

Next, you will be asked to choose the output format.  All images will be converted to the same format.  Not all images can convert to all formats -- for example, you can't create 800K nibble images or 140K DiskCopy images -- so make sure you choose an appropriate format.  CiderPress will not prompt you to specify sector ordering, so it's important for the images to have descriptive filename extensions (e.g. ".po" instead of the generic ".dsk").

-

 

-

Finally, select a folder for the output.  Creating a new, empty folder is recommended.  The converted files will have the old names but with the extension replaced, but in some cases the old extension might match the new.  For example, converting a DOS-ordered 2MG file to a ProDOS-ordered 2MG file does not change the ".2MG" extension.  CiderPress will not overwrite an existing file.

-

 

-

If CiderPress encounters a file it cannot convert, you will be shown an error message with the name of the file and given the opportunity to skip the file and continue or halt processing.  Some messages, such as warnings about unreadable blocks on nibble images, are suppressed during bulk conversions.

-

 

-

If you are converting to ShrinkIt .SDK archives, the files will be compressed with the current default compression method.  This provides an easy way to convert a large set of disk images to "deflate" format for reduced storage requirements, or back to LZW/2 for Apple II compatibility.

-

-

- diff --git a/ciderpress/app/Help/html/t24.htm b/ciderpress/app/Help/html/t24.htm deleted file mode 100644 index b8199e9..0000000 --- a/ciderpress/app/Help/html/t24.htm +++ /dev/null @@ -1,124 +0,0 @@ - - - Credits - - - - - - - - - -

Credits

-

 

-

Design & Implementation

-

 

-

The bulk of CiderPress was written by Andy McFadden (www.fadden.com) through FaddenSoft, LLC.  Andy has been writing ShrinkIt-related utilities since 1989, including "NuLib" and "NuLib2", and was the author of Westcode Software's HardPressed(tm) compression software for the Apple IIgs.

-

 

-

This software was written entirely in C++, using Microsoft Visual C++ 6.0 and the MFC user interface library, and requires over 100,000 lines of commented source code.  The major components are:

-

CiderPress application - 43K lines of code

-

MDC application - 1.5K lines

-

File converter library (used by CiderPress app only) - 15.5K lines

-

DiskImg DLL (used by CiderPress and MDC) - 42K lines

-

Common functions (used by CiderPress and MDC) - 7K lines

-

External DLLs (NufxLib, zlib, libhfs, wnaspi32, plus Windows msvcrt, mfc42, etc.)

-

 

-

ShrinkIt and the NuFX archive format were developed by Andy Nicholas.  The Binary II format was developed by Gary Little.

-

 

-

My understanding of Integer BASIC and the S-C Assembler file format was dramatically improved by comments in Paul Schlyter's "A2FID.C".

-

 

-

Many thanks to Apple Computer and especially Apple's DTS group for creating or encouraging the creation of Apple II File Type Notes.  These saved me many hours of reverse-engineering.

-

 

-

Many aspects of the CiderPress UI were inspired by WinZip (www.winzip.com).

-

 

-

Apple /// Business Basic support by David Schmidt.

-

 

-

Testing

-

 

-

v1.0 beta test volunteers:

-

Bryan "I found a bug" Carter <bryan@gamezero.com>

-

Tom "I have a suggestion" Phelps

-

Eric "Sheppy" Shepherd (www.syndicomm.com)

-

 

-

v2.0 beta test volunteers:

-

Rich Dreher

-

Bill Garber

-

Glenn Jones

-

Andrew Molloy

-

Allan Sutton

-

 

-

My thanks also go to Dave Touvell and Jeff Fink for helping track down some bugs in v2.4.

-

 

-

Other Included Software

-

 

-

Access to NuFX (ShrinkIt) archives is provided by the NufxLib library ("nufxlib2.dll").  NufxLib is an open-source project, available for download from www.nulib.com.

-

 

-

Access to .gz files, and support for the "deflate" algorithm in NuFX archives, is provided by the Zlib library ("zlib.dll").  Zlib is free; visit the web site at www.zlib.org.

-

 

-

Access to HFS volumes is provided by "libhfs", part of the "hfsutils" package written by Robert Leslie.  hfsutils is available from http://www.mars.org/home/rob/proj/hfs/.

-

 

-

The install/uninstall program was created with DeployMaster (www.deploymaster.com).

-

 

-

NList.Data is part of the Nifty List desk accessory, written by Dave Lyons.  The file was included with the permission of the author.

-

 

-

Legal Notices

-

 

-

----- for software based on CiderPress code -----

-

Copyright (C) 2014, CiderPress authors.

-

All rights reserved.

-

 

-

Redistribution and use in source and binary forms, with or without

-

modification, are permitted provided that the following conditions are met:

-

    * Redistributions of source code must retain the above copyright

-

      notice, this list of conditions and the following disclaimer.

-

    * Redistributions in binary form must reproduce the above copyright

-

      notice, this list of conditions and the following disclaimer in the

-

      documentation and/or other materials provided with the distribution.

-

    * Neither the name of FaddenSoft, LLC nor the

-

      names of its contributors may be used to endorse or promote products

-

      derived from this software without specific prior written permission.

-

 

-

THIS SOFTWARE IS PROVIDED BY FaddenSoft, LLC ``AS IS'' AND ANY

-

EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

-

WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

-

DISCLAIMED. IN NO EVENT SHALL FaddenSoft, LLC BE LIABLE FOR ANY

-

DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES

-

(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

-

LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

-

ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

-

(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

-

SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-

 

-

----- for NufxLib -----

-

Copyright (C) 2014, Andy McFadden.

-

All rights reserved.

-

 

-

Redistribution and use in source and binary forms, with or without 

-

modification, are permitted provided that the following conditions are met:

-

 

-

  * Redistributions of source code must retain the above copyright

-

    notice, this list of conditions and the following disclaimer.

-

 

-

  * Redistributions in binary form must reproduce the above copyright

-

    notice, this list of conditions and the following disclaimer in the

-

    documentation and/or other materials provided with the distribution.

-

 

-

  * Neither the name of the copyright holder nor the names of project 

-

    contributors may be used to endorse or promote products derived from

-

    this software without specific prior written permission.

-

 

-

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 

-

AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE

-

IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE

-

ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS

-

BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 

-

OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF

-

SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS

-

INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN

-

CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 

-

ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF

-

THE POSSIBILITY OF SUCH DAMAGE.

-

-

- diff --git a/ciderpress/app/Help/html/t241.htm b/ciderpress/app/Help/html/t241.htm deleted file mode 100644 index f6d159a..0000000 --- a/ciderpress/app/Help/html/t241.htm +++ /dev/null @@ -1,65 +0,0 @@ - - - Opening a Volume - - - - - - - - - - - - -

Opening a Volume

-

 

-

In some circumstances it is useful to open a raw disk volume.  Two common examples are ProDOS-formatted 1.4MB floppy disks and CFFA flash cards.  CiderPress allows you to access ProDOS and HFS filesystems directly.

-

 

-

WARNING: it's possible to destroy all data on your hard drive.  Make sure you're opening the right volume!  When in doubt, open the disk in "read only" mode by checking the "read only" checkbox.  As a safety measure, "read only" is set by default, and CiderPress will not allow you to open "C:\" or physical drive 0 if read-only mode is disabled.  Always close the volume (with File->Close) before ejecting removable media.

-

 

-

Not all devices will work under all operating systems.  Please check the hardware compatibility list on the faddenSoft web site.

-

 

-

Disks can be opened as "logical" or "physical" volumes in Windows.  Understanding the distinction is important.  The basic difference is that a "logical" volume has a drive letter associated with it (e.g. "A:\" for your first floppy drive), while a "physical" volume is numbered (e.g. physical device 0 is your primary hard drive).  From the operating system's perspective, a "physical" disk has a partition table in block 0 that describes one or more "logical" volumes.  Floppy disks are an exception; for them, "logical" and "physical" are equivalent.  A CF card reader may present itself to the system as a physical disk, in which case Windows will try to find a partition table in block 0.

-

 

-

ProDOS and HFS volumes don't have a partition table in block 0, which can cause some confusion.  In some cases you will see a "physical" volume for the CF card but no "logical" volume.  In others the "logical" volume will appear and will be as large as the "physical" disk.  If Windows thinks it has a valid partition table, the "logical" volume may be a small subset of the "physical" disk.

-

 

-

The bottom line is that it's best to use "physical" disks whenever possible.  However, it's not always possible.  You can't treat a floppy drive as a "physical" disk in Windows 2000, and access to "physical" hard drives in Win9x/ME is tricky.  You have to access floppies as "logical" volumes under Win2K, but if you do that under Win98 you will have terrible performance in some circumstances.  It would be nice if CiderPress could automatically do the right thing in all cases, but every CF card reader is different, so CiderPress tries not to limit your options.

-

 

-

CD-ROM drives have their own access method.  No version of Windows makes it easy to read blocks from a CD-ROM with an unrecognized filesystem format.  Under Win2K/XP, CiderPress relies on a technology called SPTI (SCSI Pass-Through Interface) to access the disc.  Select the drive you want from the "logical" list, by letter (e.g. "D:\").  Under Win98/ME, it's necessary to use an ASPI driver (Advanced SCSI Programming Interface), just like CD recording applications do.  CiderPress will look for "wnaspi32.dll", and use it if found (Win98 includes it -- look in the About box to see if it was loaded successfully).  CD-ROM drives show up in the "physical" list, described by manufacturer and model number.  Some external CD-ROM drives may not show up in the list.

-

 

-

Generally speaking, SPTI and ASPI have equivalent performance.  The only notable difference is that SPTI requires exclusive access to a device, so you can't have a CD-ROM open in the file list and then open it in the volume copier.  Some ASPI layers will pick up devices other than CD-ROM drives, such as SCSI hard drives, Zip drives, and even CF card readers.

-

 

-

Hard drives partitioned for an Apple II or Macintosh can be accessed if attached to an IDE or SCSI connector.  Under Win9x/ME you can only access drives that the ASPI layer can find, which usually means you can only get to SCSI hard drives.

-

 

-

Here's what you should do:

-

 

-

In Windows 2000/XP:

-
  • Use "A:\" to access your floppy drive. -
  • Use "D:\" (or something similar) to access your CD-ROM drive. -
  • Use "physical disk N" to access hard drives. -
  • Use "physical disk N" to access your CF card if that's an option.  If not, use the logical drive (e.g. "M:\"), but be aware that the card may appear to have a different size when formatted for CFFA vs. Windows.  With some card readers, the size shown for the card may be wildly inaccurate, especially on Win2K or earlier.
-

 

-

In Windows 9x/ME:

-
  • Use "floppy drive N" to access floppies.  CiderPress will actually hide "A:\" if the disk doesn't have a Windows filesystem, because "logical" access to such disks is very slow.  (It will otherwise show it, so you can identify your Windows disks.)  After overwriting a disk, Windows will continue to show the old volume label until you eject the disk. -
  • Select your CD-ROM drive by vendor name and model number. -
  • Select SCSI hard drives by vendor name and model number. -
  • Use the logical drive (e.g. "M:\") to access CF cards.  If the logical drive shows up in the list when you insert a Windows-formatted card but not with a ProDOS-formatted card, you have a brain-damaged CF card reader driver, and it's not going to work.
-

 

-

Access to physical devices other than floppy disks in Win2K/XP requires "administrator" privileges.

-

 

-

If you un-check the "read only" box, Win2K/XP will not allow you to open the volume a second time, e.g. you can't have a disk open in read-write mode and then open it again in the volume copier.

-

 

-

The "filename" for physical disks will show up as two digits, e.g. "81:\" for the second physical disk.  This is an artifact of the way the PC BIOS works.  CD-ROMs and hard drives under Win9x/ME will look like "ASPI:0:1:0\".

-

 

-

You cannot create or access files larger than 2GB under Win9x/ME.  This is an operating system limit, and cannot be circumvented.

-

 

-

CiderPress has a self-imposed 8GB volume limit (as a "reasonableness" test to keep it from running amok).  This should be large enough to handle CF cards and hard drives partitioned for use on an Apple II.

-

 

-

CiderPress does not detect media ejections or swapping.  Do not eject disks or CF cards while CiderPress has them open.

-

 

-

In some cases you may need to be patient!  Scanning the contents of a CD-ROM with 10,000 files spread across several 32MB ProDOS volumes can take a few minutes. - -

- diff --git a/ciderpress/app/Help/html/t244.htm b/ciderpress/app/Help/html/t244.htm deleted file mode 100644 index 41cb28d..0000000 --- a/ciderpress/app/Help/html/t244.htm +++ /dev/null @@ -1,64 +0,0 @@ - - - Appendix - About Removable Media (CF, floppy, CD-ROM) - - - - - - - - - - - - - -

About Removable Media (CF, floppy, CD-ROM, external HD)

-

 

-

The ability to move data between a PC and an Apple II on removable media can be very handy.  With a CFFA card installed in an Apple II, and a CF card reader on a PC, you can move entire hard drive volumes around easily.  Floppy disks are easier to insert and eject on the Apple II, but hold less.  In both cases you need suitable hardware on both the Apple II side and the PC side.

-

 

-

CiderPress does not detect media ejections or swapping.  Do not eject disks or CF cards while CiderPress has them open.

-

 

-

Floppy Disks

-

 

-

Formatting a 3.5" disk with ProDOS can be very useful if you have a way to read the disk on an Apple II or Macintosh.  Newer Macs, and Apple IIs equipped with appropriate hardware, can read and write PC-formatted 3.5" floppies.  An Apple II requires a SuperDrive (with controller card), PC Transporter, or Tulin "floptical" drive.  Without one of these, it's not possible to read PC disks.

-

 

-

Inserting a ProDOS disk into a Windows machine can cause some consternation for the OS.  For example, the capacity of a PC floppy disk cannot easily be determined.  There are BIOS calls to retrieve the type of drive, but not the type of media currently inserted.  Common practice is to examine sector counts stored in the floppy boot sector.  Since ProDOS and HFS floppies do not follow PC boot sector conventions, the size of a floppy disk must be determined by other means.  Specifically, accessing blocks to see which calls succeed and which fail.  A disk with bad blocks could be interpreted as smaller than it actually is.  Windows may actually refuse to do a "quick format" on a ProDOS formatted disk, because it's unable to tell how large it should be.

-

 

-

CiderPress has been tested with 720KB and 1.4MB 3.5" floppy disks.  It will not work with 800K or 1.6MB 3.5" floppies from an Apple II or Macintosh, because the drive in a PC is not capable of reading them.  For similar reasons, it cannot read 140KB 5.25" floppies created on an Apple II.  CiderPress has not been tested with 360KB or 1.2MB 5.25" floppy disks, because they're no longer used in the PC world, and they can't be read on an Apple II or Macintosh.

-

 

-

CiderPress does not "format" floppy disks or other volumes directly.  Instead, open the disk you want to format with the Volume Copier, and copy a ProDOS volume onto it.  If you don't have a ProDOS disk image of the appropriate size, create one with the Disk Image Creator first.

-

 

-

Compact Flash Cards

-

 

-

Richard Dreher's CFFA card for the Apple II created the ability to access inexpensive high-capacity compact flash card media as a hard drive.  Because the cards are relatively easy to install, and CF card readers are common on PCs (especially laptops), they make an ideal way to transport large quantities of information.

-

 

-

As with floppies, CF cards formatted for ProDOS or HFS are not handled well by Windows.  Disks can be accessed as "physical" or "logical" volumes.  "Physical" disks correspond to physically connected devices.  Block 0 holds a partition table for the rest of the disk.  ProDOS and HFS don't have a Windows-style partition table, so Windows can become very confused when a non-Windows CF card is plugged in.  "Logical" volumes, such as "C:", are created by the device driver.  For a disk device they are generated from the partition table, so a CF card may have one or more logical volumes that correspond to part or all of the physical storage.

-

 

-

Long story short: whether or not your CF card is viewable in Windows depends on what hardware and drivers you're using to access the card.  You may be able to access it as a physical disk, logical disk, both, or neither.  Sometimes under Win98 the ASPI driver will find it.  See "Opening a Volume" for additional information.

-

 

-

Assuming it's possible to access the CF card, figuring out what's on it can be tricky.  A CF card can be formatted with 4 partitions or 8 depending on the firmware setting on the CFFA card in the Apple II.  With a GS/OS driver, the 4-partition mode can actually hold 6 partitions.  If you are using a card larger than 128MB, CiderPress will have to make some educated guesses as to where your partitions are and what's in them.  For cards 1GB and smaller, it usually guesses right.  However, you can override its choice by enabling "Confirm disk image format" and selecting an alternate partitioning scheme when opening the volume.

-

 

-

One caution: if you format a disk for ProDOS, and then format it with your camera, you may find that it still appears to have some ProDOS files on it.  (CiderPress should identify it as an MS-DOS "FAT" filesystem, but if the camera uses a non-standard boot block it may not be detected correctly.)  Some files may appear to be damaged.  This is because the camera's format routine didn't zero out all of the blocks, so some of the ProDOS directory structure is still present.  Attempting to read or write files to the volume as if it were a ProDOS disk is not recommended.  The safe way to switch between Apple II and Windows formatting is to use the image-copy tool to overwrite the entire CF card.  Be aware that formatting with a camera can reduce the number of blocks available on the drive, which will make copying images onto it impossible: the image copier only works if the destination volume is at least as large as the source volume.

-

 

-

The speed at which CF cards are read or written depends primarly on your card reader.  USB2.0 readers will be faster than USB1.x, and Firewire, PCMCIA, or IDE interfaces will usually be faster than USB2.0.  Also, some cards have a higher speed rating than others.  Speeds of 200-400KB/sec are typical when copying from a USB1.x device, while writing to it may reach 700KB/sec.  Writing tends to be faster than reading because of block caching.

-

 

-

CiderPress assumes that the first partition on a CFFA card will be ProDOS or HFS.  If it's not, the image will not be detected as CFFA.

-

 

-

CD-ROM

-

 

-

CD-ROMs for Apple IIs and Macintoshes were created with a partition table identical to those found on Macintosh hard drives.  This allows a CD-ROM to have several ProDOS and HFS partitions.  Happily, the format is well defined, and there is no ambiguity in the size or location of each partition.  Occasionally the partition information is wrong, and an "Apple_Extra" partition refers to parts of the disc outside the recorded area.  In such cases CiderPress will trim its internal copy of the map to fit what's actually on the disc.

-

 

-

Access to files on CD-ROMs can be slow, especially on ProDOS volumes.  Unlike ISO-9660 discs, the file layout on Apple II CD-ROMs is not optimized for sequential accesses, and the drive head has to seek to a new position often.  If the speed becomes a problem, extract the CD-ROM into a ".ISO" image file and use that instead.

-

 

-

CiderPress does not have the ability to write to CD-Rs.

-

 

-

Hard Drives

-

 

-

Apple II or Macintosh hard drives can be connected, assuming you have the necessary hardware (e.g. SCSI interface).  Non-SCSI drives, such as the Applied Ingenuity InnerDrive or Vulcan drives, may not be formatted with the Macintosh partioning scheme and hence may be inaccessible.

-

 

-

Figuring out the size of a hard drive is a bit tricky under Windows, which uses different interfaces in different versions of the OS.  Some interfaces return different answers depending on what version you're running.  CiderPress currently scans the drive to determine its size.

-

-

- diff --git a/ciderpress/app/Help/html/t245.htm b/ciderpress/app/Help/html/t245.htm deleted file mode 100644 index 9c209dd..0000000 --- a/ciderpress/app/Help/html/t245.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - Tool - Windows Volume Copier - - - - - - - - - - - -

Windows Volume Copier

-

 

-

This tool lets you copy all or part of a Windows volume to or from a file.  This can be used to make images of 3.5" floppy disks, copy disk images onto 3.5" disks, back up a CFFA card, or swap partitions in and out of a hard drive.  This can also operate on normal disk image files and block images extracted from devices.

-

 

-

WARNING: it's possible to destroy all data on your hard drive.  Make sure you're opening the right volume!  When in doubt, open the disk in "read only" mode.

-

 

-

There are two versions of the tool.  If you select "Volume copier (open volume)", you will be given a list of volumes to choose from.  Select the volume you want to work with.  If you check "read only", copying data into the volume will be disallowed.  If it's not checked, the volume will be opened with write access enabled.  In either case, under Windows 2000 or XP you will need to have administrator privileges to access volumes other than the floppy drive.

-

 

-

If you select "Volume copier (open file)", you can open any disk image of your choosing.  This can be a handy way to extract from or modify a multi-partition image file.

-

 

-

CiderPress will try to figure out what format the volume is in, automatically detecting any sub-volumes, such as CFFA partitions and hard drive partitions.  If the volume has a single filesystem on it, CiderPress will display the volume name, format, and volume size in blocks and megabytes.  If the volume has sub-volumes, you will see one entry for the entire volume (shown with a large green dot) and below it one entry for each sub-volume (with a smaller blue dot).

-

 

-

Click on the volume or sub-volume you want to manipulate.  To copy the volume to a file, click on "copy to file".  This will create a ProDOS ordered ("xxx.po") disk image that some Apple II emulators will be able to use directly.  To copy a file onto the volume, click on "copy from file".  This pulls blocks out of the disk image file and writes them to the volume or sub-volume.  You can open any disk image format that CiderPress supports.

-

 

-

When copying from a file to a volume, the file must be smaller than the volume.  You cannot, for example, copy a 32MB ProDOS volume onto a 1.4MB floppy.  If you copy a 1.4MB floppy image onto a 32MB CFFA partition, you will have a 1.4MB ProDOS partition and 30.6MB of wasted space.  If you copy a 140K DOS 3.3 image onto a 32MB CFFA partition, you will end up with a useless partition, because nothing will look for a DOS volume there.  For safety, CiderPress will not allow you to copy data onto a volume 8GB or larger.

-

 

-

When copying large volumes to disk, CiderPress starts by creating an empty disk image file.  For large (512MB+) volumes, this may take several seconds as Windows creates an empty file.

-

 

-

If you are overwriting the first volume of a CFFA image, make sure you're copying ProDOS or HFS in.  If CiderPress can't recognize the first partition, it may not be able to detect that the volume is a CFFA card, and will not display the CFFA sub-volumes.

-

 

-

The sizes used are for the entire partition.  If you formatted a 32MB ProDOS volume in a 1GB partition on a CFFA card, CiderPress will treat it as a 1GB volume, even though ProDOS is only on the first part of it.  Extracting that ProDOS volume may be awkward.

-

 

-

Hard drives with Macintosh-style partioning have explicit filesystem identification for each partition.  That is, each partition will be labeled as ProDOS, HFS, a device driver, or whatever is appropriate.  CiderPress does not currently have the ability to change these labels.  Copying the wrong thing onto a partition, such as putting a ProDOS volume into a partition meant for HFS, could have unexpected results.

-

 

-

Bear in mind that old hard drives are pretty slow by today's standards.  A 2GB drive purchased in the mid-1990s will deliver 4-5MB/sec on bulk reads, which means it'll take about 8 minutes to back up the entire drive.  These drives tended to have small caches and slow seeks though, so it can take 30 seconds to a minute for the contents of the disk to be loaded, because scanning the list of files requires lots of single-block reads.

-

 

-

If CiderPress encounters errors while reading, it will start doing single-block reads instead of bulk reads.  This can be *significantly* slower, on the order of 5-10x for some types of drives.  Errors encountered while writing halt copying immediately.

-

-

- diff --git a/ciderpress/app/Help/html/t247.htm b/ciderpress/app/Help/html/t247.htm deleted file mode 100644 index 92a5879..0000000 --- a/ciderpress/app/Help/html/t247.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - Disk Image Creator - - - - - - - - - -

Create Disk Image

-

 

-

This allows you to create blank, formatted disk images in a variety of formats.  The images created can be used with CiderPress or an Apple II emulator.

-

 

-

Start by selecting the filesystem.  CiderPress currently supports creation of images in DOS 3.2, DOS 3.3, ProDOS, and UCSD Pascal formats.  You can also choose to create a completely blank file with the specified size, though this is only useful in a few circumstances.

-

 

-

The choice of filesystem determines which size options are available to you.  DOS 3.2/3.3 formatting is only allowed on 140K floppies, Pascal can be written to 140K or 800K floppies, and ProDOS can be written to images from 16 blocks up to 32MB.  Blank images can be as small as 1 block or as large as 8GB.  Your filesystem selection also enables some filesystem-specific options:

-

 

-

DOS 3.2/3.3: choose the disk volume number (default 254) and whether or not a DOS image should be written.  If "Allocate DOS tracks" is checked, tracks 1 and 2 are marked "in use", and a bootable DOS image is written to the disk.  If it's not checked, tracks 1 and 2 are marked as free space, and the disk will not be bootable.

-

 

-

ProDOS: choose the volume name.  ProDOS volume names must start with a letter, contain only letters, numbers, and '.', and can be at most 15 characters long.  To make the disk bootable, you will need to copy the "PRODOS" file from another ProDOS 8 disk.

-

 

-

Pascal: choose the volume name.  Pascal volume names can only be 7 characters long, but may contain letters, numbers, and symbols other than "$=?,[#:".  To make the disk bootable, you will need "SYSTEM.APPLE" and "SYSTEM.PASCAL" from a Pascal system disk.

-

 

-

After you hit "OK", you will be prompted for the name of the file to save to.  For 140K floppy images you can select DOS order (".do", the default) or ProDOS order (".po").  For DOS 3.2, ".d13" must be used.  For other images only ProDOS ordering is available.  If you want the image to be in a different format, such as .SDK or .2MG, use the Disk Image Converter tool.

-

 

-

If you want to create a blank filesystem image on physical media (e.g. format a 1.4MB floppy disk for ProDOS), create an image of the appropriate size, open the floppy disk with the Volume Copier, then copy the image onto the disk with the "load from file" button.

-

 

-

Tip: if you want to create several images of the same kind, create one and then use Windows Explorer commands to make multiple copies of the file.

-

-

- diff --git a/ciderpress/app/Help/html/t25.htm b/ciderpress/app/Help/html/t25.htm deleted file mode 100644 index c0d294c..0000000 --- a/ciderpress/app/Help/html/t25.htm +++ /dev/null @@ -1,45 +0,0 @@ - - - Using the File Viewer - - - - - - - - - - -

Using the File Viewer

-

 

-

With the file viewer you can examine files stored in an archive or disk image without having to extract them first.  Files may be converted from several different Apple II formats.

-

 

-

When the file viewer opens you will be shown the first file you selected from the file list.  If you selected multiple files, the "Prev" and "Next" buttons can be used to move between them.

-

 

-

The "data", "resource", and "comment" buttons on the left can be used to choose which part of the file you want to view.  If the file doesn't have the part in question, the button will be dimmed.  Disk images in NuFX archives are treated as data forks.

-

 

-

You can choose how you want the file to be formatted by choosing a formatter from the drop-down list.  You will always have the option of viewing data in raw form or as a hex dump.  If not disabled, a generic text converter that changes end-of-line characters and strips "high ASCII" will also be available.  Certain types of files will have additional options that can be chosen from the list.  The buttons labeled "best", "hex", and "raw" change the selection to the best formatter, the hex dump formatter, and the raw dump, respectively.

-

 

-

The "best" option, which is always the first in the list, is determined by examining the file.  Certain file viewer preferences, such as disabling reformatters or setting a preference for black & white hi-res images, will affect the conversion.  The other modes are still available and may be chosen from the list, but the viewer will default to your preference.

-

 

-

For text documents, the font can be changed with the "Font" button.  A monospace font like Courier or Courier New is recommended for Apple II files since most of them were created with a fixed-width font in mind.  The default is 10-point Courier New.  If you're viewing a IIgs word processing file, such as a Teach document, the file may be in multiple fonts and multiple sizes.  Using the "font" button will force the text to use a single font and size.

-

 

-

You can use the "Find" button to search for text.  The search starts at the currently selected text or, if none is selected, at the blinking cursor.

-

 

-

You can print text or graphics with the "Print" button.  The document will be sent to the printer using the fonts you see on screen.

-

 

-

If a format converter fails, perhaps because the file being converted has become corrupted, the "raw" output will be displayed instead.  If an error occurs, such as trying to view a file larger than the configured maximum limit, a message will be displayed on a yellow background.

-

 

-

All hex dumps, text, and pictures can be cut and pasted directly from the file viewer.  Printing "raw" output is generally not a good idea, because the output may contain "page feed" characters that leave all or part of a page blank.

-

 

-

You can also copy documents to other programs.  To select the entire document, click on the document to set the input focus, then hit Ctrl-A to select all and Ctrl-C to copy it to the clipboard.  Switch to another application (Windows WordPad and Microsoft Word are the most appropriate) and hit Ctrl-V to paste.

-

 

-

Bear in mind that not all applications support all formats.  Pasting text into Windows notepad, which doesn't support Rich Text Format, will cause highlighed BASIC listings and formatted AppleWorks documents to be converted to plain text.  Pasting graphics into Notepad doesn't work at all.

-

 

-

Click "Done" to close the window.

-

 

-

Tip: hit the Tab key to highlight the format selector, then use the up and down arrows to move between different conversion modes.

-

-

- diff --git a/ciderpress/app/Help/html/t257.htm b/ciderpress/app/Help/html/t257.htm deleted file mode 100644 index 7192fb7..0000000 --- a/ciderpress/app/Help/html/t257.htm +++ /dev/null @@ -1,23 +0,0 @@ - - - Select Location - - - - - - - - -

Select Location

-

 

-

Select the disk or subdirectory where the files will be added.  If the current disk has sub-volumes (e.g. embedded DOS 3.3 volumes or CFFA partitions), they will be shown under the encapsulating disk volume.

-

 

-

Disks in formats that cannot be written are shown with a red 'X' through the disk icon.  This commonly appears in the root volume of a partitioned image.  For example, you can add files to CFFA partitions, but you can't add files to the CFFA entry because it only contains other partitions.  It can also appear for partitions that appear to be damaged or hold damaged files.

-

 

-

You can skip this step on ProDOS disks by selecting a directory before using "add files".  The files will be placed in the selected directory.

-

 

-

If you want to add or extract partitions, use the volume copier tool instead.

-

-

- diff --git a/ciderpress/app/Help/html/t258.htm b/ciderpress/app/Help/html/t258.htm deleted file mode 100644 index b337c6a..0000000 --- a/ciderpress/app/Help/html/t258.htm +++ /dev/null @@ -1,62 +0,0 @@ - - - Archive Info - - - - - - - - -

Archive Info

-

 

-

The Archive Info command in the "File" menu provides a way to view information about the currently open disk image or file archive.  The set of information shown depends on the kind of file currently open.

-

 

-

NuFX (ShrinkIt) Archive

-

 

-

Filename: the name of the file that is currently open.

-

Format: archives can be plain ShrinkIt archives (NuFX), wrapped in a Binary II wrapper (usually named .BXY), GS/ShrinkIt self-extracting (.SEA), or GS/ShrinkIt self-extracting in a Binary II wrapper (.BSE).

-

Records: the number of records in the archive.  Each file, whether forked or not, occupies one record.

-

Master Version: the master version number of the NuFX archive.  This can be useful when trying to determine if an archive is from a really old version of ShrinkIt.  The most current version is 2.

-

Created: the date and time when the archive was created, if available.

-

Modified: the date and time when the archive was last modified.

-

Junk Skipped: the number of bytes skipped over when scanning for the start of the archive.  NufxLib skips over MacBinary headers and the leftover HTTP junk that seems to show up in files on some FTP sites.  Most other NuFX applications, e.g. ShrinkIt, do not, so this can explain why an archive won't open on an Apple II.

-

 

-

Disk Image

-

 

-

Filename: the name of the file that is currently open.

-

Outer Format: the name of an external "wrapper", if any; usually gzip (.gz) or zip (.zip).

-

File Format: the overall format of the disk image file.  Common formats are 2IMG (.2MG) and unadorned (.PO, .DO, .DSK).

-

Physical Format: internal layout of the file.  Most files are "cooked" sectors, but some are in a nibble format.

-

 

-

The Sub-Volume box shows the name of the volume that the rest of the information applies to.  Most disks don't have sub-volumes, and the box will be greyed out.  UNIDOS 800K disks, Macintosh-style partitions, CFFA cards, and ProDOS disks with embedded DOS images will have entries here.  Selecting a different entry will change the information below.

-

 

-

Sector Ordering: sector layout within the image.  Most disks use DOS (.DO) or ProDOS (.PO) order.

-

Filesystem Format: the type of filesystem on the disk, e.g. DOS 3.3, ProDOS, Pascal.  For "hybrid" disks, such as DOS3.3/ProDOS mixed on a 5.25" disk, only the dominant filesystem will be shown.

-

Files+Directories: the number of files and directories on the disk.  For ProDOS the count includes the volume directory.

-

Storage Capacity: how many blocks or sectors the disk can hold.  In some cases, such as an 800K disk image copied to a 32MB CFFA partition, a second number will be shown indicating the maximum size of the disk image area.

-

Free Space: how much free space is on the disk.  For formats like CFFA, which just hold other disk images, this is meaningless.

-

Writable Format: says whether or not CiderPress is capable of adding and deleting files on disks with this format.  Currently this is "yes" for DOS 3.3, ProDOS, and UCSD Pascal.

-

Damaged: this indicates whether or not CiderPress believes the disk is damaged.  If it does, the disk will be marked read-only, and attempts to add or delete files will be blocked.

-

Notes: if CiderPress detects damage or other anomalies when scanning the disk, they wll be noted here.

-

 

-

More information about the different disk formats can be found here.

-

 

-

Binary II Archive

-

 

-

Filename: the name of the file that is currently open.

-

Records: the number of entries in the Binary II archive.

-

 

-

AppleLink Compression Utility Archive

-

 

-

Filename: the name of the file that is currently open.

-

Records: the number of entries in the ACU archive.

-

 

-

AppleSingle file

-

 

-

Filename: the name of the file that is currently open.

-

Info: a few facts about the file.

-

-

- diff --git a/ciderpress/app/Help/html/t259.htm b/ciderpress/app/Help/html/t259.htm deleted file mode 100644 index fff864c..0000000 --- a/ciderpress/app/Help/html/t259.htm +++ /dev/null @@ -1,33 +0,0 @@ - - - Preferences - Disk Image - - - - - - - - - - -

Disk Image Preferences

-

 

-

You can access the Disk Image Preferences by selecting "Preferences..." from the "Edit" menu, and then clicking on the "Disk Images" tab.

-

 

-

General

-

 

-

If "confirm disk image format" is enabled, you will be shown the Disk Image Characteristics dialog whenever a disk image is opened.  This gives you an opportunity to see what format CiderPress believes the disk is in, and to override it.

-

 

-

The "default to read-only when opening volumes" setting determines whether the "read-only" box is checked in the Open Volume dialog.  This should normally be set as a safety feature, but if you find yourself writing to physical disks frequently, you can save yourself a click by disabling this option.

-

 

-

"Allow write access to physical disk 0" disabled a safety feature.  On most systems, physical disk 0 is your boot disk (i.e. C:\).  If you have multiple drives, this may not be the case.  By default, CiderPress prevents you from opening physical disk 0 for writing; if you set this checkbox, write access will be allowed.  It's best to leave this disabled unless you get a message that says, "Unable to open '80:\': for safety, write access to this volume is forbidden" while trying to open a disk that you are sure contains Apple II data.

-

 

-

ProDOS

-

 

-

When Apple released GS/OS, they added the ability to have lower-case letters and spaces in file and volume names.  This made file listings nicer to look at, but broke compatibility with versions of ProDOS 8 older than v1.8.  If "allow lower-case letters and spaces in filenames" is enabled, files added to ProDOS disks will use mixed-case filenames.  If disabled, all filenames are stored in upper case.  Uncheck this item for best compatibility.

-

 

-

The "use 'sparse' allocation for empty blocks" option enables a handy space-saving feature.  Disk blocks filled entirely with zeroes aren't actually written to disk.  Every version of ProDOS supports this feature, so there's no real reason to disable it, but it's there if you want to experiment.

-

-

- diff --git a/ciderpress/app/Help/html/t262.htm b/ciderpress/app/Help/html/t262.htm deleted file mode 100644 index 9fd396b..0000000 --- a/ciderpress/app/Help/html/t262.htm +++ /dev/null @@ -1,41 +0,0 @@ - - - Appendix - Administrator Privileges - - - - - - - - - - -

Administrator Privileges

-

 

-

Windows 2000, Windows XP, and presumably any subsequent version of Windows requires "administrator" privileges to access logical and physical devices directly.  This is required because the direct block access bypasses normal file access control mechanisms.  You either need to log in as an administrator to use the device-access features of CiderPress, or upgrade the privilege level of your non-administrator account.

-

 

-

Most Win2K/XP users are set up with administrator access, but some may not be.  CiderPress will tell you if devices are failing to open because of access permissions.  To gain administrator access, you have to start by having administrator access to the machine.  Yes, this is a bit of a chicken and egg problem, but there's no way around it.  If you don't know how to log in as administrator, ask the person who set up your computer.

-

 

-

Windows 2000:

-
  • From the "Start" menu, select "Settings" and then "Control Panels". -
  • Open the "Users and Passwords" control panel.  If you're not allowed to do this, you're not an administrator, and cannot proceed further. -
  • You should see a list of users on the machine.  Find the username you log in as, and make sure that "Administrator" shows up in the list of groups. -
  • If you're not in the "administrator" group, click on the "Advanced" tab, and click the "Advanced" button to bring up the "Local Users and Groups" dialog. -
  • Click on the "Users" folder on the left side, then click on your user name on the right side.  From the "Action" menu select "Properties". -
  • Click the "Member Of" tab.  Click the "Add" button to open the "Select Groups" dialog. -
  • Click on "Administrators".  Click "Add".  Click "OK" in this dialog and the previous one. -
  • Close the dialog boxes.  You should be all set.
-

 

-

Windows XP:

-
  • From the "Start" menu, select "Control Panel". -
  • Click on "User Accounts".  If you only see the current account, and it says "limited user", you're not an administator and cannot proceed further. -
  • Click on the account you want to change. -
  • Click on "Change my account type". -
  • Click the "Computer Administrator" radio button.  Click "change account type". -
  • Close the dialog boxes.  You should be all set.
-

 

-

Win9x/ME does not have the concept of user privilege levels, so none of the above is necessary.

-

-

- diff --git a/ciderpress/app/Help/html/t263.htm b/ciderpress/app/Help/html/t263.htm deleted file mode 100644 index ce019a2..0000000 --- a/ciderpress/app/Help/html/t263.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - Creating Subdirectories - - - - - - - - - -

Creating Subdirectories

-

 

-

To create a new subdirectory on a ProDOS disk, select the subdirectory in which it will reside, then use the File->Create Subdirectory command.

-

 

-

This feature is disabled for ShrinkIt archives, because they don't store subdirectories explicitly.  You can, however, rename files to make them appear as if they were in a subdirectory.

-

-

- diff --git a/ciderpress/app/Help/html/t268.htm b/ciderpress/app/Help/html/t268.htm deleted file mode 100644 index d46d0d6..0000000 --- a/ciderpress/app/Help/html/t268.htm +++ /dev/null @@ -1,34 +0,0 @@ - - - Renaming a Volume - - - - - - - - -

Renaming a Volume

-

 

-

You can change the volume name of a ProDOS or Pascal disk image, or the volume number of a DOS 3.3 disk image, by selecting "Rename volume" from the Actions menu.

-

 

-

You will be presented with a tree of volumes to rename.  In most cases there will only be one possible, but if you're working with a disk image that has embedded DOS sub-volumes, or a partitioned image like a CFFA card, you will see multiple entries.  Click on the one you want to rename.

-

 

-

In the box at the bottom, enter the new volume name or number.  When you click "OK", CiderPress will update the volume name.

-

 

-

Volume names for ProDOS and Pascal are very similar to file names.  ProDOS volume names aren't allowed to have a space in them, even if "allow lower case" is enabled, but are otherwise identical.  Pascal volume names use the same set of characters as filenames, but are limited to 7 characters.

-

 

-

The volume "name" for a DOS 3.3 disk is its volume number, which can range from 1 to 254.  (Initializing a disk under DOS 3.3 with volume number 0 yields volume number 254.  Attempting to use volume number 255 results in a "range error".)

-

 

-

The DOS volume number is actually stored in up to three places:

-

1. If the file has a 2MG header, the volume number may be specified there.

-

2. If the file is a nibble image, a copy of the volume number is stored in the address header of every sector on the disk.

-

3. A copy is stored in the disk Volume Table of Contents (VTOC).

-

 

-

The "rename volume" function only updates #3.  The DOS "CATALOG" command uses #2, which means that changing the volume number with CiderPress may not have an impact on what you see in an emulator.  For non-nibble images, most emulators just generate the default volume number (254) into each sector.  CiderPress itself prefers #2 over #3, so changing the volume number on a nibble image may not have any noticeable impact within CiderPress itself.

-

 

-

Some emulators will obey the 2MG volume number (#1), so you may want to change it in both places.  You can set the value in the 2MG header with the 2MG Properties Editor.  All things considered, it's probably best to just leave it set to 254. - -

- diff --git a/ciderpress/app/Help/html/t272.htm b/ciderpress/app/Help/html/t272.htm deleted file mode 100644 index 858678e..0000000 --- a/ciderpress/app/Help/html/t272.htm +++ /dev/null @@ -1,46 +0,0 @@ - - - Tool - EOL Scanner - - - - - - - - - -

EOL Scanner

-

 

-

EOL is an acronym for "end of line".  Each line of a text file ends with an end-of-line marker.  The Apple II and Macintosh use a carriage return, UNIX systems use a linefeed, and Windows uses a carriage return followed by a linefeed.  These are usually abbreviated "CR", "LF", and "CRLF".

-

 

-

When text files are moved from one system to another, the end-of-line markers on text files need to be converted.  Unfortunately, overzealous converters will sometimes convert a non-text file, resulting in corrupted data.  The only way to tell if a file has been corrupted is to count up the occurrences of CR, LF, and CRLF, and see if they make sense.

-

 

-

The EOL Scanner tool does exactly that.  The number of times CR, LF, and CRLF appear are counted and displayed.  This information, combined with some knowledge of the format of the file, will tell you if a file has been corrupted by an EOL conversion.  The tool also counts up "high-ASCII" characters to test for conversions to and from DOS text files.

-

 

-

Take for example a healthy 140K disk image.  The scanner reports the following:

-

143360 characters

-

43582 high-ASCII characters

-

381 carriage returns

-

863 line feeds

-

3 CRLFs

-

 

-

A typical disk image will have a smattering of CR (hex value 0x0d) and LF (hex value 0x0a).  Occasionally they will appear near each other and form a CRLF.  A disk image with lots of text files will have many more CRs than LFs, while a disk with programs on it could have any amount of either.  (To be accurate, a DOS 3.3 disk with text files won't show a large number of CRs, because DOS 3.3 uses "high ASCII" 0x8d instead of 0x0d.)  Note that an occurrence of "CRLF" only updates the CRLF counter, not the CR and LF counters.

-

 

-

Now lets look at a disk image that doesn't seem to work.  The scanner comes back with:

-

143360 characters

-

56085 high-ASCII characters

-

530 carriage returns

-

0 line feeds

-

0 CRLFs

-

 

-

The disk image has absolutely no line feeds whatsoever.  A blank formatted ProDOS disk will have 3 or 4 carriage returns and line feeds, a blank unbootable DOS disk 1 of each.  A non-empty disk should never have zero CRs or LFs.  This disk was corrupted by a converter that changed all of the linefeeds to carriage returns.

-

 

-

Similar results hold for compressed data archives.  Data that is well compressed will show a fairly even distribution of all possible characters, so a total absence of CR or LF is a big red flag.  Of course, if the archive is only a few hundred bytes long, it's quite possible that no CRs or LFs will be found.

-

 

-

Disk images in nibble format (.nib) have no CRs or LFs in them.  This is normal.  The entire file should be "high ASCII" characters.

-

 

-

Besides its use as a diagnostic tool, the EOL Scanner can also tell you what format a text file is in, and also how many lines it has. - -

- diff --git a/ciderpress/app/Help/html/t273.htm b/ciderpress/app/Help/html/t273.htm deleted file mode 100644 index dc275ff..0000000 --- a/ciderpress/app/Help/html/t273.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - Copy and Paste - - - - - - - - - -

Copy and Paste

-

 

-

CiderPress can use the Windows clipboard to copy Apple II files around.  You can copy files from any disk image or file archive that CiderPress can open, and paste them into NuFX (ShrinkIt) archives and writeable disk images (currently DOS, ProDOS, and Pascal are supported).

-

 

-

To copy files, open the archive, select the files you want from the file list, and use "Edit->Copy".  (Or, right click and choose "Copy".  Or hit Ctrl-C.)  The files will be extracted from the archive or disk image and placed on the Windows clipboard.

-

 

-

Next, open the archive where you want the files to go.  If it's a ProDOS disk image, select the directory where you want the files to appear.  Select "Edit->Paste".  (Or right click and choose "Paste".  Or hit Ctrl-V.)  The files will be added.  You can also have two copies of CiderPress open, and copy from one and paste to the other.

-

 

-

If you're pasting to a disk image with sub-volumes, or pasting to a ProDOS disk with subdirectories and didn't select a subdirectory, you will be asked to choose where the files will go.  This is similar to the way "add files" works -- if the destination isn't obvious, CiderPress will ask.

-

 

-

Copying and and pasting is meant to be simple, but there's a lot of complexity underneath it.  Here are some things you should be aware of:

-

 

-
  • Files copied from ProDOS volumes are extracted with their full paths, e.g. "subdir1:subdir2:foo.txt".  You may choose to strip the subdirectories off with a global preference.  This determines whether pasting "subdir1:subdir2:foo.txt" into "subdir3" yields "subdir3:foo.txt" or "subdir3:subdir1:subdir2:foo.txt".  Files pasted to DOS or Pascal disks have their paths stripped automatically. -
  • Unlike the "add files" approach, which prompts you when new filenames clash with existing files, "paste" automatically handles conflicting files for you.  If you paste a file called "FOO" to the same disk image twice, the second one will be called "FOO1".  If you paste files with the same name multiple times to a NuFX archive, you will simply have multiple entries called "FOO", because NuFX allows duplicate filenames. -
  • Files converted between different operating systems will have their file types converted.  The mapping of DOS and Pascal types to ProDOS types is shown here.  Files pasted to DOS disks that don't have a direct file type conversion will become 'B' if they are less than 64K, or 'S' if they are more.  ProDOS "SRC" files become 'T'.  Most files converted to Pascal disks become "PDA" (generic data).  CiderPress does not currently convert in and out of Pascal Text (PTX) format. -
  • DOS text files use "high ASCII" format.  Text files pasted to and from DOS volumes will be converted appropriately.  (Text files copied from one DOS volume to another are left alone, not converted twice.)  Because all Apple II formats use carriage returns to indicate the ends of lines, no end-of-line conversion is done when pasting files. -
  • The comments stored in NuFX archives are not copied with the files.  (This may be fixed in a future release.) -
  • Copying and pasting files from Windows Explorer is not currently supported.  You can, however, paste into an application such as Notepad to get a tab-delimited copy of the file list (pathname, file type, aux type, etc).  If you paste this into a spreadsheet, such as Microsoft Excel, each field gets its own row and column. -
  • Selecting a directory on a ProDOS disk copies the directory, not the directory and all of its contents.  Pasting the directory into a ProDOS disk image creates an empty directory.  If a directory with that name already exists, nothing happens.  NuFX archives do not store directories, so pasting a directory into a NuFX archive has no effect.  You cannot copy the volume directory of a ProDOS disk, though there is no harm in trying. -
  • Resource forks are not pasted to DOS or Pascal disks.  If you paste a forked file, only the data fork is added.  (All forked files on ProDOS disks have a data fork, even if it's just an empty one, but forked files in NuFX archives may have just the resource fork.  Pasting a forked file to a DOS disk may or may not actually create a file.) -
  • Some DOS games were stored in 'B' files with a short "loader" segment.  The "loader" would start immediately and load the rest of the game itself.  CiderPress obeys the shorter length value, and as a result will not copy the entire file (as evidenced by the pasted file being significantly smaller than the original). -
  • Files are copied and pasted in the order in which they appear in the file list.  You can alphabetize your disks by sorting the list by pathname and copying & pasting the files to a new disk. -
  • Damaged files are not copied to the clipboard. -
  • In Win98, copying 16MB or more to the clipboard causes the system to lock up.  CiderPress therefore limits you to copying less than 16MB of data under Win98/ME.  Because of overhead added to the clipboard data, it's not possible to copy 16MB files (the largest possible under ProDOS) to the clipboard.
-

-

- diff --git a/ciderpress/app/Help/html/t277.htm b/ciderpress/app/Help/html/t277.htm deleted file mode 100644 index 5da6bf1..0000000 --- a/ciderpress/app/Help/html/t277.htm +++ /dev/null @@ -1,32 +0,0 @@ - - - Tool - 2MG Properties Editor - - - - - - - - - - - -

2MG Properties Editor

-

 

-

This tool allows you to see and update some of the attributes embedded in a 2MG disk image file.  Select the disk image you want to open (it will usually have a ".2mg" or ".2img" suffix), and click "Open".

-

 

-

Some fields from the file header are shown at the top.  "Creator" is a 4-letter code for the software that created the file (CiderPress uses 'CdrP').  "Version" is the 2MG file version number, usually 0 or 1.  "Image format" tells you whether the image uses DOS-ordered sectors, ProDOS-ordered sectors, or raw nibbles.  "Blocks" is the number of 512-byte blocks in the file.

-

 

-

"Locked" is a software flag used to indicate whether or not the virtual disk has been write-protected.  Some emulators obey the flag, some don't.

-

 

-

"Specify disk volume number" indicates whether or not a volume number has been explicitly defined.  This is potentially of use for 140K 5.25" disk images.  5.25." floppies have a volume number embedded in the address field of every sector on the disk, but this value is lost when disks are converted to sector images (you have to use nibble images to retain it).  Some emulators will use the volume number from the 2MG header to determine the sector-address-field volume number.  This is only useful for a handful of mildly copy-protected disks that actually care about the volume number.

-

 

-

The "Volume number" field is enabled if the previous checkbox is set.  You may enter a number from 1 to 254.

-

 

-

The "Comment" box holds the comment for this file.  It can be any length you want.

-

 

-

If CiderPress is unable to open the file for writing, perhaps because it's marked read-only in Windows (a common occurrence for files copied off of a CD-ROM), the file will be opened read-only.  You will be able to see the contents of the fields but not edit them. - -

- diff --git a/ciderpress/app/Help/html/t28.htm b/ciderpress/app/Help/html/t28.htm deleted file mode 100644 index 674c22c..0000000 --- a/ciderpress/app/Help/html/t28.htm +++ /dev/null @@ -1,30 +0,0 @@ - - - Preferences - Files - - - - - - - - - -

File Preferences

-

 

-

Temp Folder

-

 

-

The folder for temporary files is set by default to the system temporary folder.  You may set this anywhere you wish.

-

 

-

The folder is used when you double-click on a ShrinkIt archive, Binary II archive, or archived disk image in the file list.  The file in question is copied into the temp folder and passed as an argument to a new instance of CiderPress.

-

 

-

External File Viewer Extensions

-

 

-

In some cases you may want to launch an external viewer to handle certain types of files stored in ShrinkIt archives or disk images.  For example, CiderPress does not have a built-in GIF or JPEG viewer.  If you put "gif; jpg; jpeg" in this field, then any files that end with those extensions will be opened using the default viewer (the program that gets run if you double-click on the file in Windows).

-

 

-

The file extensions are separated by semicolons, and should not include a leading dot ('.').  You will need to configure the default "open" handler in Windows for the specified file extensions yourself.  This can usually be done by opening a folder from My Computer, selecting "Folder Options" from the "Tools" menu, clicking on the "File Types" tab, and then editing the entry for the file type in question.

-

 

-

When you double-click a file with a matching extension, it is extracted to a temporary file and handed to the external viewer.  When you exit CiderPress, all of the temporary files created in this manner are deleted.  If an external viewer still has the file open, it may not be possible for CiderPress to remove it.  The file will eventually be removed by the Windows disk cleanup feature.

-

-

- diff --git a/ciderpress/app/Help/html/t284.htm b/ciderpress/app/Help/html/t284.htm deleted file mode 100644 index 5364184..0000000 --- a/ciderpress/app/Help/html/t284.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - CiderPress Help - - - - - - - - -

CiderPress Help

-

 

-

If you're reading this much, you know how to access the "help" system.  In case you got here by accident: select "Contents..." from the "Help" menu, or hit F1 while the menus are active.  Many of the interactive dialogs also have "Help" buttons that will take you directly to a relevant page.

-

 

-

Most dialog windows have a little question mark ('?') in the upper right-hand corner.  If you click on that, then click on a button or checkbox in the same window, you will get a short description of what that button does.

-

 

-

A Frequently Asked Questions list and a CiderPress tutorial are available from http://a2ciderpress.com/.

-

 

-

 

-

The "About..." item on the Help menu brings up the CiderPress "About Box".  This shows the version you're using, what versions of DLLs were loaded, and to whom your copy is registered.  If the product has not been registered, it shows how many days are left in the 30-day evaluation period.

-

 

-

"DLL" is an acronym for Dynamic Loadable Library.  That's a fancy way of saying that it's a part of the program that is stored in a separate file, which means it can be shared among multiple copies of the same program or multiple different programs.  The DLLs used by CiderPress are:

-

 

-
  • DiskImg.  All of the code for accessing disk images is here.  This is an integral part of CiderPress, and is shared by the CiderPress application and MDC (Multi-Disk Catalog). -
  • NufxLib.  All of the code for accessing NuFX (ShrinkIt) archives is here.  This is an open-source library developed by the author of CiderPress.  It's also used by the NuLib2 command-line utility. -
  • zlib.  Free software from the authors of gzip.  This provides "deflate" compression, and is used by hundreds of different applications. -
  • wnaspi32, a/k/a "the ASPI layer".  Unlike the previous DLLs, this one is only loaded when necessary.  For CiderPress, it's only necessary under Win98/ME, so Win2K/XP users won't ever see this loaded.
-

 

-

The first three DLLs are distributed with CiderPress, and are updated whenever you download a new version of CiderPress.  The ASPI DLL is part of the operating system, and may be replaced when you install CD recording software.  (If you want to use a specific version of wnaspi32.dll, copy it into the CiderPress installation directory, usually C:\Program Files\faddenSoft\CiderPress\.)

-

 

-

Click on "Credits" to bring up some information about the development of CiderPress.  If your copy has not been registered, click on "Enter registration code" to enter the code you received from faddenSoft after buying the product.

-

-

- diff --git a/ciderpress/app/Help/html/t29.htm b/ciderpress/app/Help/html/t29.htm deleted file mode 100644 index e6ca1fc..0000000 --- a/ciderpress/app/Help/html/t29.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - Preferences - Compression - - - - - - - - - -

Compression Preferences

-

 

-

You can access the Compression Preferences by selecting "Preferences..." from the "Edit" menu, and then clicking on the "Compression" tab.

-

 

-

This screen lets you choose the default compression method.  This will be used for all files added to an archive, and will be the default choice when you ask CiderPress to re-compress files.  It's also used by the disk image conversion tools.

-

 

-

The compression algorithms are described in detail here.

-

-

- diff --git a/ciderpress/app/Help/html/t38.htm b/ciderpress/app/Help/html/t38.htm deleted file mode 100644 index 37aac45..0000000 --- a/ciderpress/app/Help/html/t38.htm +++ /dev/null @@ -1,24 +0,0 @@ - - - Choose Folder - - - - - - - - - -

Choose Folder

-

 

-

This allows you to choose a folder on a local disk or across a network.  Click on the folder you want in the "tree" display, then click "Select".  Click on '+' signs to expand branches of the tree, '-' signs to collapse branches.

-

 

-

The full name of the folder that is currently selected is shown in the grey box underneath the tree.  Some folders, such as "My Computer", are special and do not have a pathname.  When these are selected in the tree, the "Select" button will be dimmed.

-

 

-

If you know the full pathname of the folder you want, you can type it in where it says "type the folder name" and then click on "Expand Tree".  The folder you entered will be selected in the tree.

-

 

-

If you click on the button next to the grey box (looks like a folder with a star in the upper right corner), CiderPress brings up the "New Folder" dialog.  This allows you to create a new folder inside the currently-selected folder.

-

-

- diff --git a/ciderpress/app/Help/html/t39.htm b/ciderpress/app/Help/html/t39.htm deleted file mode 100644 index 7c88f8f..0000000 --- a/ciderpress/app/Help/html/t39.htm +++ /dev/null @@ -1,79 +0,0 @@ - - - Extracting Files - - - - - - - - - - -

Extracting Files

-

 

-

If you only want to extract a few files, select them from the file list before clicking on "Extract...".  Selected subdirectories are ignored; only plain files are examined.

-

 

-

The extraction options dialog presents a large set of choices.  If you find the range of options bewildering, you may want to walk through the tutorial on the faddenSoft web site.  The Quick Configuration buttons, described later, can simplify common tasks.

-

 

-

Files to Extract

-

 

-

You may choose to extract the files selected in the file list, or all of the files in the archive or disk image.  If no files have been selected, "all files" will be the only option available.

-

 

-

Parts to Extract

-

 

-

You can further refine your selection by choosing to extract only certain kinds of entries.  You will almost always want to leave "data forks" and "disk images" enabled, unless you have an archive that is a mixture of the two.  "Disk images" refers to ShrinkIt-archived disk images (the file type is listed as "Disk"), not disk images stored as files.

-

 

-

If you just want to view the contents of the files under Windows, there's usually not much value in extracting the resource forks.

-

 

-

Attempting to extract a ProDOS subdirectory file (type "DIR") will not work.  You can examine them as raw data in the file viewer, but extracting them as raw data doesn't make much sense.  Instead, folders are automatically created as they are needed.

-

 

-

Format Conversion

-

 

-

Two options are available.  If you select "Convert to non-Apple II formats", then files will be converted in the same way they would when viewed with the file viewer.  For example, BASIC programs will be converted to TXT or RTF files, and graphic files are extracted as Windows bitmaps (BMP).  The individual converters can be turned on and off from the file viewer preferences page.  Files without specific conversions are treated as text or binary data according to the rules specified in the "text conversion" options (described below).

-

 

-

The "Extract disks as .2MG" selects whether disk images in NuFX archives are extracted to ProDOS-order ".PO" files or Universal Disk Format 2IMG files.  The latter is generally preferred for use with Apple II emulators because the format contains some useful information about the disk image.

-

 

-

Filenames

-

 

-

There are a couple of ways to tweak the filenames.  Selecting "Add file attribute preservation" will extend the filename with an attribute preservation sequence, and will replace any characters not legal in Windows with "%xx" codes.  This can be used to restore the file type, aux type, original filename, and whether the file holds a data fork, resource fork, or disk image.  More detail can be found here.  This setting is useful when you plan to add the files back into an archive.

-

 

-

The "Add type extension" option adds a three-letter extension to files that lack one.  For example, any file of type TXT will be given a ".TXT" extension so that you can open the file with Notepad by double-clicking on it.

-

 

-

The filename alterations that affect the list view, such as converting filenames to lower case or replacing spaces with underscores, are also taken into account here.  These are configured in the General Preferences screen.

-

 

-

Text Conversion

-

 

-

Text files on the Apple II have carriage returns (CR, ASCII code 13) at the end of each line.  UNIX and its derivatives use linefeeds (LF, ASCII code 10), and Windows prefers a carriage return followed by a linefeed (CRLF).  CiderPress can convert these "end-of-line markers" to CRLF so that Windows applications can read the files.

-

 

-

Not all files of type TXT are text files, and not all text files have type TXT, so CiderPress can try to sort things out for you.  There are four settings for end-of-line conversion: off, by file type, automatic scan, and on.  When it's set to "off", nothing is converted; when it's "on", everything is converted.  If you select "Convert text files by file type", only files of type "TXT" or "SRC" are converted.  In the "Auto-detect & convert files with text" mode, CiderPress will scan each file and convert the ones that look like text files, regardless of file type.

-

 

-

Apple DOS 3.3 complicates matters somewhat with "high ASCII" text files.  These look garbled unless the high bit of every byte is stripped.  However, stripping every text file would cause Apple IIgs documents with "smart quotes" and accented characters to appear damaged.  CiderPress only strips the high bits if the end-of-line converter is also enabled, and does so only on DOS 3.3 text files or on text files that are entirely composed of characters with the high bit set.  (The "strip high ASCII text files" option is dimmed when "don't convert text files" is selected.)

-

 

-

Archived disk images (e.g. "Disk" entries in a ShrinkIt Archive) never undergo text conversion, even when "Convert ALL files" is selected.  Neither do resource forks.

-

 

-

Miscellaneous

-

 

-

Two options that you may find useful.  These are not affected by the Quick Configuration buttons.

-

 

-

"Strip folder names" reduces the pathnames shown in the list to just their filenames.  Everything you extract will end up in the same folder.

-

 

-

If you select "Overwrite existing files", you will not be asked to confirm overwriting of files with the same name as files being extracted.

-

 

-

Quick Configuration Buttons

-

 

-

There are two large buttons at the bottom of the dialog that allow you to choose from the two most common configurations.  Pressing the buttons just changes the state of the controls in the dialog.  There are no hidden options or side-effects.

-

 

-

"Configure to preserve Apple II formats" is useful when you want to extract files from one archive and add them to another.  Resource forks are extracted, file attribute preservation is enabled, and format converters (including text conversion) are disabled.  These files will most likely not be readily usable under Windows, so file extensions are not added.

-

 

-

Click on "Configure for easy access in Windows" when you want to extract files for viewing under Windows.  File viewer and text converters are enabled, and file extensions are added where appropriate.  Disks are extracted in 2MG format.

-

 

-

Other Notes

-

 

-

Extracting a file that already exists brings up a small dialog that lets you choose whether or not to overwrite the file.  You can choose to overwrite it, skip it, overwrite it and all other conflicting files, skip it and all other conflicting files, rename the file being extracted, or cancel the whole thing.

-

 

-

"Damaged" files will not be extracted.

-

-

- diff --git a/ciderpress/app/Help/html/t41.htm b/ciderpress/app/Help/html/t41.htm deleted file mode 100644 index 70de36f..0000000 --- a/ciderpress/app/Help/html/t41.htm +++ /dev/null @@ -1,70 +0,0 @@ - - - Add Files Dialog - - - - - - - - - -

Add Files

-

 

-

Use this feature to add files to a disk image or ShrinkIt archive.  This dialog is similar in many ways to the standard Windows "open file(s)" dialog, but it has been customized to allow you to select folders as well as files.

-

 

-

Select the files and folders you want to add, then click on "Accept".  You can click on a file and then shift-click on another to select a range of files.  Use control-click to select or unselect a single file in a group.  Double-clicking a file immediately selects that file and starts the "add files" process, while double-clicking a folder simply opens the folder.

-

 

-

(The differences from the standard Windows "open files" behavior may occasionally cause confusion.  For the most part you shouldn't notice anything unusual.  Just remember that clicking on "Accept" will add everything selected, including folders.)

-

 

-

CiderPress tries to ensure that all files you select are added without conflicting with each other.  For example, if you add "ReallyLongFilenameA" and "ReallyLongFilenameB" to a ProDOS disk, it will add them as "ReallyLongFilen" and "ReallyLongFile1".  CiderPress will try to preserve filename extensions, so "ReallyLongFileName.c" becomes "ReallyLongFil.c".

-

 

-

The dialog has a few options you can change, described below.

-

 

-

File attribute preservation

-

 

-

Use this to decide whether CiderPress will accept or ignore file attribute preservation sequences in filenames.  If a file was extracted with tags (strings that look like "#062000"), they will be used to determine the file type and fork of the file being added.  Also, any characters that were invalid in Windows filenames and had to be changed to "%xx" sequences will be converted back.

-

 

-

If the option is set to "Ignore file attribute preservation tags", CiderPress will act as if it has never heard of the things.  Files will be added as type NON, and the "#062000" and "%xx" stuff will be added exactly as it appears on disk.  You should only use this when adding files that have '#' or '%' characters that are confusing CiderPress.

-

 

-

If the option is set to "Use file attribute preservation tags", CiderPress will use them whenever it finds them.  If it doesn't find them, the file will be added with type NON.  This is the default setting.

-

 

-

The "Use tags and guess type from extension" goes a little farther, and tries to set the ProDOS file type based on the Windows extension.  For example, ".txt" files will be added with type TXT, and ".shk" files will become $e0 with auxtype $8002.  This is very useful when adding files to DOS 3.3 disks, because it ensures that ".txt" files get file type 'T'.  Without this, ".txt" files will be added as 'B'.  Use this option if you are adding files that weren't generated on an Apple II, like Windows text files or graphics.

-

 

-

Text conversion

-

 

-

This option allows you to convert text files from Windows text conventions (carriage return followed by linefeed at the end of each line) to Apple II conventions (carriage return at the end of each line).  Due to limitations in the NufxLib library -- as well as philosophical concerns about altering archived files -- the feature is disabled when adding files to ShrinkIt archives.  It's enabled for disk images only.

-

 

-

You have four choices.  The most basic are "Don't convert text files", which adds every file exactly as it is, and "Convert ALL files", which converts all EOL markers found in every file.  The "convert all" option is generally not recommended, because it will try to convert non-text files, potentially rendering them unusable.

-

 

-

The "Convert text files by file type" option will convert anything added as "TXT or "SRC", and leave everything else alone.  The file type is determined based on the file attribute preservation setting (discussed above).  If CiderPress is configured to ignore attribute preservation tags, files will be added with type "NON", which does not undergo a text conversion with this setting.

-

 

-

When "Auto-detect & convert files with text" is selected, CiderPress scans the file to see if it appears to be text.  The file type is ignored.  This can be helpful, because it'll convert Windows files with names like "README" or old documents called "file.doc".  (It was common practice to name text files ".doc" until Microsoft Word claimed that extension.)

-

 

-

If CiderPress gets the auto-detection wrong, delete the files, and re-add the files that didn't work, setting the conversion to be always off or always on.  (Tip: if you delete the bad ones, you can re-add the entire set, and when asked if you want to overwrite an existing file, say "no to all".  That way you don't have to hand-select just the files to re-add.)

-

 

-

There is a small performance boost when text conversion is turned off.  If you're adding files that previously came out of an Apple II disk image, and no text conversion was performed during the extraction, turn text conversion off when adding files.  Conversely, if you're adding text files or source code that you've been editing under Windows, set it to "by type" or "auto".

-

 

-

Resource forks are never converted, regardless of their contents and the current settings.  Any file added to a DOS disk that is identified as text will also be converted to "high ASCII".

-

 

-

Miscellaneous

-

 

-

If "Include subfolders" is checked, then CiderPress will descend into any folders you have selected, adding files found in them and descending into their sub-folders.  If it's not checked, only files in the current directory will be added.  You will usually want to have this checked.

-

 

-

If you decide you don't want folder names, check the "Strip folder names" box.  This removes the folder names, effectively putting all files into the same folder in the archive.  This option is enabled automatically when adding files to DOS 3.3 and Pascal disks, which don't have folders.

-

 

-

The "Overwrite existing files" setting will cause any entries with the same name (case-insensitive) as a file being added to be overwritten by the newly-added file.  If this box is not checked, you will be presented with the option of overwriting the existing file, skipping the add of the new file, or renaming the new file.  (The option to rename the file being added is not available for ShrinkIt archives, because NufxLib doesn't support it.)

-

 

-

Storage prefix

-

 

-

This option is only available when adding to a ShrinkIt archive.  Anything you put here will be prepended to the filename stored in the archive.  The reasons for doing this may not be immediately obvious.

-

 

-

Suppose you want to add a file called "foo" to an archive.  If you move to the folder with "foo" in it, select it, and click on "Accept", it will be added to the archive as "foo".  This is fine, unless you want it to appear in the same sub-folder in the archive as other files.  Perhaps you have a file called "bar" stored as "dir1:subdir2:bar", and you want "foo" and "bar" to be extracted to the same place.

-

 

-

You could create a folder called "dir1" under Windows, and then create another folder called "subdir2" in that, and then put "foo" in that folder, and finally have CiderPress do an Add Files on "dir1".  If you put "dir1:subdir2" into the Storage Prefix box, and then just add "foo", you will get exactly the same result -- files added as "dir1:subdir2:foo" -- with significantly less effort.

-

 

-

(You could, in this case, just add "foo" and then use the Rename Entry feature to change the pathname, but that becomes tedious when a large set of files is involved.)

-

-

- diff --git a/ciderpress/app/Help/html/t42.htm b/ciderpress/app/Help/html/t42.htm deleted file mode 100644 index 3cc8245..0000000 --- a/ciderpress/app/Help/html/t42.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - Rename Entry - - - - - - - - - -

Rename Entry

-

 

-

This feature is disabled when an archive or disk image is opened in read-only mode.

-

 

-

Select one or more files to rename, then activate the "Rename..." command.  A dialog opens for the first file.

-

 

-

The grey edit field on the top is the current name of the file.  The edit field on the bottom is the name to change the file to; it defaults to the current name of the file.  The box labeled "Path separator character" shows the character used to separate pathname components from each other in this specific file.  This requires a little explanation.

-

 

-

If you have a folder "subdir", in which is a file called "foo", you would access it as "subdir\foo" under Windows.  Under UNIX or ProDOS 8 you would use "subdir/foo", and under GS/OS you would write "subdir:foo".  CiderPress follows the GS/OS convention, so pathnames from disk images and in files archived by CiderPress always use ':'.

-

 

-

Suppose you tried to extract a file called "subdir/foo:bar".  On a UNIX system this would be a folder called "subdir" with a file called "foo:bar" in it, but on an HFS filesystem that would be a folder called "subdir/foo" with a file called "bar" in it.  ShrinkIt archives were intended to be multi-platform, so the format requires the application adding the file to specify the value for the separator.  Most archives use '/' or ':', but a few use '\'.

-

 

-

This is important to understand because, when renaming files in a ShrinkIt archive, you're not just changing the filename, you're changing the entire pathname.  You can effectively move a file into a different subdirectory by renaming "subdir1:foo" to "subdir2:foo".  You need to be aware that the path separator character isn't always the same for every file.

-

 

-

You can change the path separator character for files in NuFX archives.  The NufxLib library used by CiderPress currently does not allow a null value for the separator, so if you try to delete the character altogether, CiderPress will substitute ASCII value 0xff, which looks like a 'y' with an umlaut under Windows.  This value can also appear for files copied & pasted from DOS disk images.  This behavior may be corrected in a future release of NufxLib.

-

 

-

If you're renaming a file on a disk image, you will be shown the full path but can only change the file name.  (There is currently no way to "move" a file to a different subdirectory, but you can copy and paste the file to a new folder if the disk has sufficient space.)

-

 

-

After choosing the new name, press "OK" to accept it.  The dialog will update to show the name of the next file in the list.  If you decide you don't want to rename this file, you can either click on "Skip" to move on to the next entry or "Cancel" to drop out of the process.

-

 

-

ShrinkIt stores disk images with the disk volume name, not a pathname.  For this reason, the filename separator character cannot be part of a disk image name.

-

 

-

If you have one of the filename alterations enabled, such as conversion to lower case or replacing spaces with underscores, you will be shown the name with the modifications already made.  If you press "OK", the alterations will become a permanent part of the archive, and will not go away when you turn the alterations off.  You may want to switch these off before renaming entries.

-

 

-

The aux type of AppleWorks files is not updated in ShrinkIt archives when the filename changes.  (The aux type is used as lower-case flags.)  This may result in strange-looking filenames when the contents are extracted to a disk image.  The lower case flags are set appropriately when AppleWorks files are renamed on a disk image, even if the ProDOS "allow lower case" preference is turned off.

-

 

-

You cannot rename the volume directory of a ProDOS or HFS volume.  Attempts to do so will be silently ignored.  Use the Rename Volume command instead.

-

 

-

Some HFS files may have strange characters in the filenames.  These are "Macintosh Roman" characters that don't translate directly to Windows equivalents.  The names in the file list are "sanitized", with characters like '¥' converted to simple letters and numbers, but when renaming a file the name is presented unmodified. - -

- diff --git a/ciderpress/app/Help/html/t43.htm b/ciderpress/app/Help/html/t43.htm deleted file mode 100644 index 43aa8e1..0000000 --- a/ciderpress/app/Help/html/t43.htm +++ /dev/null @@ -1,25 +0,0 @@ - - - Edit Comment - - - - - - - - -

Edit Comment

-

 

-

This feature only works for NuFX archives, because that is the only Apple II format CiderPress supports that allows file comments.  It will be disabled for archives opened read-only.

-

 

-

To add, edit, or delete a comment, select the entry you want to modify.  You may only select one entry at a time.  Activate the "Edit comment..." command.  If the entry does not already have a comment attached, you will be asked if you want to add one.

-

 

-

The dialog that appears allows you to edit the text of the comment or delete it entirely.  If you hit "Cancel", your changes will be discarded.

-

 

-

Comments in NuFX archives use a "pre-sized" buffer.  For example, when GS/ShrinkIt adds files or disks to an archive, the first entry has space set aside for a 200-character comment.  The idea was to allow comments to be updated without having to shift lots of data around to make room.  This space is allocated by GS/ShrinkIt whether you choose to write a comment or not.  Any comments you add with CiderPress will also be "over-sized".

-

 

-

If you enable the "Mimic ShrinkIt" feature on the General Preferences screen, CiderPress will behave like GS/ShrinkIt and add an empty comment to the first entry whenever you add files or disks to an archive.

-

-

- diff --git a/ciderpress/app/Help/html/t44.htm b/ciderpress/app/Help/html/t44.htm deleted file mode 100644 index 59de355..0000000 --- a/ciderpress/app/Help/html/t44.htm +++ /dev/null @@ -1,21 +0,0 @@ - - - Edit File Associations - - - - - - - - -

Edit File Associations

-

 

-

CiderPress can be "associated" with file types, meaning that when a file with an associated extension is opened (usually by double-clicking on it from Windows Explorer), CiderPress is launched to handle it.  The recognized extensions, and the default behavior when opening them, are listed here.

-

 

-

When CiderPress is first installed, all recognized extensions that do not already have an association are linked to it.  You can use this dialog to associate or disassociate any of the file types by clicking in the checkbox next to the extension.

-

 

-

This dialog changes the settings but does not make them permanent.  Hit "OK" or "Apply" in the preferences pages to actually make the update, or "Cancel" to undo the changes.

-

-

- diff --git a/ciderpress/app/Help/html/t45.htm b/ciderpress/app/Help/html/t45.htm deleted file mode 100644 index 58de3a3..0000000 --- a/ciderpress/app/Help/html/t45.htm +++ /dev/null @@ -1,73 +0,0 @@ - - - Appendix - File Extensions - - - - - - - - - -

File Extensions

-

 

-

CiderPress handles a variety of Apple II file and disk image formats.  If you associate these files with CiderPress, opening the documents (perhaps by double-clicking on them from Windows Explorer) will launch CiderPress automatically.

-

 

-

Some files can be interpreted in more than one way.  For example, ".BXY" files are ShrinkIt archives wrapped in a Binary II header.  The file selector you choose in the "Open File" dialog determines how the file is opened.  When double-clicking on a file, however, you don't have an opportunity to make a choice, so CiderPress tries to guess which way you wanted.  The same applies when opening a file with the selector set to "All Files (*.*)".

-

 

-

The following table summarizes the types handled and the method of handling them.

-

 

-

 

-

Ext Description Default Action  

-

 

-

.2MG 2IMG disk image Open as disk image  

-

 

-

.APP Disk image in TrackStar nibble format Open as disk image  

-

 

-

.BNY Binary II Open as Binary II  

-

 

-

.BQY Compressed Binary II Open as Binary II  

-

 

-

.BSE GSHK Self-Extracting Archive, wrapped in Binary II Open as NuFX  

-

 

-

.BXY NuFX wrapped in Binary II Open as NuFX  

-

 

-

.DDD DDD-compressed disk Open as disk image  

-

 

-

.DO DOS-order disk image Open as disk image  

-

 

-

.DC6 DiskCopy 6.x image Open as disk image  

-

 

-

.DSK Disk image (often DiskCopy 4.2) Open as disk image  

-

 

-

.GZ Disk image compressed with gzip Open as disk image  

-

 

-

.HDV Raw disk image Open as disk image  

-

 

-

.IMG Disk image (usually Copy ][+) Open as disk image  

-

 

-

.PO ProDOS-order disk image Open as disk image  

-

 

-

.RAW Raw disk image Open as disk image  

-

 

-

.SDK NuFX disk archive Open as disk image  

-

 

-

.SEA GSHK Self-Extracting Archive Open as NuFX  

-

 

-

.SHK NuFX file or disk archive Open as NuFX  

-

 

-

.ZIP Disk image compressed with ZIP Open as disk image  

-

 

-

(none) File with no extension Open as disk image  

-

 

-

 

-

 

-

If the default handler fails, the file will not be opened.

-

 

-

DiskCopy 6 can create images in a "raw" format or with fancy headers.  CiderPress only handles the "raw" form.

-

 

-

CiderPress does not create file associations for .RAW because there are many other PC file formats that use the same extension.  A similar exclusion is made for .GZ and .ZIP, because the extension only indicates that "gzip" or "zip" was used to compress the file.  It doesn't say anything about the contents. - -

- diff --git a/ciderpress/app/Help/html/t46.htm b/ciderpress/app/Help/html/t46.htm deleted file mode 100644 index 3439047..0000000 --- a/ciderpress/app/Help/html/t46.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - How (and Why) to Register - - - - - - - - - -

How (and Why) to Register

-

 

-

Registration is no longer required.

-

-

- diff --git a/ciderpress/app/Help/html/t47.htm b/ciderpress/app/Help/html/t47.htm deleted file mode 100644 index 1ccd388..0000000 --- a/ciderpress/app/Help/html/t47.htm +++ /dev/null @@ -1,108 +0,0 @@ - - - Features - - - - - - - - -

Features

-

 

-

CiderPress is a fully-functional "ShrinkIt for Windows" that can operate on disk images and file archives.  Files can be viewed or extracted in their native form, or converted to formats better suited for browsing in Windows.

-

 

-

When working with NuFX (ShrinkIt) archives and DOS 3.2/3.3, ProDOS, HFS, and UCSD Pascal disk images, you can:

-
  • View files without having to extract them first.  Special converters are provided for several popular file formats. -
  • Extract files and directories, optionally converting them.  File type information can be preserved, using the method developed for use in NuLib2. -
  • Add individual files or entire directories full of files. -
  • Copy & paste files through the Windows clipboard. -
  • Rename files. -
  • Delete files or entire subdirectories. -
  • Edit file type, auxtype, and access permissions of ProDOS files. -
  • Print a listing of the files in the archive.
-

 

-

On NuFX archives, you can also:

-
  • Convert a ShrinkIt file archive directly to a ProDOS disk image. -
  • Re-compress entries with a different algorithm.  All compression methods defined in the NuFX specification are supported, as well as gzip "deflate". -
  • Add, update, and delete comments. -
  • Test entries or whole archives.
-

 

-

On disk images, you can also:

-
  • Create subdirectories. -
  • Change volume names and numbers.
-

 

-

Read-only access (viewing and extracting files, but not adding or deleting) is supported for Binary II archives, AppleLink Compression Utility archives, and CP/M and RDOS disk images.

-

 

-

The file viewer can convert several formats for easier viewing on a PC:

-
  • ProDOS text with carriage returns to Windows "CRLF" format. -
  • DOS 3.3 "high ASCII" text to plain text. -
  • CP/M text to plain text. -
  • UCSD Pascal text to plain text. -
  • UCSD Pascal code to partially analyzed hex dump. -
  • Applesoft BASIC to text (matches output of "LIST" command), with or without color syntax highlighting. -
  • Integer BASIC to text (matches output of "LIST" command), with or without color syntax highlighting. -
  • Apple /// Business BASIC to text (matches output of "LIST" command), with or without color syntax highlighting. -
  • S-C Assembler to text. -
  • LISA tokenized assembly source to text. -
  • Merlin assembly source to properly-tabbed text. -
  • 65C02/65816 code disassembly, to //e or IIgs "monitor" output. -
  • Magic Window / Magic Window II "formatted" files to text. -
  • AppleWorks Word Processor to Rich Text Format (supports left/right margins, bold, italic, etc). -
  • AppleWorks Database to CSV (suitable for import into Excel or other applications). -
  • AppleWorks Spreadsheet to CSV. -
  • Teach (GWP $5445) document to Rich Text Format. -
  • AppleWorks GS Word Processor (GWP $8010) to Rich Text Format. -
  • Hi-Res graphics to 560x384 16-color .BMP (B&W or color with half-pixel shifting). -
  • Double Hi-Res graphics to 560x384 16-color .BMP (B&W or color). -
  • Super Hi-Res graphics to 640x400 256-color .BMP.  Supported formats include unpacked ($c1/0000), Paintworks ($c0/0000), packed ($c0/0001), DreamGrafix ($c0/8005), and Apple Preferred Format ($c0/0002) up to 1024x1024. -
  • 3200-color Super Hi-Res graphics to 640x400 24-bit .BMP.  Supported formats include unpacked ($c1/0002 or ".3200"), packed (".3201"), DreamGrafix ($c0/8005), and Apple Preferred Format ($c0/0002 with "MULTIPAL"). -
  • Print Shop and Print Shop GS graphics to 88x52 .BMP (B&W and color, respectively). -
  • Macintosh Paint images to 576x720 2-color .BMP. -
  • Apple IIgs resource forks are split into individual resources and displayed as hex dumps.
-

 

-

In addition, any fork of any file can be viewed in its "raw" state or as a hex dump.

-

 

-

CiderPress has the ability to identify most common disk image formats, and view or extract files directly from them.  Supported file formats include:

-
  • Universal Disk Images (.2mg), all formats -
  • DiskCopy 4.2 (.dsk) -
  • Copy II Plus (.img) -
  • Sim //e HDV images (.hdv) -
  • TrackStar images (.app) -
  • Dalton's Disk Disintegrator (DDD 2.1+, DDD Pro 1.1+) (.ddd) -
  • FDI "raw" images of 5.25" and 3.5" disks (.fdi) -
  • Unadorned sector-format files (.po, .do, .d13, .raw, .hdv, .iso) -
  • Unadorned nibble-format files (.nib, .nb2) -
  • Any of the above compressed with gzip (.gz) -
  • Any of the above compressed with zip (.zip) -
  • ShrinkIt (NuFX) compressed disk images (.shk, .sdk)
-

 

-

The file format, filesystem, and sector ordering are determined automatically for most disks.  The settings can be overridden if necessary.  The recognized disk formats are:

-
  • DOS 3.2/3.3 (13, 16, or 32 sectors, up to 50 tracks) -
  • ProDOS -
  • HFS -
  • UCSD Pascal -
  • CP/M -
  • SSI's RDOS (13-sector, 16-sector, and 13 converted to 16) -
  • UNIDOS / AmDOS / OzDOS (two DOS volumes on an 800k disk) -
  • ProSel Uni-DOS / DOS Master (DOS volumes embedded in an 800K ProDOS disk) -
  • CFFA fixed-size partitions (4-part and 8-part formats) -
  • Macintosh-style partitoning (for CD-ROMs and SCSI hard drives) -
  • ///SHH Systeme MicroDrive partitioning -
  • FocusDrive partitioning
-

 

-

Images larger than floppies, such as ProDOS hard drive partition images, are fully supported.  Physical media, including floppy disks, CD-ROMs, CFFA-formatted CompactFlash cards, and SCSI hard drives can be accessed directly (assuming you have appropriate hardware).

-

 

-

CiderPress includes a collection of tools:

-
  • Disk viewer - view disk images as blocks, sectors, or nibblized tracks. -
  • Disk image converter - convert disk images from one format to another.  Can operate on individual images or perform a bulk conversion on a large set. -
  • Disk image creator - create bootable DOS 3.3, ProDOS, or Pascal disk images from scratch. -
  • Volume copier - copy all or part of a disk image from one place to another. -
  • SST image merge - combine images generated by SST into a single .nib image file. -
  • 2MG properties editor - edit comments, lock/unlock images. -
  • EOL scanner - check for ASCII transfer damage. -
  • Import from WAV - extract programs from Apple II cassette tapes.
-

-

- diff --git a/ciderpress/app/Help/html/t48.htm b/ciderpress/app/Help/html/t48.htm deleted file mode 100644 index a908d49..0000000 --- a/ciderpress/app/Help/html/t48.htm +++ /dev/null @@ -1,61 +0,0 @@ - - - Available Commands - - - - - - - - - - -

Available Commands

-

 

-

If you've used other file archive software, such as WinZip or WinRAR, then you already know most of what you need to know to use CiderPress.  There are some differences, mainly because CiderPress is optimized for use with Apple II files.

-

 

-

Things you can do:

-

 

-

Selecting the command you want.

-

Opening, closing, and creating archives and disk images.

-

Opening raw disk volumes.

-

Getting information about an archive.

-

Working with the file list.

-

Printing the file list.

-

Viewing the contents of files in an archive or disk image.

-

Adding files and disk images to an archive.

-

Creating new subdirectories.

-

Extracting files and disk images.

-

Copying and pasting files.

-

Testing entries in an archive.

-

Renaming entries.

-

Renaming disk volumes.

-

Deleting entries.

-

Editing archive comments.

-

Editing attributes of archived files.

-

Convert disk image to file archive.

-

Convert file archive to disk image.

-

Import files from Apple II cassette tapes.

-

Import BASIC programs from text files.

-

 

-

CiderPress has some additional tools that you may find useful:

-

 

-

Disk sector viewer.

-

Disk image converter.

-

Bulk disk image converter.

-

SST image merge.

-

Volume copier.

-

2MG properties editor.

-

End-Of-Line scanner.

-

 

-

There are several screens of preferences:

-

 

-

General preferences.

-

Disk Image preferences.

-

Default compression selection.

-

File viewer settings.

-

File and folder locations.

-

-

- diff --git a/ciderpress/app/Help/html/t49.htm b/ciderpress/app/Help/html/t49.htm deleted file mode 100644 index 6060be5..0000000 --- a/ciderpress/app/Help/html/t49.htm +++ /dev/null @@ -1,25 +0,0 @@ - - - Entering Registration Data - - - - - - - - - - -

Entering Registration Data

-

 

-

After you register CiderPress you will receive an e-mail message with your registration code.  Copy the information from the message exactly as it appears.  Upper-case vs. lower case is important, as is all punctuation.  Don't forget to put the hyphens ('-') in when typing the registration key.

-

 

-

The easiest way to fill the fields out is to cut and paste directly from the registration message.  Copy the text from the message, click in the appropriate field in the CiderPress dialog, and hit Ctrl-V to paste it.

-

 

-

As you paste or type the text in, you will see the four-character "checksum" value change.  Make sure the checksum shown in your confirmation letter matches what you see on screen.  If they don't match, something has been mis-typed.  (If you're cutting & pasting, make sure you don't accidentally paste the checksum values into the box.)

-

 

-

When you hit "OK", CiderPress will verify your key.  If all fields have been entered correctly, you will return to the "About" box with your name and company shown, and the "Enter registration code" button will be disabled.

-

-

- diff --git a/ciderpress/app/Help/html/t50.htm b/ciderpress/app/Help/html/t50.htm deleted file mode 100644 index 0ed5d84..0000000 --- a/ciderpress/app/Help/html/t50.htm +++ /dev/null @@ -1,44 +0,0 @@ - - - Working With the File List - - - - - - - - - -

Working With the File List

-

 

-

After an archive or disk image is successfully opened you will be presented with a list of files found inside.  The list is divided into several columns:

-

 

-

Pathname

-

File type

-

Aux type

-

Modification date

-

Format

-

Size

-

Ratio

-

Packed size

-

Access

-

 

-

Columns can be hidden or returned to default sizes with buttons in the preferences screens.  You can resize them by clicking and dragging on the space between items in the column header.  Hide columns by shrinking them down until they disappear.

-

 

-

Any column can be sorted.  Click on the column header once to sort on that column.  Click on it a second time to change the direction of the sort.  You can also use the "Sort" feature in the "Edit" menu to choose a column to sort on, or choose to return to the original order of the files.

-

 

-

Column widths and sort order are retained across invocations.  If you change the screen font sizes with the Windows "appearances" settings, use the "Defaults" button in the preferences screen to adjust the widths for the new font size.

-

 

-

Select entries in the list by clicking on them.  To select multiple entries, click on the first item and then shift-click on the last item.  You can control-click to add or remove items from the selection list.  To select all items, type Ctrl-A, or use the "Select all" function from the "Edit" menu.  The "Invert selection" feature selects everything that isn't selected, and un-selects everything that is.

-

 

-

Double-clicking on most items will immediately bring up the file viewer.  Double-clicking on ShrinkIt archives, Binary II archives, or archived disk images will cause the file to be copied into the temp folder and opened in a new instance of CiderPress.  (The opened copy will be marked read-only because the file in the temp folder is thrown out as soon as CiderPress exits.)

-

 

-

If you double-click on multiple files (click, hold the shift key down, then double-click), the file viewer is opened for all of them regardless of what they contain.

-

 

-

Right-clicking brings up an abbreviated "actions" menu.

-

 

-

You can search for entries in large archives or disk images with the "Find" feature.  Hit Ctrl-F, or select "Find" from the "Edit" menu.  You can make the search case-sensitive if you like, and if you select "whole words" it will only match complete filenames or filename components.  The search begins from the currently selected item, and wraps around when it reaches the end of the list. - -

- diff --git a/ciderpress/app/Help/html/t51.htm b/ciderpress/app/Help/html/t51.htm deleted file mode 100644 index 763ae4d..0000000 --- a/ciderpress/app/Help/html/t51.htm +++ /dev/null @@ -1,35 +0,0 @@ - - - Opening, Closing, and Creating Files - - - - - - - - - - -

Opening, Closing, and Creating Files

-

 

-

Open an archive or disk image by selecting "Open..." from the "File" menu.  Select the file to open, and click the "Open" button.  All of the standard Windows "open file dialog" features work here.  For example, if you want to change the current folder to "C:\apple2\disks", you could navigate there by selecting "C:" from the drop-down menu and then double-clicking on the "apple2" folder and then the "disks" folder.  A perhaps faster approach is to click in the "File name:" box, type "C:\apple2\disks", and hit return.

-

 

-

The class of files you select in the "Files of type:" box does more than just choose which files you get to see.  In some cases, it determines how the file is opened.  For example, a .BXY file is a ShrinkIt archive with a Binary II header.  If you select "ShrinkIt Archives" or "All Files", the file will be opened as a ShrinkIt archive.  If you select "Binary II Archives", the file will be opened as a Binary II archive (with a single ShrinkIt archive inside).  The same applies to ShrinkIt archives with a single disk image in them.  They can be opened either as ShrinkIt archives or as disk images.

-

 

-

You may want to examine the default behavior defined for each file extension when double-clicked from Windows Explorer or opened with "All Files (*.*)".

-

 

-

If you select "Open as read-only", the file will be open in read-only mode.  This means that operations that modify the file, such as adding, deleting, or renaming files, will be deactivated.  The file will automatically be opened in read-only mode if the file itself is marked "read only" in Windows.  (You can change this setting before opening the file by right-clicking on the filename, selecting "properties", and un-checking the "Read-only" box.)  Disk images that appear to be damaged will also be opened in read-only mode to prevent modifications from exacerbating problems.  You can check for damage with the Archive Info feature.

-

 

-

CiderPress can open disk images stored in gzip (.gz) or Zip (.zip) archives, so long as the archive contains only one file, and the file is 32MB or less when expanded.  Because the disk image is uncompressed to memory, CiderPress does not handle compressed images larger than 32MB.  Also, ShrinkIt archives inside Zip or gzip archives cannot be opened.  You can extract them with a program like WinZip or by enabling "compressed folder" support in Windows XP.

-

 

-

You can create a new, empty ShrinkIt archive by selecting "New -> ShrinkIt archive..." from the File menu.  The archive is created immediately.  If you close the archive while it is still empty, the file will be removed.  You can create disk images with the "New -> Disk image..." function.

-

 

-

In most cases, any changes you make to a file archive or disk image happen immediately.  If you're working with a compressed disk image, stored in a ShrinkIt, ZIP, or gzip file, the image isn't recompressed until you close it.  You can flush the changed data sooner by using the "Save changes" menu item.  This will automatically be invoked for you if you use one of the tools that can modify disk images (e.g. the volume copier).

-

 

-

Close the current archive or disk image with "Close" from the File menu.  You won't often need to use this, since the files are closed automatically when a new file is opened or the program exits.

-

 

-

Changes made to an archive or disk image by another program, such as an Apple II emulator, will not be detected automatically.  For example, if you add or delete files from a disk image with Copy ][+ in an emulator, you will need to close and re-open the file.  The easiest way to do this is with the "Reopen" command in the File menu. - -

- diff --git a/ciderpress/app/Help/html/t52.htm b/ciderpress/app/Help/html/t52.htm deleted file mode 100644 index 1c5600c..0000000 --- a/ciderpress/app/Help/html/t52.htm +++ /dev/null @@ -1,27 +0,0 @@ - - - Selecting Commands - - - - - - - - -

Selecting Commands

-

 

-

There are three distinct ways to perform actions:

-

 

-

1. Select a command from the menu at the top of the screen.

-

2. Select a command from the toolbar below the menu.

-

3. When a file is open, right-click and select a command from the menu that pops up.

-

 

-

All three approaches produce the same results.

-

 

-

There is a fourth way to initiate action: double-click on an item in the file listing.  The exact behavior depends on what it is you clicked on.  See Working With the File List for details.

-

 

-

Most features will be disabled unless an archive or disk image is open.  Some will be disabled until you select one or more entries from the list.  Some are only active for ShrinkIt archives, or will be dimmed if the file is opened read-only.

-

-

- diff --git a/ciderpress/app/Help/html/t53.htm b/ciderpress/app/Help/html/t53.htm deleted file mode 100644 index ad3d228..0000000 --- a/ciderpress/app/Help/html/t53.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - Printing - - - - - - - - - -

Printing

-

 

-

The output sent to the printer looks very much like the file list when all columns are enabled.  The sort order matches the current file list.

-

 

-

Select "Print..." from the "File" menu to bring up a standard Windows print dialog.  Choose your printer and, optionally, a range of pages to print.

-

-

- diff --git a/ciderpress/app/Help/html/t54.htm b/ciderpress/app/Help/html/t54.htm deleted file mode 100644 index f667650..0000000 --- a/ciderpress/app/Help/html/t54.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - Viewing Files - - - - - - - - - -

Viewing Files

-

 

-

To use the "View..." command, first select the files you want to view.  Select the "View..." menu item or click on the toolbar button.  The File Viewer opens.

-

 

-

Double-clicking on most kinds of files will open the file viewer.  However, if you double-click on a ShrinkIt archive or disk image, a new instance of CiderPress will be launched with the file displayed in it.  (You can get the same behavior with disk images by selecting the image and then using "Open as disk image" from the Actions menu.)

-

-

- diff --git a/ciderpress/app/Help/html/t55.htm b/ciderpress/app/Help/html/t55.htm deleted file mode 100644 index c5ce276..0000000 --- a/ciderpress/app/Help/html/t55.htm +++ /dev/null @@ -1,39 +0,0 @@ - - - Adding Files and Disks - - - - - - - - - -

Adding Files and Disks

-

 

-

Add files and folders to an archive by selecting "Add files..." from the "Actions" menu.

-

 

-

In most cases you will immediately be asked to choose the files you want to add.  See the explanation of the "Add Files" dialog for details on what to do next.

-

 

-

If you're adding files to a ProDOS disk that has subdirectories, or to an image with sub-volumes (e.g. CFFA or Macintosh partition image), you will need to select which disk and which subdirectory you want to add files to.  There are two ways to do this.

-

 

-
  • The first way is to click on the name of an existing subdirectory, or the ProDOS volume directory.  If CiderPress sees that you have a single subdirectory selected, or if there's only one place the files could possibly go, the files will be added there. -
  • If you don't have a subdirectory selected, and there's more than one place where files could be added, you will be given the opportunity to choose the location from a tree of possible locations.
-

 

-

In either case, the subdirectory chosen will show up in the "storage prefix" box in the next screen.

-

 

-

Files added to a ProDOS disk get file type "NON" and auxtype $0000 unless one of the file attribute preservation options is enabled.  DOS 3.3 disks get file types in a similar way, but files that aren't 'A', 'I', or 'T' will be set to type 'B'.  Any non-text file over 64K will get file type 'S'.

-

 

-

All files added to a Pascal disk get the "PDA" type (Pascal Data file), unless they have a ProDOS type that matches one of the Pascal values.  (Text files added to a Pascal disk become PDA, not PTX.  This may change in a future release.)  Some parts of the UCSD Pascal system rely on the filename suffix rather than the file type, e.g. "file.text" vs. "file.code", so changing the file's type may not have an effect on some applications.

-

 

-

Disk images added to a ShrinkIt archive are added one at a time.  If the disk image format can't be determined, you will be asked to specify the details on a "disk image characteristics" dialog.

-

 

-

If an entry already exists, you will be given the opportunity to decide if you want to overwrite it.

-

 

-

When adding "forked" files, you need to add the data and resource forks at the same time.  If you try to add them in two steps, CiderPress will assume that they are parts from different files that happen to have the same name, and will offer to replace the existing file with the new one instead of combining the two together.

-

 

-

Tip: if disk images were extracted with file attribute preservation enabled, they can be added en masse using the "Add files..." mechanism.  Just tell CiderPress to make use of the preservation sequences, and the disk images will be detected automatically.

-

-

- diff --git a/ciderpress/app/Help/html/t56.htm b/ciderpress/app/Help/html/t56.htm deleted file mode 100644 index 82ae1f9..0000000 --- a/ciderpress/app/Help/html/t56.htm +++ /dev/null @@ -1,22 +0,0 @@ - - - Testing Archives - - - - - - - - - -

Testing Archives

-

 

-

This feature does not work on disk images, because they don't have any sort of checksum or CRC on the files.  Every file on a disk archive is scanned when the disk is opened, and any obviously damaged files are labeled as such in the file list.

-

 

-

Files in ShrinkIt, Binary II, and AppleLink Compression Utility archives can be tested, but not all of those have CRCs either.  In those cases CiderPress will read the data to verify that the archive hasn't been truncated.  Errors in compressed data can sometimes be detected without a CRC.  The overall structure of the archive, including any CRCs on header fields, is verified when the archive is opened.

-

 

-

For files that do have checksums or CRCs, CiderPress will use the appropriate error detection algorithm, uncompressing the data when necessary.  Testing an archive essentially does a full extraction, doing everything except write the data to disk. - -

- diff --git a/ciderpress/app/Help/html/t57.htm b/ciderpress/app/Help/html/t57.htm deleted file mode 100644 index f7744cc..0000000 --- a/ciderpress/app/Help/html/t57.htm +++ /dev/null @@ -1,23 +0,0 @@ - - - Delete Entries - - - - - - - - -

Delete Entries

-

 

-

Use this feature to delete some or all files in an archive or disk image.  Select the files you want to delete in the file list, then activate the "Delete..." function.  When you confirm by pressing "OK", all entries are immediately deleted.

-

 

-

There is no "undo" or "undelete" feature.  The entries you delete are gone forever as soon as you press "OK".  Use this feature with caution.

-

 

-

If you delete all entries from a ShrinkIt archive, and close the archive without adding any new ones, the archive file will be removed.

-

 

-

If you select a folder on a ProDOS disk image, all files in the folder will automatically be selected for you when you click "delete".  You cannot delete the volume directory.

-

-

- diff --git a/ciderpress/app/Help/html/t58.htm b/ciderpress/app/Help/html/t58.htm deleted file mode 100644 index c035624..0000000 --- a/ciderpress/app/Help/html/t58.htm +++ /dev/null @@ -1,26 +0,0 @@ - - - Re-Compress Entries - - - - - - - - - -

Re-Compress Entries

-

 

-

This feature is only enabled for ShrinkIt archives, because disk images don't have compressed entries and Binary II archives are always opened read-only in CiderPress.

-

 

-

Use this function to change the way files and archived disk images are compressed.  ShrinkIt archives created with ProDOS 8 ShrinkIt or the original NuLib are compressed with "dynamic LZW/1", while archives created with GS/ShrinkIt or NuLib2 are compressed with the slightly enhanced "dynamic LZW/2".  You may want to use the gzip "deflate" algorithm to reduce the size of the files even further.

-

 

-

Be aware that not all compression formats can be unpacked with all software.  The best overall choice is "dynamic LZW/2".  If the files will only ever be opened with CiderPress or NuLib2, you may want to use "deflate".  See the Compression Algorithms page for more information about the algorithms and application compatibility.

-

 

-

Each fork of each file is expanded and re-compressed individually.  The files are read in batches, compressed, and written back out.  If you cancel the process, entries that have previously been written out will remain in their re-compressed state.

-

 

-

At the end of the process a dialog appears showing the size before and after of the files in the archive.

-

-

- diff --git a/ciderpress/app/Help/html/t59.htm b/ciderpress/app/Help/html/t59.htm deleted file mode 100644 index a0c45cb..0000000 --- a/ciderpress/app/Help/html/t59.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - List - Pathname - - - - - - - - -

List - Pathname

-

 

-

The full pathname of the file.  In disk archives that don't support folders (DOS 3.2/3.3, Pascal, CP/M, RDOS), this is just the filename.  For ProDOS disks and ShrinkIt archives, the pathname includes all folders out to the "root" of the collection.

-

 

-

The names are usually presented exactly as they appear in the original, but there are a few exceptions:

-

 

-
  • ProDOS names are converted to lower case based on the case flags.  These were added in ProDOS 8 v1.8 to allow the GS/OS FST to put lower case and spaces in names. -
  • AppleWorks filenames stored on a ProDOS disk are converted to lower case based on the aux type.  This value overrides the ProDOS flags.  (AppleWorks files stored in ShrinkIt archives are simply presented as they were stored.) -
  • The names of files on DOS 3.3 volumes are "sanitized", so that inverse and flashing characters are converted to normal text. -
  • If the appropriate preference is enabled, filenames stored on DOS 3.3 volumes are converted to mixed case based on common rules for English titles.  Filenames with lower case in them already -- uncommon but not unheard-of -- are left alone. -
  • If the appropriate preference is enabled, all filenames have spaces converted to underscores.  This could be useful for files destined to be served directly from a web site.
-

 

-

The ProDOS volume name is not shown as part of the pathname, except in the entry for the volume directory itself (identifiable as a pathname starting with ':').

-

 

-

There may be one of four icons to the left of the filename.  A document icon that is "empty" (has a white interior) indicates a ShrinkIt archive entry with an empty comment.  Most archives created by GS/ShrinkIt have an empty comment added to the first entry, so this is fairly common.  If the document icon is solid yellow, that means there is a comment with information in it.  This can be viewed with the file viewer or by using the "edit comment" feature.  A red 'X' icon indicates a damaged entry, usually on a disk image.  CiderPress was unable to fully process the file, so it has been marked as unavailable.  A blue '?' icon indicates a suspicious entry; it's not unreadable, but it doesn't look healthy.

-

 

-

Files in a sub-volume, such as a DOS 3.3 disk embedded in a ProDOS disk, are shown as being in a folder (something like "_DOS001").  This is done so that the files will be extracted into a separate folder from the outer-volume content, and also to keep the files grouped together when the file list is sorted by name. - -

- diff --git a/ciderpress/app/Help/html/t60.htm b/ciderpress/app/Help/html/t60.htm deleted file mode 100644 index dde04b0..0000000 --- a/ciderpress/app/Help/html/t60.htm +++ /dev/null @@ -1,61 +0,0 @@ - - - List - File Type - - - - - - - - - -

List - File Type

-

 

-

This field displays the ProDOS file type.  Types that don't have three-letter abbreviations assigned are shown in hexadecimal notation.  Archived disk images are listed simply as "Disk".

-

 

-

Forked files are noted with a plus sign ('+') following the file type.  Folders appear with a trailing slash ('/'), and will usually appear as "DIR/".  However, it is possible (but not recommended!) for a folder to have a different file type.

-

 

-

Macintosh files stored in a ShrinkIt archive may be displayed with a 32-bit Mac file type instead.

-

 

-

File types from non-ProDOS disks are converted to the most appropriate type.  The following conversions are used:

-

 

-

DOS 'T' TXT  

-

 

-

DOS 'I' INT  

-

 

-

DOS 'A' BAS  

-

 

-

DOS 'B' BIN  

-

 

-

DOS 'S' $F2  

-

 

-

DOS 'R' REL  

-

 

-

DOS 'A' #2 $F3  

-

 

-

DOS 'B' #2 $F4  

-

  

-

 

-

Pascal Untyped NON  

-

 

-

Pascal Xdsk (bad blocks) BAD  

-

 

-

Pascal Code PCD  

-

 

-

Pascal Text PTX  

-

 

-

Pascal Info $F3  

-

 

-

Pascal Data PDA  

-

 

-

Pascal Graf $F4  

-

 

-

Pascal Foto FOT  

-

 

-

Pascal SecurDir $F5  

-

  

-

Files given "$Fn" types have formats that are not well defined.

-

-

- diff --git a/ciderpress/app/Help/html/t61.htm b/ciderpress/app/Help/html/t61.htm deleted file mode 100644 index 9cddf9f..0000000 --- a/ciderpress/app/Help/html/t61.htm +++ /dev/null @@ -1,26 +0,0 @@ - - - List - Aux Type - - - - - - - - - -

List - Aux Type

-

 

-

The auxiliary type of the file.  The exact meaning of this varies depending on the archive or disk image.

-

 

-
  • ShrinkIt archives: the ProDOS "aux type", or the size of an archived disk image. -
  • Binary II, ProDOS: the value of the 16-bit "aux type" field.  For BIN files this is usually the load address.  For Applesoft BAS files it's the start address of the program. -
  • DOS 3.2/3.3: for 'B' (BIN) the load address is pulled out of the first sector of the file.  For 'A' (BAS) the field is always set to $0801, and for 'I' (INT) and 'T' (TXT) it's always set to $0000. -
  • Pascal, CP/M: set to $0000 for all files. -
  • RDOS: the load address of the file.
-

 

-

ShrinkIt is capable of storing Macintosh files from HFS volumes, in which case the "aux type" field will hold a 32-bit creator type.

-

-

- diff --git a/ciderpress/app/Help/html/t62.htm b/ciderpress/app/Help/html/t62.htm deleted file mode 100644 index 95c30d9..0000000 --- a/ciderpress/app/Help/html/t62.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - List - Mod Date - - - - - - - - - - -

List - Mod Date

-

 

-

The modification date of the entry.

-

 

-

ShrinkIt archives use the IIgs "DateTime" format, which includes year/month/day and hours/minutes/seconds.

-

 

-

ProDOS disks and Binary II archives use the ProDOS date format, which has year/month/day and hours/minutes but not seconds.

-

 

-

Pascal disks use the Pascal date format, which has year/month/day but no time.

-

 

-

DOS 3.3, CP/M, and RDOS do not store dates.

-

 

-

When no date is available, "[no date]" is shown.  This is expected on disk images and archives created on systems without clocks.  If the stored date is invalid (e.g. February 31st), it will be shown as "<invalid>".

-

-

- diff --git a/ciderpress/app/Help/html/t63.htm b/ciderpress/app/Help/html/t63.htm deleted file mode 100644 index dee4667..0000000 --- a/ciderpress/app/Help/html/t63.htm +++ /dev/null @@ -1,24 +0,0 @@ - - - List - Format - - - - - - - - - -

List - Format

-

 

-

For ShrinkIt archives, this describes the compression format used.  Possible values are described here.

-

 

-

For Binary II archives, this holds a guess at whether the file is stored uncompressed or "squeezed".  There is no 100% reliable way to determine if a file stored in Binary II was squeezed, so a quick examination of the file contents is used to determine the status.

-

 

-

For disk archives, the name of the file system is shown.  This is done to emphasize that files with the same file type may be structurally different on different file systems.  For example, DOS text files use ASCII characters with the high bit set, while ProDOS text files leave the high bit unset.

-

 

-

It's also useful on mixed-filesystem disks, such as ProSel "Uni-DOS" 800K disks that have a 200K DOS 3.3 filesystem embedded in an 800K ProDOS volume.

-

-

- diff --git a/ciderpress/app/Help/html/t64.htm b/ciderpress/app/Help/html/t64.htm deleted file mode 100644 index 103601e..0000000 --- a/ciderpress/app/Help/html/t64.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - List - Size - - - - - - - - - -

List - Size

-

 

-

This is the full length of the file when uncompressed and "sparse" blocks filled are in.

-

 

-

The size does not reflect format conversions (e.g. converting a graphic to a BMP) or text conversions (changing end-of-line markers), either of which can result in significantly different extracted sizes.  The size will be accurate only when all conversions are off ("preservation mode").

-

-

- diff --git a/ciderpress/app/Help/html/t65.htm b/ciderpress/app/Help/html/t65.htm deleted file mode 100644 index b13d96b..0000000 --- a/ciderpress/app/Help/html/t65.htm +++ /dev/null @@ -1,18 +0,0 @@ - - - List - Ratio - - - - - - - - - -

List - Ratio

-

 

-

This is simply the ratio of  the "Size" field to the "Packed" field.  An uncompressed, non-sparse file is displayed as "100%".

-

-

- diff --git a/ciderpress/app/Help/html/t66.htm b/ciderpress/app/Help/html/t66.htm deleted file mode 100644 index a4aab2f..0000000 --- a/ciderpress/app/Help/html/t66.htm +++ /dev/null @@ -1,20 +0,0 @@ - - - List - Packed - - - - - - - - - -

List - Packed

-

 

-

This represents the compressed size of data in a ShrinkIt archive.  The size does not include archive overhead (e.g. storage of the filename, CRCs, and file attributes).

-

 

-

On a disk archive, this indicates the size needed to store the file.  Some files take less space than you might expect because they are "sparse".  On filesystems that support sparse files (ProDOS, and to some extent DOS 3.3), a block filled entirely with zeros isn't actually stored on the disk.  Instead, the file system structure is updated to indicate that there is a gap, and when the file is read the missing data is filled in with zero bytes.

-

-

- diff --git a/ciderpress/app/Help/html/t67.htm b/ciderpress/app/Help/html/t67.htm deleted file mode 100644 index 46327b7..0000000 --- a/ciderpress/app/Help/html/t67.htm +++ /dev/null @@ -1,29 +0,0 @@ - - - List - Access - - - - - - - - - - - -

List - Access

-

 

-

This column shows the ProDOS access permissions for each entry.  The letters are:

-

 

-

d - file may be deleted

-

n - file may be renamed

-

b - backup needed

-

i - file is invisible

-

w - file may be modified

-

r - file may be read

-

 

-

DOS 3.3 and CP/M only understand "locked" and "unlocked", which correspond to "r" and "dnwr", respectively.  For Pascal and RDOS disks, all files are effectively "unlocked".

-

-

- diff --git a/ciderpress/app/Help/html/t68.htm b/ciderpress/app/Help/html/t68.htm deleted file mode 100644 index b80ed12..0000000 --- a/ciderpress/app/Help/html/t68.htm +++ /dev/null @@ -1,48 +0,0 @@ - - - Appendix - File Attribute Preservation - - - - - - - - - -

File Attribute Preservation

-

 

-

The definitive guide to the file attribute preservation mechanism employed by CiderPress can be found in the "library" section of the www.nulib.com web site.  This is a brief introduction to the topic.

-

 

-

There are four attributes that must be restored when adding Apple II files: file type, aux type, pathname, and file part (i.e. data fork, resource fork, disk image, or comment).

-

 

-

File Type and Aux Type

-

 

-

ProDOS files use an 8-bit file type and a 16-bit aux type.  These can be encoded in a six-character hexadecimal string that looks like "#062000".  The '#' is used to indicate the start of an attribute preservation string.

-

 

-

HFS files with 32-bit file and creator types require more digits, resulting in a 16-character hexadecimal string.

-

 

-

Extracted folders do not have preservation strings added.

-

 

-

Pathname

-

 

-

Some characters, such as '/', are legal in Apple II filesystems (DOS 3.3, HFS) but illegal under Windows.  These are converted to "%nn" sequences, e.g. "foo/bar" becomes "foo%2fbar".

-

 

-

Some words, such as "AUX" and "PRN", cannot be used as filenames under Windows.  These are prefixed with "%00", so "Aux" would become "%00Aux".  (If file attribute preservation is turned off, these will be prefixed with underscores, e.g. "_Aux".)  The "%00" is removed when the file is added.

-

 

-

The complete list of illegal names: CON, PRN, NUL, AUX, LPT1, LPT2, LPT3, LPT4, COM1, COM2, COM3, COM4.  This also affects files that start with the name and a dot, e.g. "AUX.FOO" is also illegal and will be altered.

-

 

-

File Part

-

 

-

HFS and ProDOS support the notion of "forked" files.  This horrible idea has caused problems for users and developers alike since the Macintosh was first released in 1984.  The basic idea is that a file is actually two files with the same name, one of which (the "data fork") has unstructured data such as ASCII text, while the other one (the "resource fork") holds highly-structured data.  Some ProDOS literature refers to these as "extended" files.

-

 

-

Because Windows doesn't handle "forked" files, each fork must be stored in a separate file.  Resource forks are indicated by adding the letter 'r' to the end of the preservation sequences, and disk images have the letter 'i'.  These files are automatically converted back to the correct file part when added to an archive.  If two files have the same name, but the preservation sequences indicate one is a data fork and the other a resource fork, then the two forks will be combined into a single file in the archive.

-

 

-

Other Considerations

-

 

-

The modification date of the original file, if any, can be stored in the local filesystem.  Access permissions aren't completely restored, but they usually don't need to be -- most Apple II files are either "locked" or "unlocked", and that can be encoded in Windows access permissions.

-

 

-

The scheme falls apart somewhat if the generated filenames are longer than Windows can support.  Windows "vfat", "fat32", and "ntfs" filesystems can handle names far longer than any Apple II filesystem, so this should not be a problem.

-

-

- diff --git a/ciderpress/app/Help/html/t69.htm b/ciderpress/app/Help/html/t69.htm deleted file mode 100644 index d247ad7..0000000 --- a/ciderpress/app/Help/html/t69.htm +++ /dev/null @@ -1,62 +0,0 @@ - - - Appendix - Compression Algorithms - - - - - - - - - -

Compression Algorithms

-

 

-

CiderPress supports every compression algorithm listed in the NuFX and Binary II specifications, as well as those listed in the "NuFX addendum" on www.nulib.com.  The actual compression code lives in the NufxLib library, however, and the library can be built with some or all of the compression code disabled.  You can see which algorithms are enabled by going to the Compression Preferences screen.

-

 

-

No compression

-

 

-

Exactly what it says: the contents are not compressed in any way.

-

 

-

Squeeze (sometimes "SQueeze")

-

 

-

This format became popular under CP/M with the "sq" and "usq" commands, which created ".QQ" or ".QQQ" files.  These appeared on the Apple II as Donald Elton's "sq3" and "usq2" utilities, and un-squeezing became part of Floyd Zink's "BLU" utility.  The algorithm, a combination of RLE (Run-Length Encoding) and Huffman encoding, is generally inferior to LZ-based techniques, and Squeeze fell out of common use after ShrinkIt was released.  It was, however, an official part of the NuFX format specification.

-

 

-

Most NuFX utilities can handle Squeezed files, but ProDOS 8 ShrinkIt has a bug that prevents it from unpacking the files correctly.  GS/ShrinkIt handles them correctly.  No Apple II utility actually creates Squeezed NuFX archives.

-

 

-

Dynamic LZW/1

-

 

-

Originally designed to compress tracks on 5.25" disks, the original ShrinkIt compression algorithm grabs a 4K chunk of data, compresses it with RLE, and then compresses the result with LZW (Lempel-Ziv-Welch).  A 16-bit CRC is computed on each compressed chunk.

-

 

-

All NuFX utilities can handle this algorithm.

-

 

-

Dynamic LZW/2

-

 

-

This is a slightly improved version of the original, first introduced in GS/ShrinkIt.  All NuFX utilities can handle this algorithm, making this the best choice for compressing Apple II files.

-

 

-

LZC-12

-

 

-

Before ShrinkIt was born, UNIX systems had the "compress" command.  A fairly pure implementation of the LZW algorithm, "compress" was widely used until the Unisys GIF/LZW controversy sped adoption of the superior "gzip".  LZC-12 produces output equivalent to "compress -b12", i.e. LZW with a maximum code length of 12 bits.

-

 

-

The algorithm was first supported in NuLib, and can be unpacked by NuLib, NuLib2, and GS/ShrinkIt.

-

 

-

LZC-16

-

 

-

Just like LZC-12, but the maximum code length is increased to 16 bits.  Usually offers improved compression over LZC-12, though it's only noticeable on larger files.

-

 

-

Same compatibility as LZC-12.

-

 

-

Deflate

-

 

-

This algorithm uses LZH (Lempel-Ziv + Huffman), which is in a different LZ family from LZW.  The algorithm became popular in archive utilities like LHArc, and eventually became the standard PKZIP archive format.  Some of the authors of the free "Info-Zip" utility decided to write a replacement for UNIX "compress" called GNU Zip ("gzip" for short), using the same algorithm.  It quickly became popular, and "deflate" is now defined as an Internet standard (RFC 1951), and is used for everything from file distribution to PNG graphics.  It was first implemented for NuFX archives in NuLib2 v1.1, and is the best general-purpose compression that CiderPress has to offer.

-

 

-

Unfortunately, few NuFX utilities can unpack this format.  It is unlikely you will ever be able to access "deflated" files on an Apple II.  Only use this for archives that will only be accessed from CiderPress or NuLib2.

-

 

-

Bzip2

-

 

-

This is a relatively new format that relies on the BWT (Burrows-Wheeler Transform) algorithm.  It is slower and more memory-intensive than "deflate", but does exceptionally well on large collections of text files or source code.  It is becoming the standard way to compress source code distributed on the Internet.  It is generally inferior to "deflate" on Apple II files, so unless you have an 800K disk image full of text files there's not much point in using it.

-

 

-

It was first implemented for NuFX archives in NuLib2 v1.1, and it is unlikely to be supported by anything else, so it's usually disabled in NufxLib.

-

-

- diff --git a/ciderpress/app/HelpTopics.h b/ciderpress/app/HelpTopics.h deleted file mode 100644 index 96d39df..0000000 --- a/ciderpress/app/HelpTopics.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Constants for help topics. - */ -#ifndef APP_HELP_TOPICS_H -#define APP_HELP_TOPICS_H - -#define HELP_TOPIC_WELCOME 10 -#define HELP_TOPIC_DISKEDIT 13 -#define HELP_TOPIC_PREFS_GENERAL 19 -#define HELP_TOPIC_DISK_IMAGES 20 -#define HELP_TOPIC_PREFS_FVIEW 23 -#define HELP_TOPIC_CREDITS 24 -#define HELP_TOPIC_FILE_VIEWER 25 -#define HELP_TOPIC_PREFS_FILES 28 -#define HELP_TOPIC_PREFS_COMPRESSION 29 -#define HELP_TOPIC_CHOOSE_FOLDER 38 -#define HELP_TOPIC_EXT_OPTIONS 39 -#define HELP_TOPIC_ADD_FILES_DLG 41 -#define HELP_TOPIC_RENAME_ENTRY 42 -#define HELP_TOPIC_EDIT_COMMENT 43 -#define HELP_TOPIC_EDIT_ASSOC 44 -#define HELP_TOPIC_ORDERING_INFO 46 -#define HELP_TOPIC_ENTER_REG_DATA 49 -#define HELP_TOPIC_IMPORT_CASSETTE 111 -#define HELP_TOPIC_IMPORT_BASIC 112 -#define HELP_TOPIC_DISK_CONV 187 -#define HELP_TOPIC_EDIT_PROPS 203 -#define HELP_TOPIC_BULK_DISK_CONV 233 -#define HELP_TOPIC_OPEN_VOLUME 241 -#define HELP_TOPIC_VOLUME_COPIER 245 -#define HELP_TOPIC_IMAGE_CREATOR 247 -#define HELP_TOPIC_CHOOSE_TARGET 257 -#define HELP_TOPIC_ARCHIVE_INFO 258 -#define HELP_TOPIC_PREFS_DISK_IMAGE 259 -#define HELP_TOPIC_RENAME_VOLUME 268 -#define HELP_TOPIC_EOL_SCAN 272 - -#endif /*APP_HELP_TOPICS_H*/ diff --git a/ciderpress/app/ImageFormatDialog.cpp b/ciderpress/app/ImageFormatDialog.cpp deleted file mode 100644 index 6e7a280..0000000 --- a/ciderpress/app/ImageFormatDialog.cpp +++ /dev/null @@ -1,306 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "ImageFormatDialog.h" -#include "../diskimg/DiskImg.h" - - -BEGIN_MESSAGE_MAP(ImageFormatDialog, CDialog) - ON_BN_CLICKED(IDC_DECONF_HELP, OnHelp) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - - -/* - * Conversion tables. - * - * If you add something else, remember to turn off "sort" in the drop list. - * - * The tables contain only the formats we currently support, and are in the - * order in which we want to present them to the user. - * - * THOUGHT: drop "name" from the tables, and use a DiskImg::ToString lookup - * to get the text string. That way we'd be consistent. - */ -typedef struct ImageFormatDialog::ConvTable { - int enumval; // a DiskImg::enum type - const WCHAR* name; -} ConvTable; - -const int kLastEntry = -1; - -/* DiskImg::OuterFormat */ -static const ConvTable gOuterFormats[] = { - { DiskImg::kOuterFormatUnknown, L"Unknown format" }, - { DiskImg::kOuterFormatNone, L"(none)" }, -// { DiskImg::kOuterFormatCompress, L"UNIX compress" }, - { DiskImg::kOuterFormatGzip, L"gzip" }, -// { DiskImg::kOuterFormatBzip2, L"bzip2" }, - { DiskImg::kOuterFormatZip, L"Zip archive" }, - { kLastEntry, NULL } -}; -/* DiskImg::FileFormat */ -static const ConvTable gFileFormats[] = { - { DiskImg::kFileFormatUnknown, L"Unknown format" }, - { DiskImg::kFileFormatUnadorned, L"Unadorned raw data" }, - { DiskImg::kFileFormat2MG, L"2MG" }, - { DiskImg::kFileFormatNuFX, L"NuFX (ShrinkIt)" }, - { DiskImg::kFileFormatDiskCopy42, L"DiskCopy 4.2" }, -// { DiskImg::kFileFormatDiskCopy60, L"DiskCopy 6.0" }, -// { DiskImg::kFileFormatDavex, L"Davex volume image" }, - { DiskImg::kFileFormatSim2eHDV, L"Sim //e HDV" }, - { DiskImg::kFileFormatDDD, L"DDD" }, - { DiskImg::kFileFormatTrackStar, L"TrackStar image" }, - { DiskImg::kFileFormatFDI, L"FDI image" }, -// { DiskImg::kFileFormatDDDDeluxe, L"DDDDeluxe" }, - { kLastEntry, NULL } -}; -/* DiskImg::PhysicalFormat */ -static const ConvTable gPhysicalFormats[] = { - { DiskImg::kPhysicalFormatUnknown, L"Unknown format" }, - { DiskImg::kPhysicalFormatSectors, L"Sectors" }, - { DiskImg::kPhysicalFormatNib525_6656, L"Raw nibbles (6656-byte)" }, - { DiskImg::kPhysicalFormatNib525_6384, L"Raw nibbles (6384-byte)" }, - { DiskImg::kPhysicalFormatNib525_Var, L"Raw nibbles (variable len)" }, - { kLastEntry, NULL } -}; -/* DiskImg::SectorOrder */ -static const ConvTable gSectorOrders[] = { - { DiskImg::kSectorOrderUnknown, L"Unknown ordering" }, - { DiskImg::kSectorOrderProDOS, L"ProDOS block ordering" }, - { DiskImg::kSectorOrderDOS, L"DOS sector ordering" }, - { DiskImg::kSectorOrderCPM, L"CP/M block ordering" }, - { DiskImg::kSectorOrderPhysical, L"Physical sector ordering" }, - { kLastEntry, NULL } -}; -/* DiskImg::FSFormat */ -static const ConvTable gFSFormats[] = { - { DiskImg::kFormatUnknown, L"Unknown filesystem" }, - { DiskImg::kFormatGenericDOSOrd, L"Generic DOS sectors" }, - { DiskImg::kFormatGenericProDOSOrd, L"Generic ProDOS blocks" }, - { DiskImg::kFormatGenericPhysicalOrd, L"Generic raw sectors" }, - { DiskImg::kFormatGenericCPMOrd, L"Generic CP/M blocks" }, - { DiskImg::kFormatProDOS, L"ProDOS" }, - { DiskImg::kFormatDOS33, L"DOS 3.3" }, - { DiskImg::kFormatDOS32, L"DOS 3.2" }, - { DiskImg::kFormatPascal, L"Pascal" }, - { DiskImg::kFormatMacHFS, L"HFS" }, -// { DiskImg::kFormatMacMFS, L"MFS" }, -// { DiskImg::kFormatLisa, L"Lisa" }, - { DiskImg::kFormatCPM, L"CP/M" }, - { DiskImg::kFormatMSDOS, L"MS-DOS FAT" }, -// { DiskImg::kFormatISO9660, L"ISO-9660" }, - { DiskImg::kFormatUNIDOS, L"UNIDOS (400K DOS x2)" }, - { DiskImg::kFormatOzDOS, L"OzDOS (400K DOS x2)" }, - { DiskImg::kFormatCFFA4, L"CFFA (4 or 6 partitions)" }, - { DiskImg::kFormatCFFA8, L"CFFA (8 partitions)" }, - { DiskImg::kFormatMacPart, L"Macintosh partitioned disk" }, - { DiskImg::kFormatMicroDrive, L"MicroDrive partitioned disk" }, - { DiskImg::kFormatFocusDrive, L"FocusDrive partitioned disk" }, - { DiskImg::kFormatRDOS33, L"RDOS 3.3 (16-sector)" }, - { DiskImg::kFormatRDOS32, L"RDOS 3.2 (13-sector)" }, - { DiskImg::kFormatRDOS3, L"RDOS 3 (cracked 13-sector)" }, - { DiskImg::kFormatGutenberg, L"Gutenberg" }, - { kLastEntry, NULL } -}; - - -void ImageFormatDialog::InitializeValues(const DiskImg* pImg) -{ - fOuterFormat = pImg->GetOuterFormat(); - fFileFormat = pImg->GetFileFormat(); - fPhysicalFormat = pImg->GetPhysicalFormat(); - fSectorOrder = pImg->GetSectorOrder(); - fFSFormat = pImg->GetFSFormat(); - - if (pImg->ShowAsBlocks()) - fDisplayFormat = kShowAsBlocks; - else - fDisplayFormat = kShowAsSectors; - if (!pImg->GetHasBlocks() && !pImg->GetHasSectors()) - fDisplayFormat = kShowAsNibbles; - - fHasSectors = pImg->GetHasSectors(); - fHasBlocks = pImg->GetHasBlocks(); - fHasNibbles = pImg->GetHasNibbles(); - - // "Unknown" formats default to sectors, but sometimes it's block-only - if (fDisplayFormat == kShowAsSectors && !fHasSectors) - fDisplayFormat = kShowAsBlocks; - - fInitialized = true; -} - -BOOL ImageFormatDialog::OnInitDialog(void) -{ - ASSERT(fInitialized); - - LoadComboBoxes(); - - return CDialog::OnInitDialog(); // do DDX/DDV -} - -void ImageFormatDialog::LoadComboBoxes(void) -{ - CWnd* pWnd; - CButton* pButton; - - pWnd = GetDlgItem(IDC_DECONF_SOURCE); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fFileSource); - - if (fQueryDisplayFormat) { - pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASSECTORS); - ASSERT(pButton != NULL); - pButton->SetCheck(fDisplayFormat == kShowAsSectors); - if (!fHasSectors) - pButton->EnableWindow(FALSE); - - pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASBLOCKS); - ASSERT(pButton != NULL); - pButton->SetCheck(fDisplayFormat == kShowAsBlocks); - if (!fHasBlocks) - pButton->EnableWindow(FALSE); - - pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASNIBBLES); - ASSERT(pButton != NULL); - pButton->SetCheck(fDisplayFormat == kShowAsNibbles); - if (!fHasNibbles) - pButton->EnableWindow(FALSE); - } else { - /* if we don't need to ask, don't show the buttons */ - pWnd = GetDlgItem(IDC_DECONF_VIEWAS); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DECONF_VIEWASBLOCKS); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DECONF_VIEWASSECTORS); - pWnd->DestroyWindow(); - pWnd = GetDlgItem(IDC_DECONF_VIEWASNIBBLES); - pWnd->DestroyWindow(); - } - - LoadComboBox(IDC_DECONF_OUTERFORMAT, gOuterFormats, fOuterFormat); - LoadComboBox(IDC_DECONF_FILEFORMAT, gFileFormats, fFileFormat); - LoadComboBox(IDC_DECONF_PHYSICAL, gPhysicalFormats, fPhysicalFormat); - LoadComboBox(IDC_DECONF_SECTORORDER, gSectorOrders, fSectorOrder); - LoadComboBox(IDC_DECONF_FSFORMAT, gFSFormats, fFSFormat); -} - -void ImageFormatDialog::LoadComboBox(int boxID, const ConvTable* pTable, - int dflt) -{ - CComboBox* pCombo; -// const ConvTable* pBaseTable = pTable; - int current = -1; - int idx, idxShift; - - pCombo = (CComboBox*) GetDlgItem(boxID); - ASSERT(pCombo != NULL); - - idx = idxShift = 0; - while (pTable[idx].enumval != kLastEntry) { - /* special-case the generic FS formats */ - if (pTable == gFSFormats && !fAllowGenericFormats && - DiskImg::IsGenericFormat((DiskImg::FSFormat)pTable[idx].enumval)) - { - LOGI("LoadComboBox skipping '%ls'", pTable[idx].name); - idxShift++; - } else { - // Note to self: AddString returns the combo box item ID; - // should probably use that instead of doing math. - pCombo->AddString(pTable[idx].name); - pCombo->SetItemData(idx - idxShift, pTable[idx].enumval); - } - - if (pTable[idx].enumval == dflt) - current = idx - idxShift; - - idx++; - } - - if (current != -1) { - LOGI(" Set default for %d/%d to %d", boxID, dflt, current); - pCombo->SetCurSel(current); - } else { - LOGI(" No matching default for %d (%d)", boxID, dflt); - } -} - -int ImageFormatDialog::ConvComboSel(int boxID, const ConvTable* pTable) -{ - CComboBox* pCombo; - int idx, enumval; - - pCombo = (CComboBox*) GetDlgItem(boxID); - ASSERT(pCombo != NULL); - idx = pCombo->GetCurSel(); - - if (idx < 0) { - /* nothing selected?! */ - ASSERT(false); - return 0; - } - -// enumval = pTable[idx].enumval; - enumval = pCombo->GetItemData(idx); - ASSERT(enumval >= 0 && enumval < 100); - - if (pTable != gFSFormats) { - ASSERT(enumval == pTable[idx].enumval); - } - - // Note pTable[idx].name is not always correct here, because the generic - // formats may not have been loaded into the combo box. - LOGI(" Returning ev=%d for %d'", enumval, boxID); - - return enumval; -} - -void ImageFormatDialog::OnOK(void) -{ - CButton* pButton; - - if (fQueryDisplayFormat) { - pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASSECTORS); - ASSERT(pButton != NULL); - if (pButton->GetCheck()) - fDisplayFormat = kShowAsSectors; - - pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASBLOCKS); - ASSERT(pButton != NULL); - if (pButton->GetCheck()) - fDisplayFormat = kShowAsBlocks; - - pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASNIBBLES); - ASSERT(pButton != NULL); - if (pButton->GetCheck()) - fDisplayFormat = kShowAsNibbles; - } - - /* outer format, file format, and physical format are immutable */ - - fSectorOrder = (DiskImg::SectorOrder) - ConvComboSel(IDC_DECONF_SECTORORDER, gSectorOrders); - fFSFormat = (DiskImg::FSFormat) - ConvComboSel(IDC_DECONF_FSFORMAT, gFSFormats); - - if (fSectorOrder == DiskImg::kSectorOrderUnknown) { - MessageBox(L"You must choose a sector ordering.", L"Error", - MB_OK | MB_ICONEXCLAMATION); - return; - } - - if (fFSFormat == DiskImg::kFormatUnknown && - !fAllowUnknown) - { - MessageBox(L"You must choose a filesystem format. If not known," - L" use one of the 'generic' entries.", - L"Error", MB_OK | MB_ICONEXCLAMATION); - return; - } - - CDialog::OnOK(); -} diff --git a/ciderpress/app/ImageFormatDialog.h b/ciderpress/app/ImageFormatDialog.h deleted file mode 100644 index 71283c5..0000000 --- a/ciderpress/app/ImageFormatDialog.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#ifndef APP_IMAGEFORMATDIALOG_H -#define APP_IMAGEFORMATDIALOG_H - -#include "resource.h" -#include "../diskimg/DiskImg.h" -using namespace DiskImgLib; - -/* - * Dialog asking the user to confirm certain details of a disk image. - * - * The default values can be initialized individually or from a prepped - * DiskImg structure. - */ -class ImageFormatDialog : public CDialog { -public: - ImageFormatDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_DECONF, pParentWnd) - { - fInitialized = false; - fFileSource = L""; - fAllowUnknown = false; - fOuterFormat = DiskImg::kOuterFormatUnknown; - fFileFormat = DiskImg::kFileFormatUnknown; - fPhysicalFormat = DiskImg::kPhysicalFormatUnknown; - fSectorOrder = DiskImg::kSectorOrderUnknown; - fFSFormat = DiskImg::kFormatUnknown; - fDisplayFormat = kShowAsBlocks; - - fQueryDisplayFormat = true; - fAllowGenericFormats = true; - fHasSectors = fHasBlocks = fHasNibbles = false; - } - - /* - * Initialize our members by querying the associated DiskImg. - */ - void InitializeValues(const DiskImg* pImg); - - bool fInitialized; - CString fFileSource; - bool fAllowUnknown; // allow "unknown" choice? - - DiskImg::OuterFormat fOuterFormat; - DiskImg::FileFormat fFileFormat; - DiskImg::PhysicalFormat fPhysicalFormat; - DiskImg::SectorOrder fSectorOrder; - DiskImg::FSFormat fFSFormat; - - enum { kShowAsBlocks=0, kShowAsSectors=1, kShowAsNibbles=2 }; - int fDisplayFormat; - - void SetQueryDisplayFormat(bool val) { fQueryDisplayFormat = val; } - void SetAllowGenericFormats(bool val) { fAllowGenericFormats = val; } - -protected: - virtual BOOL OnInitDialog(void) override; - - /* - * Handle the "OK" button by extracting values from the dialog and - * verifying that reasonable settings are in place. - */ - void OnOK(void) override; - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_DISK_IMAGES); - } - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - struct ConvTable; - - /* - * Load the combo boxes with every possible entry, and set the current - * value appropriately. - * - * While we're at it, initialize the "source" edit text box and the - * "show as blocks" checkbox. - */ - void LoadComboBoxes(void); - - /* - * Load the strings from ConvTable into the combo box, setting the - * entry matching "default" as the current entry. - */ - void LoadComboBox(int boxID, const ConvTable* pTable, int dflt); - - /* - * Find the enum value for the specified index. - */ - int ConvComboSel(int boxID, const ConvTable* pTable); - - bool fQueryDisplayFormat; - bool fAllowGenericFormats; - bool fHasSectors; - bool fHasBlocks; - bool fHasNibbles; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_IMAGEFORMATDIALOG_H*/ diff --git a/ciderpress/app/Main.cpp b/ciderpress/app/Main.cpp deleted file mode 100644 index b74b9a6..0000000 --- a/ciderpress/app/Main.cpp +++ /dev/null @@ -1,2489 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Main window management. - */ -#include "stdafx.h" -#include "Main.h" -#include "MyApp.h" -#include "AboutDialog.h" -#include "NufxArchive.h" -#include "DiskArchive.h" -#include "BNYArchive.h" -#include "ACUArchive.h" -#include "AppleSingleArchive.h" -#include "ArchiveInfoDialog.h" -#include "PrefsDialog.h" -#include "EnterRegDialog.h" -#include "OpenVolumeDialog.h" -#include "Print.h" -#include "../util/UtilLib.h" -#include "resource.h" - -/* use MFC's fancy version of new for debugging */ -//#define new DEBUG_NEW - -static const WCHAR kWebSiteURL[] = L"http://www.a2ciderpress.com/"; - -/* custom class name for main frame */ -static const WCHAR kMainWindowClassName[] = L"faddenSoft.CiderPress.4"; - - -/* - * Filters for the "open file" command. In some cases a file may be opened - * in more than one format, so it's necessary to keep track of what the - * file filter was set to when the file was opened. - * - * With Vista-style dialogs, the second part of the string (the filespec) - * will sometimes be included in the pop-up. Sometimes not. It's - * deterministic but I haven't been able to figure out what the pattern is -- - * it's not simply length of a given filter or of the entire string, or based - * on the presence of certain characters. The filter works correctly, so it - * doesn't seem to be malformed. It's just ugly to have the open dialog - * popup show an enormous, redundant filter string. - * - * I tried substituting '\0' for '|' and placing the string directly into - * the dialog; no change. - * - * CFileDialog::ApplyOFNToShellDialog in {VisualStudio}\VC\atlmfc\src\mfc\dlgfile.cpp - * appears to be doing the parsing. Single-stepping through the code shows - * that it's working fine, so something later on is choosing to merge - * pszName and pszSpec when generating the pop-up menu. - * - * The good news is that if I exclude the list of extensions from the name - * section, the popup will (so far) always includes the spec. The bad news - * is that this means we won't display the list of extensions on WinXP, which - * uses the older style of dialog. We could switch from public constants to - * a function that generates the filter based on a bit mask and the current - * OS version, but that might be more trouble than it's worth. - */ -const WCHAR MainWindow::kOpenNuFX[] = - L"ShrinkIt Archives|*.shk;*.sdk;*.bxy;*.sea;*.bse|"; -const WCHAR MainWindow::kOpenBinaryII[] = - L"Binary II Archives|*.bny;*.bqy;*.bxy|"; -const WCHAR MainWindow::kOpenACU[] = - L"ACU Archives|*.acu|"; -const WCHAR MainWindow::kOpenAppleSingle[] = - L"AppleSingle files|*.as|"; -const WCHAR MainWindow::kOpenDiskImage[] = - L"Disk Images|" - L"*.shk;*.sdk;*.dsk;*.po;*.do;*.d13;*.2mg;*.img;*.nib;*.nb2;*.raw;*.hdv;*.dc;*.dc6;*.ddd;*.app;*.fdi;*.iso;*.gz;*.zip|"; -const WCHAR MainWindow::kOpenAll[] = - L"All Files|*.*|"; -const WCHAR MainWindow::kOpenEnd[] = - L"|"; - -/* - * Used when guessing archive type from extension when no "-mode" argument - * was specified. - * - * This does *not* apply to files double-clicked from the content list; see - * HandleDoubleClick() for that. - */ -static const struct { - WCHAR extension[4]; - FilterIndex idx; -} gExtensionToIndex[] = { - { L"shk", kFilterIndexDiskImage }, // DiskImage probably better than NuFX - { L"bxy", kFilterIndexNuFX }, - { L"bse", kFilterIndexNuFX }, - { L"sea", kFilterIndexNuFX }, - { L"bny", kFilterIndexBinaryII }, - { L"bqy", kFilterIndexBinaryII }, - { L"acu", kFilterIndexACU }, - { L"as", kFilterIndexAppleSingle }, - { L"sdk", kFilterIndexDiskImage }, - { L"dsk", kFilterIndexDiskImage }, - { L"po", kFilterIndexDiskImage }, - { L"do", kFilterIndexDiskImage }, - { L"d13", kFilterIndexDiskImage }, - { L"2mg", kFilterIndexDiskImage }, - { L"img", kFilterIndexDiskImage }, - { L"nib", kFilterIndexDiskImage }, - { L"nb2", kFilterIndexDiskImage }, - { L"raw", kFilterIndexDiskImage }, - { L"hdv", kFilterIndexDiskImage }, - { L"dc", kFilterIndexDiskImage }, - { L"dc6", kFilterIndexDiskImage }, - { L"ddd", kFilterIndexDiskImage }, - { L"app", kFilterIndexDiskImage }, - { L"fdi", kFilterIndexDiskImage }, - { L"iso", kFilterIndexDiskImage }, - { L"gz", kFilterIndexDiskImage }, // assume disk image inside - { L"zip", kFilterIndexDiskImage }, // assume disk image inside -}; - -const WCHAR MainWindow::kModeNuFX[] = L"nufx"; -const WCHAR MainWindow::kModeBinaryII[] = L"bin2"; -const WCHAR MainWindow::kModeACU[] = L"acu"; -const WCHAR MainWindow::kModeAppleSingle[] = L"as"; -const WCHAR MainWindow::kModeDiskImage[] = L"disk"; - - -/* - * =========================================================================== - * MainWindow - * =========================================================================== - */ - -static const UINT gFindReplaceID = RegisterWindowMessage(FINDMSGSTRING); - -BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) - ON_WM_CREATE() - ON_MESSAGE(WMU_LATE_INIT, OnLateInit) - //ON_MESSAGE(WMU_CLOSE_MAIN_DIALOG, OnCloseMainDialog) - ON_WM_SIZE() - ON_WM_GETMINMAXINFO() - ON_WM_PAINT() - //ON_WM_MOUSEWHEEL() - ON_WM_SETFOCUS() - ON_WM_HELPINFO() - ON_WM_QUERYENDSESSION() - ON_WM_ENDSESSION() - ON_REGISTERED_MESSAGE(gFindReplaceID, OnFindDialogMessage) - ON_COMMAND( IDM_FILE_NEW_ARCHIVE, OnFileNewArchive) - ON_COMMAND( IDM_FILE_OPEN, OnFileOpen) - ON_COMMAND( IDM_FILE_OPEN_VOLUME, OnFileOpenVolume) - ON_UPDATE_COMMAND_UI(IDM_FILE_OPEN_VOLUME, OnUpdateFileOpenVolume) - ON_COMMAND( IDM_FILE_REOPEN, OnFileReopen) - ON_UPDATE_COMMAND_UI(IDM_FILE_REOPEN, OnUpdateFileReopen) - ON_COMMAND( IDM_FILE_SAVE, OnFileSave) - ON_UPDATE_COMMAND_UI(IDM_FILE_SAVE, OnUpdateFileSave) - ON_COMMAND( IDM_FILE_CLOSE, OnFileClose) - ON_UPDATE_COMMAND_UI(IDM_FILE_CLOSE, OnUpdateFileClose) - ON_COMMAND( IDM_FILE_ARCHIVEINFO, OnFileArchiveInfo) - ON_UPDATE_COMMAND_UI(IDM_FILE_ARCHIVEINFO, OnUpdateFileArchiveInfo) - ON_COMMAND( IDM_FILE_PRINT, OnFilePrint) - ON_UPDATE_COMMAND_UI(IDM_FILE_PRINT, OnUpdateFilePrint) - ON_COMMAND( IDM_FILE_EXIT, OnFileExit) - ON_COMMAND( IDM_EDIT_COPY, OnEditCopy) - ON_UPDATE_COMMAND_UI(IDM_EDIT_COPY, OnUpdateEditCopy) - ON_COMMAND( IDM_EDIT_PASTE, OnEditPaste) - ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE, OnUpdateEditPaste) - ON_COMMAND( IDM_EDIT_PASTE_SPECIAL, OnEditPasteSpecial) - ON_UPDATE_COMMAND_UI(IDM_EDIT_PASTE_SPECIAL, OnUpdateEditPasteSpecial) - ON_COMMAND( IDM_EDIT_FIND, OnEditFind) - ON_UPDATE_COMMAND_UI(IDM_EDIT_FIND, OnUpdateEditFind) - ON_COMMAND( IDM_EDIT_SELECT_ALL, OnEditSelectAll) - ON_UPDATE_COMMAND_UI(IDM_EDIT_SELECT_ALL, OnUpdateEditSelectAll) - ON_COMMAND( IDM_EDIT_INVERT_SELECTION, OnEditInvertSelection) - ON_UPDATE_COMMAND_UI(IDM_EDIT_INVERT_SELECTION, OnUpdateEditInvertSelection) - ON_COMMAND( IDM_EDIT_PREFERENCES, OnEditPreferences) - ON_COMMAND_RANGE( IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnEditSort) - ON_UPDATE_COMMAND_UI_RANGE(IDM_SORT_PATHNAME, IDM_SORT_ORIGINAL, OnUpdateEditSort) - ON_COMMAND( IDM_ACTIONS_VIEW, OnActionsView) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_VIEW, OnUpdateActionsView) - ON_COMMAND( IDM_ACTIONS_ADD_FILES, OnActionsAddFiles) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_FILES, OnUpdateActionsAddFiles) - ON_COMMAND( IDM_ACTIONS_ADD_DISKS, OnActionsAddDisks) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_ADD_DISKS, OnUpdateActionsAddDisks) - ON_COMMAND( IDM_ACTIONS_CREATE_SUBDIR, OnActionsCreateSubdir) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CREATE_SUBDIR, OnUpdateActionsCreateSubdir) - ON_COMMAND( IDM_ACTIONS_EXTRACT, OnActionsExtract) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EXTRACT, OnUpdateActionsExtract) - ON_COMMAND( IDM_ACTIONS_TEST, OnActionsTest) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_TEST, OnUpdateActionsTest) - ON_COMMAND( IDM_ACTIONS_DELETE, OnActionsDelete) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_DELETE, OnUpdateActionsDelete) - ON_COMMAND( IDM_ACTIONS_RENAME, OnActionsRename) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME, OnUpdateActionsRename) - ON_COMMAND( IDM_ACTIONS_RECOMPRESS, OnActionsRecompress) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RECOMPRESS, OnUpdateActionsRecompress) - ON_COMMAND( IDM_ACTIONS_OPENASDISK, OnActionsOpenAsDisk) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_OPENASDISK, OnUpdateActionsOpenAsDisk) - ON_COMMAND( IDM_ACTIONS_EDIT_COMMENT, OnActionsEditComment) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_COMMENT, OnUpdateActionsEditComment) - ON_COMMAND( IDM_ACTIONS_EDIT_PROPS, OnActionsEditProps) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_EDIT_PROPS, OnUpdateActionsEditProps) - ON_COMMAND( IDM_ACTIONS_RENAME_VOLUME, OnActionsRenameVolume) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_RENAME_VOLUME, OnUpdateActionsRenameVolume) - ON_COMMAND( IDM_ACTIONS_CONV_DISK, OnActionsConvDisk) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_DISK, OnUpdateActionsConvDisk) - ON_COMMAND( IDM_ACTIONS_CONV_FILE, OnActionsConvFile) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FILE, OnUpdateActionsConvFile) - ON_COMMAND( IDM_ACTIONS_CONV_TOWAV, OnActionsConvToWav) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_TOWAV, OnUpdateActionsConvToWav) - ON_COMMAND( IDM_ACTIONS_CONV_FROMWAV, OnActionsConvFromWav) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_CONV_FROMWAV, OnUpdateActionsConvFromWav) - ON_COMMAND( IDM_ACTIONS_IMPORT_BAS, OnActionsImportBAS) - ON_UPDATE_COMMAND_UI(IDM_ACTIONS_IMPORT_BAS, OnUpdateActionsImportBAS) - ON_COMMAND( IDM_TOOLS_DISKEDIT, OnToolsDiskEdit) - ON_COMMAND( IDM_TOOLS_IMAGECREATOR, OnToolsDiskImageCreator) - ON_COMMAND( IDM_TOOLS_DISKCONV, OnToolsDiskConv) - ON_COMMAND( IDM_TOOLS_BULKDISKCONV, OnToolsBulkDiskConv) - ON_COMMAND( IDM_TOOLS_SST_MERGE, OnToolsSSTMerge) - ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_VOLUME, OnToolsVolumeCopierVolume) - ON_COMMAND( IDM_TOOLS_VOLUMECOPIER_FILE, OnToolsVolumeCopierFile) - ON_COMMAND( IDM_TOOLS_EOLSCANNER, OnToolsEOLScanner) - ON_COMMAND( IDM_TOOLS_TWOIMGPROPS, OnToolsTwoImgProps) - ON_COMMAND( IDM_HELP_CONTENTS, OnHelpContents) - ON_COMMAND( IDM_HELP_WEBSITE, OnHelpWebSite) - ON_COMMAND( IDM_HELP_ORDERING, OnHelpOrdering) - ON_COMMAND( IDM_HELP_ABOUT, OnHelpAbout) -// ON_COMMAND( IDM_RTCLK_DEFAULT, OnRtClkDefault) - - ON_COMMAND(ID_HELP_FINDER, CFrameWnd::OnHelpFinder) - ON_COMMAND(ID_HELP, CFrameWnd::OnHelp) - ON_COMMAND(ID_CONTEXT_HELP, CFrameWnd::OnContextHelp) - ON_COMMAND(ID_DEFAULT_HELP, CFrameWnd::OnHelpFinder) -END_MESSAGE_MAP() - -/* - * MainWindow constructor. Creates the main window and sets - * its properties. - */ -MainWindow::MainWindow() -{ - static const WCHAR kAppName[] = L"CiderPress"; - - fpContentList = NULL; - fpOpenArchive = NULL; - //fpSelSet = NULL; - fpActionProgress = NULL; - fpProgressCounter = NULL; - fpFindDialog = NULL; - - fFindDown = true; - fFindMatchCase = false; - fFindMatchWholeWord = false; - - fAbortPrinting = false; - fhDevMode = NULL; - fhDevNames = NULL; - fNeedReopen = false; - - CString wndClass = AfxRegisterWndClass( - CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/, - gMyApp.LoadStandardCursor(IDC_ARROW), - NULL /*(HBRUSH) (COLOR_WINDOW + 1)*/, - gMyApp.LoadIcon(IDR_MAINFRAME) ); - - Create(wndClass, kAppName, WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN*/, - rectDefault, NULL, MAKEINTRESOURCE(IDR_MAINFRAME)); - - LoadAccelTable(MAKEINTRESOURCE(IDR_MAINFRAME)); - - // initialize some OLE garbage - AfxOleInit(); - - // required by MFC if Rich Edit controls are used - AfxInitRichEdit(); - - // required?? - //AfxEnableControlContainer(); - - SetCPTitle(); - - int cc = PostMessage(WMU_LATE_INIT, 0, 0); - ASSERT(cc != 0); -} - -/* - * MainWindow destructor. Close the archive if one is open, but don't try - * to shut down any controls in child windows. By this point, Windows has - * already snuffed them. - */ -MainWindow::~MainWindow() -{ - LOGI("~MainWindow"); - - //LOGI("MainWindow destructor"); - CloseArchiveWOControls(); - - //int cc; - //cc = ::WinHelp(m_hWnd, ::AfxGetApp()->m_pszHelpFilePath, HELP_QUIT, 0); - //LOGI("Turning off WinHelp returned %d", cc); - ::HtmlHelp(NULL, NULL, HH_CLOSE_ALL, 0); - - // free stuff used by print dialog - ::GlobalFree(fhDevMode); - ::GlobalFree(fhDevNames); - - fPreferences.SaveToRegistry(); - LOGI("MainWindow destructor complete"); -} - -BOOL MainWindow::PreCreateWindow(CREATESTRUCT& cs) -{ - BOOL res = CFrameWnd::PreCreateWindow(cs); - - cs.dwExStyle &= ~(WS_EX_CLIENTEDGE); - - // This changes the window class name to a value that the installer can - // detect. This allows us to prevent installation while CiderPress is - // running. (If we don't do that, the installation will offer to reboot - // the computer to complete installation.) - WNDCLASS wndCls; - if (!GetClassInfo(AfxGetInstanceHandle(), cs.lpszClass, &wndCls)) { - LOGW("GetClassInfo failed"); - } else { - cs.lpszClass = kMainWindowClassName; - wndCls.lpszClassName = kMainWindowClassName; - if (!AfxRegisterClass(&wndCls)) { - LOGW("AfxRegisterClass failed"); - } - } - - return res; -} - -void MainWindow::GetClientRect(LPRECT lpRect) const -{ - CRect sizeRect; - int toolBarHeight, statusBarHeight; - - fToolBar.GetWindowRect(&sizeRect); - toolBarHeight = sizeRect.bottom - sizeRect.top; - fStatusBar.GetWindowRect(&sizeRect); - statusBarHeight = sizeRect.bottom - sizeRect.top; - - //LOGI("HEIGHTS = %d/%d", toolBarHeight, statusBarHeight); - CFrameWnd::GetClientRect(lpRect); - lpRect->top += toolBarHeight; - lpRect->bottom -= statusBarHeight; -} - -void MainWindow::DoIdle(void) -{ - /* - * Make sure that the filename field in the content list is always - * visible, since that what the user clicks on to select things. Would - * be nice to have a way to prevent it, but for now we'll just shove - * things back where they're supposed to be. - */ - if (fpContentList != NULL) { - /* get the current column 0 width, with current user adjustments */ - fpContentList->ExportColumnWidths(); - int width = fPreferences.GetColumnLayout()->GetColumnWidth(0); - - if (width >= 0 && width < ColumnLayout::kMinCol0Width) { - /* column is too small, but don't change it until user lets mouse up */ - if (::GetAsyncKeyState(VK_LBUTTON) >= 0) { - LOGI("Resetting column 0 width"); - fPreferences.GetColumnLayout()->SetColumnWidth(0, - ColumnLayout::kMinCol0Width); - fpContentList->NewColumnWidths(); - } - } - } - - /* - * Put an asterisk at the end of the title if we have an open archive - * and it has pending modifications. Remove it if nothing is pending. - */ - if (fpOpenArchive != NULL) { - CString title; - int len; - - GetWindowText(/*ref*/ title); - len = title.GetLength(); - if (len > 0 && title.GetAt(len-1) == '*') { - if (!fpOpenArchive->IsModified()) { - /* remove the asterisk and the preceeding space */ - title.Delete(len-2, 2); - SetWindowText(title); - } - } else { - if (fpOpenArchive->IsModified()) { - /* add an asterisk */ - title += " *"; - SetWindowText(title); - } - } - } -} - -void MainWindow::ProcessCommandLine(void) -{ - /* - * Get the command line and break it down into an argument vector. - * - * Usage: - * CiderPress [[-temparc] [-mode {nufx,bin2,disk}] [-dispname name] filename] - */ - const WCHAR* cmdLine = ::GetCommandLine(); - if (cmdLine == NULL || wcslen(cmdLine) == 0) - return; - - WCHAR* mangle = wcsdup(cmdLine); - if (mangle == NULL) - return; - - LOGI("Mangling '%ls'", mangle); - WCHAR* argv[8]; - int argc = 8; - VectorizeString(mangle, argv, &argc); - - LOGI("Args:"); - for (int i = 0; i < argc; i++) { - LOGI(" %d '%ls'", i, argv[i]); - } - - /* - * Figure out what the arguments are. - */ - const WCHAR* filename = NULL; - const WCHAR* dispName = NULL; - FilterIndex filterIndex = kFilterIndexGeneric; - bool temp = false; - - for (int i = 1; i < argc; i++) { - if (argv[i][0] == '-') { - if (wcsicmp(argv[i], L"-mode") == 0) { - if (i == argc-1) { - LOGI("WARNING: -mode specified without mode"); - } else - i++; - if (wcsicmp(argv[i], kModeNuFX) == 0) - filterIndex = kFilterIndexNuFX; - else if (wcsicmp(argv[i], kModeBinaryII) == 0) - filterIndex = kFilterIndexBinaryII; - else if (wcsicmp(argv[i], kModeACU) == 0) - filterIndex = kFilterIndexACU; - else if (wcsicmp(argv[i], kModeAppleSingle) == 0) - filterIndex = kFilterIndexAppleSingle; - else if (wcsicmp(argv[i], kModeDiskImage) == 0) - filterIndex = kFilterIndexDiskImage; - else { - LOGI("WARNING: unrecognized mode '%ls'", argv[i]); - } - } else if (wcsicmp(argv[i], L"-dispname") == 0) { - if (i == argc-1) { - LOGI("WARNING: -dispname specified without name"); - } else - i++; - dispName = argv[i]; - } else if (wcsicmp(argv[i], L"-temparc") == 0) { - temp = true; - } else if (wcsicmp(argv[i], L"-install") == 0) { - // see MyApp::InitInstance - LOGI("Got '-install' flag, doing nothing"); - } else if (wcsicmp(argv[i], L"-uninstall") == 0) { - // see MyApp::InitInstance - LOGI("Got '-uninstall' flag, doing nothing"); - } else { - LOGI("WARNING: unrecognized flag '%ls'", argv[i]); - } - } else { - /* must be the filename */ - if (i != argc-1) { - LOGI("WARNING: ignoring extra arguments (e.g. '%ls')", - argv[i+1]); - } - filename = argv[i]; - break; - } - } - if (argc != 1 && filename == NULL) { - LOGI("WARNING: args specified but no filename found"); - } - - LOGI("Argument handling:"); - LOGI(" index=%d temp=%d filename='%ls'", - filterIndex, temp, filename == NULL ? L"(null)" : filename); - - if (filename != NULL) { - PathName path(filename); - CString ext = path.GetExtension(); - - // drop the leading '.' from the extension - if (ext.Left(1) == ".") - ext.Delete(0, 1); - - /* load the archive, mandating read-only if it's a temporary file */ - if (LoadArchive(filename, ext, filterIndex, temp) == 0) { - /* success, update title bar */ - if (temp) - fOpenArchivePathName = path.GetFileName(); - else - fOpenArchivePathName = filename; - if (dispName != NULL) - fOpenArchivePathName = dispName; - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - } - - /* if it's a temporary file, arrange to have it deleted before exit */ - if (temp) { - int len = wcslen(filename); - - if (len > 4 && wcsicmp(filename + (len-4), L".tmp") == 0) { - fDeleteList.Add(filename); - } else { - LOGI("NOT adding '%ls' to DeleteList -- does not end in '.tmp'", - filename); - } - } - } - - free(mangle); -} - - -/* - * =================================== - * Command handlers - * =================================== - */ - -const int kProgressPane = 1; - -int MainWindow::OnCreate(LPCREATESTRUCT lpcs) -{ - // add a toolbar and status bar - - LOGI("Now in OnCreate!"); - if (CFrameWnd::OnCreate(lpcs) == -1) - return -1; - - /* - * Create the tool bar. - */ -#if 0 - static UINT buttonList[] = { - IDM_FILE_OPEN, - IDM_FILE_NEW_ARCHIVE, - // spacer - IDM_FILE_PRINT, - }; -#endif - fToolBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_TOP | - CBRS_TOOLTIPS | CBRS_FLYBY); - fToolBar.LoadToolBar(IDR_TOOLBAR1); - - /* - * Create the status bar. - */ - static UINT indicators[] = { ID_SEPARATOR, ID_INDICATOR_COMPLETE }; - fStatusBar.Create(this); - fStatusBar.SetIndicators(indicators, NELEM(indicators)); - //fStatusBar.SetPaneInfo(0, ID_SEPARATOR, SBPS_NOBORDERS | SBPS_STRETCH, 0); - - fStatusBar.SetPaneText(kProgressPane, L""); - - return 0; -} - -LONG MainWindow::OnLateInit(UINT, LONG) -{ - /* - * Catch a message sent to inspire us to perform one-time initializations of - * preferences and libraries. - * - * We're doing this the long way around because we want to be able to - * put up a dialog box if the version is bad. If we tried to handle this - * in the constructor we'd be acting before the window was fully created. - */ - CString result; - CString appName; - CString niftyListFile; - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - LOGI("----- late init begins -----"); - - /* - * Handle all other messages. This gives the framework a chance to dim - * all of the toolbar buttons. This is especially useful when opening - * a file from the command line that doesn't exist, causing an error - * dialog and blocking main window messages. - */ - PeekAndPump(); - - /* - * Initialize libraries. This includes a version check. - */ - result = NufxArchive::AppInit(); - if (!result.IsEmpty()) - goto fail; - result = DiskArchive::AppInit(); - if (!result.IsEmpty()) - goto fail; - result = BnyArchive::AppInit(); - if (!result.IsEmpty()) - goto fail; - - niftyListFile = gMyApp.GetExeBaseName(); - niftyListFile += "NList.Data"; - if (!NiftyList::AppInit(niftyListFile)) { - CString file2 = niftyListFile + ".TXT"; - if (!NiftyList::AppInit(file2)) { - CString msg; - msg.Format(IDS_NLIST_DATA_FAILED, niftyListFile, file2); - MessageBox(msg, appName, MB_OK); - } - } - - /* - * Read preferences from registry. - */ - fPreferences.LoadFromRegistry(); - -#if 0 - /* - * Check to see if we're registered; if we're not, and we've expired, it's - * time to bail out. - */ - MyRegistry::RegStatus regStatus; - //regStatus = gMyApp.fRegistry.CheckRegistration(&result); - regStatus = MyRegistry::kRegValid; - LOGI("CheckRegistration returned %d", regStatus); - switch (regStatus) { - case MyRegistry::kRegNotSet: - case MyRegistry::kRegValid: - ASSERT(result.IsEmpty()); - break; - case MyRegistry::kRegExpired: - case MyRegistry::kRegInvalid: - MessageBox(result, appName, MB_OK|MB_ICONINFORMATION); - LOGI("FORCING REG"); - if (EnterRegDialog::GetRegInfo(this) != 0) { - result = ""; - goto fail; - } - SetCPTitle(); // update title bar with new reg info - break; - case MyRegistry::kRegFailed: - ASSERT(!result.IsEmpty()); - goto fail; - default: - ASSERT(false); - CString confused; - confused.Format(L"Registration check failed. %ls", (LPCWSTR) result); - result = confused; - goto fail; - } -#endif - - /* - * Process command-line options, possibly loading an archive. - */ - ProcessCommandLine(); - - return 0; - -fail: - if (!result.IsEmpty()) - ShowFailureMsg(this, result, IDS_FAILED); - int cc = PostMessage(WM_CLOSE, 0, 0); - ASSERT(cc != 0); - - return 0; -} - -BOOL MainWindow::OnQueryEndSession(void) -{ - // The system wants to know if we're okay with shutting down. - LOGI("Got QueryEndSession"); - return TRUE; -} - -void MainWindow::OnEndSession(BOOL bEnding) -{ - LOGI("Got EndSession (bEnding=%d)", bEnding); - - if (bEnding) { - CloseArchiveWOControls(); - - fPreferences.SaveToRegistry(); - } -} - -void MainWindow::OnSize(UINT nType, int cx, int cy) -{ - /* - * The main window is resizing. We don't automatically redraw on resize, - * so we will need to update the client region. If it's filled with a - * control, the control's resize & redraw function will take care of it. - * If not, we need to explicitly invalidate the client region so the - * window will repaint itself. - */ - CFrameWnd::OnSize(nType, cx, cy); - ResizeClientArea(); -} - -void MainWindow::ResizeClientArea(void) -{ - CRect sizeRect; - - GetClientRect(&sizeRect); - if (fpContentList != NULL) - fpContentList->MoveWindow(sizeRect); - else - Invalidate(false); -} - -void MainWindow::OnGetMinMaxInfo(MINMAXINFO* pMMI) -{ - // Restrict the minimum window size to something reasonable. - pMMI->ptMinTrackSize.x = 256; - pMMI->ptMinTrackSize.y = 192; -} - -void MainWindow::OnPaint(void) -{ - // Repaint the main window. - CPaintDC dc(this); - CRect clientRect; - - GetClientRect(&clientRect); - - /* - * If there's no control in the window, fill in the client area with - * what looks like an empty MDI client rect. - */ - if (fpContentList == NULL) { - DrawEmptyClientArea(&dc, clientRect); - } - -#if 0 - CPen pen(PS_SOLID, 1, RGB(255, 0, 0)); // red pen, 1 pixel wide - CPen* pOldPen = dc.SelectObject(&pen); - - dc.MoveTo(clientRect.left, clientRect.top); - dc.LineTo(clientRect.right-1, clientRect.top); - dc.LineTo(clientRect.right, clientRect.bottom); - dc.LineTo(clientRect.left, clientRect.bottom-1); - dc.LineTo(clientRect.left, clientRect.top); - - dc.SelectObject(pOldPen); -#endif -} - -#if 0 -afx_msg BOOL MainWindow::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt) -{ - LOGI("MOUSE WHEEL"); - return FALSE; - - WPARAM wparam; - LPARAM lparam; - - wparam = nFlags | (zDelta << 16); - lparam = pt.x | (pt.y << 16); - if (fpContentList != NULL) - fpContentList->SendMessage(WM_MOUSEWHEEL, wparam, lparam); - return CWnd::OnMouseWheel(nFlags, zDelta, pt); -// return TRUE; -} -#endif - -void MainWindow::OnSetFocus(CWnd* /*pOldWnd*/) -{ - // Make sure open controls keep the input focus. - if (fpContentList != NULL) { - LOGD("Returning focus to ContentList"); - fpContentList->SetFocus(); - } -} - -BOOL MainWindow::OnHelpInfo(HELPINFO* /*lpHelpInfo*/) -{ - LOGD("MainWindow::OnHelpInfo"); - MyApp::HandleHelp(this, HELP_TOPIC_WELCOME); - return TRUE; -} - -void MainWindow::OnEditPreferences(void) -{ - // Handle Edit->Preferences by popping up a property sheet. - - PrefsSheet ps; - ColumnLayout* pColLayout = fPreferences.GetColumnLayout(); - - /* pull any user header tweaks out of list so we can configure prefs */ - if (fpContentList != NULL) - fpContentList->ExportColumnWidths(); - - /* set up PrefsGeneralPage */ - for (int i = 0; i < kNumVisibleColumns; i++) { - ps.fGeneralPage.fColumn[i] = (pColLayout->GetColumnWidth(i) != 0); - } - ps.fGeneralPage.fMimicShrinkIt = fPreferences.GetPrefBool(kPrMimicShrinkIt); - ps.fGeneralPage.fBadMacSHK = fPreferences.GetPrefBool(kPrBadMacSHK); - ps.fGeneralPage.fReduceSHKErrorChecks = fPreferences.GetPrefBool(kPrReduceSHKErrorChecks); - ps.fGeneralPage.fCoerceDOSFilenames = fPreferences.GetPrefBool(kPrCoerceDOSFilenames); - ps.fGeneralPage.fSpacesToUnder = fPreferences.GetPrefBool(kPrSpacesToUnder); - ps.fGeneralPage.fPasteJunkPaths = fPreferences.GetPrefBool(kPrPasteJunkPaths); - ps.fGeneralPage.fBeepOnSuccess = fPreferences.GetPrefBool(kPrBeepOnSuccess); - - /* set up PrefsDiskImagePage */ - ps.fDiskImagePage.fQueryImageFormat = fPreferences.GetPrefBool(kPrQueryImageFormat); - ps.fDiskImagePage.fOpenVolumeRO = fPreferences.GetPrefBool(kPrOpenVolumeRO); - ps.fDiskImagePage.fOpenVolumePhys0 = fPreferences.GetPrefBool(kPrOpenVolumePhys0); - ps.fDiskImagePage.fProDOSAllowLower = fPreferences.GetPrefBool(kPrProDOSAllowLower); - ps.fDiskImagePage.fProDOSUseSparse = fPreferences.GetPrefBool(kPrProDOSUseSparse); - - /* set up PrefsCompressionPage */ - ps.fCompressionPage.fCompressType = fPreferences.GetPrefLong(kPrCompressionType); - - /* set up PrefsFviewPage */ - ps.fFviewPage.fMaxViewFileSizeKB = - (fPreferences.GetPrefLong(kPrMaxViewFileSize) + 1023) / 1024; - ps.fFviewPage.fNoWrapText = fPreferences.GetPrefBool(kPrNoWrapText); - - ps.fFviewPage.fHighlightHexDump = fPreferences.GetPrefBool(kPrHighlightHexDump); - ps.fFviewPage.fHighlightBASIC = fPreferences.GetPrefBool(kPrHighlightBASIC); - ps.fFviewPage.fConvDisasmOneByteBrkCop = fPreferences.GetPrefBool(kPrDisasmOneByteBrkCop); - ps.fFviewPage.fConvMouseTextToASCII = fPreferences.GetPrefBool(kPrConvMouseTextToASCII); - ps.fFviewPage.fConvHiResBlackWhite = fPreferences.GetPrefBool(kPrConvHiResBlackWhite); - ps.fFviewPage.fConvDHRAlgorithm = fPreferences.GetPrefLong(kPrConvDHRAlgorithm); - ps.fFviewPage.fRelaxGfxTypeCheck = fPreferences.GetPrefBool(kPrRelaxGfxTypeCheck); -// ps.fFviewPage.fEOLConvRaw = fPreferences.GetPrefBool(kPrEOLConvRaw); -// ps.fFviewPage.fConvHighASCII = fPreferences.GetPrefBool(kPrConvHighASCII); - ps.fFviewPage.fConvTextEOL_HA = fPreferences.GetPrefBool(kPrConvTextEOL_HA); - ps.fFviewPage.fConvCPMText = fPreferences.GetPrefBool(kPrConvCPMText); - ps.fFviewPage.fConvPascalText = fPreferences.GetPrefBool(kPrConvPascalText); - ps.fFviewPage.fConvPascalCode = fPreferences.GetPrefBool(kPrConvPascalCode); - ps.fFviewPage.fConvApplesoft = fPreferences.GetPrefBool(kPrConvApplesoft); - ps.fFviewPage.fConvInteger = fPreferences.GetPrefBool(kPrConvInteger); - ps.fFviewPage.fConvBusiness = fPreferences.GetPrefBool(kPrConvBusiness); - ps.fFviewPage.fConvGWP = fPreferences.GetPrefBool(kPrConvGWP); - ps.fFviewPage.fConvText8 = fPreferences.GetPrefBool(kPrConvText8); - ps.fFviewPage.fConvAWP = fPreferences.GetPrefBool(kPrConvAWP); - ps.fFviewPage.fConvADB = fPreferences.GetPrefBool(kPrConvADB); - ps.fFviewPage.fConvASP = fPreferences.GetPrefBool(kPrConvASP); - ps.fFviewPage.fConvSCAssem = fPreferences.GetPrefBool(kPrConvSCAssem); - ps.fFviewPage.fConvDisasm = fPreferences.GetPrefBool(kPrConvDisasm); - ps.fFviewPage.fConvHiRes = fPreferences.GetPrefBool(kPrConvHiRes); - ps.fFviewPage.fConvDHR = fPreferences.GetPrefBool(kPrConvDHR); - ps.fFviewPage.fConvSHR = fPreferences.GetPrefBool(kPrConvSHR); - ps.fFviewPage.fConvPrintShop = fPreferences.GetPrefBool(kPrConvPrintShop); - ps.fFviewPage.fConvMacPaint = fPreferences.GetPrefBool(kPrConvMacPaint); - ps.fFviewPage.fConvProDOSFolder = fPreferences.GetPrefBool(kPrConvProDOSFolder); - ps.fFviewPage.fConvResources = fPreferences.GetPrefBool(kPrConvResources); - - /* set up PrefsFilesPage */ - ps.fFilesPage.fTempPath = fPreferences.GetPrefString(kPrTempPath); - ps.fFilesPage.fExtViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); - - if (ps.DoModal() == IDOK) - ApplyNow(&ps); -} - -void MainWindow::ApplyNow(PrefsSheet* pPS) -{ - // Apply a change from the preferences sheet. - LOGV("APPLY CHANGES"); - - bool mustReload = false; - - ColumnLayout* pColLayout = fPreferences.GetColumnLayout(); - - if (pPS->fGeneralPage.fDefaultsPushed) { - /* reset all sizes to defaults, then factor in checkboxes */ - LOGI(" Resetting all widths to defaults"); - - /* copy defaults over */ - for (int i = 0; i < kNumVisibleColumns; i++) - pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted); - } - - /* handle column checkboxes */ - for (int i = 0; i < kNumVisibleColumns; i++) { - if (pColLayout->GetColumnWidth(i) == 0 && - pPS->fGeneralPage.fColumn[i]) - { - /* restore column */ - LOGI(" Column %d restored", i); - pColLayout->SetColumnWidth(i, ColumnLayout::kWidthDefaulted); - } else if (pColLayout->GetColumnWidth(i) != 0 && - !pPS->fGeneralPage.fColumn[i]) - { - /* disable column */ - LOGI(" Column %d hidden", i); - pColLayout->SetColumnWidth(i, 0); - } - } - if (fpContentList != NULL) - fpContentList->NewColumnWidths(); - fPreferences.SetPrefBool(kPrMimicShrinkIt, - pPS->fGeneralPage.fMimicShrinkIt != 0); - fPreferences.SetPrefBool(kPrBadMacSHK, pPS->fGeneralPage.fBadMacSHK != 0); - fPreferences.SetPrefBool(kPrReduceSHKErrorChecks, - pPS->fGeneralPage.fReduceSHKErrorChecks != 0); - if (fPreferences.GetPrefBool(kPrCoerceDOSFilenames)!= - (pPS->fGeneralPage.fCoerceDOSFilenames != 0)) - { - LOGI("DOS filename coercion pref now %d", - pPS->fGeneralPage.fCoerceDOSFilenames); - fPreferences.SetPrefBool(kPrCoerceDOSFilenames, - pPS->fGeneralPage.fCoerceDOSFilenames != 0); - mustReload = true; - } - if (fPreferences.GetPrefBool(kPrSpacesToUnder) != - (pPS->fGeneralPage.fSpacesToUnder != 0)) - { - LOGI("Spaces-to-underscores now %d", pPS->fGeneralPage.fSpacesToUnder); - fPreferences.SetPrefBool(kPrSpacesToUnder, pPS->fGeneralPage.fSpacesToUnder != 0); - mustReload = true; - } - fPreferences.SetPrefBool(kPrPasteJunkPaths, pPS->fGeneralPage.fPasteJunkPaths != 0); - fPreferences.SetPrefBool(kPrBeepOnSuccess, pPS->fGeneralPage.fBeepOnSuccess != 0); - - if (pPS->fGeneralPage.fOurAssociations != NULL) { - LOGI("NEW ASSOCIATIONS!"); - -#ifdef CAN_UPDATE_FILE_ASSOC - for (int assoc = 0; assoc < gMyApp.fRegistry.GetNumFileAssocs(); assoc++) - { - gMyApp.fRegistry.SetFileAssoc(assoc, - pPS->fGeneralPage.fOurAssociations[assoc]); - } -#endif - - /* delete them so, if they hit "apply" again, we only update once */ - delete[] pPS->fGeneralPage.fOurAssociations; - pPS->fGeneralPage.fOurAssociations = NULL; - - LOGV("Sending association-change notification to Windows shell"); - ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); - } - - fPreferences.SetPrefBool(kPrQueryImageFormat, pPS->fDiskImagePage.fQueryImageFormat != 0); - fPreferences.SetPrefBool(kPrOpenVolumeRO, pPS->fDiskImagePage.fOpenVolumeRO != 0); - fPreferences.SetPrefBool(kPrOpenVolumePhys0, pPS->fDiskImagePage.fOpenVolumePhys0 != 0); - fPreferences.SetPrefBool(kPrProDOSAllowLower, pPS->fDiskImagePage.fProDOSAllowLower != 0); - fPreferences.SetPrefBool(kPrProDOSUseSparse, pPS->fDiskImagePage.fProDOSUseSparse != 0); - - fPreferences.SetPrefLong(kPrCompressionType, pPS->fCompressionPage.fCompressType); - - fPreferences.SetPrefLong(kPrMaxViewFileSize, pPS->fFviewPage.fMaxViewFileSizeKB * 1024); - fPreferences.SetPrefBool(kPrNoWrapText, pPS->fFviewPage.fNoWrapText != 0); - - fPreferences.SetPrefBool(kPrHighlightHexDump, pPS->fFviewPage.fHighlightHexDump != 0); - fPreferences.SetPrefBool(kPrHighlightBASIC, pPS->fFviewPage.fHighlightBASIC != 0); - fPreferences.SetPrefBool(kPrDisasmOneByteBrkCop, pPS->fFviewPage.fConvDisasmOneByteBrkCop != 0); - fPreferences.SetPrefBool(kPrConvMouseTextToASCII, pPS->fFviewPage.fConvMouseTextToASCII != 0); - fPreferences.SetPrefBool(kPrConvHiResBlackWhite, pPS->fFviewPage.fConvHiResBlackWhite != 0); - fPreferences.SetPrefLong(kPrConvDHRAlgorithm, pPS->fFviewPage.fConvDHRAlgorithm); - fPreferences.SetPrefBool(kPrRelaxGfxTypeCheck, pPS->fFviewPage.fRelaxGfxTypeCheck != 0); -// fPreferences.SetPrefBool(kPrEOLConvRaw, pPS->fFviewPage.fEOLConvRaw != 0); -// fPreferences.SetPrefBool(kPrConvHighASCII, pPS->fFviewPage.fConvHighASCII != 0); - fPreferences.SetPrefBool(kPrConvTextEOL_HA, pPS->fFviewPage.fConvTextEOL_HA != 0); - fPreferences.SetPrefBool(kPrConvCPMText, pPS->fFviewPage.fConvCPMText != 0); - fPreferences.SetPrefBool(kPrConvPascalText, pPS->fFviewPage.fConvPascalText != 0); - fPreferences.SetPrefBool(kPrConvPascalCode, pPS->fFviewPage.fConvPascalCode != 0); - fPreferences.SetPrefBool(kPrConvApplesoft, pPS->fFviewPage.fConvApplesoft != 0); - fPreferences.SetPrefBool(kPrConvInteger, pPS->fFviewPage.fConvInteger != 0); - fPreferences.SetPrefBool(kPrConvBusiness, pPS->fFviewPage.fConvBusiness != 0); - fPreferences.SetPrefBool(kPrConvGWP, pPS->fFviewPage.fConvGWP != 0); - fPreferences.SetPrefBool(kPrConvText8, pPS->fFviewPage.fConvText8 != 0); - fPreferences.SetPrefBool(kPrConvAWP, pPS->fFviewPage.fConvAWP != 0); - fPreferences.SetPrefBool(kPrConvADB, pPS->fFviewPage.fConvADB != 0); - fPreferences.SetPrefBool(kPrConvASP, pPS->fFviewPage.fConvASP != 0); - fPreferences.SetPrefBool(kPrConvSCAssem, pPS->fFviewPage.fConvSCAssem != 0); - fPreferences.SetPrefBool(kPrConvDisasm, pPS->fFviewPage.fConvDisasm != 0); - fPreferences.SetPrefBool(kPrConvHiRes, pPS->fFviewPage.fConvHiRes != 0); - fPreferences.SetPrefBool(kPrConvDHR, pPS->fFviewPage.fConvDHR != 0); - fPreferences.SetPrefBool(kPrConvSHR, pPS->fFviewPage.fConvSHR != 0); - fPreferences.SetPrefBool(kPrConvPrintShop, pPS->fFviewPage.fConvPrintShop != 0); - fPreferences.SetPrefBool(kPrConvMacPaint, pPS->fFviewPage.fConvMacPaint != 0); - fPreferences.SetPrefBool(kPrConvProDOSFolder, pPS->fFviewPage.fConvProDOSFolder != 0); - fPreferences.SetPrefBool(kPrConvResources, pPS->fFviewPage.fConvResources != 0); - - fPreferences.SetPrefString(kPrTempPath, pPS->fFilesPage.fTempPath); - LOGI("--- Temp path now '%ls'", fPreferences.GetPrefString(kPrTempPath)); - fPreferences.SetPrefString(kPrExtViewerExts, pPS->fFilesPage.fExtViewerExts); - - -// if ((pPS->fGeneralPage.fShowToolbarText != 0) != fPreferences.GetShowToolbarText()) { -// fPreferences.SetShowToolbarText(pPS->fGeneralPage.fShowToolbarText != 0); -// //SetToolbarTextMode(); -// ResizeClientArea(); -// } - - /* allow open archive to track changes to preferences */ - if (fpOpenArchive != NULL) - fpOpenArchive->PreferencesChanged(); - - if (mustReload) { - LOGI("Preferences apply requesting GA/CL reload"); - if (fpOpenArchive != NULL) - fpOpenArchive->Reload(); - if (fpContentList != NULL) - fpContentList->Reload(); - } - - /* export to registry */ - fPreferences.SaveToRegistry(); - - //Invalidate(); -} - -void MainWindow::OnEditFind(void) -{ - // Handle IDM_EDIT_FIND. - - DWORD flags = 0; - - if (fpFindDialog != NULL) - return; - - if (fFindDown) - flags |= FR_DOWN; - if (fFindMatchCase) - flags |= FR_MATCHCASE; - if (fFindMatchWholeWord) - flags |= FR_WHOLEWORD; - - fpFindDialog = new CFindReplaceDialog; - - fpFindDialog->Create(TRUE, // "find" only - fFindLastStr, // default string to search for - NULL, // default string to replace - flags, // flags - this); // parent -} - -void MainWindow::OnUpdateEditFind(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != NULL); -} - -LRESULT MainWindow::OnFindDialogMessage(WPARAM wParam, LPARAM lParam) -{ - // Handle activity in the modeless "find" dialog. - - assert(fpFindDialog != NULL); - - fFindDown = (fpFindDialog->SearchDown() != 0); - fFindMatchCase = (fpFindDialog->MatchCase() != 0); - fFindMatchWholeWord = (fpFindDialog->MatchWholeWord() != 0); - - if (fpFindDialog->IsTerminating()) { - fpFindDialog = NULL; - return 0; - } - - if (fpFindDialog->FindNext()) { - fFindLastStr = fpFindDialog->GetFindString(); - fpContentList->FindNext(fFindLastStr, fFindDown, fFindMatchCase, - fFindMatchWholeWord); - } else { - LOGI("Unexpected find dialog activity"); - } - - return 0; -} - -void MainWindow::OnEditSort(UINT id) -{ - // Handle IDM_SORT_*. - // The "sort" menu item should really only be active if we have a file open. - LOGD("EDIT SORT %d", id); - - ASSERT(id >= IDM_SORT_PATHNAME && id <= IDM_SORT_ORIGINAL); - fPreferences.GetColumnLayout()->SetSortColumn(id - IDM_SORT_PATHNAME); - fPreferences.GetColumnLayout()->SetAscending(true); - if (fpContentList != NULL) - fpContentList->NewSortOrder(); -} - -void MainWindow::OnUpdateEditSort(CCmdUI* pCmdUI) -{ - unsigned int column = fPreferences.GetColumnLayout()->GetSortColumn(); - - pCmdUI->SetCheck(pCmdUI->m_nID - IDM_SORT_PATHNAME == column); -} - -void MainWindow::OnHelpContents(void) -{ - MyApp::HandleHelp(this, HELP_TOPIC_WELCOME); - //WinHelp(0, HELP_FINDER); -} - -void MainWindow::OnHelpWebSite(void) -{ - int err; - - err = (int) ::ShellExecute(m_hWnd, L"open", kWebSiteURL, NULL, NULL, - SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - if (err == ERROR_FILE_NOT_FOUND) { - msg = L"Windows call failed: web browser not found. (Sometimes" - L" it mistakenly reports this when IE is not the default" - L" browser.)"; - ShowFailureMsg(this, msg, IDS_FAILED); - } else { - msg.Format(L"Unable to launch web browser (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } - } -} - -void MainWindow::OnHelpOrdering(void) -{ - // How to order... ka-ching! - // TODO: no longer used? - LOGW("OnHelpOrdering -- not implemented"); - //WinHelp(HELP_TOPIC_ORDERING_INFO, HELP_CONTEXT); -} - -void MainWindow::OnHelpAbout(void) -{ - int result; - - AboutDialog dlg(this); - - result = dlg.DoModal(); - LOGV("HelpAbout returned %d", result); - - /* - * User could've changed registration. If we're showing the registered - * user name in the title bar, update it. - */ - if (fpOpenArchive == NULL) - SetCPTitle(); -} - -void MainWindow::OnFileNewArchive(void) -{ - // Create a new SHK archive, using a "save as" dialog to select the name. - - CString filename, saveFolder, errStr; - GenericArchive* pOpenArchive; - CString errMsg; - - CFileDialog dlg(FALSE, L"shk", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - L"ShrinkIt Archives (*.shk)|*.shk||", this); - - dlg.m_ofn.lpstrTitle = L"New Archive"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - filename = dlg.GetPathName(); - LOGI("NEW FILE '%ls'", (LPCWSTR) filename); - - /* remove file if it already exists */ - errMsg = RemoveFile(filename); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - pOpenArchive = new NufxArchive; - errStr = pOpenArchive->New(filename, NULL); - if (!errStr.IsEmpty()) { - CString failed; - CheckedLoadString(&failed, IDS_FAILED); - MessageBox(errStr, failed, MB_ICONERROR); - - delete pOpenArchive; - } else { - SwitchContentList(pOpenArchive); - fOpenArchivePathName = dlg.GetPathName(); - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - } - -bail: - LOGD("--- OnFileNewArchive done"); -} - -void MainWindow::OnFileOpen(void) -{ - // Handle request to open an archive or disk image. - - CString openFilters; - CString saveFolder; - - /* set up filters; the order must match enum FilterIndex */ - openFilters = kOpenNuFX; - openFilters += kOpenBinaryII; - openFilters += kOpenACU; - openFilters += kOpenAppleSingle; - openFilters += kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, L"shk", NULL, - OFN_FILEMUSTEXIST, openFilters, this); - - DWORD savedIndex = fPreferences.GetPrefLong(kPrLastOpenFilterIndex); - if (savedIndex < kFilterIndexFIRST || savedIndex > kFilterIndexMAX) { - // default to *.* if not set (zero) or out of range - savedIndex = kFilterIndexGeneric; - } - dlg.m_ofn.nFilterIndex = savedIndex; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - fPreferences.SetPrefLong(kPrLastOpenFilterIndex, dlg.m_ofn.nFilterIndex); - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - FilterIndex filterIndex = (FilterIndex) dlg.m_ofn.nFilterIndex; - if (filterIndex < kFilterIndexFIRST || filterIndex > kFilterIndexMAX) { - ASSERT(false); - LOGW("invalid filterIndex %d", filterIndex); - filterIndex = kFilterIndexGeneric; - } - DoOpenArchive(dlg.GetPathName(), dlg.GetFileExt(), - filterIndex, dlg.GetReadOnlyPref() != 0); - -bail: - LOGD("--- OnFileOpen done"); -} - -void MainWindow::OnFileOpenVolume(void) -{ - // Handle request to open a raw disk volume. - LOGD("--- OnFileOpenVolume"); - - int result; - - OpenVolumeDialog dlg(this); - - result = dlg.DoModal(); - if (result != IDOK) - goto bail; - - //DiskImg::SetAllowWritePhys0(fPreferences.GetPrefBool(kPrOpenVolumePhys0)); - DoOpenVolume(dlg.fChosenDrive, dlg.fReadOnly != 0); - -bail: - return; -} - -void MainWindow::OnUpdateFileOpenVolume(CCmdUI* pCmdUI) -{ - // don't really need this function - pCmdUI->Enable(TRUE); -} - -void MainWindow::DoOpenArchive(const WCHAR* pathName, const WCHAR* ext, - FilterIndex filterIndex, bool readOnly) -{ - if (LoadArchive(pathName, ext, filterIndex, readOnly) == 0) { - /* success, update title bar */ - fOpenArchivePathName = pathName; - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - } else { - /* some failures will close an open archive */ - //if (fpOpenArchive == NULL) - // SetCPTitle(); - } -} - -void MainWindow::OnFileReopen(void) -{ - ReopenArchive(); -} - -void MainWindow::OnUpdateFileReopen(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != NULL); -} - -void MainWindow::OnFileSave(void) -{ - /* - * This may be called directly from tools, so don't assume that the - * conditions checked for in OnUpdateFileSave hold here. - */ - CString errMsg; - - if (fpOpenArchive == NULL) - return; - - { - CWaitCursor waitc; - errMsg = fpOpenArchive->Flush(); - } - if (!errMsg.IsEmpty()) - ShowFailureMsg(this, errMsg, IDS_FAILED); - - // update the title bar - DoIdle(); -} - -void MainWindow::OnUpdateFileSave(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != NULL && fpOpenArchive->IsModified()); -} - -void MainWindow::OnFileClose(void) -{ - CloseArchive(); - //SetCPTitle(); - LOGD("--- OnFileClose done"); -} - -void MainWindow::OnUpdateFileClose(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpOpenArchive != NULL); -} - -void MainWindow::OnFileArchiveInfo(void) -{ - ArchiveInfoDialog* pDlg = NULL; - ASSERT(fpOpenArchive != NULL); - - switch (fpOpenArchive->GetArchiveKind()) { - case GenericArchive::kArchiveNuFX: - pDlg = new NufxArchiveInfoDialog((NufxArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveDiskImage: - pDlg = new DiskArchiveInfoDialog((DiskArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveBNY: - pDlg = new BnyArchiveInfoDialog((BnyArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveACU: - pDlg = new AcuArchiveInfoDialog((AcuArchive*) fpOpenArchive, this); - break; - case GenericArchive::kArchiveAppleSingle: - pDlg = new AppleSingleArchiveInfoDialog((AppleSingleArchive*) fpOpenArchive, this); - break; - default: - LOGW("Unexpected archive type %d", fpOpenArchive->GetArchiveKind()); - ASSERT(false); - return; - }; - - pDlg->DoModal(); - - delete pDlg; -} - -void MainWindow::OnUpdateFileArchiveInfo(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL); -} - -void MainWindow::OnFilePrint(void) -{ - PrintListing(fpContentList); -} - -void MainWindow::OnUpdateFilePrint(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL && fpContentList->GetItemCount() > 0); -} - -void MainWindow::PrintListing(const ContentList* pContentList) -{ - CPrintDialog dlg(FALSE); // use CPrintDialogEx for Win2K? CPageSetUpDialog? - PrintContentList pcl; - CDC dc; - int itemCount, numPages; - - itemCount = pContentList->GetItemCount(); - numPages = (itemCount + (pcl.GetLinesPerPage()-1)) / pcl.GetLinesPerPage(); - - dlg.m_pd.nFromPage = dlg.m_pd.nMinPage = 1; - dlg.m_pd.nToPage = dlg.m_pd.nMaxPage = numPages; - - dlg.m_pd.hDevMode = fhDevMode; - dlg.m_pd.hDevNames = fhDevNames; - dlg.m_pd.Flags |= PD_USEDEVMODECOPIESANDCOLLATE; - dlg.m_pd.Flags &= ~(PD_NOPAGENUMS); - if (dlg.DoModal() != IDOK) - return; - if (dc.Attach(dlg.GetPrinterDC()) != TRUE) { - CString msg; - CheckedLoadString(&msg, IDS_PRINTER_NOT_USABLE); - ShowFailureMsg(this, msg, IDS_FAILED); - return; - } - - pcl.Setup(&dc, this); - if (dlg.m_pd.Flags & PD_PAGENUMS) - pcl.Print(pContentList, dlg.m_pd.nFromPage, dlg.m_pd.nToPage); - else - pcl.Print(pContentList); - - fhDevMode = dlg.m_pd.hDevMode; - fhDevNames = dlg.m_pd.hDevNames; -} - -void MainWindow::OnFileExit(void) -{ - // Handle Exit item by sending a close request. - SendMessage(WM_CLOSE, 0, 0); -} - -void MainWindow::OnEditSelectAll(void) -{ - ASSERT(fpContentList != NULL); - fpContentList->SelectAll(); -} - -void MainWindow::OnUpdateEditSelectAll(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL); -} - -void MainWindow::OnEditInvertSelection(void) -{ - ASSERT(fpContentList != NULL); - fpContentList->InvertSelection(); -} - -void MainWindow::OnUpdateEditInvertSelection(CCmdUI* pCmdUI) -{ - pCmdUI->Enable(fpContentList != NULL); -} - -GenericEntry* MainWindow::GetSelectedItem(ContentList* pContentList) -{ - if (pContentList->GetSelectedCount() != 1) - return NULL; - - POSITION posn; - posn = pContentList->GetFirstSelectedItemPosition(); - if (posn == NULL) { - ASSERT(false); - return NULL; - } - int num = pContentList->GetNextSelectedItem(/*ref*/ posn); - GenericEntry* pEntry = (GenericEntry*) pContentList->GetItemData(num); - if (pEntry == NULL) { - LOGW(" Glitch: couldn't find entry %d", num); - ASSERT(false); - } - - return pEntry; -} - -void MainWindow::HandleDoubleClick(void) -{ - const uint32_t kTypeDimg = 0x64496d67; - const uint32_t kCreatorDcpy = 0x64437079; - bool handled = false; - - ASSERT(fpContentList != NULL); - if (fpContentList->GetSelectedCount() == 0) { - /* nothing selected, they double-clicked outside first column */ - LOGI("Double-click but nothing selected"); - return; - } - if (fpContentList->GetSelectedCount() != 1) { - /* multiple items, just bring up viewer */ - HandleView(); - return; - } - - /* - * Find the GenericEntry that corresponds to this item. - */ - GenericEntry* pEntry = GetSelectedItem(fpContentList); - if (pEntry == NULL) - return; - - LOGI(" Double-click got '%ls'", (LPCWSTR) pEntry->GetPathNameUNI()); - const WCHAR* ext; - long fileType, auxType; - - ext = PathName::FindExtension(pEntry->GetPathNameUNI(), pEntry->GetFssep()); - fileType = pEntry->GetFileType(); - auxType = pEntry->GetAuxType(); - - /* // unit tests for MatchSemicolonList - MatchSemicolonList("gif; jpeg; jpg", "jpeg"); - MatchSemicolonList("gif; jpeg; jpg", "jpg"); - MatchSemicolonList("gif; jpeg; jpg", "gif"); - MatchSemicolonList("gif;jpeg;jpg", "gif;"); - MatchSemicolonList("gif; jpeg; jpg", "jpe"); - MatchSemicolonList("gif; jpeg; jpg", "jpegx"); - MatchSemicolonList("gif; jpeg; jpg", "jp"); - MatchSemicolonList("gif; jpeg; jpg", "jpgx"); - MatchSemicolonList("gif; jpeg; jpg", "if"); - MatchSemicolonList("gif; jpeg; jpg", "gifs"); - MatchSemicolonList("gif, jpeg; jpg", "jpeg"); - MatchSemicolonList("", "jpeg"); - MatchSemicolonList(";", "jpeg"); - MatchSemicolonList("gif, jpeg; jpg", ""); - */ - - /* - * Figure out what to do with it. - */ - CString extViewerExts; - extViewerExts = fPreferences.GetPrefString(kPrExtViewerExts); - if (ext != NULL && MatchSemicolonList(extViewerExts, ext+1)) { - LOGI(" Launching external viewer for '%ls'", ext); - TmpExtractForExternal(pEntry); - handled = true; - } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindFile) { - if ((ext != NULL && ( - wcsicmp(ext, L".shk") == 0 || - wcsicmp(ext, L".sdk") == 0 || - wcsicmp(ext, L".bxy") == 0)) || - (fileType == 0xe0 && auxType == 0x8002)) - { - LOGI(" Guessing NuFX"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeNuFX); - handled = true; - } else - if ((ext != NULL && ( - wcsicmp(ext, L".bny") == 0 || - wcsicmp(ext, L".bqy") == 0)) || - (fileType == 0xe0 && auxType == 0x8000)) - { - LOGI(" Guessing Binary II"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeBinaryII); - handled = true; - } else - if ((ext != NULL && ( - wcsicmp(ext, L".acu") == 0)) || - (fileType == 0xe0 && auxType == 0x8001)) - { - LOGI(" Guessing ACU"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeACU); - handled = true; - } else - if ((ext != NULL && ( - wcsicmp(ext, L".as") == 0)) || - (fileType == 0xe0 && auxType == 0x0001)) - { - LOGI(" Guessing AppleSingle"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeAppleSingle); - handled = true; - } else - if (fileType == kTypeDimg && auxType == kCreatorDcpy && - pEntry->GetDataForkLen() == 819284) - { - /* type is dImg, creator is dCpy, length is 800K + DC stuff */ - LOGI(" Looks like a DiskCopy disk image"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage); - handled = true; - } - } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindForkedFile) { - /* - * On Mac HFS volumes these can have resource forks (which we ignore). - * We could probably just generally ignore resource forks for all of - * the above files, rather than pulling this one out separately, but - * this is the only one that could reasonably have a resource fork. - */ - if (fileType == kTypeDimg && auxType == kCreatorDcpy && - pEntry->GetDataForkLen() == 819284) - { - /* type is dImg, creator is dCpy, length is 800K + DC stuff */ - LOGI(" Looks like a (forked) DiskCopy disk image"); - TmpExtractAndOpen(pEntry, GenericEntry::kDataThread, kModeDiskImage); - handled = true; - } - } else if (pEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) { - LOGI(" Opening archived disk image"); - TmpExtractAndOpen(pEntry, GenericEntry::kDiskImageThread, kModeDiskImage); - handled = true; - } - - if (!handled) { - // standard viewer - HandleView(); - } - - /* set "/t" temp flag and delete afterward, warning user (?) */ -} - -int MainWindow::TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, - const WCHAR* modeStr) -{ - CString dispName; - bool mustDelete = false; - - /* - * Get the name to display in the title bar. Double quotes will - * screw it up, so we have to replace them. (We could escape them, - * but then we'd also have to escape the escape char.) - */ - dispName = pEntry->GetFileName(); - dispName.Replace('"', '_'); - - WCHAR nameBuf[MAX_PATH]; - UINT unique; - unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), - L"CPfile", 0, nameBuf); - if (unique == 0) { - DWORD dwerr = ::GetLastError(); - LOGI("GetTempFileName failed on '%ls' (err=%ld)", - fPreferences.GetPrefString(kPrTempPath), dwerr); - return dwerr; - } - mustDelete = true; - - /* - * Open the temp file and extract the data into it. - */ - CString errMsg; - int result; - FILE* fp; - - fp = _wfopen(nameBuf, L"wb"); - if (fp != NULL) { - LOGD("Extracting to '%ls' (unique=%d)", nameBuf, unique); - result = pEntry->ExtractThreadToFile(threadKind, fp, - GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, - &errMsg); - fclose(fp); - if (result == IDOK) { - /* success */ - CString parameters; - - parameters.Format(L"-mode %ls -dispname \"%ls\" -temparc \"%ls\"", - modeStr, (LPCWSTR) dispName, nameBuf); - int err; - - err = (int) ::ShellExecute(m_hWnd, L"open", - gMyApp.GetExeFileName(), parameters, NULL, - SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - msg.Format(L"Unable to launch CiderPress (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } else { - /* during dev, "missing DLL" causes false-positive success */ - LOGI("Successfully launched CiderPress, mode=%ls", modeStr); - mustDelete = false; // up to newly-launched app - } - } else { - ShowFailureMsg(this, errMsg, IDS_FAILED); - } - } else { - CString msg; - msg.Format(L"Unable to open temp file '%ls'.", nameBuf); - ::ShowFailureMsg(this, msg, IDS_FAILED); - } - - if (mustDelete) { - LOGI("Deleting '%ls'", nameBuf); - _wunlink(nameBuf); - } - - return 0; -} - -int MainWindow::TmpExtractForExternal(GenericEntry* pEntry) -{ - const WCHAR* ext; - - ext = PathName::FindExtension(pEntry->GetPathNameUNI(), pEntry->GetFssep()); - - WCHAR nameBuf[MAX_PATH]; - UINT unique; - unique = GetTempFileName(fPreferences.GetPrefString(kPrTempPath), - L"CPfile", 0, nameBuf); - if (unique == 0) { - DWORD dwerr = ::GetLastError(); - LOGI("GetTempFileName failed on '%ls' (err=%ld)", - fPreferences.GetPrefString(kPrTempPath), dwerr); - return dwerr; - } - fDeleteList.Add(nameBuf); // file is created by GetTempFileName - - wcscat(nameBuf, ext); - - /* - * Open the temp file and extract the data into it. - */ - CString errMsg; - int result; - FILE* fp; - - fp = _wfopen(nameBuf, L"wb"); - if (fp != NULL) { - fDeleteList.Add(nameBuf); // second file created by fopen - LOGI("Extracting to '%ls' (unique=%d)", nameBuf, unique); - result = pEntry->ExtractThreadToFile(GenericEntry::kDataThread, fp, - GenericEntry::kConvertEOLOff, GenericEntry::kConvertHAOff, - &errMsg); - fclose(fp); - if (result == IDOK) { - /* success */ - int err; - - err = (int) ::ShellExecute(m_hWnd, L"open", nameBuf, NULL, - NULL, SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - msg.Format(L"Unable to launch external viewer (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } else { - LOGI("Successfully launched external viewer"); - } - } else { - ShowFailureMsg(this, errMsg, IDS_FAILED); - } - } else { - CString msg; - msg.Format(L"Unable to open temp file '%ls'.", nameBuf); - ShowFailureMsg(this, msg, IDS_FAILED); - } - - return 0; -} - -#if 0 -/* - * Handle a "default action" selection from the right-click menu. The - * action only applies to the record that was clicked on, so we need to - * retrieve that from the control. - */ -void MainWindow::OnRtClkDefault(void) -{ - int idx; - - ASSERT(fpContentList != NULL); - - idx = fpContentList->GetRightClickItem(); - ASSERT(idx != -1); - LOGI("OnRtClkDefault %d", idx); - - fpContentList->ClearRightClickItem(); -} -#endif - - -/* - * =================================== - * Progress meter - * =================================== - */ - -/* - * There are two different mechanisms for reporting progress: ActionProgress - * dialogs (for adding/extracting files) and a small box in the lower - * right-hand corner (for opening archives). These functions will set - * the progress in the active action progress dialog if it exists, or - * will set the percentage in the window frame if not. - */ - -void MainWindow::SetProgressBegin(void) -{ - if (fpActionProgress != NULL) - fpActionProgress->SetProgress(0); - else - fStatusBar.SetPaneText(kProgressPane, L"--%"); - //LOGI(" Complete: BEGIN"); - - /* redraw stuff with the changes */ - (void) PeekAndPump(); -} - -int MainWindow::SetProgressUpdate(int percent, const WCHAR* oldName, - const WCHAR* newName) -{ - int status = IDOK; - - if (fpActionProgress != NULL) { - status = fpActionProgress->SetProgress(percent); - if (oldName != NULL) - fpActionProgress->SetArcName(oldName); - if (newName != NULL) - fpActionProgress->SetFileName(newName); - } else { - WCHAR buf[8]; - wsprintf(buf, L"%d%%", percent); - fStatusBar.SetPaneText(kProgressPane, buf); - //LOGI(" Complete: %ls", buf); - } - - if (!PeekAndPump()) { - LOGI("SetProgressUpdate: shutdown?!"); - } - - //EventPause(10); // DEBUG DEBUG - return status; -} - -void MainWindow::SetProgressEnd(void) -{ - if (fpActionProgress != NULL) - fpActionProgress->SetProgress(100); - else - fStatusBar.SetPaneText(kProgressPane, L""); -// EventPause(100); // DEBUG DEBUG - //LOGI(" Complete: END"); -} - -bool MainWindow::SetProgressCounter(const WCHAR* str, long val) -{ - /* if the main window is enabled, user could activate menus */ - ASSERT(!IsWindowEnabled()); - - if (fpProgressCounter != NULL) { - //LOGI("SetProgressCounter '%ls' %d", str, val); - CString msg; - - if (str != NULL) - fpProgressCounter->SetCounterFormat(str); - fpProgressCounter->SetCount((int) val); - } else { - if (val < 0) { - fStatusBar.SetPaneText(kProgressPane, L""); - } else { - CString tmpStr; - tmpStr.Format(L"%ld", val); - fStatusBar.SetPaneText(kProgressPane, tmpStr); - } - } - - if (!PeekAndPump()) { - LOGI("SetProgressCounter: shutdown?!"); - } - //EventPause(10); // DEBUG DEBUG - - if (fpProgressCounter != NULL) - return !fpProgressCounter->GetCancel(); - else - return true; -} - -BOOL MainWindow::PeekAndPump(void) -{ - MSG msg; - - while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - if (!AfxGetApp()->PumpMessage()) { - ::PostQuitMessage(0); - return FALSE; - } - } - - LONG lIdle = 0; - while (AfxGetApp()->OnIdle(lIdle++)) - ; - return TRUE; -} - -void MainWindow::EventPause(int duration) -{ - int count = duration / 10; - - for (int i = 0; i < count; i++) { - PeekAndPump(); - ::Sleep(10); - } -} - -/*static*/ BOOL CALLBACK MainWindow::PrintAbortProc(HDC hDC, int nCode) -{ - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - - pMain->PeekAndPump(); - if (pMain->GetAbortPrinting()) { - LOGI("PrintAbortProc returning FALSE (abort printing)"); - return FALSE; - } - LOGI(" PrintAbortProc returning TRUE (continue printing)"); - return TRUE; -} - -/* - * =================================== - * Support functions - * =================================== - */ - -void MainWindow::DrawEmptyClientArea(CDC* pDC, const CRect& clientRect) -{ - CBrush brush; - brush.CreateSolidBrush(::GetSysColor(COLOR_APPWORKSPACE)); // dk gray - CBrush* pOldBrush = pDC->SelectObject(&brush); - pDC->FillRect(&clientRect, &brush); - pDC->SelectObject(pOldBrush); - - CPen penWH(PS_SOLID, 1, ::GetSysColor(COLOR_3DHIGHLIGHT)); // white - CPen penLG(PS_SOLID, 1, ::GetSysColor(COLOR_3DLIGHT)); // lt gray - CPen penDG(PS_SOLID, 1, ::GetSysColor(COLOR_3DSHADOW)); // dk gray - CPen penBL(PS_SOLID, 1, ::GetSysColor(COLOR_3DDKSHADOW)); // near-black - CPen* pOldPen = pDC->SelectObject(&penWH); - //pDC->SelectObject(&penWH); - pDC->MoveTo(clientRect.right-1, clientRect.top); - pDC->LineTo(clientRect.right-1, clientRect.bottom-1); - pDC->LineTo(clientRect.left-1, clientRect.bottom-1); - pDC->SelectObject(&penBL); - pDC->MoveTo(clientRect.right-3, clientRect.top+1); - pDC->LineTo(clientRect.left+1, clientRect.top+1); - pDC->LineTo(clientRect.left+1, clientRect.bottom-2); - pDC->SelectObject(&penLG); - pDC->MoveTo(clientRect.right-2, clientRect.top+1); - pDC->LineTo(clientRect.right-2, clientRect.bottom-2); - pDC->LineTo(clientRect.left, clientRect.bottom-2); - pDC->SelectObject(&penDG); - pDC->MoveTo(clientRect.right-2, clientRect.top); - pDC->LineTo(clientRect.left, clientRect.top); - pDC->LineTo(clientRect.left, clientRect.bottom-1); - - pDC->SelectObject(pOldPen); -} - -GenericArchive* MainWindow::CreateArchiveInstance(FilterIndex filterIndex) const -{ - GenericArchive* pOpenArchive = NULL; - - switch (filterIndex) { - case kFilterIndexNuFX: pOpenArchive = new NufxArchive; break; - case kFilterIndexBinaryII: pOpenArchive = new BnyArchive; break; - case kFilterIndexACU: pOpenArchive = new AcuArchive; break; - case kFilterIndexAppleSingle: pOpenArchive = new AppleSingleArchive; break; - case kFilterIndexDiskImage: pOpenArchive = new DiskArchive; break; - default: ASSERT(false); break; - } - - return pOpenArchive; -} - -int MainWindow::LoadArchive(const WCHAR* fileName, const WCHAR* extension, - FilterIndex filterIndex, bool readOnly) -{ - static const WCHAR kFileArchive[] = L"This appears to be a file archive."; - GenericArchive::OpenResult openResult; - const FilterIndex origFilterIndex = filterIndex; - GenericArchive* pOpenArchive = NULL; - - LOGI("LoadArchive: '%ls' ro=%d idx=%d", fileName, readOnly, filterIndex); - - /* close any existing archive to avoid weirdness from re-open */ - CloseArchive(); - - /* - * If they used the "All Files (*.*)" filter, try guess based - * on the file extension. - */ - if (filterIndex == kFilterIndexGeneric) { - int i; - - for (i = 0; i < NELEM(gExtensionToIndex); i++) { - if (wcsicmp(extension, gExtensionToIndex[i].extension) == 0) { - filterIndex = gExtensionToIndex[i].idx; - break; - } - } - - if (i == NELEM(gExtensionToIndex)) { - // no match found, use "disk image" as initial guess - filterIndex = kFilterIndexDiskImage; - } - } - - /* - * Try to open the file according to the specified filter index. If - * it works, we're done. Trying this first ensures that you can choose - * to open, say, a .SDK file as either ShrinkIt or Disk Image. - * - * It's possible to cancel the file open if you have "confirm disk image - * format set" and the file is a disk image. In that case we want to - * return with an error result, but without showing an error dialog. - */ - CString firstErrStr; - pOpenArchive = CreateArchiveInstance(filterIndex); - LOGD("First try: %ls", (LPCWSTR) pOpenArchive->GetDescription()); - openResult = pOpenArchive->Open(fileName, readOnly, &firstErrStr); - if (openResult == GenericArchive::kResultSuccess) { - // success! - SwitchContentList(pOpenArchive); - return 0; - } else if (openResult == GenericArchive::kResultFileArchive) { - if (wcsicmp(extension, L"zip") == 0) { - // we could probably just return in this case - firstErrStr = L"Zip archives with multiple files are not supported"; - } else { - firstErrStr = kFileArchive; - } - } else if (openResult == GenericArchive::kResultCancel) { - LOGD("canceled"); - delete pOpenArchive; - return -1; - } - delete pOpenArchive; - - /* - * That didn't work. Try the others. - */ - for (int i = kFilterIndexFIRST; i <= kFilterIndexLASTNG; i++) { - if (i == filterIndex) continue; - - pOpenArchive = CreateArchiveInstance((FilterIndex) i); - LOGD("Now trying: %ls", (LPCWSTR) pOpenArchive->GetDescription()); - CString dummyErrStr; - openResult = pOpenArchive->Open(fileName, readOnly, &dummyErrStr); - if (openResult == GenericArchive::kResultSuccess) { - // success! - SwitchContentList(pOpenArchive); - return 0; - } else if (openResult == GenericArchive::kResultCancel) { - LOGD("cancelled"); - delete pOpenArchive; - return -1; - } else if (i == kFilterIndexNuFX && firstErrStr == kFileArchive) { - // For .shk files we first check to see if it's a disk image, - // then we try by file. If it's a damaged file archive, we - // really want to present the file archive failure message, not - // "that looks like a file archive". So we tweak the result a - // little here. - // - // This doesn't catch all the cases where the NufxLib error - // message is more useful than the DiskImg error message, but - // it's hard to accurately determine what the most-accurate - // message is in some cases. - firstErrStr = dummyErrStr; - } - delete pOpenArchive; - } - - /* - * Nothing worked. Show the original error message so that it matches - * up with the chosen filter index -- if they seemed to be trying to - * open an AppleSingle, don't tell them we failed because the file isn't - * a disk image. - * - * If they were using the Generic filter, they'll get the message from - * the disk image library. It might make more sense to just say "we - * couldn't figure out what this was", but most of the time people are - * trying to open disk images anyway. - */ - if (firstErrStr.IsEmpty()) { - // not expected; put up a generic message if it happens - firstErrStr = L"Unable to determine what kind of file this is."; - } - ShowFailureMsg(this, firstErrStr, IDS_FAILED); - return -1; -} - -int MainWindow::DoOpenVolume(CString drive, bool readOnly) -{ - int result = -1; - - ASSERT(drive.GetLength() > 0); - - CString errStr; - //char filename[4] = "_:\\"; - //filename[0] = driveLetter; - - LOGI("FileOpenVolume '%ls' %d", (LPCWSTR)drive, readOnly); - - /* close existing archive */ - CloseArchive(); - - GenericArchive* pOpenArchive = NULL; - pOpenArchive = new DiskArchive; - { - CWaitCursor waitc; - GenericArchive::OpenResult openResult; - - openResult = pOpenArchive->Open(drive, readOnly, &errStr); - if (openResult == GenericArchive::kResultCancel) { - // this bubbles out of the format confirmation dialog - goto bail; - } else if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - } - - // success! - SwitchContentList(pOpenArchive); - pOpenArchive = NULL; - fOpenArchivePathName = drive; - result = 0; - - fOpenArchivePathName = drive; - SetCPTitle(fOpenArchivePathName, fpOpenArchive); - -bail: - if (pOpenArchive != NULL) { - ASSERT(result != 0); - delete pOpenArchive; - } - return result; -} - -void MainWindow::ReopenArchive(void) -{ - if (fpOpenArchive == NULL) { - ASSERT(false); - return; - } - - /* clear the flag, regardless of success or failure */ - fNeedReopen = false; - - GenericArchive* pOpenArchive = NULL; - CString pathName = fpOpenArchive->GetPathName(); - bool readOnly = fpOpenArchive->IsReadOnly(); - GenericArchive::ArchiveKind archiveKind = fpOpenArchive->GetArchiveKind(); - GenericArchive::OpenResult openResult; - CString errStr; - - /* if the open fails we *don't* want to leave the previous content up */ - LOGI("Reopening '%ls' ro=%d kind=%d", - (LPCWSTR) pathName, readOnly, archiveKind); - CloseArchive(); - - switch (archiveKind) { - case GenericArchive::kArchiveDiskImage: - pOpenArchive = new DiskArchive; - break; - case GenericArchive::kArchiveNuFX: - pOpenArchive = new NufxArchive; - break; - case GenericArchive::kArchiveBNY: - pOpenArchive = new BnyArchive; - break; - default: - ASSERT(false); - return; - } - - openResult = pOpenArchive->Open(pathName, readOnly, &errStr); - if (openResult == GenericArchive::kResultCancel) { - // this bubbles out of the format confirmation dialog - goto bail; - } else if (openResult != GenericArchive::kResultSuccess) { - if (!errStr.IsEmpty()) - ShowFailureMsg(this, errStr, IDS_FAILED); - goto bail; - } - - LOGI(" Reopen was successful"); - SwitchContentList(pOpenArchive); - pOpenArchive = NULL; - SetCPTitle(pathName, fpOpenArchive); - -bail: - delete pOpenArchive; -} - -bool MainWindow::IsOpenPathName(const WCHAR* path) -{ - if (fpOpenArchive == NULL) - return false; - - if (wcsicmp(path, fpOpenArchive->GetPathName()) == 0) - return true; - - return false; -} - -void MainWindow::SwitchContentList(GenericArchive* pOpenArchive) -{ - assert(pOpenArchive != NULL); - - /* - * We've got an archive opened successfully. If we already had one - * open, shut it. (This assumes that closing an archive is a simple - * matter of closing files and freeing storage. If we needed to do - * something that might fail, like flush changes, we should've done - * that before getting this far to avoid confusion.) - */ - if (fpOpenArchive != NULL) - CloseArchive(); - - ASSERT(fpOpenArchive == NULL); - ASSERT(fpContentList == NULL); - - /* - * Without this we get an assertion failure in CImageList::Attach if we - * call here from ReopenArchive. I think Windows needs to do some - * cleanup, though I don't understand how the reopen case differs from - * the usual case. Maybe there's more stuff pending in the "reopen" - * case? In any event, this seems to work, which is all you can hope - * for from MFC. It does, however, make the screen flash, which it - * didn't do before. - * - * UPDATE: this tripped once while I was debugging, even with this. The - * PeekAndPump function does force the idle loop to run, so I'm not sure - * why it failed, unless the debugger somehow affected the idle - * processing. Yuck. - * - * The screen flash bugged me so I took it back out. And the assert - * didn't hit. I really, really love Windows. - */ - //PeekAndPump(); - - - fpContentList = new ContentList(pOpenArchive, - fPreferences.GetColumnLayout()); - - CRect sizeRect; - GetClientRect(&sizeRect); - fpContentList->Create(WS_CHILD | WS_VISIBLE | WS_VSCROLL, - sizeRect, this, IDC_CONTENT_LIST); - - fpOpenArchive = pOpenArchive; -} - -void MainWindow::CloseArchiveWOControls(void) -{ - if (fpOpenArchive != NULL) { - //fpOpenArchive->Close(); - LOGI("Deleting OpenArchive"); - delete fpOpenArchive; - fpOpenArchive = NULL; - } -} - -void MainWindow::CloseArchive(void) -{ - CWaitCursor waitc; // closing large compressed archive can be slow - - // destroy the ContentList - if (fpContentList != NULL) { - LOGI("Destroying ContentList"); - fpContentList->DestroyWindow(); // auto-cleanup invokes "delete" - fpContentList = NULL; - } - - // destroy the GenericArchive - CloseArchiveWOControls(); - - // reset the title bar - SetCPTitle(); -} - -void MainWindow::SetCPTitle(const WCHAR* pathname, GenericArchive* pOpenArchive) -{ - ASSERT(pathname != NULL); - CString title; - CString appName; - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - CString archiveDescription(pOpenArchive->GetDescription()); - title.Format(L"%ls - %ls (%ls)", (LPCWSTR) appName, pathname, - (LPCWSTR) archiveDescription); - - if (fpOpenArchive->IsReadOnly()) { - CString readOnly; - CheckedLoadString(&readOnly, IDS_READONLY); - title += L" "; - title += readOnly; - } - - SetWindowText(title); -} - -void MainWindow::SetCPTitle(void) -{ - CString appName, regName, title; - CString user, company, reg, versions, expire; - -#if 0 - if (gMyApp.fRegistry.GetRegistration(&user, &company, ®, &versions, - &expire) == 0) - { - if (reg.IsEmpty()) { - regName += _T(" (unregistered)"); - } else { - regName += _T(" (registered to "); - regName += user; - regName += _T(")"); - // include company? - } - } -#endif - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - title = appName + regName; - SetWindowText(title); -} - -CString MainWindow::GetPrintTitle(void) -{ - CString title; - - if (fpOpenArchive == NULL) { - ASSERT(false); - return title; - } - - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - CString archiveDescription(fpOpenArchive->GetDescription()); - title.Format(L"%ls - %ls (%ls)", (LPCWSTR) appName, - (LPCWSTR) fOpenArchivePathName, (LPCWSTR) archiveDescription); - - return title; -} - -void MainWindow::SuccessBeep(void) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { - LOGI(""); - ::MessageBeep(MB_OK); - } -} - -void MainWindow::FailureBeep(void) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - if (pPreferences->GetPrefBool(kPrBeepOnSuccess)) { - LOGI(""); - ::MessageBeep(MB_ICONEXCLAMATION); // maybe MB_ICONHAND? - } -} - -CString MainWindow::RemoveFile(const WCHAR* fileName) -{ - CString errMsg; - - int cc; - cc = _wunlink(fileName); - if (cc < 0 && errno != ENOENT) { - int err = errno; - LOGI("Failed removing file '%ls', errno=%d", fileName, err); - errMsg.Format(L"Unable to remove '%ls': %hs.", - fileName, strerror(err)); - if (err == EACCES) - errMsg += L"\n\n(Make sure the file isn't open.)"; - } - - return errMsg; -} - -/*static*/ void MainWindow::ConfigureReformatFromPreferences(ReformatHolder* pReformat) -{ - const Preferences* pPreferences = GET_PREFERENCES(); - - pReformat->SetReformatAllowed(ReformatHolder::kReformatRaw, true); - pReformat->SetReformatAllowed(ReformatHolder::kReformatHexDump, true); - - pReformat->SetReformatAllowed(ReformatHolder::kReformatTextEOL_HA, - pPreferences->GetPrefBool(kPrConvTextEOL_HA)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatResourceFork, - pPreferences->GetPrefBool(kPrConvResources)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatProDOSDirectory, - pPreferences->GetPrefBool(kPrConvProDOSFolder)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalText, - pPreferences->GetPrefBool(kPrConvPascalText)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatPascalCode, - pPreferences->GetPrefBool(kPrConvPascalCode)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatCPMText, - pPreferences->GetPrefBool(kPrConvCPMText)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft, - pPreferences->GetPrefBool(kPrConvApplesoft)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatApplesoft_Hilite, - pPreferences->GetPrefBool(kPrConvApplesoft)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger, - pPreferences->GetPrefBool(kPrConvInteger)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatInteger_Hilite, - pPreferences->GetPrefBool(kPrConvInteger)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatBusiness, - pPreferences->GetPrefBool(kPrConvBusiness)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatBusiness_Hilite, - pPreferences->GetPrefBool(kPrConvBusiness)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSCAssem, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMerlin, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA2, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA3, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatLISA4, - pPreferences->GetPrefBool(kPrConvSCAssem)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor8, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmMerlin8, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Long, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMonitor16Short, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDisasmOrcam16, - pPreferences->GetPrefBool(kPrConvDisasm)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatAWGS_WP, - pPreferences->GetPrefBool(kPrConvGWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatTeach, - pPreferences->GetPrefBool(kPrConvGWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatGWP, - pPreferences->GetPrefBool(kPrConvGWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMagicWindow, - pPreferences->GetPrefBool(kPrConvText8)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatGutenberg, - pPreferences->GetPrefBool(kPrConvGutenberg)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP, - pPreferences->GetPrefBool(kPrConvAWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatAWP, - pPreferences->GetPrefBool(kPrConvAWP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatADB, - pPreferences->GetPrefBool(kPrConvADB)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatASP, - pPreferences->GetPrefBool(kPrConvASP)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes, - pPreferences->GetPrefBool(kPrConvHiRes)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatHiRes_BW, - pPreferences->GetPrefBool(kPrConvHiRes)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Latched, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_BW, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Plain140, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatDHR_Window, - pPreferences->GetPrefBool(kPrConvDHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_PIC, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_JEQ, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Paintworks, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_Packed, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_APF, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3200, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_3201, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG256, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatSHR_DG3200, - pPreferences->GetPrefBool(kPrConvSHR)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatPrintShop, - pPreferences->GetPrefBool(kPrConvPrintShop)); - pReformat->SetReformatAllowed(ReformatHolder::kReformatMacPaint, - pPreferences->GetPrefBool(kPrConvMacPaint)); - - pReformat->SetOption(ReformatHolder::kOptHiliteHexDump, - pPreferences->GetPrefBool(kPrHighlightHexDump)); - pReformat->SetOption(ReformatHolder::kOptHiliteBASIC, - pPreferences->GetPrefBool(kPrHighlightBASIC)); - pReformat->SetOption(ReformatHolder::kOptHiResBW, - pPreferences->GetPrefBool(kPrConvHiResBlackWhite)); - pReformat->SetOption(ReformatHolder::kOptDHRAlgorithm, - pPreferences->GetPrefLong(kPrConvDHRAlgorithm)); - pReformat->SetOption(ReformatHolder::kOptRelaxGfxTypeCheck, - pPreferences->GetPrefBool(kPrRelaxGfxTypeCheck)); - pReformat->SetOption(ReformatHolder::kOptOneByteBrkCop, - pPreferences->GetPrefBool(kPrDisasmOneByteBrkCop)); - pReformat->SetOption(ReformatHolder::kOptMouseTextToASCII, - pPreferences->GetPrefBool(kPrConvMouseTextToASCII)); -} - -/*static*/ ReformatHolder::SourceFormat MainWindow::ReformatterSourceFormat( - DiskImg::FSFormat format) -{ - /* - * Gutenberg both UsesDOSFileStructure and is formatted with - * kFormatGutenberg, so check for the latter first. - */ - if (format == DiskImg::kFormatGutenberg) - return ReformatHolder::kSourceFormatGutenberg; - else if (DiskImg::UsesDOSFileStructure(format)) - return ReformatHolder::kSourceFormatDOS; - else if (format == DiskImg::kFormatCPM) - return ReformatHolder::kSourceFormatCPM; - else - return ReformatHolder::kSourceFormatGeneric; -} diff --git a/ciderpress/app/Main.h b/ciderpress/app/Main.h deleted file mode 100644 index ac43139..0000000 --- a/ciderpress/app/Main.h +++ /dev/null @@ -1,937 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Application UI classes. - */ -#ifndef APP_MAIN_H -#define APP_MAIN_H - -#include "ContentList.h" -#include "GenericArchive.h" -#include "PrefsDialog.h" -#include "ActionProgressDialog.h" -#include "ProgressCounterDialog.h" -#include "AddFilesDialog.h" -#include "ExtractOptionsDialog.h" -#include "ConvFileOptionsDialog.h" -#include "DiskConvertDialog.h" -#include "FileNameConv.h" -//#include "ProgressCancelDialog.h" - -/* user-defined window messages */ -#define WMU_LATE_INIT (WM_USER+0) -#define WMU_START (WM_USER+1) // used by ActionProgressDialog - -// The filter index is saved in the registry, so if you reorder this list -// you will briefly annoy existing users. -enum FilterIndex { - kFilterIndexFIRST = 1, // first index, must be non-Generic - kFilterIndexNuFX = 1, - kFilterIndexBinaryII = 2, - kFilterIndexACU = 3, - kFilterIndexAppleSingle = 4, - kFilterIndexDiskImage = 5, - kFilterIndexLASTNG = 5, // last non-Generic index - - kFilterIndexGeneric = 6, // *.* filter used - kFilterIndexMAX = 6 // highest valid number -}; - -struct FileCollectionEntry; // fwd - -/* - * The main UI window. - */ -class MainWindow : public CFrameWnd -{ -public: - MainWindow(void); - ~MainWindow(void); - - /* - * Override the pre-create function to tweak the window style. - */ - BOOL PreCreateWindow(CREATESTRUCT& cs) override; - - /* - * Override GetClientRect so we can factor in the status and tool bars. - * - * (The method in question isn't declared virtual, so we're not actually - * overriding it.) - */ - void GetClientRect(LPRECT lpRect) const; - - // get a pointer to the preferences - const Preferences* GetPreferences(void) const { return &fPreferences; } - Preferences* GetPreferencesWr(void) { return &fPreferences; } - // apply an update from the Preferences pages - void ApplyNow(PrefsSheet*); - - // get the text of the next file in the selection list - int GetPrevFileText(ReformatHolder* pHolder, CString* pTitle); - int GetNextFileText(ReformatHolder* pHolder, CString* pTitle); - - // update the progress meter - void SetProgressBegin(void); - int SetProgressUpdate(int percent, const WCHAR* oldName, - const WCHAR* newName); - void SetProgressEnd(void); - - /* - * Set a number in the "progress counter". Useful for loading large archives - * where we're not sure how much stuff is left, so showing a percentage is - * hard. - * - * Pass in -1 to erase the counter. - * - * Returns "true" if we'd like things to continue. - */ - bool SetProgressCounter(const WCHAR* fmt, long val); - - /* - * Handle a double-click in the content view. - * - * Individual items get special treatment, multiple items just get handed off - * to the file viewer. - */ - void HandleDoubleClick(void); - - // do some idle processing - void DoIdle(void); - - /* - * Come up with a title to put at the top of a printout. This is essentially - * the same as the window title, but without some flags (e.g. "read-only"). - */ - CString GetPrintTitle(void); - - // raise flag to abort the current print job - void SetAbortPrinting(bool val) { fAbortPrinting = val; } - bool GetAbortPrinting(void) const { return fAbortPrinting; } - - /* - * Printer abort procedure; allows us to abort a print job. The DC - * SetAbortProc() function calls here periodically. The return value from - * this function determine whether or not printing halts. - * - * This checks a global "print cancel" variable, which is set by our print - * cancel button dialog. - * - * If this returns TRUE, printing continues; FALSE, and printing aborts. - */ - static BOOL CALLBACK PrintAbortProc(HDC hDC, int nCode); - - bool fAbortPrinting; - // track printer choice - HANDLE fhDevMode; - HANDLE fhDevNames; - - // set flag to abort current operation - //void SetAbortOperation(bool val) { fAbortOperation = val; } - //bool fAbortOperation; - - /* - * Go to sleep for a little bit, waking up 100x per second to check - * the idle loop. Used for debugging. - */ - void EventPause(int duration); - - ContentList* GetContentList(void) const { return fpContentList; } - - void SetActionProgressDialog(ActionProgressDialog* pActionProgress) { - fpActionProgress = pActionProgress; - } - void SetProgressCounterDialog(ProgressCounterDialog* pProgressCounter) { - fpProgressCounter = pProgressCounter; - } - GenericArchive* GetOpenArchive(void) const { return fpOpenArchive; } - - /* - * Extract every part of the file into "ReformatHolder". Does not try to - * reformat anything, just extracts the parts. - * - * Returns IDOK on success, IDCANCEL if the user cancelled, or -1 on error. - * On error, the reformatted text buffer gets the error message. - */ - int GetFileParts(const GenericEntry* pEntry, - ReformatHolder** ppHolder) const; - - /* - * Allow events to flow through the message queue whenever the - * progress meter gets updated. This will allow us to redraw with - * reasonable frequency. - * - * Calling this can result in other code being called, such as Windows - * message handlers, which can lead to reentrancy problems. Make sure - * you're adequately semaphored before calling here. - * - * Returns TRUE if all is well, FALSE if we're trying to quit. - */ - BOOL PeekAndPump(); - - /* - * After successful completion of a command, make a happy noise (but only - * if we're configured to do so). - */ - void SuccessBeep(void); - - /* - * If something fails, make noise if we're configured for loudness. - */ - void FailureBeep(void); - - /* - * Remove a file. Returns a helpful error string on failure. - * - * The absence of the file is not considered an error. - */ - CString RemoveFile(const WCHAR* fileName); - - /* - * Figure out where they want to add files. - * - * If the volume directory of a disk is chosen, *ppTargetSubdir will - * be set to NULL. - */ - bool ChooseAddTarget(DiskImgLib::A2File** ppTargetSubdir, - DiskImgLib::DiskFS** ppDiskFS); - - /* - * Put up the ImageFormatDialog and apply changes to "pImg". - * - * "*pDisplayFormat" gets the result of user changes to the display format. - * If "pDisplayFormat" is NULL, the "query image format" feature will be - * disabled. - * - * Returns IDCANCEL if the user cancelled out of the dialog, IDOK otherwise. - * On error, "*pErrMsg" will be non-empty. - */ - int TryDiskImgOverride(DiskImg* pImg, const WCHAR* fileSource, - DiskImg::FSFormat defaultFormat, int* pDisplayFormat, - bool allowUnknown, CString* pErrMsg); - - /* - * Do a block copy or track copy from one disk image to another. - * - * If "bulk" is set, warning dialogs are suppressed. If "partial" is set, - * copies between volumes of different sizes are allowed. - */ - DIError CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, - bool partial, ProgressCancelDialog* pPCDialog); - - // Determine whether path matches the pathname of the currently open archive. - bool IsOpenPathName(const WCHAR* path); - - // raise a flag to cause a full reload of the open file - void SetReopenFlag(void) { fNeedReopen = true; } - - /* - * Configure a ReformatHolder based on the current preferences. - */ - static void ConfigureReformatFromPreferences(ReformatHolder* pReformat); - - /* - * Convert a DiskImg format spec into a ReformatHolder SourceFormat. - */ - static ReformatHolder::SourceFormat ReformatterSourceFormat(DiskImg::FSFormat format); - - /* - * Saves a buffer of data as a file in a disk image or file archive. - * Utility function used by cassette import. - * - * May modify contents of *pDetails. - * - * On failure, returns with an error message in errMsg. - */ - static bool SaveToArchive(GenericArchive::LocalFileDetails* pDetails, - const uint8_t* dataBuf, long dataLen, - const uint8_t* rsrcBuf, long rsrcLen, - CString* pErrMsg, CWnd* pDialog); - - static const WCHAR kOpenNuFX[]; - static const WCHAR kOpenBinaryII[]; - static const WCHAR kOpenACU[]; - static const WCHAR kOpenAppleSingle[]; - static const WCHAR kOpenDiskImage[]; - static const WCHAR kOpenAll[]; - static const WCHAR kOpenEnd[]; - -private: - static const WCHAR kModeNuFX[]; - static const WCHAR kModeBinaryII[]; - static const WCHAR kModeACU[]; - static const WCHAR kModeAppleSingle[]; - static const WCHAR kModeDiskImage[]; - - // Command handlers - afx_msg int OnCreate(LPCREATESTRUCT lpcs); - afx_msg LONG OnLateInit(UINT, LONG); - afx_msg void OnSize(UINT nType, int cx, int cy); - afx_msg void OnGetMinMaxInfo(MINMAXINFO* pMMI); - afx_msg void OnPaint(void); - afx_msg void OnSetFocus(CWnd* pOldWnd); - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo); - afx_msg BOOL OnQueryEndSession(void); - afx_msg void OnEndSession(BOOL bEnding); - afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam); - afx_msg void OnFileNewArchive(void); - afx_msg void OnFileOpen(void); - afx_msg void OnFileOpenVolume(void); - afx_msg void OnUpdateFileOpenVolume(CCmdUI* pCmdUI); - afx_msg void OnFileReopen(void); - afx_msg void OnUpdateFileReopen(CCmdUI* pCmdUI); - afx_msg void OnFileSave(void); - afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI); - afx_msg void OnFileClose(void); - afx_msg void OnUpdateFileClose(CCmdUI* pCmdUI); - afx_msg void OnFileArchiveInfo(void); - afx_msg void OnUpdateFileArchiveInfo(CCmdUI* pCmdUI); - afx_msg void OnFilePrint(void); - afx_msg void OnUpdateFilePrint(CCmdUI* pCmdUI); - afx_msg void OnFileExit(void); - - /* - * Copy data to the clipboard. - */ - afx_msg void OnEditCopy(void); - afx_msg void OnUpdateEditCopy(CCmdUI* pCmdUI); - - /* - * Paste data from the clipboard, using the configured defaults. - */ - afx_msg void OnEditPaste(void); - afx_msg void OnUpdateEditPaste(CCmdUI* pCmdUI); - - /* - * Paste data from the clipboard, giving the user the opportunity to select - * how the files are handled. - */ - afx_msg void OnEditPasteSpecial(void); - afx_msg void OnUpdateEditPasteSpecial(CCmdUI* pCmdUI); - - afx_msg void OnEditFind(void); - afx_msg void OnUpdateEditFind(CCmdUI* pCmdUI); - afx_msg void OnEditSelectAll(void); - afx_msg void OnUpdateEditSelectAll(CCmdUI* pCmdUI); - afx_msg void OnEditInvertSelection(void); - afx_msg void OnUpdateEditInvertSelection(CCmdUI* pCmdUI); - afx_msg void OnEditPreferences(void); - afx_msg void OnEditSort(UINT id); - afx_msg void OnUpdateEditSort(CCmdUI* pCmdUI); - /* - * View a file stored in the archive. - * - * Control bounces back through Get*FileText() to get the actual - * data to view. - */ - afx_msg void OnActionsView(void); - afx_msg void OnUpdateActionsView(CCmdUI* pCmdUI); - - /* - * View a file stored in the archive. - * - * Control bounces back through Get*FileText() to get the actual - * data to view. - */ - afx_msg void OnActionsOpenAsDisk(void); - afx_msg void OnUpdateActionsOpenAsDisk(CCmdUI* pCmdUI); - - /* - * Add files to an archive. - */ - afx_msg void OnActionsAddFiles(void); - afx_msg void OnUpdateActionsAddFiles(CCmdUI* pCmdUI); - - /* - * Add a disk to an archive. Not all archive formats support disk images. - * - * We open a single disk archive file as a DiskImg, get the format - * figured out, then write it block-by-block into a file chosen by the user. - * Standard open/save dialogs work fine here. - */ - afx_msg void OnActionsAddDisks(void); - afx_msg void OnUpdateActionsAddDisks(CCmdUI* pCmdUI); - - /* - * Create a subdirectory inside another subdirectory (or volume directory). - * - * Simply asserting that an existing subdir be selected in the list does - * away with all sorts of testing. Creating subdirs on DOS disks and NuFX - * archives is impossible because neither has subdirs. Nested volumes are - * selected for us by the user. - */ - afx_msg void OnActionsCreateSubdir(void); - afx_msg void OnUpdateActionsCreateSubdir(CCmdUI* pCmdUI); - - /* - * Extract files. - */ - afx_msg void OnActionsExtract(void); - afx_msg void OnUpdateActionsExtract(CCmdUI* pCmdUI); - - /* - * Test files. - */ - afx_msg void OnActionsTest(void); - afx_msg void OnUpdateActionsTest(CCmdUI* pCmdUI); - - /* - * Delete archive entries. - */ - afx_msg void OnActionsDelete(void); - afx_msg void OnUpdateActionsDelete(CCmdUI* pCmdUI); - - /* - * Rename archive entries. Depending on the structure of the underlying - * archive, we may only allow the user to alter the filename component. - * Anything else would constitute moving the file around in the filesystem. - */ - afx_msg void OnActionsRename(void); - afx_msg void OnUpdateActionsRename(CCmdUI* pCmdUI); - - /* - * Edit a comment, creating it if necessary. - */ - afx_msg void OnActionsEditComment(void); - afx_msg void OnUpdateActionsEditComment(CCmdUI* pCmdUI); - - /* - * Edit file properties. - * - * This causes a reload of the list, which isn't really necessary. We - * do need to re-evaluate the sort order if one of the fields they modified - * is the current sort key, but it would be nice if we could at least retain - * the selection. Since we're not reloading the GenericArchive, we *can* - * remember the selection. - */ - afx_msg void OnActionsEditProps(void); - afx_msg void OnUpdateActionsEditProps(CCmdUI* pCmdUI); - - /* - * Change a volume name or volume number. - */ - afx_msg void OnActionsRenameVolume(void); - afx_msg void OnUpdateActionsRenameVolume(CCmdUI* pCmdUI); - - /* - * Recompress files. - */ - afx_msg void OnActionsRecompress(void); - afx_msg void OnUpdateActionsRecompress(CCmdUI* pCmdUI); - - /* - * Select files to convert. - */ - afx_msg void OnActionsConvDisk(void); - afx_msg void OnUpdateActionsConvDisk(CCmdUI* pCmdUI); - - /* - * Select files to convert. - */ - afx_msg void OnActionsConvFile(void); - afx_msg void OnUpdateActionsConvFile(CCmdUI* pCmdUI); - - /* - * Convert BAS, INT, or BIN to a cassette-audio WAV file. - * (not implemented) - */ - afx_msg void OnActionsConvToWav(void); - afx_msg void OnUpdateActionsConvToWav(CCmdUI* pCmdUI); - - /* - * Convert a WAV file with a digitized Apple II cassette tape into an - * Apple II file, and add it to the current disk. - */ - afx_msg void OnActionsConvFromWav(void); - afx_msg void OnUpdateActionsConvFromWav(CCmdUI* pCmdUI); - - /* - * Import an Applesoft BASIC program from a text file. - * - * We currently allow the user to select a single file for import. Someday - * we may want to allow multi-file import. - */ - afx_msg void OnActionsImportBAS(void); - afx_msg void OnUpdateActionsImportBAS(CCmdUI* pCmdUI); - - // edit a disk - afx_msg void OnToolsDiskEdit(void); - // convert a disk image from one format to another - afx_msg void OnToolsDiskConv(void); - // bulk disk conversion - afx_msg void OnToolsBulkDiskConv(void); - // merge two SST images into a single NIB image - afx_msg void OnToolsSSTMerge(void); - afx_msg void OnToolsVolumeCopierVolume(void); - afx_msg void OnToolsVolumeCopierFile(void); - // scan and report on the end-of-line markers found in a file - afx_msg void OnToolsEOLScanner(void); - // edit the properties (but not the disk image inside) a .2MG disk image - afx_msg void OnToolsTwoImgProps(void); - // create a new disk image - afx_msg void OnToolsDiskImageCreator(void); - afx_msg void OnHelpContents(void); - afx_msg void OnHelpWebSite(void); - afx_msg void OnHelpOrdering(void); - afx_msg void OnHelpAbout(void); - afx_msg void OnRtClkDefault(void); - - // Handle command-line arguments. - void ProcessCommandLine(void); - - void ResizeClientArea(void); - - /* - * Draw what looks like an empty client area. - */ - void DrawEmptyClientArea(CDC* pDC, const CRect& clientRect); - - /* - * Extract a record to the temp folder and open it with a new instance of - * CiderPress. We might want to extract disk images as 2MG files to take - * the mystery out of opening them, but since they're coming out of a - * ShrinkIt archive they're pretty un-mysterious anyway. - * - * We tell the new instance to open it read-only, and flag it for - * deletion on exit. - * - * Returns 0 on success, nonzero error status on failure. - */ - int TmpExtractAndOpen(GenericEntry* pEntry, int threadKind, - const WCHAR* modeStr); - - /* - * Extract a record to the temp folder and open it with an external viewer. - * The file must be created with the correct extension so ShellExecute - * does the right thing. - * - * The files will be added to the "delete on exit" list, so that they will - * be cleaned up when CiderPress exits (assuming the external viewer no longer - * has them open). - * - * The GetTempFileName function creates a uniquely-named temp file. We - * create a file that has that name plus an extension. To ensure that we - * don't try to use the same temp filename twice, we have to hold off on - * deleting the unused .tmp files until we're ready to delete the - * corresponding .gif (or whatever) files. Thus, each invocation of this - * function creates two files and two entries in the delete-on-exit set. - * - * Returns 0 on success, nonzero error status on failure. - */ - int TmpExtractForExternal(GenericEntry* pEntry); - - void DoOpenArchive(const WCHAR* pathName, const WCHAR* ext, - FilterIndex filterIndex, bool readOnly); - - GenericArchive* CreateArchiveInstance(FilterIndex filterIndex) const; - - /* - * Load an archive, using the appropriate GenericArchive subclass. - * - * "filename" is the full path to the file, "extension" is the - * filetype component of the name (without the leading '.'), "filterIndex" - * is the offset into the set of filename filters used in the standard - * file dialog, and "readOnly" reflects the state of the stdfile dialog - * checkbox. - * - * Returns 0 on success, nonzero on failure. - */ - int LoadArchive(const WCHAR* filename, const WCHAR* extension, - FilterIndex filterIndex, bool readOnly); - - /* - * Open a raw disk volume. Useful for ProDOS-formatted 1.44MB floppy disks - * and CFFA flash cards. - * - * Assume it's a disk image -- it'd be a weird place for a ShrinkIt archive. - * CFFA cards can actually hold multiple volumes, but that's all taken care - * of inside the diskimg DLL. - * - * Returns 0 on success, nonzero on failure. - */ - int DoOpenVolume(CString drive, bool readOnly); - - /* - * Switch the content list to a new archive, closing the previous if one - * was already open. - */ - void SwitchContentList(GenericArchive* pOpenArchive); - - /* - * Close the existing archive file, but don't try to shut down the child - * windows. This should only be used from the destructor. - */ - void CloseArchiveWOControls(void); - - /* - * Close the existing archive file, and throw out the control we're - * using to display it. - */ - void CloseArchive(void); - - /* - * Set the title bar on the main window. - * - * "pathname" is often different from pOpenArchive->GetPathName(), especially - * when we were launched from another instance of CiderPress and handed a - * temp file whose name we're trying to conceal. - */ - void SetCPTitle(const WCHAR* pathname, GenericArchive* pArchive); - - /* - * Set the title bar to something boring when nothing is open. - */ - void SetCPTitle(void); - - /* - * Get the one selected item from the current display. Primarily useful - * for the double-click handler, but also used for "action" menu items - * that insist on operating on a single menu item (edit prefs, create subdir). - * - * Returns NULL if the item couldn't be found or if more than one item was - * selected. - */ - GenericEntry* GetSelectedItem(ContentList* pContentList); - - /* - * Handle a request to view stuff. - * - * If "query" pref is set, we ask the user to confirm some choices. If - * not, we just go with the defaults. - * - * We include "damaged" files so that we can show the user a nice message - * about how the file is damaged. - */ - void HandleView(void); - - void DeleteFileOnExit(const WCHAR* name); - - /* - * Close and re-open the current archive. - */ - void ReopenArchive(void); - - /* - * ===== Actions.cpp ===== - */ - - /* - * Load the requested part. - */ - void GetFilePart(const GenericEntry* pEntry, int whichThread, - ReformatHolder* pHolder) const; - - /* - * Handle a bulk extraction. - */ - void DoBulkExtract(SelectionSet* pSelSet, - const ExtractOptionsDialog* pExtOpts); - - /* - * Extract a single entry. - * - * If pHolder is non-NULL, it holds the data from the file, and can be - * used for formatted or non-formatted output. If it's NULL, we need to - * extract the data ourselves. - * - * Returns true on success, false on failure. - */ - bool ExtractEntry(GenericEntry* pEntry, int thread, - ReformatHolder* pHolder, const ExtractOptionsDialog* pExtOpts, - bool* pOverwriteExisting, bool* pOvwrForAll); - - /* - * Open an output file. - * - * "outputPath" holds the name of the file to create. "origPath" is the - * name as it was stored in the archive. "pOverwriteExisting" tells us - * if we should just go ahead and overwrite the existing file, while - * "pOvwrForAll" tells us if a "To All" button was hit previously. - * - * If the file exists, *pOverwriteExisting is false, and *pOvwrForAll - * is false, then we will put up the "do you want to overwrite?" dialog. - * One possible outcome of the dialog is renaming the output path. - * - * On success, *pFp will be non-NULL, and IDOK will be returned. On - * failure, IDCANCEL will be returned. The values in *pOverwriteExisting - * and *pOvwrForAll may be updated, and *pOutputPath will change if - * the user chose to rename the file. - */ - int OpenOutputFile(CString* pOutputPath, const PathProposal& pathProp, - time_t arcFileModWhen, bool* pOverwriteExisting, bool* pOvwrForAll, - FILE** pFp); - - bool DoBulkRecompress(ActionProgressDialog* pActionProgress, - SelectionSet* pSelSet, const RecompressOptionsDialog* pRecompOpts); - - /* - * Compute the total size of all files in the GenericArchive. - */ - void CalcTotalSize(LONGLONG* pUncomp, LONGLONG* pComp) const; - - /* - * Print a ContentList. - */ - void PrintListing(const ContentList* pContentList); - - /* - * ===== Clipboard.cpp ===== - */ - - /* - * Create a list of selected files. - * - * The returned string uses tab-delineated fields with CSV-style quoting - * around the filename (so that double quotes in the filename don't confuse - * applications like MS Excel). - */ - CString CreateFileList(SelectionSet* pSelSet); - - /* - * Double-up all double quotes. - */ - static CString DblDblQuote(const WCHAR* str); - - /* - * Compute the size of everything currently on the clipboard. - */ - long GetClipboardContentLen(void); - - /* - * Create the file collection. - */ - HGLOBAL CreateFileCollection(SelectionSet* pSelSet); - - /* - * Copy the contents of the file referred to by "pEntry" into the buffer - * "*pBuf", which has "*pBufLen" bytes in it. - * - * The call fails if "*pBufLen" isn't large enough. - * - * Returns an empty string on success, or an error message on failure. - * On success, "*pBuf" will be advanced past the data added, and "*pBufLen" - * will be reduced by the amount of data copied into "buf". - */ - CString CopyToCollection(GenericEntry* pEntry, void** pBuf, long* pBufLen); - - /* - * Do some prep work and then call ProcessClipboard to copy files in. - */ - void DoPaste(bool pasteJunkPaths); - - /* - * Process the data in the clipboard. - * - * Returns an empty string on success, or an error message on failure. - */ - CString ProcessClipboard(const void* vbuf, long bufLen, - bool pasteJunkPaths); - - /* - * Process a single clipboard entry. - * - * On entry, "buf" points to the start of the first chunk of data (either - * data fork or resource fork). If the file has empty forks or is a - * subdirectory, then "buf" is actually pointing at the start of the - * next entry. - */ - CString ProcessClipboardEntry(const FileCollectionEntry* pCollEnt, - const WCHAR* pathName, const uint8_t* buf, long remLen); - - /* - * ===== Tools.cpp ===== - */ - - /* - * Determines the settings we need to pass into DiskImgLib to create the - * desired disk image format. - * - * Returns 0 on success, -1 on failure. - */ - int DetermineImageSettings(int convertIdx, bool addGzip, - DiskImg::OuterFormat* pOuterFormat, DiskImg::FileFormat* pFileFormat, - DiskImg::PhysicalFormat* pPhysicalFormat, - DiskImg::SectorOrder* pSectorOrder); - - /* - * Converts one image during a bulk conversion. - * - * On failure, the reason for failure is stuffed into "*pErrMsg". - */ - void BulkConvertImage(const WCHAR* pathName, const WCHAR* targetDir, - const DiskConvertDialog& convDlg, CString* pErrMsg); - - /* - * Opens one of the SST images. Configures "pDiskImg" appropriately. - * - * Returns 0 on success, nonzero on failure. - */ - int SSTOpenImage(int seqNum, DiskImg* pDiskImg); - - /* - * Copies 17.5 tracks of data from the SST image to a .NIB image. - * - * Data is stored in all 16 sectors of track 0, followed by the first - * 12 sectors of track 1, then on to track 2. Total of $1a00 bytes. - * - * Returns 0 on success, -1 on failure. - */ - int SSTLoadData(int seqNum, DiskImg* pDiskImg, uint8_t* trackBuf, - long* pBadCount); - - /* - * Compute the destination file offset for a particular source track. The - * track number ranges from 0 to 69 inclusive. Sectors from two adjacent - * "cooked" tracks are combined into a single "raw nibbilized" track. - * - * The data is ordered like this: - * track 1 sector 15 --> track 1 sector 4 (12 sectors) - * track 0 sector 13 --> track 0 sector 0 (14 sectors) - * - * Total of 26 sectors, or $1a00 bytes. - */ - long SSTGetBufOffset(int track); - - /* - * Count the number of "bad" bytes in the sector. - * - * Strictly speaking, a "bad" byte is anything that doesn't appear in the - * 6&2 decoding table, 5&3 decoding table, special list (D5, AA), and - * can't be used as a 4+4 encoding value. - * - * We just use $80 - $92, which qualify for all of the above. - */ - long SSTCountBadBytes(const uint8_t* sctBuf, int count); - - /* - * Run through the data, adding 0x80 everywhere and re-aligning the - * tracks so that the big clump of sync bytes is at the end. - */ - void SSTProcessTrackData(uint8_t* trackBuf); - - /* - * Select a volume and then invoke the volcopy dialog. - */ - void VolumeCopier(bool openFile); - - /* - * Edit the properties of a 2MG file. - * - * Returns "true" if the file was modified, "false" if not. - */ - bool EditTwoImgProps(const WCHAR* fileName); - - - // set when one of the tools modifies the file we have open - bool fNeedReopen; - - CToolBar fToolBar; - CStatusBar fStatusBar; - - // currently-open archive, if any - GenericArchive* fpOpenArchive; - // name of open archive, for display only -- if this is a temporary - // file launched from another instance of CP, this won't be the name - // of an actual file on disk. - CString fOpenArchivePathName; // for display only - - // archive viewer, open when file is open - // NOTE: make a super-class for a tree-structured display or other - // kinds of display, so we can avoid the if/then/else. Rename - // ContentList to DetailList or FlatList or something. - ContentList* fpContentList; - - // currently selected set of goodies; used when viewing, extracting, etc. - //SelectionSet* fpSelSet; - - // action progress meter, if any - ActionProgressDialog* fpActionProgress; - - // progress counter meter, if any - ProgressCounterDialog* fpProgressCounter; - - // modeless standard "find" dialog - CFindReplaceDialog* fpFindDialog; - CString fFindLastStr; - bool fFindDown; - bool fFindMatchCase; - bool fFindMatchWholeWord; - - // our preferences - Preferences fPreferences; - - /* - * Manage a list of files that must be deleted before we exit. - */ - class DeleteList { - private: - class DeleteListNode { - public: - DeleteListNode(const CString& name) : fName(name), - fPrev(NULL), fNext(NULL) {} - ~DeleteListNode(void) {} - - DeleteListNode* fPrev; - DeleteListNode* fNext; - CString fName; - }; - - public: - DeleteList(void) { fHead = NULL; } - ~DeleteList(void) { - LOGD("Processing DeleteList (head=0x%p)", fHead); - DeleteListNode* pNode = fHead; - DeleteListNode* pNext; - - while (pNode != NULL) { - pNext = pNode->fNext; - if (_wunlink(pNode->fName) != 0) { - LOGW(" WARNING: delete of '%ls' failed, err=%d", - (LPCWSTR) pNode->fName, errno); - } else { - LOGI(" Deleted '%ls'", (LPCWSTR) pNode->fName); - } - delete pNode; - pNode = pNext; - } - LOGD("Processing DeleteList completed"); - } - - void Add(const CString& name) { - DeleteListNode* pNode = new DeleteListNode(name); - if (fHead != NULL) { - fHead->fPrev = pNode; - pNode->fNext = fHead; - } - fHead = pNode; - LOGI("Delete-on-exit '%ls'", (LPCWSTR) name); - } - - DeleteListNode* fHead; - }; - DeleteList fDeleteList; - - DECLARE_MESSAGE_MAP() -}; - -#define GET_MAIN_WINDOW() ((MainWindow*)::AfxGetMainWnd()) - -#define SET_PROGRESS_BEGIN() ((MainWindow*)::AfxGetMainWnd())->SetProgressBegin() -#define SET_PROGRESS_UPDATE(perc) \ - ((MainWindow*)::AfxGetMainWnd())->SetProgressUpdate(perc, NULL, NULL) -#define SET_PROGRESS_UPDATE2(perc, oldName, newName) \ - ((MainWindow*)::AfxGetMainWnd())->SetProgressUpdate(perc, oldName, newName) -#define SET_PROGRESS_END() ((MainWindow*)::AfxGetMainWnd())->SetProgressEnd() - -#define SET_PROGRESS_COUNTER(val) \ - ((MainWindow*)::AfxGetMainWnd())->SetProgressCounter(NULL, val) -#define SET_PROGRESS_COUNTER_2(fmt, val) \ - ((MainWindow*)::AfxGetMainWnd())->SetProgressCounter(fmt, val) - -#define GET_PREFERENCES() ((MainWindow*)::AfxGetMainWnd())->GetPreferences() -#define GET_PREFERENCES_WR() ((MainWindow*)::AfxGetMainWnd())->GetPreferencesWr() - -#endif /*APP_MAIN_H*/ diff --git a/ciderpress/app/MyApp.cpp b/ciderpress/app/MyApp.cpp deleted file mode 100644 index 87dac70..0000000 --- a/ciderpress/app/MyApp.cpp +++ /dev/null @@ -1,530 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * The application object. - */ -#include "stdafx.h" -#include "../util/UtilLib.h" -#include "MyApp.h" -#include "Registry.h" -#include "Main.h" -#include "DiskArchive.h" -#include "Help/PopUpIds.h" - -/* magic global that MFC finds (or that finds MFC) */ -MyApp gMyApp; - -/* used for debug logging */ -DebugLog* gDebugLog = NULL; - - -/* - * This is the closest thing to "main" that we have, but we - * should wait for InitInstance for most things. - */ -MyApp::MyApp() : CWinAppEx() -{ -#ifdef _DEBUG - // TODO: make this a setting, rather than a debug-build-only feature - gDebugLog = new DebugLog(L"C:\\src\\cplog.txt"); -#endif - - time_t now = time(NULL); - - LOGI("CiderPress v%d.%d.%d%ls started at %.24hs", - kAppMajorVersion, kAppMinorVersion, kAppBugVersion, - kAppDevString, ctime(&now)); - -#ifdef _DEBUG - int tmpDbgFlag; - // enable memory leak detection - tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; - _CrtSetDbgFlag(tmpDbgFlag); - LOGI("Leak detection enabled"); -#endif - - EnableHtmlHelp(); -} - -/* - * This is the last point of control we have. - */ -MyApp::~MyApp(void) -{ - DiskArchive::AppCleanup(); - NiftyList::AppCleanup(); - - LOGI("SHUTTING DOWN\n"); - delete gDebugLog; -} - -BOOL MyApp::InitInstance(void) -{ - // Create the main window. - m_pMainWnd = new MainWindow; - m_pMainWnd->ShowWindow(m_nCmdShow); - m_pMainWnd->UpdateWindow(); - - LOGD("Happily in InitInstance!"); - - /* find our .EXE file */ - //HMODULE hModule = ::GetModuleHandle(NULL); - WCHAR buf[MAX_PATH]; - if (::GetModuleFileName(NULL /*hModule*/, buf, NELEM(buf)) != 0) { - LOGD("Module name is '%ls'", buf); - fExeFileName = buf; - - WCHAR* cp = wcsrchr(buf, '\\'); - if (cp == NULL) - fExeBaseName = L""; - else - fExeBaseName = fExeFileName.Left(cp - buf +1); - } else { - LOGW("GLITCH: GetModuleFileName failed (err=%ld)", ::GetLastError()); - } - - LogModuleLocation(L"riched.dll"); - LogModuleLocation(L"riched20.dll"); - LogModuleLocation(L"riched32.dll"); - LogModuleLocation(L"msftedit.dll"); - - // This causes functions like SetProfileInt to use the registry rather - // than a .INI file. The registry key is "usually the name of a company". -#ifdef CAN_UPDATE_FILE_ASSOC - SetRegistryKey(fRegistry.GetAppRegistryKey()); -#else - SetRegistryKey(L"faddenSoft"); -#endif - - //LOGI("Registry key is '%ls'", m_pszRegistryKey); - //LOGI("Profile name is '%ls'", m_pszProfileName); - LOGI("Short command line is '%ls'", m_lpCmdLine); - //LOGI("CP app name is '%ls'", m_pszAppName); - //LOGI("CP exe name is '%ls'", m_pszExeName); - LOGI("CP help file is '%ls'", m_pszHelpFilePath); - LOGI("Command line is '%ls'", ::GetCommandLine()); - - //if (!WriteProfileString("SectionOne", "MyEntry", "test")) - // LOGI("WriteProfileString failed"); - -#ifdef CAN_UPDATE_FILE_ASSOC - /* - * If we're installing or uninstalling, do what we need to and then - * bail immediately. This will hemorrhage memory, but I'm sure the - * incredibly robust Windows environment will take it in stride. - */ - if (wcscmp(m_lpCmdLine, L"-install") == 0) { - LOGI("Invoked with INSTALL flag"); - fRegistry.OneTimeInstall(); - ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); - exit(0); - } else if (wcscmp(m_lpCmdLine, L"-uninstall") == 0) { - LOGI("Invoked with UNINSTALL flag"); - fRegistry.OneTimeUninstall(); - ::SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); - exit(1); // tell DeployMaster to continue with uninstall - } - - fRegistry.FixBasicSettings(); -#endif - - return TRUE; -} - -void MyApp::LogModuleLocation(const WCHAR* name) -{ - HMODULE hModule; - WCHAR fileNameBuf[256]; - hModule = ::GetModuleHandle(name); - if (hModule != NULL && - ::GetModuleFileName(hModule, fileNameBuf, NELEM(fileNameBuf)) != 0) - { - // GetModuleHandle does not increase ref count, so no need to release - LOGI("Module '%ls' loaded from '%ls'", name, fileNameBuf); - } else { - LOGI("Module '%ls' not loaded", name); - } -} - -BOOL MyApp::OnIdle(LONG lCount) -{ - BOOL bMore = CWinApp::OnIdle(lCount); - - //if (lCount == 0) { - // LOGI("IDLE lcount=%d", lCount); - //} - - /* - * If MFC is done, we take a swing. - */ - if (bMore == false) { - /* downcast */ - ((MainWindow*)m_pMainWnd)->DoIdle(); - } - - return bMore; -} - -// TODO: figure out why we have help topics without matching control ID constants -/*static*/ const DWORD MyApp::PopUpHelpIds[] = { - IDOK, IDH_IDOK, - IDCANCEL, IDH_IDCANCEL, - IDHELP, IDH_IDHELP, - IDC_NUFXLIB_VERS_TEXT, IDH_NUFXLIB_VERS_TEXT, - IDC_CONTENT_LIST, IDH_CONTENT_LIST, - IDC_COL_PATHNAME, IDH_COL_PATHNAME, - IDC_COL_TYPE, IDH_COL_TYPE, - IDC_COL_AUXTYPE, IDH_COL_AUXTYPE, - IDC_COL_MODDATE, IDH_COL_MODDATE, - IDC_COL_FORMAT, IDH_COL_FORMAT, - IDC_COL_SIZE, IDH_COL_SIZE, - IDC_COL_RATIO, IDH_COL_RATIO, - IDC_COL_PACKED, IDH_COL_PACKED, - IDC_COL_ACCESS, IDH_COL_ACCESS, - IDC_COL_DEFAULTS, IDH_COL_DEFAULTS, - IDC_DEFC_UNCOMPRESSED, IDH_DEFC_UNCOMPRESSED, - IDC_DEFC_SQUEEZE, IDH_DEFC_SQUEEZE, - IDC_DEFC_LZW1, IDH_DEFC_LZW1, - IDC_DEFC_LZW2, IDH_DEFC_LZW2, - IDC_DEFC_LZC12, IDH_DEFC_LZC12, - IDC_DEFC_LZC16, IDH_DEFC_LZC16, - IDC_DEFC_DEFLATE, IDH_DEFC_DEFLATE, - IDC_DEFC_BZIP2, IDH_DEFC_BZIP2, - 1024, IDH_TOPIC1024, - IDC_PVIEW_NOWRAP_TEXT, IDH_PVIEW_NOWRAP_TEXT, - IDC_PVIEW_BOLD_HEXDUMP, IDH_PVIEW_BOLD_HEXDUMP, - IDC_PVIEW_BOLD_BASIC, IDH_PVIEW_BOLD_BASIC, - IDC_PVIEW_DISASM_ONEBYTEBRKCOP, IDH_PVIEW_DISASM_ONEBYTEBRKCOP, - IDC_PVIEW_HIRES_BW, IDH_PVIEW_HIRES_BW, - IDC_PVIEW_DHR_CONV_COMBO, IDH_PVIEW_DHR_CONV_COMBO, - IDC_PVIEW_MOUSETEXT_TO_ASCII, IDH_PVIEW_MOUSETEXT_TO_ASCII, - IDC_PVIEW_HITEXT, IDH_PVIEW_HITEXT, - IDC_PVIEW_PASCALTEXT, IDH_PVIEW_PASCALTEXT, - IDC_PVIEW_APPLESOFT, IDH_PVIEW_APPLESOFT, - IDC_PVIEW_INTEGER, IDH_PVIEW_INTEGER, - IDC_PVIEW_HIRES, IDH_PVIEW_HIRES, - IDC_PVIEW_DHR, IDH_PVIEW_DHR, - IDC_PVIEW_SHR, IDH_PVIEW_SHR, - IDC_PVIEW_AWP, IDH_PVIEW_AWP, - IDC_PVIEW_PRODOSFOLDER, IDH_PVIEW_PRODOSFOLDER, - IDC_PVIEW_RESOURCES, IDH_PVIEW_RESOURCES, - IDC_PVIEW_RELAX_GFX, IDH_PVIEW_RELAX_GFX, - IDC_PVIEW_ADB, IDH_PVIEW_ADB, - IDC_PVIEW_SCASSEM, IDH_PVIEW_SCASSEM, - IDC_PVIEW_ASP, IDH_PVIEW_ASP, - IDC_PVIEW_MACPAINT, IDH_PVIEW_MACPAINT, - IDC_PVIEW_PASCALCODE, IDH_PVIEW_PASCALCODE, - IDC_PVIEW_CPMTEXT, IDH_PVIEW_CPMTEXT, - IDC_PVIEW_GWP, IDH_PVIEW_GWP, - IDC_PVIEW_DISASM, IDH_PVIEW_DISASM, - IDC_PVIEW_PRINTSHOP, IDH_PVIEW_PRINTSHOP, - IDC_PVIEW_TEXT8, IDH_PVIEW_TEXT8, - IDC_PVIEW_SIZE_EDIT, IDH_PVIEW_SIZE_EDIT, - IDC_PVIEW_SIZE_SPIN, IDH_PVIEW_SIZE_SPIN, - IDC_DISKEDIT_DOREAD, IDH_DISKEDIT_DOREAD, - IDC_DISKEDIT_DOWRITE, IDH_DISKEDIT_DOWRITE, - IDC_DISKEDIT_TRACK, IDH_DISKEDIT_TRACK, - IDC_DISKEDIT_TRACKSPIN, IDH_DISKEDIT_TRACK, // remapped - IDC_DISKEDIT_SECTOR, IDH_DISKEDIT_TRACK, // remapped - IDC_DISKEDIT_SECTORSPIN, IDH_DISKEDIT_TRACK, // remapped - IDC_DISKEDIT_OPENFILE, IDH_DISKEDIT_OPENFILE, - IDC_DISKEDIT_EDIT, IDH_DISKEDIT_EDIT, - IDC_DISKEDIT_PREV, IDH_DISKEDIT_PREV, - IDC_DISKEDIT_NEXT, IDH_DISKEDIT_NEXT, - IDC_STEXT_SECTOR, IDH_DISKEDIT_TRACK, // remapped - IDC_STEXT_TRACK, IDH_DISKEDIT_TRACK, // remapped - IDC_DISKEDIT_DONE, IDH_DISKEDIT_DONE, - IDC_DISKEDIT_HEX, IDH_DISKEDIT_HEX, - IDC_DISKEDIT_SUBVOLUME, IDH_DISKEDIT_SUBVOLUME, - 1082, IDH_TOPIC1082, - 1089, IDH_TOPIC1089, - IDC_DECONF_FSFORMAT, IDH_DECONF_FSFORMAT, - IDC_DECONF_SECTORORDER, IDH_DECONF_SECTORORDER, - IDC_DECONF_PHYSICAL, IDH_DECONF_PHYSICAL, - IDC_DECONF_FILEFORMAT, IDH_DECONF_FILEFORMAT, - IDC_DECONF_SOURCE, IDH_DECONF_SOURCE, - IDC_DISKIMG_VERS_TEXT, IDH_DISKIMG_VERS_TEXT, - IDC_FVIEW_EDITBOX, IDH_FVIEW_EDITBOX, - IDC_SELECTED_COUNT, IDH_SELECTED_COUNT, - 1103, IDH_TOPIC1103, - 1105, IDH_TOPIC1105, - IDC_DECONF_HELP, IDH_DECONF_HELP, - IDC_SUBV_LIST, IDH_SUBV_LIST, - IDC_DEFILE_FILENAME, IDH_DEFILE_FILENAME, - IDC_DEFILE_RSRC, IDH_DEFILE_RSRC, - IDC_CIDERPRESS_VERS_TEXT, IDH_CIDERPRESS_VERS_TEXT, - IDC_PREF_TEMP_FOLDER, IDH_PREF_TEMP_FOLDER, - //IDC_CHOOSEDIR_TREE, IDH_CHOOSEDIR_TREE, // dialog removed - //IDC_CHOOSEDIR_PATHEDIT, IDH_CHOOSEDIR_PATHEDIT, - //IDC_CHOOSEDIR_EXPAND_TREE, IDH_CHOOSEDIR_EXPAND_TREE, - //IDC_CHOOSEDIR_PATH, IDH_CHOOSEDIR_PATH, - //IDC_CHOOSEDIR_NEW_FOLDER, IDH_CHOOSEDIR_NEW_FOLDER, - IDC_PREF_CHOOSE_TEMP_FOLDER, IDH_PREF_CHOOSE_TEMP_FOLDER, - IDC_FVIEW_FONT, IDH_FVIEW_FONT, - IDC_FVIEW_NEXT, IDH_FVIEW_NEXT, - IDC_FVIEW_PREV, IDH_FVIEW_PREV, - //IDC_NEWFOLDER_CURDIR, IDH_NEWFOLDER_CURDIR, // dialog removed - //IDC_NEWFOLDER_NAME, IDH_NEWFOLDER_NAME, - IDC_EXT_PATH, IDH_EXT_PATH, - IDC_EXT_CONVEOLTEXT, IDH_EXT_CONVEOLTEXT, - IDC_EXT_CONVEOLALL, IDH_EXT_CONVEOLALL, - IDC_EXT_STRIP_FOLDER, IDH_EXT_STRIP_FOLDER, - IDC_EXT_OVERWRITE_EXIST, IDH_EXT_OVERWRITE_EXIST, - IDC_EXT_SELECTED, IDH_EXT_SELECTED, - IDC_EXT_ALL, IDH_EXT_ALL, - IDC_EXT_REFORMAT, IDH_EXT_REFORMAT, - IDC_EXT_DATAFORK, IDH_EXT_DATAFORK, - IDC_EXT_RSRCFORK, IDH_EXT_RSRCFORK, - IDC_EXT_CONVEOLNONE, IDH_EXT_CONVEOLNONE, - IDC_EXT_CHOOSE_FOLDER, IDH_EXT_CHOOSE_FOLDER, - IDC_PROG_ARC_NAME, IDH_PROG_ARC_NAME, - IDC_PROG_FILE_NAME, IDH_PROG_FILE_NAME, - IDC_PROG_VERB, IDH_PROG_VERB, - IDC_PROG_TOFROM, IDH_PROG_TOFROM, - IDC_PROG_PROGRESS, IDH_PROG_PROGRESS, - IDC_OVWR_YES, IDH_OVWR_YES, - IDC_OVWR_YESALL, IDH_OVWR_YESALL, - IDC_OVWR_NO, IDH_OVWR_NO, - IDC_OVWR_NOALL, IDH_OVWR_NOALL, - IDC_OVWR_NEW_INFO, IDH_OVWR_NEW_INFO, - IDC_OVWR_RENAME, IDH_OVWR_RENAME, - IDC_OVWR_EXIST_NAME, IDH_OVWR_EXIST_NAME, - IDC_OVWR_EXIST_INFO, IDH_OVWR_EXIST_INFO, - IDC_OVWR_NEW_NAME, IDH_OVWR_NEW_NAME, - IDC_RENOVWR_SOURCE_NAME, IDH_RENOVWR_SOURCE_NAME, - IDC_RENOVWR_ORIG_NAME, IDH_RENOVWR_ORIG_NAME, - IDC_RENOVWR_NEW_NAME, IDH_RENOVWR_NEW_NAME, - IDC_SELECT_ACCEPT, IDH_SELECT_ACCEPT, - IDC_ADDFILES_PREFIX, IDH_ADDFILES_PREFIX, - IDC_ADDFILES_INCLUDE_SUBFOLDERS, IDH_ADDFILES_INCLUDE_SUBFOLDERS, - IDC_ADDFILES_STRIP_FOLDER, IDH_ADDFILES_STRIP_FOLDER, - IDC_ADDFILES_NOPRESERVE, IDH_ADDFILES_NOPRESERVE, - IDC_ADDFILES_PRESERVE, IDH_ADDFILES_PRESERVE, - IDC_ADDFILES_PRESERVEPLUS, IDH_ADDFILES_PRESERVEPLUS, - IDC_ADDFILES_STATIC1, IDH_ADDFILES_STATIC1, - IDC_ADDFILES_STATIC2, IDH_ADDFILES_STATIC2, - IDC_ADDFILES_STATIC3, IDH_ADDFILES_STATIC3, - IDC_ADDFILES_OVERWRITE, IDH_ADDFILES_OVERWRITE, - IDC_PREF_SHRINKIT_COMPAT, IDH_PREF_SHRINKIT_COMPAT, - IDC_USE_SELECTED, IDH_USE_SELECTED, - IDC_USE_ALL, IDH_USE_ALL, - IDC_RENAME_OLD, IDH_RENAME_OLD, - IDC_RENAME_NEW, IDH_RENAME_NEW, - IDC_RENAME_PATHSEP, IDH_RENAME_PATHSEP, - IDC_COMMENT_EDIT, IDH_COMMENT_EDIT, - IDC_COMMENT_DELETE, IDH_COMMENT_DELETE, - IDC_RECOMP_COMP, IDH_RECOMP_COMP, - IDC_PREF_ASSOCIATIONS, IDH_PREF_ASSOCIATIONS, - IDC_ASSOCIATION_LIST, IDH_ASSOCIATION_LIST, - IDC_REG_COMPANY_NAME, IDH_REG_COMPANY_NAME, - IDC_REG_EXPIRES, IDH_REG_EXPIRES, - IDC_ABOUT_ENTER_REG, IDH_ABOUT_ENTER_REG, - IDC_REGENTER_USER, IDH_REGENTER_USER, - IDC_REGENTER_COMPANY, IDH_REGENTER_COMPANY, - IDC_REGENTER_REG, IDH_REGENTER_REG, - IDC_REG_USER_NAME, IDH_REG_USER_NAME, - IDC_ZLIB_VERS_TEXT, IDH_ZLIB_VERS_TEXT, - IDC_EXT_CONVHIGHASCII, IDH_EXT_CONVHIGHASCII, - IDC_EXT_DISKIMAGE, IDH_EXT_DISKIMAGE, - IDC_EXT_DISK_2MG, IDH_EXT_DISK_2MG, - IDC_EXT_ADD_PRESERVE, IDH_EXT_ADD_PRESERVE, - IDC_EXT_ADD_EXTEN, IDH_EXT_ADD_EXTEN, - IDC_EXT_CONFIG_PRESERVE, IDH_EXT_CONFIG_PRESERVE, - IDC_EXT_CONFIG_CONVERT, IDH_EXT_CONFIG_CONVERT, - IDC_PREF_COERCE_DOS, IDH_PREF_COERCE_DOS, - IDC_PREF_SPACES_TO_UNDER, IDH_PREF_SPACES_TO_UNDER, - IDC_REGENTER_USERCRC, IDH_REGENTER_USERCRC, - IDC_REGENTER_COMPCRC, IDH_REGENTER_COMPCRC, - IDC_REGENTER_REGCRC, IDH_REGENTER_REGCRC, - IDC_RENAME_SKIP, IDH_RENAME_SKIP, - IDC_DECONF_VIEWASBLOCKS, IDH_DECONF_VIEWASBLOCKS, - IDC_DECONF_VIEWASSECTORS, IDH_DECONF_VIEWASSECTORS, - IDC_DECONF_VIEWASNIBBLES, IDH_DECONF_VIEWASNIBBLES, - IDC_DECONF_OUTERFORMAT, IDH_DECONF_OUTERFORMAT, - IDC_DECONF_VIEWAS, IDH_DECONF_VIEWAS, - IDC_IMAGE_TYPE, IDH_IMAGE_TYPE, - IDC_DISKCONV_DOS, IDH_DISKCONV_DOS, - IDC_DISKCONV_DOS2MG, IDH_DISKCONV_DOS2MG, - IDC_DISKCONV_PRODOS, IDH_DISKCONV_PRODOS, - IDC_DISKCONV_PRODOS2MG, IDH_DISKCONV_PRODOS2MG, - IDC_DISKCONV_NIB, IDH_DISKCONV_NIB, - IDC_DISKCONV_NIB2MG, IDH_DISKCONV_NIB2MG, - IDC_DISKCONV_D13, IDH_DISKCONV_D13, - IDC_DISKCONV_DC42, IDH_DISKCONV_DC42, - IDC_DISKCONV_SDK, IDH_DISKCONV_SDK, - IDC_DISKCONV_TRACKSTAR, IDH_DISKCONV_TRACKSTAR, - IDC_DISKCONV_HDV, IDH_DISKCONV_HDV, - IDC_DISKCONV_DDD, IDH_DISKCONV_DDD, - IDC_DISKCONV_GZIP, IDH_DISKCONV_GZIP, - IDC_DISKEDIT_NIBBLE_PARMS, IDH_DISKEDIT_NIBBLE_PARMS, - IDC_PROPS_PATHNAME, IDH_PROPS_PATHNAME, - IDC_PROPS_FILETYPE, IDH_PROPS_FILETYPE, - IDC_PROPS_AUXTYPE, IDH_PROPS_AUXTYPE, - IDC_PROPS_ACCESS_R, IDH_PROPS_ACCESS_R, - IDC_PROPS_ACCESS_W, IDH_PROPS_ACCESS_W, - IDC_PROPS_ACCESS_N, IDH_PROPS_ACCESS_N, - IDC_PROPS_ACCESS_D, IDH_PROPS_ACCESS_D, - IDC_PROPS_ACCESS_I, IDH_PROPS_ACCESS_I, - IDC_PROPS_ACCESS_B, IDH_PROPS_ACCESS_B, - IDC_PROPS_MODWHEN, IDH_PROPS_MODWHEN, - IDC_PROPS_TYPEDESCR, IDH_PROPS_TYPEDESCR, - 1269, IDH_TOPIC1269, - IDC_CONVFILE_PRESERVEDIR, IDH_CONVFILE_PRESERVEDIR, - IDC_CONVDISK_140K, IDH_CONVDISK_140K, - IDC_CONVDISK_800K, IDH_CONVDISK_800K, - IDC_CONVDISK_1440K, IDH_CONVDISK_1440K, - IDC_CONVDISK_5MB, IDH_CONVDISK_5MB, - IDC_CONVDISK_16MB, IDH_CONVDISK_16MB, - IDC_CONVDISK_20MB, IDH_CONVDISK_20MB, - IDC_CONVDISK_32MB, IDH_CONVDISK_32MB, - IDC_CONVDISK_SPECIFY, IDH_CONVDISK_SPECIFY, - IDC_IMAGE_SIZE_TEXT, IDH_IMAGE_SIZE_TEXT, - IDC_BULKCONV_PATHNAME, IDH_BULKCONV_PATHNAME, - IDC_PREF_EXTVIEWER_EXTS, IDH_PREF_EXTVIEWER_EXTS, - IDC_VOLUME_LIST, IDH_VOLUME_LIST, - IDC_OPENVOL_READONLY, IDH_OPENVOL_READONLY, - IDC_VOLUMECOPYPROG_FROM, IDH_VOLUMECOPYPROG_FROM, - IDC_VOLUMECOPYPROG_TO, IDH_VOLUMECOPYPROG_TO, - IDC_VOLUMECOPYPROG_PROGRESS, IDH_VOLUMECOPYPROG_PROGRESS, - IDC_CONVDISK_SPECIFY_EDIT, IDH_CONVDISK_SPECIFY_EDIT, - IDC_CONVDISK_COMPUTE, IDH_CONVDISK_COMPUTE, - IDC_DEOW_FILE, IDH_DEOW_FILE, - IDC_CONVDISK_SPACEREQ, IDH_CONVDISK_SPACEREQ, - IDC_DEOW_VOLUME, IDH_DEOW_VOLUME, - IDC_DEOW_CURRENT, IDH_DEOW_CURRENT, - 1306, IDH_TOPIC1306, - IDC_CONVDISK_VOLNAME, IDH_CONVDISK_VOLNAME, - IDC_VOLUME_FILTER, IDH_VOLUME_FILTER, - IDC_VOLUMECOPYSEL_LIST, IDH_VOLUMECOPYSEL_LIST, - IDC_VOLUEMCOPYSEL_TOFILE, IDH_VOLUEMCOPYSEL_TOFILE, - IDC_VOLUEMCOPYSEL_FROMFILE, IDH_VOLUEMCOPYSEL_FROMFILE, - IDC_CREATEFS_DOS32, IDH_CREATEFS_DOS32, - IDC_CREATEFS_DOS33, IDH_CREATEFS_DOS33, - IDC_CREATEFS_PRODOS, IDH_CREATEFS_PRODOS, - IDC_CREATEFS_PASCAL, IDH_CREATEFS_PASCAL, - IDC_CREATEFS_HFS, IDH_CREATEFS_HFS, - IDC_CREATEFS_BLANK, IDH_CREATEFS_BLANK, - 1320, IDH_TOPIC1320, - IDC_CREATEFSDOS_ALLOCDOS, IDH_CREATEFSDOS_ALLOCDOS, - IDC_CREATEFSDOS_VOLNUM, IDH_CREATEFSDOS_VOLNUM, - IDC_CREATEFSPRODOS_VOLNAME, IDH_CREATEFSPRODOS_VOLNAME, - IDC_CREATEFSPASCAL_VOLNAME, IDH_CREATEFSPASCAL_VOLNAME, - IDC_ASPI_VERS_TEXT, IDH_ASPI_VERS_TEXT, - IDC_PREF_SUCCESS_BEEP, IDH_PREF_SUCCESS_BEEP, - IDC_ADD_TARGET_TREE, IDH_ADD_TARGET_TREE, - IDC_AIDISK_SUBVOLSEL, IDH_AIDISK_SUBVOLSEL, - IDC_AIDISK_NOTES, IDH_AIDISK_NOTES, - IDC_AI_FILENAME, IDH_AI_FILENAME, - IDC_AIBNY_RECORDS, IDH_AIBNY_RECORDS, - IDC_AINUFX_FORMAT, IDH_AINUFX_FORMAT, - IDC_AINUFX_RECORDS, IDH_AINUFX_RECORDS, - IDC_AINUFX_MASTERVERSION, IDH_AINUFX_MASTERVERSION, - IDC_AINUFX_CREATEWHEN, IDH_AINUFX_CREATEWHEN, - IDC_AINUFX_MODIFYWHEN, IDH_AINUFX_MODIFYWHEN, - IDC_AINUFX_JUNKSKIPPED, IDH_AINUFX_JUNKSKIPPED, - IDC_AIDISK_OUTERFORMAT, IDH_AIDISK_OUTERFORMAT, - IDC_AIDISK_FILEFORMAT, IDH_AIDISK_FILEFORMAT, - IDC_AIDISK_PHYSICALFORMAT, IDH_AIDISK_PHYSICALFORMAT, - IDC_AIDISK_SECTORORDER, IDH_AIDISK_SECTORORDER, - IDC_AIDISK_FSFORMAT, IDH_AIDISK_FSFORMAT, - IDC_AIDISK_FILECOUNT, IDH_AIDISK_FILECOUNT, - IDC_AIDISK_CAPACITY, IDH_AIDISK_CAPACITY, - IDC_AIDISK_FREESPACE, IDH_AIDISK_FREESPACE, - IDC_AIDISK_DAMAGED, IDH_AIDISK_DAMAGED, - IDC_AIDISK_WRITEABLE, IDH_AIDISK_WRITEABLE, - IDC_PDISK_CONFIRM_FORMAT, IDH_PDISK_CONFIRM_FORMAT, - IDC_PDISK_PRODOS_ALLOWLOWER, IDH_PDISK_PRODOS_ALLOWLOWER, - IDC_PDISK_PRODOS_USESPARSE, IDH_PDISK_PRODOS_USESPARSE, - IDC_FVIEW_PRINT, IDH_FVIEW_PRINT, - IDC_CREATESUBDIR_BASE, IDH_CREATESUBDIR_BASE, - IDC_CREATESUBDIR_NEW, IDH_CREATESUBDIR_NEW, - IDC_RENAMEVOL_TREE, IDH_RENAMEVOL_TREE, - IDC_RENAMEVOL_NEW, IDH_RENAMEVOL_NEW, - IDC_ADDFILES_CONVEOLNONE, IDH_ADDFILES_CONVEOLNONE, - IDC_ADDFILES_CONVEOLTEXT, IDH_ADDFILES_CONVEOLTEXT, - IDC_ADDFILES_CONVEOLALL, IDH_ADDFILES_CONVEOLALL, - IDC_ADDFILES_STATIC4, IDH_ADDFILES_STATIC4, - IDC_PROPS_CREATEWHEN, IDH_PROPS_CREATEWHEN, - IDC_EOLSCAN_CR, IDH_EOLSCAN_CR, - IDC_EOLSCAN_LF, IDH_EOLSCAN_LF, - IDC_EOLSCAN_CRLF, IDH_EOLSCAN_CRLF, - IDC_EOLSCAN_CHARS, IDH_EOLSCAN_CHARS, - IDC_PREF_PASTE_JUNKPATHS, IDH_PREF_PASTE_JUNKPATHS, - IDC_EXT_CONVEOLTYPE, IDH_EXT_CONVEOLTYPE, - IDC_ADDFILES_CONVEOLTYPE, IDH_ADDFILES_CONVEOLTYPE, - IDC_TWOIMG_LOCKED, IDH_TWOIMG_LOCKED, - IDC_TWOIMG_DOSVOLSET, IDH_TWOIMG_DOSVOLSET, - IDC_TWOIMG_DOSVOLNUM, IDH_TWOIMG_DOSVOLNUM, - IDC_TWOIMG_COMMENT, IDH_TWOIMG_COMMENT, - IDC_TWOIMG_CREATOR, IDH_TWOIMG_CREATOR, - IDC_TWOIMG_VERSION, IDH_TWOIMG_VERSION, - IDC_TWOIMG_FORMAT, IDH_TWOIMG_FORMAT, - IDC_TWOIMG_BLOCKS, IDH_TWOIMG_BLOCKS, - IDC_FVIEW_DATA, IDH_FVIEW_DATA, - IDC_FVIEW_RSRC, IDH_FVIEW_RSRC, - IDC_FVIEW_CMMT, IDH_FVIEW_CMMT, - IDC_FVIEW_FORMATSEL, IDH_FVIEW_FORMATSEL, - IDC_FVIEW_FMT_HEX, IDH_FVIEW_FMT_HEX, - IDC_FVIEW_FMT_RAW, IDH_FVIEW_FMT_RAW, - IDC_FVIEW_FMT_BEST, IDH_FVIEW_FMT_BEST, - IDC_PDISK_OPENVOL_RO, IDH_PDISK_OPENVOL_RO, - IDC_EOLSCAN_HIGHASCII, IDH_EOLSCAN_HIGHASCII, - IDC_CASSETTE_LIST, IDH_CASSETTE_LIST, - IDC_IMPORT_CHUNK, IDH_IMPORT_CHUNK, - IDC_CASSETTE_ALG, IDH_CASSETTE_ALG, - IDC_CASSETTE_INPUT, IDH_CASSETTE_INPUT, - IDC_CASSIMPTARG_FILENAME, IDH_CASSIMPTARG_FILENAME, - IDC_CASSIMPTARG_BAS, IDH_CASSIMPTARG_BAS, - IDC_CASSIMPTARG_INT, IDH_CASSIMPTARG_INT, - IDC_CASSIMPTARG_BIN, IDH_CASSIMPTARG_BIN, - IDC_CASSIMPTARG_BINADDR, IDH_CASSIMPTARG_BINADDR, - IDC_CASSIMPTARG_RANGE, IDH_CASSIMPTARG_RANGE, - IDC_CLASH_RENAME, IDH_CLASH_RENAME, - IDC_CLASH_SKIP, IDH_CLASH_SKIP, - IDC_CLASH_WINNAME, IDH_CLASH_WINNAME, - IDC_CLASH_STORAGENAME, IDH_CLASH_STORAGENAME, - IDC_PREF_REDUCE_SHK_ERROR_CHECKS, IDH_PREF_REDUCE_SHK_ERROR_CHECKS, - IDC_IMPORT_BAS_RESULTS, IDH_IMPORT_BAS_RESULTS, - IDC_IMPORT_BAS_SAVEAS, IDH_IMPORT_BAS_SAVEAS, - IDC_FVIEW_FIND, IDH_FVIEW_FIND, - IDC_CREATEFSHFS_VOLNAME, IDH_CREATEFSHFS_VOLNAME, - IDC_PROPS_HFS_FILETYPE, IDH_PROPS_HFS_FILETYPE, - IDC_PROPS_HFS_AUXTYPE, IDH_PROPS_HFS_AUXTYPE, - IDC_PROPS_HFS_MODE, IDH_PROPS_HFS_MODE, - IDC_PROPS_HFS_LABEL, IDH_PROPS_HFS_LABEL, - IDC_PASTE_SPECIAL_COUNT, IDH_PASTE_SPECIAL_COUNT, - IDC_PASTE_SPECIAL_PATHS, IDH_PASTE_SPECIAL_PATHS, - IDC_PASTE_SPECIAL_NOPATHS, IDH_PASTE_SPECIAL_NOPATHS, - IDC_PROGRESS_COUNTER_COUNT, IDH_PROGRESS_COUNTER_COUNT, - IDC_PROGRESS_COUNTER_DESC, IDH_PROGRESS_COUNTER_DESC, - IDC_PDISK_OPENVOL_PHYS0, IDH_PDISK_OPENVOL_PHYS0, - IDC_PREF_SHK_BAD_MAC, IDH_PREF_SHK_BAD_MAC, - 0 -}; - -/*static*/ BOOL MyApp::HandleHelpInfo(HELPINFO* lpHelpInfo) -{ - CString path(gMyApp.m_pszHelpFilePath); - path += "::/PopUp.txt"; - - LOGD("HandleHelpInfo ID=%d", lpHelpInfo->iCtrlId); - ::HtmlHelp((HWND) lpHelpInfo->hItemHandle, path, - HH_TP_HELP_WM_HELP, (DWORD) PopUpHelpIds); - - return TRUE; -} - -/*static*/ void MyApp::HandleHelp(CWnd* pWnd, DWORD topicId) -{ - // The CWnd#HtmlHelp() function is insisting on using the top-level - // parent, but if we do that with the Add Files custom file dialog - // then the help window pops up behind the app rather than in front. - CWnd* pParent = pWnd->GetTopLevelParent(); - LOGD("HandleHelp ID=%lu pWnd=%p parent=%p", topicId, pWnd, pParent); - ::HtmlHelp(pWnd->m_hWnd, gMyApp.m_pszHelpFilePath, - HH_HELP_CONTEXT, topicId); -} diff --git a/ciderpress/app/MyApp.h b/ciderpress/app/MyApp.h deleted file mode 100644 index 30f0971..0000000 --- a/ciderpress/app/MyApp.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * The application object. - */ -#ifndef APP_MYAPP_H -#define APP_MYAPP_H - -#include "Registry.h" - -/* CiderPress version numbers */ -#define kAppMajorVersion 4 -#define kAppMinorVersion 0 -#define kAppBugVersion 3 -#define kAppDevString L"" - -/* - * Windows application object. - */ -class MyApp: public CWinAppEx -{ -public: - MyApp(); - virtual ~MyApp(void); - -#ifdef CAN_UPDATE_FILE_ASSOC - MyRegistry fRegistry; -#endif - - const WCHAR* GetExeFileName(void) const { return fExeFileName; } - const WCHAR* GetExeBaseName(void) const { return fExeBaseName; } - - /* - * Handles pop-up help requests. Call this from OnHelpInfo. - */ - static BOOL HandleHelpInfo(HELPINFO* lpHelpInfo); - - /* - * Handles help topic requests. Call this from OnHelp. - */ - static void HandleHelp(CWnd* pWnd, DWORD topicId); - -private: - virtual BOOL InitInstance(void) override; - virtual BOOL OnIdle(LONG lCount) override; - - /* - * Show where we got something from. Handy for checking DLL load locations. - * - * If "name" is NULL, we show the EXE info. - */ - void LogModuleLocation(const WCHAR* name); - - /* - * This holds pairs of control IDs and popup help IDs, for use by - * HtmlHelp HH_TP_HELP_WM_HELP. - * - * The control and help ID values are identical just to make life - * simpler, but we need a table anyway. - */ - static const DWORD PopUpHelpIds[]; - - CString fExeFileName; - CString fExeBaseName; -}; - -extern MyApp gMyApp; - -#endif /*APP_MYAPP_H*/ diff --git a/ciderpress/app/NewDiskSize.cpp b/ciderpress/app/NewDiskSize.cpp deleted file mode 100644 index d850d95..0000000 --- a/ciderpress/app/NewDiskSize.cpp +++ /dev/null @@ -1,131 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "NewDiskSize.h" -#include "resource.h" - -/* - * Number of blocks in the disks we create. - * - * These must be in ascending order. - */ -/*static*/ const NewDiskSize::RadioCtrlMap NewDiskSize::kCtrlMap[] = { - { IDC_CONVDISK_140K, 280 }, - { IDC_CONVDISK_800K, 1600 }, - { IDC_CONVDISK_1440K, 2880 }, - { IDC_CONVDISK_5MB, 10240 }, - { IDC_CONVDISK_16MB, 32768 }, - { IDC_CONVDISK_20MB, 40960 }, - { IDC_CONVDISK_32MB, 65535 }, - { IDC_CONVDISK_SPECIFY, kSpecified }, -}; -static const int kEditBoxID = IDC_CONVDISK_SPECIFY_EDIT; - -/*static*/ unsigned int NewDiskSize::GetNumSizeEntries(void) -{ - return NELEM(kCtrlMap); -} - -/*static*/ long NewDiskSize::GetDiskSizeByIndex(int idx) -{ - ASSERT(idx >= 0 && idx < NELEM(kCtrlMap)); - return kCtrlMap[idx].blocks; -} - -/*static*/ void NewDiskSize::EnableButtons(CDialog* pDialog, BOOL state) -{ - CWnd* pWnd; - - for (int i = 0; i < NELEM(kCtrlMap); i++) { - pWnd = pDialog->GetDlgItem(kCtrlMap[i].ctrlID); - if (pWnd != NULL) - pWnd->EnableWindow(state); - } -} - -/*static*/ void NewDiskSize::EnableButtons_ProDOS(CDialog* pDialog, - long totalBlocks, long blocksUsed) -{ - CButton* pButton; - long usedWithoutBitmap = blocksUsed - GetNumBitmapBlocks_ProDOS(totalBlocks); - bool first = true; - - LOGI("EnableButtons_ProDOS total=%ld used=%ld usedw/o=%ld", - totalBlocks, blocksUsed, usedWithoutBitmap); - - for (int i = 0; i < NELEM(kCtrlMap); i++) { - pButton = (CButton*) pDialog->GetDlgItem(kCtrlMap[i].ctrlID); - if (pButton == NULL) { - LOGI("WARNING: couldn't find ctrlID %d", kCtrlMap[i].ctrlID); - continue; - } - - if (kCtrlMap[i].blocks == kSpecified) { - pButton->SetCheck(BST_UNCHECKED); - pButton->EnableWindow(TRUE); - CWnd* pWnd = pDialog->GetDlgItem(kEditBoxID); - pWnd->EnableWindow(FALSE); - continue; - } - - if (usedWithoutBitmap + GetNumBitmapBlocks_ProDOS(kCtrlMap[i].blocks) <= - kCtrlMap[i].blocks) - { - pButton->EnableWindow(TRUE); - if (first) { - pButton->SetCheck(BST_CHECKED); - first = false; - } else { - pButton->SetCheck(BST_UNCHECKED); - } - } else { - pButton->EnableWindow(FALSE); - pButton->SetCheck(BST_UNCHECKED); - } - } - - UpdateSpecifyEdit(pDialog); -} - -/*static*/ long NewDiskSize::GetNumBitmapBlocks_ProDOS(long totalBlocks) { - ASSERT(totalBlocks > 0); - const int kBitsPerBlock = 512 * 8; - int numBlocks = (totalBlocks + kBitsPerBlock-1) / kBitsPerBlock; - return numBlocks; -} - -/*static*/ void NewDiskSize::UpdateSpecifyEdit(CDialog* pDialog) -{ - CEdit* pEdit = (CEdit*) pDialog->GetDlgItem(kEditBoxID); - int i; - - if (pEdit == NULL) { - ASSERT(false); - return; - } - - for (i = 0; i < NELEM(kCtrlMap); i++) { - CButton* pButton = (CButton*) pDialog->GetDlgItem(kCtrlMap[i].ctrlID); - if (pButton == NULL) { - LOGI("WARNING: couldn't find ctrlID %d", kCtrlMap[i].ctrlID); - continue; - } - - if (pButton->GetCheck() == BST_CHECKED) { - if (kCtrlMap[i].blocks == kSpecified) - return; - break; - } - } - if (i == NELEM(kCtrlMap)) { - LOGI("WARNING: couldn't find a checked radio button"); - return; - } - - CString fmt; - fmt.Format(L"%ld", kCtrlMap[i].blocks); - pEdit->SetWindowText(fmt); -} diff --git a/ciderpress/app/NewDiskSize.h b/ciderpress/app/NewDiskSize.h deleted file mode 100644 index abc9f8b..0000000 --- a/ciderpress/app/NewDiskSize.h +++ /dev/null @@ -1,68 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Functions to manage the "new disk size" radio button set in dialogs. - */ -#ifndef APP_NEWDISKSIZE_H -#define APP_NEWDISKSIZE_H - -/* - * All members are static. Don't instantiate the class. - */ -class NewDiskSize { -public: - NewDiskSize(void) { ASSERT(false); } - - /* - * Return the #of entries in the table. - */ - static unsigned int GetNumSizeEntries(void); - - /* - * Return the "size" field from an array entry. - */ - static long GetDiskSizeByIndex(int idx); - - enum { kSpecified = -1 }; - - static void EnableButtons(CDialog* pDialog, BOOL state = true); - - /* - * Run through the set of radio buttons, disabling any that don't have enough - * space to hold the ProDOS volume with the specified parameters. - * - * The space required is equal to the blocks required for data plus the blocks - * required for the free-space bitmap. Since the free-space bitmap size is - * smaller for smaller volumes, we have to adjust it for each. - * - * Pass in the total blocks and #of blocks used on a particular ProDOS volume. - * This will compute how much space would be required for larger and smaller - * volumes, and enable or disable radio buttons as appropriate. (You can get - * these values from DiskFS::GetFreeBlockCount()). - */ - static void EnableButtons_ProDOS(CDialog* pDialog, long totalBlocks, - long blocksUsed); - - /* - * Compute the #of blocks needed to hold the ProDOS block bitmap. - */ - static long GetNumBitmapBlocks_ProDOS(long totalBlocks); - - /* - * Update the "specify size" edit box. - */ - static void UpdateSpecifyEdit(CDialog* pDialog); - -private: - typedef struct { - int ctrlID; - long blocks; - } RadioCtrlMap; - - static const RadioCtrlMap kCtrlMap[]; -}; - -#endif /*APP_NEWDISKSIZE_H*/ diff --git a/ciderpress/app/NufxArchive.cpp b/ciderpress/app/NufxArchive.cpp deleted file mode 100644 index 034ba9b..0000000 --- a/ciderpress/app/NufxArchive.cpp +++ /dev/null @@ -1,2504 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Bridge between NufxLib and GenericArchive. - */ -#include "stdafx.h" -#include "NufxArchive.h" -#include "ConfirmOverwriteDialog.h" -#include "RenameEntryDialog.h" -#include "RecompressOptionsDialog.h" -#include "AddClashDialog.h" -#include "Main.h" -#include "../nufxlib/NufxLib.h" -#include "../reformat/Charset.h" - -/* - * NufxLib doesn't currently allow an fssep of '\0', so we use this instead - * to indicate the absence of an fssep char. Not quite right, but it'll do - * until NufxLib gets fixed. - */ -const unsigned char kNufxNoFssep = 0xff; - - -/* - * =========================================================================== - * NufxEntry - * =========================================================================== - */ - -int NufxEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const -{ - NuError nerr; - char* dataBuf = NULL; - NuDataSink* pDataSink = NULL; - NuThread thread; - unsigned long actualThreadEOF; - NuThreadIdx threadIdx; - bool needAlloc = true; - int result = -1; - - ASSERT(IDOK != -1 && IDCANCEL != -1); // make sure return vals don't clash - - if (*ppText != NULL) - needAlloc = false; - - FindThreadInfo(which, &thread, pErrMsg); - if (!pErrMsg->IsEmpty()) - goto bail; - threadIdx = thread.threadIdx; - actualThreadEOF = thread.actualThreadEOF; - - /* - * We've got the right thread. Create an appropriately-sized buffer - * and extract the data into it (WITHOUT doing EOL conversion). - * - * First check for a length of zero. - */ - if (actualThreadEOF == 0) { - LOGI("Empty thread"); - if (needAlloc) { - *ppText = new char[1]; - **ppText = '\0'; - } - *pLength = 0; - result = IDOK; - goto bail; - } - - if (needAlloc) { - dataBuf = new char[actualThreadEOF]; - if (dataBuf == NULL) { - pErrMsg->Format(L"allocation of %ld bytes failed", - actualThreadEOF); - goto bail; - } - } else { - if (*pLength < (long) actualThreadEOF) { - pErrMsg->Format(L"buf size %ld too short (%ld)", - *pLength, actualThreadEOF); - goto bail; - } - dataBuf = *ppText; - } - nerr = NuCreateDataSinkForBuffer(true, kNuConvertOff, - (unsigned char*)dataBuf, actualThreadEOF, &pDataSink); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"unable to create buffer data sink: %hs", - NuStrError(nerr)); - goto bail; - } - - SET_PROGRESS_BEGIN(); - nerr = NuExtractThread(fpArchive, threadIdx, pDataSink); - if (nerr != kNuErrNone) { - if (nerr == kNuErrAborted) { - result = IDCANCEL; - //::sprintf(errorBuf, "Cancelled.\n"); - } else if (nerr == kNuErrBadFormat) { - pErrMsg->Format(L"The compression method used on this file is not supported " - L"by your copy of \"nufxlib.dll\". For more information, " - L"please visit us on the web at " - L"http://www.faddensoft.com/ciderpress/"); - } else { - pErrMsg->Format(L"unable to extract thread %ld: %hs", - threadIdx, NuStrError(nerr)); - } - goto bail; - } - - if (needAlloc) - *ppText = dataBuf; - *pLength = actualThreadEOF; - result = IDOK; - -bail: - if (result == IDOK) { - SET_PROGRESS_END(); - ASSERT(pErrMsg->IsEmpty()); - } else { - ASSERT(result == IDCANCEL || !pErrMsg->IsEmpty()); - if (needAlloc) { - delete[] dataBuf; - ASSERT(*ppText == NULL); - } - } - if (pDataSink != NULL) - NuFreeDataSink(pDataSink); - return result; -} - -int NufxEntry::ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const -{ - NuDataSink* pDataSink = NULL; - NuError nerr; - NuThread thread; - unsigned long actualThreadEOF; - NuThreadIdx threadIdx; - int result = -1; - - ASSERT(outfp != NULL); - - //CString errMsg; - FindThreadInfo(which, &thread, pErrMsg); - if (!pErrMsg->IsEmpty()) - goto bail; - threadIdx = thread.threadIdx; - actualThreadEOF = thread.actualThreadEOF; - - /* we've got the right thread, see if it's empty */ - if (actualThreadEOF == 0) { - LOGI("Empty thread"); - result = IDOK; - goto bail; - } - - /* set EOL conversion flags */ - NuValue nuConv; - switch (conv) { - case kConvertEOLOff: nuConv = kNuConvertOff; break; - case kConvertEOLOn: nuConv = kNuConvertOn; break; - case kConvertEOLAuto: nuConv = kNuConvertAuto; break; - default: - ASSERT(false); - pErrMsg->Format(L"internal error: bad conv flag %d", conv); - goto bail; - } - if (which == kDiskImageThread) { - /* override the above; never EOL-convert a disk image */ - nuConv = kNuConvertOff; - } - - switch (convHA) { - case kConvertHAOff: - nerr = NuSetValue(fpArchive, kNuValueStripHighASCII, false); - break; - case kConvertHAOn: - case kConvertHAAuto: - nerr = NuSetValue(fpArchive, kNuValueStripHighASCII, true); - break; - default: - ASSERT(false); - pErrMsg->Format(L"internal error: bad convHA flag %d", convHA); - goto bail; - } - - /* make sure we convert to CRLF */ - nerr = NuSetValue(fpArchive, kNuValueEOL, kNuEOLCRLF); // for Win32 - if (nerr != kNuErrNone) { - pErrMsg->Format(L"failed setting EOL value: %hs", NuStrError(nerr)); - goto bail; - } - - /* create a data sink for "outfp" */ - nerr = NuCreateDataSinkForFP(true, nuConv, outfp, &pDataSink); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"unable to create FP data sink: %hs", - NuStrError(nerr)); - goto bail; - } - - /* extract the thread to the file */ - SET_PROGRESS_BEGIN(); - nerr = NuExtractThread(fpArchive, threadIdx, pDataSink); - if (nerr != kNuErrNone) { - if (nerr == kNuErrAborted) { - /* user hit the "cancel" button */ - *pErrMsg = L"cancelled"; - result = IDCANCEL; - } else if (nerr == kNuErrBadFormat) { - pErrMsg->Format(L"The compression method used on this file is not supported " - L"by your copy of \"nufxlib.dll\". For more information, " - L"please visit us on the web at " - L"http://www.faddensoft.com/ciderpress/"); - } else { - pErrMsg->Format(L"unable to extract thread %ld: %hs", - threadIdx, NuStrError(nerr)); - } - goto bail; - } - - result = IDOK; - -bail: - if (result == IDOK) { - SET_PROGRESS_END(); - } - if (pDataSink != NULL) - NuFreeDataSink(pDataSink); - return result; -} - -void NufxEntry::FindThreadInfo(int which, NuThread* pRetThread, - CString* pErrMsg) const -{ - NuError nerr; - - ASSERT(pErrMsg->IsEmpty()); - - /* - * Retrieve the record from the archive. - */ - const NuRecord* pRecord; - nerr = NuGetRecord(fpArchive, fRecordIdx, &pRecord); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"NufxLib unable to locate record %ld: %hs", - fRecordIdx, NuStrError(nerr)); - goto bail; - } - - /* - * Find the right thread. - */ - const NuThread* pThread; - unsigned long wantedThreadID; - switch (which) { - case kDataThread: wantedThreadID = kNuThreadIDDataFork; break; - case kRsrcThread: wantedThreadID = kNuThreadIDRsrcFork; break; - case kDiskImageThread: wantedThreadID = kNuThreadIDDiskImage; break; - case kCommentThread: wantedThreadID = kNuThreadIDComment; break; - default: - pErrMsg->Format(L"looking for bogus thread 0x%02x", which); - goto bail; - } - - int i; - pThread = NULL; - for (i = 0; i < (int)NuRecordGetNumThreads(pRecord); i++) { - pThread = NuGetThread(pRecord, i); - assert(pThread != NULL); - if (NuGetThreadID(pThread) == wantedThreadID) - break; - } - if (i == (int)NuRecordGetNumThreads(pRecord)) { - /* didn't find the thread we wanted */ - pErrMsg->Format(L"searched %d threads but couldn't find 0x%02x", - NuRecordGetNumThreads(pRecord), which); - goto bail; - } - - assert(pThread != NULL); - memcpy(pRetThread, pThread, sizeof(*pRetThread)); - -bail: - return; -} - - -//static const char* gShortFormatNames[] = { -// "unc", "squ", "lz1", "lz2", "u12", "u16", "dfl", "bzp" -//}; -static const WCHAR* gFormatNames[] = { - L"Uncompr", L"Squeeze", L"LZW/1", L"LZW/2", L"LZC-12", - L"LZC-16", L"Deflate", L"Bzip2" -}; - -void NufxEntry::AnalyzeRecord(const NuRecord* pRecord) -{ - /* - * The "best format" and "record type" stuff assume that the entire - * record contains only a disk thread or a file thread, and that any - * format is interesting so long as it isn't "no compression". In - * general these will be true, because ShrinkIt and NuLib create files - * this way. - * - * You could, of course, create a single record with a data thread and - * a disk image thread, but it's a fair bet ShrinkIt would ignore one - * or the other. - * - * NOTE: we don't currently work around the GSHK zero-length file bug. - * Such records, which have a filename thread but no data threads at all, - * will be categorized as "unknown". We could detect the situation and - * correct it, but we might as well flag it in a user-visible way. - */ - const NuThread* pThread; - NuThreadID threadID; - unsigned long idx; - RecordKind recordKind; - unsigned long uncompressedLen; - unsigned long compressedLen; - unsigned short format; - - recordKind = kRecordKindUnknown; - uncompressedLen = compressedLen = 0; - format = kNuThreadFormatUncompressed; - - for (idx = 0; idx < pRecord->recTotalThreads; idx++) { - pThread = NuGetThread(pRecord, idx); - ASSERT(pThread != NULL); - - threadID = NuMakeThreadID(pThread->thThreadClass, - pThread->thThreadKind); - - if (pThread->thThreadClass == kNuThreadClassData) { - /* replace what's there if this might be more interesting */ - if (format == kNuThreadFormatUncompressed) - format = (unsigned short) pThread->thThreadFormat; - - if (threadID == kNuThreadIDRsrcFork) - recordKind = kRecordKindForkedFile; - else if (threadID == kNuThreadIDDiskImage) - recordKind = kRecordKindDisk; - else if (threadID == kNuThreadIDDataFork && - recordKind == kRecordKindUnknown) - recordKind = kRecordKindFile; - - /* sum up, so we get both forks of forked files */ - //uncompressedLen += pThread->actualThreadEOF; - compressedLen += pThread->thCompThreadEOF; - } - - if (threadID == kNuThreadIDDataFork) { - if (!GetHasDataFork() && !GetHasDiskImage()) { - SetHasDataFork(true); - SetDataForkLen(pThread->actualThreadEOF); - } else { - LOGW("WARNING: ignoring second disk image / data fork"); - } - } - if (threadID == kNuThreadIDRsrcFork) { - if (!GetHasRsrcFork()) { - SetHasRsrcFork(true); - SetRsrcForkLen(pThread->actualThreadEOF); - } else { - LOGW("WARNING: ignoring second data fork"); - } - } - if (threadID == kNuThreadIDDiskImage) { - if (!GetHasDiskImage() && !GetHasDataFork()) { - SetHasDiskImage(true); - SetDataForkLen(pThread->actualThreadEOF); - } else { - LOGW("WARNING: ignoring second disk image / data fork"); - } - } - if (threadID == kNuThreadIDComment) { - SetHasComment(true); - if (pThread->actualThreadEOF != 0) - SetHasNonEmptyComment(true); - } - } - - SetRecordKind(recordKind); - //SetUncompressedLen(uncompressedLen); - SetCompressedLen(compressedLen); - - if (format >= 0 && format < NELEM(gFormatNames)) - SetFormatStr(gFormatNames[format]); - else - SetFormatStr(L"Unknown"); -} - - -/* - * =========================================================================== - * NufxArchive - * =========================================================================== - */ - -/*static*/ CString NufxArchive::AppInit(void) -{ - NuError nerr; - CString result(""); - int32_t major, minor, bug; - - nerr = NuGetVersion(&major, &minor, &bug, NULL, NULL); - if (nerr != kNuErrNone) { - result = "Unable to get version number from NufxLib."; - goto bail; - } - - if (major != kNuVersionMajor || minor < kNuVersionMinor) { - result.Format(L"Older or incompatible version of NufxLib DLL found.\r\r" - L"Wanted v%d.%d.x, found %ld.%ld.%ld.", - kNuVersionMajor, kNuVersionMinor, - major, minor, bug); - goto bail; - } - if (bug != kNuVersionBug) { - LOGI("Different 'bug' version (built vX.X.%d, dll vX.X.%d)", - kNuVersionBug, bug); - } - - /* set NufxLib's global error message handler */ - NuSetGlobalErrorMessageHandler(NufxErrorMsgHandler); - -bail: - return result; -} - -/*static*/ bool NufxArchive::IsCompressionSupported(NuThreadFormat format) -{ - NuFeature feature; - - switch (format) { - case kNuThreadFormatUncompressed: - return true; - - case kNuThreadFormatHuffmanSQ: - feature = kNuFeatureCompressSQ; - break; - case kNuThreadFormatLZW1: - case kNuThreadFormatLZW2: - feature = kNuFeatureCompressLZW; - break; - case kNuThreadFormatLZC12: - case kNuThreadFormatLZC16: - feature = kNuFeatureCompressLZC; - break; - case kNuThreadFormatDeflate: - feature = kNuFeatureCompressDeflate; - break; - case kNuThreadFormatBzip2: - feature = kNuFeatureCompressBzip2; - break; - - default: - ASSERT(false); - return false; - } - - NuError nerr; - nerr = NuTestFeature(feature); - if (nerr == kNuErrNone) - return true; - return false; -} - -NuResult NufxArchive::NufxErrorMsgHandler(NuArchive*, void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - LOG_BASE(pErrorMessage->isDebug ? DebugLog::LOG_DEBUG : DebugLog::LOG_WARN, - pErrorMessage->file, pErrorMessage->line, " %hs", - pErrorMessage->message); - - return kNuOK; -} - -/*static*/NuResult NufxArchive::ProgressUpdater(NuArchive* pArchive, - void* vpProgress) -{ - // "oldName" ends up on top, "newName" on bottom. - const NuProgressData* pProgress = (const NuProgressData*) vpProgress; - NufxArchive* pThis; - MainWindow* pMainWin = (MainWindow*)::AfxGetMainWnd(); - int status; - const char* oldName; - const char* newName; - int perc; - - ASSERT(pProgress != NULL); - ASSERT(pMainWin != NULL); - - ASSERT(pArchive != NULL); - (void) NuGetExtraData(pArchive, (void**) &pThis); - ASSERT(pThis != NULL); - - // TODO(Unicode): NufxLib should be handing us these in UTF-16 - oldName = newName = NULL; - if (pProgress->operation == kNuOpAdd) { - oldName = pProgress->origPathnameUNI; - newName = pProgress->pathnameUNI; - if (pThis->fProgressAsRecompress) - oldName = "-"; - } else if (pProgress->operation == kNuOpTest) { - oldName = pProgress->pathnameUNI; - } else if (pProgress->operation == kNuOpExtract) { - if (pThis->fProgressAsRecompress) { - oldName = pProgress->origPathnameUNI; - newName = "-"; - } - } - - perc = pProgress->percentComplete; - if (pProgress->state == kNuProgressDone) - perc = 100; - - //LOGI("Progress: %d%% '%hs' '%hs'", perc, - // oldName == NULL ? "(null)" : oldName, - // newName == NULL ? "(null)" : newName); - - CString oldNameW(oldName); - CString newNameW(newName); - status = SET_PROGRESS_UPDATE2(perc, - oldNameW.IsEmpty() ? NULL : (LPCWSTR) oldNameW, - newNameW.IsEmpty() ? NULL : (LPCWSTR) newNameW); - - /* check to see if user hit the "cancel" button on the progress dialog */ - if (pProgress->state == kNuProgressAborted) { - LOGI("(looks like we're aborting)"); - ASSERT(status == IDCANCEL); - } - - if (status == IDCANCEL) { - LOGI("Signaling NufxLib to abort"); - return kNuAbort; - } else - return kNuOK; -} - -GenericArchive::OpenResult NufxArchive::Open(const WCHAR* filename, - bool readOnly, CString* pErrMsg) -{ - NuError nerr; - CString errMsg; - - ASSERT(fpArchive == NULL); - - CStringA filenameA(filename); - if (!readOnly) { - CString tmpname = GenDerivedTempName(filename); - LOGI("Opening file '%ls' rw (tmp='%ls')", filename, (LPCWSTR) tmpname); - fIsReadOnly = false; - CStringA tmpnameA(tmpname); - nerr = NuOpenRW(filenameA, tmpnameA, 0, &fpArchive); - - if (nerr == kNuErrFileAccessDenied || nerr == EACCES) { - LOGI("Read-write failed with access denied, trying read-only"); - readOnly = true; - } - } - if (readOnly) { - LOGI("Opening file '%ls' ro", (LPCWSTR) filename); - fIsReadOnly = true; - nerr = NuOpenRO(filenameA, &fpArchive); - } - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to open '%ls': %hs", filename, NuStrError(nerr)); - goto bail; - } else { - //LOGI("FILE OPEN SUCCESS"); - } - - nerr = SetCallbacks(); - if (nerr != kNuErrNone) { - errMsg = L"Callback init failed"; - goto bail; - } - - nerr = LoadContents(); - if (nerr != kNuErrNone) { - errMsg = L"Failed reading archive contents: "; - errMsg += NuStrError(nerr); - } - - SetPathName(filename); - -bail: - *pErrMsg = errMsg; - if (!errMsg.IsEmpty()) - return kResultFailure; - else - return kResultSuccess; -} - -CString NufxArchive::New(const WCHAR* filename, const void* options) -{ - NuError nerr; - CString retmsg; - - ASSERT(fpArchive == NULL); - ASSERT(options == NULL); - - CString tmpname = GenDerivedTempName(filename); - LOGI("Creating file '%ls' (tmp='%ls')", filename, (LPCWSTR) tmpname); - fIsReadOnly = false; - CStringA filenameA(filename); - CStringA tmpnameA(tmpname); - nerr = NuOpenRW(filenameA, tmpnameA, kNuOpenCreat | kNuOpenExcl, &fpArchive); - if (nerr != kNuErrNone) { - retmsg.Format(L"Unable to open '%ls': %hs", filename, NuStrError(nerr)); - goto bail; - } else { - LOGI("NEW FILE SUCCESS"); - } - - - nerr = SetCallbacks(); - if (nerr != kNuErrNone) { - retmsg = L"Callback init failed"; - goto bail; - } - - SetPathName(filename); - -bail: - return retmsg; -} - -NuError NufxArchive::SetCallbacks(void) -{ - NuError nerr; - - nerr = NuSetExtraData(fpArchive, this); - if (nerr != kNuErrNone) - goto bail; -// nerr = NuSetSelectionFilter(fpArchive, SelectionFilter); -// if (nerr != kNuErrNone) -// goto bail; -// nerr = NuSetOutputPathnameFilter(fpArchive, OutputPathnameFilter); -// if (nerr != kNuErrNone) -// goto bail; - NuSetProgressUpdater(fpArchive, ProgressUpdater); -// nerr = NuSetErrorHandler(fpArchive, ErrorHandler); -// if (nerr != kNuErrNone) -// goto bail; - NuSetErrorMessageHandler(fpArchive, NufxErrorMsgHandler); - - /* let NufxLib worry about buggy records without data threads */ - nerr = NuSetValue(fpArchive, kNuValueMaskDataless, kNuValueTrue); - if (nerr != kNuErrNone) - goto bail; - - /* set any values based on Preferences values */ - PreferencesChanged(); - -bail: - return nerr; -} - -void NufxArchive::PreferencesChanged(void) -{ - NuError nerr; - const Preferences* pPreferences = GET_PREFERENCES(); - bool val; - - val = pPreferences->GetPrefBool(kPrMimicShrinkIt); - nerr = NuSetValue(fpArchive, kNuValueMimicSHK, val); - if (nerr != kNuErrNone) { - LOGI("NuSetValue(kNuValueMimicSHK, %d) failed, err=%d", val, nerr); - ASSERT(false); - } else { - LOGI("Set MimicShrinkIt to %d", val); - } - - val = pPreferences->GetPrefBool(kPrReduceSHKErrorChecks); - NuSetValue(fpArchive, kNuValueIgnoreLZW2Len, val); - NuSetValue(fpArchive, kNuValueIgnoreCRC, val); - - val = pPreferences->GetPrefBool(kPrBadMacSHK); - NuSetValue(fpArchive, kNuValueHandleBadMac, val); -} - -long NufxArchive::GetCapability(Capability cap) -{ - switch (cap) { - case kCapCanTest: - return true; - break; - case kCapCanRenameFullPath: - return true; - break; - case kCapCanRecompress: - return true; - break; - case kCapCanEditComment: - return true; - break; - case kCapCanAddDisk: - return true; - break; - case kCapCanConvEOLOnAdd: - return false; - break; - case kCapCanCreateSubdir: - return false; - break; - case kCapCanRenameVolume: - return false; - break; - default: - ASSERT(false); - return -1; - break; - } -} - -NuError NufxArchive::LoadContents(void) -{ - /* - * We will need to set an error handler if we want to be able to do things - * like "I found a bad CRC, did you want me to keep trying anyway?". - */ - long counter = 0; - NuError result; - - LOGI("NufxArchive LoadContents"); - ASSERT(fpArchive != NULL); - - { - MainWindow* pMain = GET_MAIN_WINDOW(); - ExclusiveModelessDialog* pWaitDlg = new ExclusiveModelessDialog; - pWaitDlg->Create(IDD_LOADING, pMain); - pWaitDlg->CenterWindow(); - pMain->PeekAndPump(); // redraw - CWaitCursor waitc; - - result = NuContents(fpArchive, ContentFunc); - - SET_PROGRESS_COUNTER(-1); - - pWaitDlg->DestroyWindow(); - //pMain->PeekAndPump(); // redraw - } - - return result; -} - -CString NufxArchive::Reload(void) -{ - NuError nerr; - CString errMsg; - - fReloadFlag = true; // tell everybody that cached data is invalid - - DeleteEntries(); // a GenericArchive operation - - nerr = LoadContents(); - if (nerr != kNuErrNone) { - errMsg.Format(L"ERROR: unable to reload archive contents: %hs.", - NuStrError(nerr)); - - DeleteEntries(); - fIsReadOnly = true; - } - - return errMsg; -} - -NuError NufxArchive::InternalReload(CWnd* pMsgWnd) -{ - CString errMsg; - - errMsg = Reload(); - - if (!errMsg.IsEmpty()) { - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - return kNuErrGeneric; - } - - return kNuErrNone; -} - -NuResult NufxArchive::ContentFunc(NuArchive* pArchive, void* vpRecord) -{ - const NuRecord* pRecord = (const NuRecord*) vpRecord; - NufxArchive* pThis; - NufxEntry* pNewEntry; - - ASSERT(pArchive != NULL); - ASSERT(vpRecord != NULL); - - NuGetExtraData(pArchive, (void**) &pThis); - - pNewEntry = new NufxEntry(pArchive); - - pNewEntry->SetPathNameMOR(pRecord->filenameMOR); - pNewEntry->SetFssep(NuGetSepFromSysInfo(pRecord->recFileSysInfo)); - pNewEntry->SetFileType(pRecord->recFileType); - pNewEntry->SetAuxType(pRecord->recExtraType); - pNewEntry->SetAccess(pRecord->recAccess); - pNewEntry->SetCreateWhen(DateTimeToSeconds(&pRecord->recCreateWhen)); - pNewEntry->SetModWhen(DateTimeToSeconds(&pRecord->recModWhen)); - - /* - * Our files are always ProDOS format. This is especially important - * when cutting & pasting, so that the DOS high ASCII converter gets - * invoked at appropriate times. - */ - pNewEntry->SetSourceFS(DiskImg::kFormatProDOS); - - pNewEntry->AnalyzeRecord(pRecord); - pNewEntry->SetRecordIdx(pRecord->recordIdx); - - pThis->AddEntry(pNewEntry); - if ((pThis->GetNumEntries() % 10) == 0) - SET_PROGRESS_COUNTER(pThis->GetNumEntries()); - - return kNuOK; -} - -/*static*/ time_t NufxArchive::DateTimeToSeconds(const NuDateTime* pDateTime) -{ - if (pDateTime->second == 0 && - pDateTime->minute == 0 && - pDateTime->hour == 0 && - pDateTime->year == 0 && - pDateTime->day == 0 && - pDateTime->month == 0 && - pDateTime->extra == 0 && - pDateTime->weekDay == 0) - { - // not invalid; just no date set - return kDateNone; - } - - int year; - if (pDateTime->year < 40) - year = pDateTime->year + 2000; - else - year = pDateTime->year + 1900; - - if (year < 1969) { - /* - * Years like 1963 are valid on an Apple II but cannot be represented - * as a time_t, which starts in 1970. (Depending on GMT offsets, - * it actually starts a few hours earlier at the end of 1969.) - * - * I'm catching this here because of an assert in the CTime - * constructor. The constructor seems to do the right thing, and the - * assert won't be present in the shipping version, but it's annoying - * during debugging. - */ - //LOGI(" Ignoring funky year %ld", year); - return kDateInvalid; - } - - // Must range-check values before passing them to CTime constructor, which - // now throws a remarkably fatal exception. - if (pDateTime->month > 11 || // [0,11] - pDateTime->day > 30 || // [0,30] - pDateTime->hour > 59 || // [0,59] - pDateTime->minute > 59 || // [0,59] - pDateTime->second > 59) { // [0,59] - - return kDateInvalid; - } - - CTime modTime(year, - pDateTime->month+1, - pDateTime->day+1, - pDateTime->hour, - pDateTime->minute, - pDateTime->second); - time_t result = (time_t)modTime.GetTime(); - return result; -} - -/*static*/ NuResult NufxArchive::ArrayDeleteHandler(NuArchive* pArchive, void* ptr) -{ - delete[] ptr; - return kNuOK; -} - - -/* - * =========================================================================== - * NufxArchive -- add files (or disks) - * =========================================================================== - */ - -bool NufxArchive::BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) -{ - /* - * This calls into the GenericArchive "AddFile" function, which does - * Win32-specific processing. That function calls our DoAddFile function, - * which does the NuFX stuff. - */ - NuError nerr; - CString errMsg; - WCHAR curDir[MAX_PATH] = L""; - bool retVal = false; - - LOGI("Opts: '%ls' typePres=%d", (LPCWSTR) pAddOpts->fStoragePrefix, - pAddOpts->fTypePreservation); - LOGI(" sub=%d strip=%d ovwr=%d", - pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, - pAddOpts->fOverwriteExisting); - - AddPrep(pActionProgress, pAddOpts); - - pActionProgress->SetArcName(L"(Scanning files to be added...)"); - pActionProgress->SetFileName(L""); - - /* initialize count */ - fNumAdded = 0; - - const CString& directory = pAddOpts->GetDirectory(); - LOGI("Selected path = '%ls'", (LPCWSTR) directory); - - if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { - errMsg = L"Unable to get current directory.\n"; - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - if (SetCurrentDirectory(directory) == false) { - errMsg.Format(L"Unable to set current directory to '%ls'.\n", - (LPCWSTR) directory); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - const CStringArray& fileNames = pAddOpts->GetFileNames(); - for (int i = 0; i < fileNames.GetCount(); i++) { - const CString& name = fileNames.GetAt(i); - LOGI(" file '%ls'", (LPCWSTR) name); - - /* this just provides the list of files to NufxLib */ - nerr = AddFile(pAddOpts, name, &errMsg); - if (nerr != kNuErrNone) { - if (errMsg.IsEmpty()) - errMsg.Format(L"Failed while adding file '%ls': %hs.", - (LPCWSTR) name, NuStrError(nerr)); - if (nerr != kNuErrAborted) { - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } - goto bail; - } - } - - /* actually do the work */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Unable to add files: %hs.", NuStrError(nerr)); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - goto bail; - } - - if (!fNumAdded) { - errMsg = L"No files added.\n"; - fpMsgWnd->MessageBox(errMsg, L"CiderPress", MB_OK | MB_ICONWARNING); - } else { - if (InternalReload(fpMsgWnd) == kNuErrNone) - retVal = true; - else - errMsg = L"Reload failed."; - } - -bail: - NuAbort(fpArchive); // abort anything that didn't get flushed - if (SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - // bummer, but don't signal failure - } - AddFinish(); - return retVal; -} - -bool NufxArchive::AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) -{ - NuError nerr; - CString errMsg; - const int kBlockSize = 512; - PathProposal pathProp; - PathName pathName; - DiskImg* pDiskImg; - NuDataSource* pSource = NULL; - unsigned char* diskData = NULL; - WCHAR curDir[MAX_PATH] = L"\\"; - bool retVal = false; - CStringA storageNameA; - - LOGI("AddDisk: '%ls' (count=%d)", (LPCWSTR) pAddOpts->GetDirectory(), - pAddOpts->GetFileNames().GetCount()); - LOGI("Opts: stpfx='%ls' pres=%d", (LPCWSTR) pAddOpts->fStoragePrefix, - pAddOpts->fTypePreservation); - LOGI(" sub=%d strip=%d ovwr=%d", - pAddOpts->fIncludeSubfolders, pAddOpts->fStripFolderNames, - pAddOpts->fOverwriteExisting); - - if (pAddOpts->GetFileNames().GetCount() != 1) { - LOGW("GLITCH: expected only one filename, found %d", - pAddOpts->GetFileNames().GetCount()); - goto bail; - } - - pDiskImg = pAddOpts->fpDiskImg; - ASSERT(pDiskImg != NULL); - - /* allocate storage for the entire disk */ - diskData = new uint8_t[pDiskImg->GetNumBlocks() * kBlockSize]; - if (diskData == NULL) { - errMsg.Format(L"Unable to allocate %d bytes.", - pDiskImg->GetNumBlocks() * kBlockSize); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - /* prepare to add */ - AddPrep(pActionProgress, pAddOpts); - - const CString& directory = pAddOpts->GetDirectory(); - const CStringArray& fileNames = pAddOpts->GetFileNames(); - LOGD("Selected path = '%ls'", (LPCWSTR) directory); - - if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { - errMsg = L"Unable to get current directory.\n"; - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - if (SetCurrentDirectory(directory) == false) { - errMsg.Format(L"Unable to set current directory to '%ls'.\n", - (LPCWSTR) directory); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - const CString& fileName = pAddOpts->GetFileNames().GetAt(0); - LOGI(" file '%ls'", (LPCWSTR) fileName); - - /* strip off preservation stuff, and ignore it */ - pathProp.Init(fileName); - pathProp.fStripDiskImageSuffix = true; - pathProp.LocalToArchive(pAddOpts); - - /* fill in the necessary file details */ - NuFileDetails details; - memset(&details, 0, sizeof(details)); - details.threadID = kNuThreadIDDiskImage; - details.storageType = kBlockSize; - details.access = kNuAccessUnlocked; - details.extraType = pAddOpts->fpDiskImg->GetNumBlocks(); - storageNameA = pathProp.fStoredPathName; // TODO(Unicode) - details.origName = (LPCWSTR) fileName; // pass wide string through - details.storageNameMOR = storageNameA; - details.fileSysID = kNuFileSysUnknown; - details.fileSysInfo = PathProposal::kDefaultStoredFssep; - - time_t now, then; - - pathName.SetPathName(fileName); - now = time(NULL); - then = pathName.GetModWhen(); - UNIXTimeToDateTime(&now, &details.archiveWhen); - UNIXTimeToDateTime(&then, &details.modWhen); - UNIXTimeToDateTime(&then, &details.createWhen); - - /* set up the progress updater */ - pActionProgress->SetArcName(pathProp.fStoredPathName); - pActionProgress->SetFileName(fileName); - - /* read the disk now that we have progress update titles in place */ - int block, numBadBlocks; - unsigned char* bufPtr; - numBadBlocks = 0; - for (block = 0, bufPtr = diskData; block < pDiskImg->GetNumBlocks(); block++) - { - DIError dierr; - dierr = pDiskImg->ReadBlock(block, bufPtr); - if (dierr != kDIErrNone) - numBadBlocks++; - bufPtr += kBlockSize; - } - if (numBadBlocks > 0) { - CString appName, msg; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - msg.Format(L"Skipped %ld unreadable block%ls.", numBadBlocks, - numBadBlocks == 1 ? L"" : L"s"); - fpMsgWnd->MessageBox(msg, appName, MB_OK | MB_ICONWARNING); - // keep going -- just a warning - } - - /* create a data source for the disk */ - nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, - diskData, 0, pAddOpts->fpDiskImg->GetNumBlocks() * kBlockSize, - NULL, &pSource); - if (nerr != kNuErrNone) { - errMsg = "Unable to create NufxLib data source."; - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - /* add the record; name conflicts cause the error handler to fire */ - NuRecordIdx recordIdx; - nerr = NuAddRecord(fpArchive, &details, &recordIdx); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Failed adding record: %hs.", NuStrError(nerr)); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } - goto bail; - } - - /* do the compression */ - nerr = NuAddThread(fpArchive, recordIdx, kNuThreadIDDiskImage, - pSource, NULL); - if (nerr != kNuErrNone) { - errMsg.Format(L"Failed adding thread: %hs.", NuStrError(nerr)); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - pSource = NULL; /* NufxLib owns it now */ - - /* actually do the work */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Unable to add disk: %hs.", NuStrError(nerr)); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - goto bail; - } - - if (InternalReload(fpMsgWnd) == kNuErrNone) - retVal = true; - -bail: - delete[] diskData; - NuAbort(fpArchive); // abort anything that didn't get flushed - NuFreeDataSource(pSource); - if (SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - // bummer - } - AddFinish(); - return retVal; -} - -NuError NufxArchive::DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) -{ - NuError err; - NuRecordIdx recordIdx = 0; - - do { - // Must re-get the NuFileDetails, because updating the filename for - // rename will invalidate the previous version. - const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails(); - - // TODO(Unicode): NufxLib should accept wide pathname - CStringA origNameA(pDetails->GetLocalPathName()); - CString dummyMsg; - if (!PathName::TestNarrowConversion(pDetails->GetLocalPathName(), - origNameA, &dummyMsg)) { - err = kNuErrInvalidFilename; - goto bail_quiet; - } - err = NuAddFile(fpArchive, origNameA, &nuFileDetails, false, &recordIdx); - - if (err == kNuErrNone) { - fNumAdded++; - } else if (err == kNuErrSkipped) { - /* "maybe overwrite" UI causes this if user declines */ - // fall through with the error - LOGI("DoAddFile: skipped '%ls'", (LPCWSTR) pDetails->GetLocalPathName()); - } else if (err == kNuErrRecordExists) { - AddClashDialog dlg; - - dlg.fWindowsName = pDetails->GetLocalPathName(); - dlg.fStorageName = pDetails->GetStrippedLocalPathName(); - if (dlg.DoModal() != IDOK) { - err = kNuErrAborted; - goto bail_quiet; - } - if (dlg.fDoRename) { - LOGD("add clash: rename to '%ls'", (LPCWSTR) dlg.fNewName); - pDetails->SetStrippedLocalPathName(dlg.fNewName); - continue; - } else { - LOGD("add clash: skip"); - err = kNuErrSkipped; - // fall through with error - } - } - } while (false); - - if (err != kNuErrNone && err != kNuErrAborted && err != kNuErrSkipped) { - CString msg; - msg.Format(L"Unable to add file '%ls': %hs.", - (LPCWSTR) pDetails->GetLocalPathName(), NuStrError(err)); - ShowFailureMsg(fpMsgWnd, msg, IDS_FAILED); - } -bail_quiet: - return err; -} - -void NufxArchive::AddPrep(CWnd* pMsgWnd, const AddFilesDialog* pAddOpts) -{ - NuError nerr; - const Preferences* pPreferences = GET_PREFERENCES(); - int defaultCompression; - - ASSERT(fpArchive != NULL); - - fpMsgWnd = pMsgWnd; - ASSERT(fpMsgWnd != NULL); - - fpAddOpts = pAddOpts; - ASSERT(fpAddOpts != NULL); - - //fBulkProgress = true; - - defaultCompression = pPreferences->GetPrefLong(kPrCompressionType); - nerr = NuSetValue(fpArchive, kNuValueDataCompression, - defaultCompression + kNuCompressNone); - if (nerr != kNuErrNone) { - LOGI("GLITCH: unable to set compression type to %d", - defaultCompression); - /* keep going */ - } - - if (pAddOpts->fOverwriteExisting) - NuSetValue(fpArchive, kNuValueHandleExisting, kNuAlwaysOverwrite); - else - NuSetValue(fpArchive, kNuValueHandleExisting, kNuMaybeOverwrite); - - NuSetErrorHandler(fpArchive, BulkAddErrorHandler); - NuSetExtraData(fpArchive, this); -} - -void NufxArchive::AddFinish(void) -{ - NuSetErrorHandler(fpArchive, NULL); - NuSetValue(fpArchive, kNuValueHandleExisting, kNuMaybeOverwrite); - fpMsgWnd = NULL; - fpAddOpts = NULL; - //fBulkProgress = false; -} - -/*static*/ NuResult NufxArchive::BulkAddErrorHandler(NuArchive* pArchive, - void* vErrorStatus) -{ - const NuErrorStatus* pErrorStatus = (const NuErrorStatus*)vErrorStatus; - NufxArchive* pThis; - NuResult result; - - ASSERT(pArchive != NULL); - (void) NuGetExtraData(pArchive, (void**) &pThis); - ASSERT(pThis != NULL); - ASSERT(pArchive == pThis->fpArchive); - - /* default action is to abort the current operation */ - result = kNuAbort; - - /* - * When adding files, the NuAddFile and NuAddRecord calls can return - * immediate, specific results for a single add. The only reasons for - * calling here are to decide if an existing record should be replaced - * or not (without even an option to rename), or to decide what to do - * when the NuFlush call runs into a problem while adding a file. - */ - if (pErrorStatus->operation != kNuOpAdd) { - ASSERT(false); - return kNuAbort; - } - - if (pErrorStatus->err == kNuErrRecordExists) { - /* if they want to update or freshen, don't hassle them */ - //if (NState_GetModFreshen(pState) || NState_GetModUpdate(pState)) - if (pThis->fpAddOpts->fOverwriteExisting) { - ASSERT(false); // should be handled by AddPrep()/NufxLib - result = kNuOverwrite; - } else - result = pThis->HandleReplaceExisting(pErrorStatus); - } else if (pErrorStatus->err == kNuErrFileNotFound) { - /* file was specified with NuAdd but removed during NuFlush */ - result = pThis->HandleAddNotFound(pErrorStatus); - } - - return result; -} - -NuResult NufxArchive::HandleReplaceExisting(const NuErrorStatus* pErrorStatus) -{ - NuResult result = kNuOK; - - ASSERT(pErrorStatus != NULL); - ASSERT(pErrorStatus->pathnameUNI != NULL); - - ASSERT(pErrorStatus->canOverwrite); - ASSERT(pErrorStatus->canSkip); - ASSERT(pErrorStatus->canAbort); - ASSERT(!pErrorStatus->canRename); // TODO: remember why we can't rename - - /* no firm policy, ask the user */ - ConfirmOverwriteDialog confOvwr; - PathName path(pErrorStatus->pathnameUNI); - - confOvwr.fExistingFile = Charset::ConvertMORToUNI(pErrorStatus->pRecord->filenameMOR); - confOvwr.fExistingFileModWhen = - DateTimeToSeconds(&pErrorStatus->pRecord->recModWhen); - if (pErrorStatus->origPathname != NULL) { - confOvwr.fNewFileSource = (WCHAR*) pErrorStatus->origPathname; - PathName checkPath(confOvwr.fNewFileSource); - confOvwr.fNewFileModWhen = checkPath.GetModWhen(); - } else { - confOvwr.fNewFileSource = L"???"; - confOvwr.fNewFileModWhen = kDateNone; - } - - confOvwr.fAllowRename = false; - if (confOvwr.DoModal() == IDCANCEL) { - result = kNuAbort; - goto bail; - } - if (confOvwr.fResultRename) { - ASSERT(false); - result = kNuAbort; - goto bail; - } - if (confOvwr.fResultApplyToAll) { - if (confOvwr.fResultOverwrite) { - (void) NuSetValue(fpArchive, kNuValueHandleExisting, - kNuAlwaysOverwrite); - } else { - (void) NuSetValue(fpArchive, kNuValueHandleExisting, - kNuNeverOverwrite); - } - } - if (confOvwr.fResultOverwrite) - result = kNuOverwrite; - else - result = kNuSkip; - -bail: - return result; -} - -NuResult NufxArchive::HandleAddNotFound(const NuErrorStatus* pErrorStatus) -{ - CString errMsg; - - errMsg.Format(L"Failed while adding '%hs': file no longer exists.", - pErrorStatus->pathnameUNI); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - - return kNuAbort; -} - - -/* - * =========================================================================== - * NufxArchive -- test files - * =========================================================================== - */ - -bool NufxArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - NuError nerr; - NufxEntry* pEntry; - CString errMsg; - bool retVal = false; - - ASSERT(fpArchive != NULL); - - LOGI("Testing %d entries", pSelSet->GetNumEntries()); - - SelectionEntry* pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = (NufxEntry*) pSelEntry->GetEntry(); - - LOGI(" Testing %ld '%ls'", pEntry->GetRecordIdx(), - (LPCWSTR) pEntry->GetPathNameUNI()); - nerr = NuTestRecord(fpArchive, pEntry->GetRecordIdx()); - if (nerr != kNuErrNone) { - if (nerr == kNuErrAborted) { - CString title; - CheckedLoadString(&title, IDS_MB_APP_NAME); - errMsg = "Cancelled."; - pMsgWnd->MessageBox(errMsg, title, MB_OK); - } else { - errMsg.Format(L"Failed while testing '%ls': %hs.", - (LPCWSTR) pEntry->GetPathNameUNI(), NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - } - goto bail; - } - - pSelEntry = pSelSet->IterNext(); - } - - /* show success message */ - errMsg.Format(L"Tested %d file%ls, no errors found.", - pSelSet->GetNumEntries(), - pSelSet->GetNumEntries() == 1 ? L"" : L"s"); - pMsgWnd->MessageBox(errMsg); - retVal = true; - -bail: - return retVal; -} - - -/* - * =========================================================================== - * NufxArchive -- delete files - * =========================================================================== - */ - -bool NufxArchive::DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - NuError nerr; - NufxEntry* pEntry; - CString errMsg; - bool retVal = false; - - ASSERT(fpArchive != NULL); - - LOGI("Deleting %d entries", pSelSet->GetNumEntries()); - - /* mark entries for deletion */ - SelectionEntry* pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - pEntry = (NufxEntry*) pSelEntry->GetEntry(); - - LOGI(" Deleting %ld '%ls'", pEntry->GetRecordIdx(), - (LPCWSTR) pEntry->GetPathNameUNI()); - nerr = NuDeleteRecord(fpArchive, pEntry->GetRecordIdx()); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to delete record %d: %hs.", - pEntry->GetRecordIdx(), NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - pSelEntry = pSelSet->IterNext(); - } - - /* actually do the delete */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to delete all files: %hs.", NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - } - - if (InternalReload(fpMsgWnd) == kNuErrNone) - retVal = true; - -bail: - return retVal; -} - - -/* - * =========================================================================== - * NufxArchive -- rename files - * =========================================================================== - */ - -bool NufxArchive::RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) -{ - CString errMsg; - NuError nerr; - bool retVal = false; - - ASSERT(fpArchive != NULL); - - LOGI("Renaming %d entries", pSelSet->GetNumEntries()); - - /* - * Figure out if we're allowed to change the entire path. (This is - * doing it the hard way, but what the hell.) - */ - long cap = GetCapability(GenericArchive::kCapCanRenameFullPath); - bool renameFullPath = (cap != 0); - - LOGI("Rename, fullpath=%d", renameFullPath); - - /* - * For each item in the selection set, bring up the "rename" dialog, - * and ask the GenericEntry to process it. - * - * If they hit "cancel" or there's an error, we still flush the - * previous changes. This is so that we don't have to create the - * same sort of deferred-write feature when renaming things in other - * sorts of archives (e.g. disk archives). - */ - SelectionEntry* pSelEntry = pSelSet->IterNext(); - while (pSelEntry != NULL) { - NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry(); - LOGD(" Renaming '%ls'", (LPCWSTR) pEntry->GetPathNameUNI()); - - RenameEntryDialog renameDlg(pMsgWnd); - renameDlg.SetCanRenameFullPath(renameFullPath); - renameDlg.SetCanChangeFssep(true); - renameDlg.fOldName = pEntry->GetPathNameUNI(); - renameDlg.fFssep = pEntry->GetFssep(); - renameDlg.fpArchive = this; - renameDlg.fpEntry = pEntry; - - int result = renameDlg.DoModal(); - if (result == IDOK) { - if (renameDlg.fFssep == '\0') - renameDlg.fFssep = kNufxNoFssep; - CStringA newNameA(renameDlg.fNewName); - nerr = NuRename(fpArchive, pEntry->GetRecordIdx(), - newNameA, renameDlg.fFssep); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to rename '%ls': %hs.", - (LPCWSTR) pEntry->GetPathNameUNI(), NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - break; - } - LOGD("Rename of '%ls' to '%ls' succeeded", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) renameDlg.fNewName); - } else if (result == IDCANCEL) { - LOGI("Canceling out of remaining renames"); - break; - } else { - /* 3rd possibility is IDIGNORE, i.e. skip this entry */ - LOGI("Skipping rename of '%ls'", (LPCWSTR) pEntry->GetDisplayName()); - } - - pSelEntry = pSelSet->IterNext(); - } - - /* flush pending rename calls */ - { - CWaitCursor waitc; - - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to rename all files: %hs.", - NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - } - } - - /* reload GenericArchive from NufxLib */ - if (InternalReload(fpMsgWnd) == kNuErrNone) - retVal = true; - - return retVal; -} - -CString NufxArchive::TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, char newFssep) const -{ - CString errMsg; - ASSERT(pGenericEntry != NULL); - - ASSERT(basePath.IsEmpty()); - - /* can't start or end with fssep */ - if (newName.Left(1) == newFssep || newName.Right(1) == newFssep) { - errMsg.Format(L"Names in NuFX archives may not start or end with a " - L"path separator character (%c).", - newFssep); - goto bail; - } - - /* if it's a disk image, don't allow complex paths */ - if (pGenericEntry->GetRecordKind() == GenericEntry::kRecordKindDisk) { - if (newName.Find(newFssep) != -1) { - errMsg.Format(L"Disk image names may not contain a path separator " - L"character (%c).", - newFssep); - goto bail; - } - } - - /* - * Test for case-sensitive name collisions. Each individual path - * component must be compared - */ - GenericEntry* pEntry; - pEntry = GetEntries(); - while (pEntry != NULL) { - if (pEntry != pGenericEntry && - ComparePaths(pEntry->GetPathNameUNI(), pEntry->GetFssep(), - newName, newFssep) == 0) - { - errMsg = L"An entry with that name already exists."; - } - - pEntry = pEntry->GetNext(); - } - -bail: - return errMsg; -} - - -/* - * =========================================================================== - * NufxArchive -- recompress - * =========================================================================== - */ - -bool NufxArchive::RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) -{ - /* - * We have to uncompress the files into memory and then recompress them. - * We don't want to flush after every file (too slow), but we can't wait - * until they're expanded (unbounded memory requirements). So we have - * to keep expanding until we reach a certain limit, then call flush to - * push the changes out. - * - * Since we're essentially making the changes in place (it's actually - * all getting routed through the temp file), we need to delete the thread - * and re-add it. This isn't quite as thorough as "launder", which - * actually reconstructs the entire record. - */ - const int kMaxSizeInMemory = 2 * 1024 * 1024; // 2MB - CString errMsg; - NuError nerr; - bool retVal = false; - - /* set the compression type */ - nerr = NuSetValue(fpArchive, kNuValueDataCompression, - pRecompOpts->fCompressionType + kNuCompressNone); - if (nerr != kNuErrNone) { - LOGI("GLITCH: unable to set compression type to %d", - pRecompOpts->fCompressionType); - /* keep going */ - } - - fProgressAsRecompress = true; - - /* - * Loop over all items in the selection set. Because the selection - * set has one entry for each interesting thread, we don't need to - * pry the NuRecord open and play with it. - * - * We should only be here for data forks, resource forks, and disk - * images. Comments and filenames are not compressed, and so cannot - * be recompressed. - */ - SelectionEntry* pSelEntry = pSelSet->IterNext(); - long sizeInMemory = 0; - bool result = true; - NufxEntry* pEntry = NULL; - for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) { - pEntry = (NufxEntry*) pSelEntry->GetEntry(); - - /* - * Compress each thread in turn. - */ - if (pEntry->GetHasDataFork()) { - result = RecompressThread(pEntry, GenericEntry::kDataThread, - pRecompOpts, &sizeInMemory, &errMsg); - if (!result) - break; - } - if (pEntry->GetHasRsrcFork()) { - result = RecompressThread(pEntry, GenericEntry::kRsrcThread, - pRecompOpts, &sizeInMemory, &errMsg); - if (!result) - break; - } - if (pEntry->GetHasDiskImage()) { - result = RecompressThread(pEntry, GenericEntry::kDiskImageThread, - pRecompOpts, &sizeInMemory, &errMsg); - if (!result) - break; - } - /* don't do anything with comments */ - - /* if we're sitting on too much, push it out */ - if (sizeInMemory > kMaxSizeInMemory) { - /* flush anything pending */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Unable to recompress all files: %hs.", - NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - } else { - LOGI("Cancelled out of sub-flush/compress"); - } - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - - goto bail; - } - - sizeInMemory = 0; - } - } - - /* handle errors that threw us out of the while loop */ - if (!result) { - ASSERT(pEntry != NULL); - CString dispStr; - dispStr.Format(L"Failed while recompressing '%ls': %ls.", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - - - /* flush anything pending */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Unable to recompress all files: %hs.", - NuStrError(nerr)); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - } else { - LOGI("Cancelled out of flush/compress"); - } - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - } else { - retVal = true; - } - -bail: - /* abort anything that didn't get flushed */ - NuAbort(fpArchive); - /* reload to pick up changes */ - (void) InternalReload(pMsgWnd); - - fProgressAsRecompress = false; - return retVal; -} - -bool NufxArchive::RecompressThread(NufxEntry* pEntry, int threadKind, - const RecompressOptionsDialog* pRecompOpts, long* pSizeInMemory, - CString* pErrMsg) -{ - NuThread thread; - NuThreadID threadID; - NuError nerr; - NuDataSource* pSource = NULL; - CString subErrMsg; - bool retVal = false; - char* buf = NULL; - long len = 0; - - LOGD(" Recompressing %ld '%ls'", pEntry->GetRecordIdx(), - (LPCWSTR) pEntry->GetDisplayName()); - - /* get a copy of the thread header */ - pEntry->FindThreadInfo(threadKind, &thread, pErrMsg); - if (!pErrMsg->IsEmpty()) { - pErrMsg->Format(L"Unable to locate thread for %ls (type %d)", - (LPCWSTR) pEntry->GetDisplayName(), threadKind); - goto bail; - } - threadID = NuGetThreadID(&thread); - - /* if it's already in the target format, skip it */ - if (thread.thThreadFormat == pRecompOpts->fCompressionType) { - LOGI("Skipping (fmt=%d) '%ls'", - pRecompOpts->fCompressionType, - (LPCWSTR) pEntry->GetDisplayName()); - return true; - } - - /* extract the thread */ - int result; - result = pEntry->ExtractThreadToBuffer(threadKind, &buf, &len, &subErrMsg); - if (result == IDCANCEL) { - LOGI("Cancelled during extract!"); - ASSERT(buf == NULL); - goto bail; /* abort anything that was pending */ - } else if (result != IDOK) { - pErrMsg->Format(L"Failed while extracting '%ls': %ls", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) subErrMsg); - goto bail; - } - *pSizeInMemory += len; - - /* create a data source for it */ - nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, (const uint8_t*) buf, 0, len, ArrayDeleteHandler, - &pSource); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"Unable to create NufxLib data source (len=%d).", - len); - goto bail; - } - buf = NULL; // data source owns it now - - /* delete the existing thread */ - //LOGI("+++ DELETE threadIdx=%d", thread.threadIdx); - nerr = NuDeleteThread(fpArchive, thread.threadIdx); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"Unable to delete thread %d: %hs", - pEntry->GetRecordIdx(), NuStrError(nerr)); - goto bail; - } - - /* mark the new thread for addition */ - //LOGI("+++ ADD threadID=0x%08lx", threadID); - nerr = NuAddThread(fpArchive, pEntry->GetRecordIdx(), threadID, - pSource, NULL); - if (nerr != kNuErrNone) { - pErrMsg->Format(L"Unable to add thread type %d: %hs", - threadID, NuStrError(nerr)); - goto bail; - } - pSource = NULL; // now owned by nufxlib - - /* at this point, we just wait for the flush in the outer loop */ - retVal = true; - -bail: - NuFreeDataSource(pSource); - return retVal; -} - - -/* - * =========================================================================== - * NufxArchive -- transfer files to another archive - * =========================================================================== - */ - -GenericArchive::XferStatus NufxArchive::XferSelection(CWnd* pMsgWnd, - SelectionSet* pSelSet, ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) -{ - /* - * We get one entry in the selection set per record. - * - * I think this now throws kXferCancelled whenever it's supposed to. Not - * 100% sure, but it looks good. - */ - LOGD("NufxArchive XferSelection!"); - XferStatus retval = kXferFailed; - unsigned char* dataBuf = NULL; - unsigned char* rsrcBuf = NULL; - CString errMsg, dispMsg; - - pXferOpts->fTarget->XferPrepare(pXferOpts); - - SelectionEntry* pSelEntry = pSelSet->IterNext(); - for ( ; pSelEntry != NULL; pSelEntry = pSelSet->IterNext()) { - long dataLen=-1, rsrcLen=-1; - NufxEntry* pEntry = (NufxEntry*) pSelEntry->GetEntry(); - LocalFileDetails fileDetails; - CString errMsg; - - ASSERT(dataBuf == NULL); - ASSERT(rsrcBuf == NULL); - - /* in case we start handling CRC errors better */ - if (pEntry->GetDamaged()) { - LOGI(" XFER skipping damaged entry '%ls'", - (LPCWSTR) pEntry->GetDisplayName()); - continue; - } - - LOGD(" XFER converting '%ls'", (LPCWSTR) pEntry->GetDisplayName()); - - // Generate a name that is the combination of the sub-volume name - // (if any) and the path name. This is likely the same as the - // "display name", but that is officially for display only, and - // shouldn't be used for this. - CString xferName; - const CString& subVolName = pEntry->GetSubVolName(); - if (!subVolName.IsEmpty()) { - xferName = subVolName + (WCHAR) DiskFS::kDIFssep; - } - xferName += pEntry->GetPathNameUNI(); - - fileDetails.SetStrippedLocalPathName(xferName); - fileDetails.SetFssep(PathProposal::kDefaultStoredFssep); - fileDetails.SetFileSysFmt(DiskImg::kFormatUnknown); - fileDetails.SetFileType(pEntry->GetFileType()); - fileDetails.SetExtraType(pEntry->GetAuxType()); - fileDetails.SetAccess(pEntry->GetAccess()); - fileDetails.SetStorageType(kNuStorageSeedling); - - time_t when; - NuDateTime nuwhen; - when = time(NULL); - UNIXTimeToDateTime(&when, &nuwhen); - fileDetails.SetArchiveWhen(nuwhen); - when = pEntry->GetModWhen(); - UNIXTimeToDateTime(&when, &nuwhen); - fileDetails.SetModWhen(nuwhen); - when = pEntry->GetCreateWhen(); - UNIXTimeToDateTime(&when, &nuwhen); - fileDetails.SetCreateWhen(nuwhen); - - pActionProgress->SetArcName(fileDetails.GetStrippedLocalPathName()); - if (pActionProgress->SetProgress(0) == IDCANCEL) { - retval = kXferCancelled; - goto bail; - } - - /* - * Handle all relevant threads in this record. We assume it's either - * a data/rsrc pair or a disk image. - */ - if (pEntry->GetHasDataFork()) { - /* - * Found a data thread. - */ - int result; - dataBuf = NULL; - dataLen = 0; - result = pEntry->ExtractThreadToBuffer(GenericEntry::kDataThread, - (char**) &dataBuf, &dataLen, &errMsg); - if (result == IDCANCEL) { - LOGI("Cancelled during data extract!"); - retval = kXferCancelled; - goto bail; /* abort anything that was pending */ - } else if (result != IDOK) { - dispMsg.Format(L"Failed while extracting '%ls': %ls.", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); - ShowFailureMsg(pMsgWnd, dispMsg, IDS_FAILED); - goto bail; - } - ASSERT(dataBuf != NULL); - ASSERT(dataLen >= 0); - - } else if (pEntry->GetHasDiskImage()) { - /* - * No data thread found. Look for a disk image. - */ - int result; - dataBuf = NULL; - dataLen = 0; - result = pEntry->ExtractThreadToBuffer(GenericEntry::kDiskImageThread, - (char**) &dataBuf, &dataLen, &errMsg); - if (result == IDCANCEL) { - LOGI("Cancelled during data extract!"); - goto bail; /* abort anything that was pending */ - } else if (result != IDOK) { - dispMsg.Format(L"Failed while extracting '%ls': %ls.", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); - ShowFailureMsg(pMsgWnd, dispMsg, IDS_FAILED); - goto bail; - } - ASSERT(dataBuf != NULL); - ASSERT(dataLen >= 0); - } - - /* - * See if there's a resource fork in here (either by itself or - * with a data fork). - */ - if (pEntry->GetHasRsrcFork()) { - int result; - rsrcBuf = NULL; - rsrcLen = 0; - result = pEntry->ExtractThreadToBuffer(GenericEntry::kRsrcThread, - (char**) &rsrcBuf, &rsrcLen, &errMsg); - if (result == IDCANCEL) { - LOGI("Cancelled during rsrc extract!"); - goto bail; /* abort anything that was pending */ - } else if (result != IDOK) { - dispMsg.Format(L"Failed while extracting '%ls': %ls.", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); - ShowFailureMsg(pMsgWnd, dispMsg, IDS_FAILED); - goto bail; - } - - fileDetails.SetStorageType(kNuStorageExtended); - } else { - ASSERT(rsrcBuf == NULL); - } - - if (dataLen < 0 && rsrcLen < 0) { - LOGI(" XFER: WARNING: nothing worth transferring in '%ls'", - (LPCWSTR) pEntry->GetDisplayName()); - continue; - } - - errMsg = pXferOpts->fTarget->XferFile(&fileDetails, &dataBuf, dataLen, - &rsrcBuf, rsrcLen); - if (!errMsg.IsEmpty()) { - LOGI("XferFile failed!"); - errMsg.Format(L"Failed while transferring '%ls': %ls.", - (LPCWSTR) pEntry->GetDisplayName(), (LPCWSTR) errMsg); - ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - ASSERT(dataBuf == NULL); - ASSERT(rsrcBuf == NULL); - - if (pActionProgress->SetProgress(100) == IDCANCEL) { - retval = kXferCancelled; - goto bail; - } - } - - retval = kXferOK; - -bail: - if (retval != kXferOK) - pXferOpts->fTarget->XferAbort(pMsgWnd); - else - pXferOpts->fTarget->XferFinish(pMsgWnd); - delete[] dataBuf; - delete[] rsrcBuf; - return retval; -} - -void NufxArchive::XferPrepare(const XferFileOptions* pXferOpts) -{ - /* - * We set the "allow duplicates" flag because DOS 3.3 volumes can have - * files with duplicate names. - */ - LOGI(" NufxArchive::XferPrepare"); - (void) NuSetValue(fpArchive, kNuValueAllowDuplicates, true); -} - -CString NufxArchive::XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) -{ - NuError nerr; - const int kFileTypeTXT = 0x04; - NuDataSource* pSource = NULL; - CString errMsg; - - LOGI(" NufxArchive::XferFile '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - LOGI(" dataBuf=0x%p dataLen=%ld rsrcBuf=0x%p rsrcLen=%ld", - *pDataBuf, dataLen, *pRsrcBuf, rsrcLen); - ASSERT(pDataBuf != NULL); - ASSERT(pRsrcBuf != NULL); - - /* NuFX doesn't explicitly store directories */ - if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDirectory) { - delete[] *pDataBuf; - delete[] *pRsrcBuf; - *pDataBuf = *pRsrcBuf = NULL; - goto bail; - } - - ASSERT(dataLen >= 0 || rsrcLen >= 0); - ASSERT(*pDataBuf != NULL || *pRsrcBuf != NULL); - - /* - * Odd bit of trivia: NufxLib refuses to accept an fssep of '\0'. It - * really wants to have one. Which is annoying, since files coming - * from DOS or Pascal don't have one. We therefore need to supply - * one, so we provide 0xff on the theory that nobody in their right - * mind would have it in an Apple II filename. - * - * Since we don't strip Pascal and ProDOS names down when we load - * the disks, it's possible the for 0xff to occur if the disk got - * damaged. For ProDOS we don't care, since it has an fssep, but - * Pascal could be at risk. DOS and RDOS are sanitized and so should - * be okay. - * - * One issue: we don't currently allow changing the fssep when renaming - * a file. We need to fix this, or else there's no way to rename a - * file into a subdirectory once it has been pasted in this way. - * - * TODO: fix this in NufxLib - */ - if (pDetails->GetFssep() == '\0') { - pDetails->SetFssep(kNufxNoFssep); - } - - /* add the record; we have "allow duplicates" enabled for clashes */ - NuRecordIdx recordIdx; - const NuFileDetails& nuFileDetails = pDetails->GetNuFileDetails(); - - nerr = NuAddRecord(fpArchive, &nuFileDetails, &recordIdx); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Failed adding record: %hs", NuStrError(nerr)); - //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } - // else the add was cancelled - goto bail; - } - - if (dataLen >= 0) { - ASSERT(*pDataBuf != NULL); - - /* strip the high ASCII from DOS and RDOS text files */ - if (pDetails->GetEntryKind() != LocalFileDetails::kFileKindDiskImage && - pDetails->GetFileType() == kFileTypeTXT && - DiskImg::UsesDOSFileStructure(pDetails->GetFileSysFmt())) - { - LOGI(" Stripping high ASCII from '%ls'", - (LPCWSTR) pDetails->GetStrippedLocalPathName()); - uint8_t* ucp = *pDataBuf; - long len = dataLen; - - while (len--) - *ucp++ &= 0x7f; - } - - /* create a data source for the data fork; might be zero len */ - nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, - *pDataBuf, 0, dataLen, ArrayDeleteHandler, &pSource); - if (nerr != kNuErrNone) { - errMsg = "Unable to create NufxLib data source."; - //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - *pDataBuf = NULL; /* owned by data source */ - - /* add the data fork, as a disk image if appropriate */ - NuThreadID targetID; - if (pDetails->GetEntryKind() == LocalFileDetails::kFileKindDiskImage) - targetID = kNuThreadIDDiskImage; - else - targetID = kNuThreadIDDataFork; - - nerr = NuAddThread(fpArchive, recordIdx, targetID, pSource, NULL); - if (nerr != kNuErrNone) { - errMsg.Format(L"Failed adding thread: %hs.", NuStrError(nerr)); - //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - pSource = NULL; /* NufxLib owns it now */ - } - - /* add the resource fork, if one was provided */ - if (rsrcLen >= 0) { - ASSERT(*pRsrcBuf != NULL); - - nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, - *pRsrcBuf, 0, rsrcLen, ArrayDeleteHandler, &pSource); - if (nerr != kNuErrNone) { - errMsg = L"Unable to create NufxLib data source."; - //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - *pRsrcBuf = NULL; /* owned by data source */ - - /* add the data fork */ - nerr = NuAddThread(fpArchive, recordIdx, kNuThreadIDRsrcFork, - pSource, NULL); - if (nerr != kNuErrNone) { - errMsg.Format(L"Failed adding thread: %hs.", NuStrError(nerr)); - //ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - goto bail; - } - pSource = NULL; /* NufxLib owns it now */ - } - -bail: - NuFreeDataSource(pSource); - return errMsg; -} - -void NufxArchive::XferAbort(CWnd* pMsgWnd) -{ - /* - * Since we don't do any interim flushes, we can just call NuAbort. If that - * weren't the case, we would need to delete all records and flush. - */ - NuError nerr; - CString errMsg; - - LOGI(" NufxArchive::XferAbort"); - - nerr = NuAbort(fpArchive); - if (nerr != kNuErrNone) { - errMsg.Format(L"Failed while aborting procedure: %hs.", NuStrError(nerr)); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } -} - -void NufxArchive::XferFinish(CWnd* pMsgWnd) -{ - NuError nerr; - CString errMsg; - - LOGI(" NufxArchive::XferFinish"); - - /* actually do the work */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - if (nerr != kNuErrAborted) { - errMsg.Format(L"Unable to add file: %hs.", NuStrError(nerr)); - ShowFailureMsg(fpMsgWnd, errMsg, IDS_FAILED); - } - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - goto bail; - } - - (void) InternalReload(fpMsgWnd); - -bail: - return; -} - - -/* - * =========================================================================== - * NufxArchive -- add/update/delete comments - * =========================================================================== - */ - -/* - * Extract a comment from the archive, converting line terminators to CRLF. - * - * Returns "true" on success, "false" on failure. - */ -bool NufxArchive::GetComment(CWnd* pMsgWnd, const GenericEntry* pGenericEntry, - CString* pStr) -{ - NufxEntry* pEntry = (NufxEntry*) pGenericEntry; - CString errMsg; - const char* kNewEOL = "\r\n"; - int result; - char* buf; - long len; - - ASSERT(pGenericEntry->GetHasComment()); - - /* use standard extract function to pull comment out */ - buf = NULL; - len = 0; - result = pEntry->ExtractThreadToBuffer(GenericEntry::kCommentThread, - &buf, &len, &errMsg); - if (result != IDOK) { - LOGI("Failed getting comment: %hs", buf); - ASSERT(buf == NULL); - return false; - } - - /* convert EOL and add '\0' */ - CString convStr; - const char* ccp; - - ccp = buf; - while (len-- && *ccp != '\0') { - if (len > 1 && *ccp == '\r' && *(ccp+1) == '\n') { - ccp++; - len--; - convStr += kNewEOL; - } else if (*ccp == '\r' || *ccp == '\n') { - convStr += kNewEOL; - } else { - convStr += *ccp; - } - ccp++; - } - - *pStr = convStr; - delete[] buf; - return true; -} - -bool NufxArchive::SetComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry, - const CString& str) -{ - /* - * Set the comment. This requires either adding a new comment or updating - * an existing one. The latter is constrained by the maximum size of the - * comment buffer. - * - * We want to update in place whenever possible because it's faster (don't - * have to rewrite the entire archive), but that really only holds for new - * archives or if we foolishly set the kNuValueModifyOrig flag. - * - * Cleanest approach is to delete the existing thread and add a new one. - * If somebody complains we can try to be smarter about it. - */ - NuDataSource* pSource = NULL; - NufxEntry* pEntry = (NufxEntry*) pGenericEntry; - NuError nerr; - bool retVal = false; - - /* convert CRLF to CR */ - CStringA newStr(str); - char* srcp; - char* dstp; - srcp = dstp = newStr.GetBuffer(0); - while (*srcp != '\0') { - if (*srcp == '\r' && *(srcp+1) == '\n') { - srcp++; - *dstp = '\r'; - } else { - *dstp = *srcp; - } - srcp++; - dstp++; - } - *dstp = '\0'; - newStr.ReleaseBuffer(); - - /* get the thread info */ - CString errMsg; - NuThread thread; - NuThreadIdx threadIdx; - - pEntry->FindThreadInfo(GenericEntry::kCommentThread, &thread, &errMsg); - threadIdx = thread.threadIdx; - if (errMsg.IsEmpty()) { - /* delete existing thread */ - nerr = NuDeleteThread(fpArchive, threadIdx); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to delete thread: %hs.", NuStrError(nerr)); - goto bail; - } - } - - /* set a maximum pre-size value for the thread */ - long maxLen; - maxLen = ((newStr.GetLength() + 99) / 100) * 100; - if (maxLen < 200) - maxLen = 200; - - - /* create a data source to write from */ - nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - maxLen, (const uint8_t*)(LPCSTR) newStr, 0, - newStr.GetLength(), NULL, &pSource); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to create NufxLib data source (len=%d, maxLen=%d).", - newStr.GetLength(), maxLen); - goto bail; - } - - /* add the new thread */ - nerr = NuAddThread(fpArchive, pEntry->GetRecordIdx(), - kNuThreadIDComment, pSource, NULL); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to add comment thread: %hs.", - NuStrError(nerr)); - goto bail; - } - pSource = NULL; // nufxlib owns it now - - /* flush changes */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to flush comment changes: %hs.", - NuStrError(nerr)); - goto bail; - } - - /* reload GenericArchive from NufxLib */ - if (InternalReload(fpMsgWnd) == kNuErrNone) - retVal = true; - -bail: - NuFreeDataSource(pSource); - if (!retVal) { - LOGI("FAILED: %ls", (LPCWSTR) errMsg); - NuAbort(fpArchive); - } - return retVal; -} - -bool NufxArchive::DeleteComment(CWnd* pMsgWnd, GenericEntry* pGenericEntry) -{ - CString errMsg; - NuError nerr; - NufxEntry* pEntry = (NufxEntry*) pGenericEntry; - NuThread thread; - NuThreadIdx threadIdx; - bool retVal = false; - - pEntry->FindThreadInfo(GenericEntry::kCommentThread, &thread, &errMsg); - if (!errMsg.IsEmpty()) - goto bail; - threadIdx = thread.threadIdx; - - nerr = NuDeleteThread(fpArchive, threadIdx); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to delete thread: %hs.", NuStrError(nerr)); - goto bail; - } - - /* flush changes */ - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - errMsg.Format(L"Unable to flush comment deletion: %hs.", - NuStrError(nerr)); - goto bail; - } - - /* reload GenericArchive from NufxLib */ - if (InternalReload(pMsgWnd) == kNuErrNone) - retVal = true; - -bail: - if (retVal != 0) { - LOGI("FAILED: %ls", (LPCWSTR) errMsg); - NuAbort(fpArchive); - } - return retVal; -} - - -bool NufxArchive::SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) -{ - /* - * Sets file properties via the NuSetRecordAttr call. - * - * Gets the existing properties, copies the fields from FileProps over, and - * sets them. [currently only supports file type, aux type, and access flags] - * - * Technically we should reload the GenericArchive from the NufxArchive, - * but the set of changes is pretty small, so we just make them here. - */ - NuError nerr; - NufxEntry* pNufxEntry = (NufxEntry*) pEntry; - const NuRecord* pRecord; - NuRecordAttr recordAttr; - - LOGI(" SET fileType=0x%02x auxType=0x%04x access=0x%02x", - pProps->fileType, pProps->auxType, pProps->access); - - nerr = NuGetRecord(fpArchive, pNufxEntry->GetRecordIdx(), &pRecord); - if (nerr != kNuErrNone) { - LOGI("ERROR: couldn't find recordIdx %ld: %hs", - pNufxEntry->GetRecordIdx(), NuStrError(nerr)); - return false; - } - - NuRecordCopyAttr(&recordAttr, pRecord); - recordAttr.fileType = pProps->fileType; - recordAttr.extraType = pProps->auxType; - recordAttr.access = pProps->access; - - nerr = NuSetRecordAttr(fpArchive, pNufxEntry->GetRecordIdx(), &recordAttr); - if (nerr != kNuErrNone) { - LOGI("ERROR: couldn't set recordAttr %ld: %hs", - pNufxEntry->GetRecordIdx(), NuStrError(nerr)); - return false; - } - - uint32_t statusFlags; - nerr = NuFlush(fpArchive, &statusFlags); - if (nerr != kNuErrNone) { - LOGI("ERROR: NuFlush failed: %hs", NuStrError(nerr)); - - /* see if it got converted to read-only status */ - if (statusFlags & kNuFlushReadOnly) - fIsReadOnly = true; - return false; - } - - LOGI("Props set"); - - /* do this in lieu of reloading GenericArchive */ - pEntry->SetFileType(pProps->fileType); - pEntry->SetAuxType(pProps->auxType); - pEntry->SetAccess(pProps->access); - - return true; -} diff --git a/ciderpress/app/NufxArchive.h b/ciderpress/app/NufxArchive.h deleted file mode 100644 index ea03531..0000000 --- a/ciderpress/app/NufxArchive.h +++ /dev/null @@ -1,256 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * NuFX archive support. - */ -#ifndef APP_NUFXARCHIVE_H -#define APP_NUFXARCHIVE_H - -#include "GenericArchive.h" -#include "../nufxlib/NufxLib.h" // ideally this wouldn't be here, only in .cpp - - -/* - * One file in an NuFX archive. - */ -class NufxEntry : public GenericEntry { -public: - NufxEntry(NuArchive* pArchive) : fpArchive(pArchive) - {} - virtual ~NufxEntry(void) {} - - NuRecordIdx GetRecordIdx(void) const { return fRecordIdx; } - void SetRecordIdx(NuRecordIdx idx) { fRecordIdx = idx; } - - virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength, - CString* pErrMsg) const override; - virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv, - ConvertHighASCII convHA, CString* pErrMsg) const override; - - virtual long GetSelectionSerial(void) const override { return fRecordIdx; } - - virtual bool GetFeatureFlag(Feature feature) const override { - if (feature == kFeaturePascalTypes || feature == kFeatureDOSTypes || - feature == kFeatureHasSimpleAccess) - { - return false; - } else { - return true; - } - } - - /* - * Analyzes the contents of a record to determine if it's a disk, file, - * or "other". Computes the total compressed and uncompressed lengths - * of all data threads. Fills out several GenericEntry fields. - */ - void AnalyzeRecord(const NuRecord* pRecord); - - friend class NufxArchive; - -private: - /* - * Find info for the thread we're about to extract. - * - * Given the NuRecordIdx stored in the object, find the thread whose - * ThreadID matches "which". Copies the NuThread structure into - * "*pThread". - * - * On entry *pErrMsg must be an empty string. On failure, it will - * contain an error message describing the problem. - */ - void FindThreadInfo(int which, NuThread* pThread, CString* pErrMsg) const; - - NuRecordIdx fRecordIdx; // unique record index - NuArchive* fpArchive; -}; - - -/* - * A generic archive plus NuFX-specific goodies. - */ -class NufxArchive : public GenericArchive { -public: - NufxArchive(void) : - fpArchive(NULL), - fIsReadOnly(false), - fProgressAsRecompress(false), - fNumAdded(-1), - fpMsgWnd(NULL), - fpAddOpts(NULL) - {} - virtual ~NufxArchive(void) { (void) Close(); } - - /* - * Perform one-time initialization of the NufxLib library. - * - * Returns with an error if the NufxLib version is off. Major version must - * match (since it indicates an interface change), minor version must be - * >= what we expect (in case we're relying on recent behavior changes). - * - * Returns 0 on success, nonzero on error. - */ - static CString AppInit(void); - - /* - * Finish instantiating a NufxArchive object by opening an existing file. - */ - virtual OpenResult Open(const WCHAR* filename, bool readOnly, - CString* pErrMsg) override; - - /* - * Finish instantiating a NufxArchive object by creating a new archive. - * - * Returns an error string on failure, or "" on success. - */ - virtual CString New(const WCHAR* filename, const void* options) override; - - virtual CString Flush(void) override { return ""; } - virtual CString Reload(void) override; - virtual bool IsReadOnly(void) const override { return fIsReadOnly; }; - virtual bool IsModified(void) const override { return false; } - virtual CString GetDescription() const override { return L"NuFX"; } - virtual bool BulkAdd(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override; - virtual bool AddDisk(ActionProgressDialog* pActionProgress, - const AddFilesDialog* pAddOpts) override; - virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) override; - virtual CString TestPathName(const GenericEntry* pGenericEntry, - const CString& basePath, const CString& newName, - char newFssep) const override; - virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS, - const WCHAR* newName) override - { ASSERT(false); return false; } - virtual CString TestVolumeName(const DiskFS* pDiskFS, - const WCHAR* newName) const override - { ASSERT(false); return L"!"; } - virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - const RecompressOptionsDialog* pRecompOpts) override; - virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet, - ActionProgressDialog* pActionProgress, - const XferFileOptions* pXferOpts) override; - virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry, - CString* pStr) override; - virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry, - const CString& str) override; - virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) override; - virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry, - const FileProps* pProps) override; - virtual void PreferencesChanged(void) override; - virtual long GetCapability(Capability cap) override; - - // try not to use this - NuArchive* GetNuArchivePointer(void) const { return fpArchive; } - - // determine whether a particular type of compression is supported - static bool IsCompressionSupported(NuThreadFormat format); - - // convert from DateTime format to time_t - static time_t DateTimeToSeconds(const NuDateTime* pDateTime); - -private: - virtual CString Close(void) { - if (fpArchive != NULL) { - LOGI("Closing archive (aborting any un-flushed changes)"); - NuAbort(fpArchive); - NuClose(fpArchive); - fpArchive = NULL; - } - return L""; - } - - // recompress one thread - bool RecompressThread(NufxEntry* pEntry, int threadKind, - const RecompressOptionsDialog* pRecompOpts, long* pSizeInMemory, - CString* pErrMsg); - - virtual void XferPrepare(const XferFileOptions* pXferOpts) override; - virtual CString XferFile(LocalFileDetails* pDetails, uint8_t** pDataBuf, - long dataLen, uint8_t** pRsrcBuf, long rsrcLen) override; - virtual void XferAbort(CWnd* pMsgWnd) override; - virtual void XferFinish(CWnd* pMsgWnd) override; - - virtual ArchiveKind GetArchiveKind(void) override { return kArchiveNuFX; } - - // prepare to add files - void AddPrep(CWnd* pWnd, const AddFilesDialog* pAddOpts); - - /* - * Reset some things after we finish adding files. We don't necessarily - * want these to stay in effect for other operations, e.g. extracting. - */ - void AddFinish(void); - - virtual NuError DoAddFile(const AddFilesDialog* pAddOpts, - LocalFileDetails* pDetails) override; - - /* - * Error handler callback for "bulk" adds. - */ - static NuResult BulkAddErrorHandler(NuArchive* pArchive, void* vErrorStatus); - - /* - * Decide whether or not to replace an existing file (during extract) - * or record (during add). - */ - NuResult HandleReplaceExisting(const NuErrorStatus* pErrorStatus); - - /* - * A file that used to be there isn't anymore. - * - * This should be exceedingly rare. - */ - NuResult HandleAddNotFound(const NuErrorStatus* pErrorStatus); - - /* - * Load the contents of an archive into the GenericEntry/NufxEntry list. - */ - NuError LoadContents(void); - - /* - * Reload the contents of the archive, showing an error message if the - * reload fails. - */ - NuError InternalReload(CWnd* pMsgWnd); - - /* - * Static callback function. Used for scanning the contents of an archive. - */ - static NuResult ContentFunc(NuArchive* pArchive, void* vpRecord); - - /* - * Set some standard callbacks and feature flags. - */ - NuError SetCallbacks(void); - - // handle progress update messages - static NuResult ProgressUpdater(NuArchive* pArchive, void* vpProgress); - - // handle error and debug messages from NufxLib. - static NuResult NufxErrorMsgHandler(NuArchive* pArchive, - void* vErrorMessage); - - // handle a DataSource resource release request; used for memory allocated - // with new[] - static NuResult ArrayDeleteHandler(NuArchive* pArchive, void* ptr); - - NuArchive* fpArchive; - bool fIsReadOnly; - - bool fProgressAsRecompress; // tweak progress updater - - /* state while adding files */ - int fNumAdded; - CWnd* fpMsgWnd; - const AddFilesDialog* fpAddOpts; -}; - -#endif /*APP_NUFXARCHIVE_H*/ diff --git a/ciderpress/app/OpenVolumeDialog.cpp b/ciderpress/app/OpenVolumeDialog.cpp deleted file mode 100644 index 12a161d..0000000 --- a/ciderpress/app/OpenVolumeDialog.cpp +++ /dev/null @@ -1,679 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Allow the user to select a disk volume. - */ -#include "stdafx.h" -#include "OpenVolumeDialog.h" -#include "Main.h" -#include "../diskimg/Win32Extra.h" // need disk geometry calls -#include "../diskimg/ASPI.h" - - -BEGIN_MESSAGE_MAP(OpenVolumeDialog, CDialog) - ON_COMMAND(IDHELP, OnHelp) - //ON_NOTIFY(NM_CLICK, IDC_VOLUME_LIST, OnListClick) - ON_NOTIFY(LVN_ITEMCHANGED, IDC_VOLUME_LIST, OnListChange) - ON_NOTIFY(NM_DBLCLK, IDC_VOLUME_LIST, OnListDblClick) - ON_CBN_SELCHANGE(IDC_VOLUME_FILTER, OnVolumeFilterSelChange) -END_MESSAGE_MAP() - - -BOOL OpenVolumeDialog::OnInitDialog(void) -{ - /* - * Sets up the list of drives. - */ - - CDialog::OnInitDialog(); // do any DDX init stuff - const Preferences* pPreferences = GET_PREFERENCES(); - long defaultFilter; - - /* highlight/select entire line, not just filename */ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST); - ASSERT(pListView != NULL); - - ListView_SetExtendedListViewStyleEx(pListView->m_hWnd, - LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); - - /* disable the OK button until they click on something */ - CButton* pButton = (CButton*) GetDlgItem(IDOK); - ASSERT(pButton != NULL); - - pButton->EnableWindow(FALSE); - - /* if the read-only state is fixed, don't let them change it */ - if (!fAllowROChange) { - CButton* pButton; - pButton = (CButton*) GetDlgItem(IDC_OPENVOL_READONLY); - ASSERT(pButton != NULL); - pButton->EnableWindow(FALSE); - } - - /* prep the combo box */ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER); - ASSERT(pCombo != NULL); - defaultFilter = pPreferences->GetPrefLong(kPrVolumeFilter); - if (defaultFilter >= kBoth && defaultFilter <= kPhysical) - pCombo->SetCurSel(defaultFilter); - else { - LOGI("GLITCH: invalid defaultFilter in prefs (%d)", defaultFilter); - pCombo->SetCurSel(kLogical); - } - - /* two columns */ - CRect rect; - pListView->GetClientRect(&rect); - int width; - - width = pListView->GetStringWidth(L"XXVolume or Device NameXXmmmmmm"); - pListView->InsertColumn(0, L"Volume or Device Name", LVCFMT_LEFT, width); - pListView->InsertColumn(1, L"Remarks", LVCFMT_LEFT, - rect.Width() - width - ::GetSystemMetrics(SM_CXVSCROLL)); - - // Load the drive list. - LoadDriveList(); - - // Kluge the physical drive 0 stuff. - DiskImg::SetAllowWritePhys0(GET_PREFERENCES()->GetPrefBool(kPrOpenVolumePhys0)); - - return TRUE; -} - -void OpenVolumeDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Check(pDX, IDC_OPENVOL_READONLY, fReadOnly); - LOGI("DoDataExchange: fReadOnly==%d", fReadOnly); -} - -void OpenVolumeDialog::LoadDriveList(void) -{ - CWaitCursor waitc; - CComboBox* pCombo; - CListCtrl* pListView; - int itemIndex = 0; - int filterSelection; - - pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST); - ASSERT(pListView != NULL); - pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER); - ASSERT(pCombo != NULL); - - pListView->DeleteAllItems(); - - /* - * Load the logical and physical drive sets as needed. Do the "physical" - * set first because it's usually what we want. - */ - filterSelection = pCombo->GetCurSel(); - if (filterSelection == kPhysical || filterSelection == kBoth) - LoadPhysicalDriveList(pListView, &itemIndex); - if (filterSelection == kLogical || filterSelection == kBoth) - LoadLogicalDriveList(pListView, &itemIndex); -} - -bool OpenVolumeDialog::LoadLogicalDriveList(CListCtrl* pListView, int* pItemIndex) -{ - DWORD drivesAvailable; - bool isWin9x = IsWin9x(); - int itemIndex = *pItemIndex; - - ASSERT(pListView != NULL); - - drivesAvailable = GetLogicalDrives(); - if (drivesAvailable == 0) { - LOGI("GetLogicalDrives failed, err=0x%08lx", drivesAvailable); - return false; - } - LOGI("GetLogicalDrives returned 0x%08lx", drivesAvailable); - - // SetErrorMode(SEM_FAILCRITICALERRORS) - - /* run through the list, from A-Z */ - int i; - for (i = 0; i < kMaxLogicalDrives; i++) { - fVolumeInfo[i].driveType = DRIVE_UNKNOWN; - - if ((drivesAvailable >> i) & 0x01) { - WCHAR driveName[] = L"_:\\"; - driveName[0] = 'A' + i; - - unsigned int driveType; - const WCHAR* driveTypeComment = NULL; - BOOL result; - - driveType = fVolumeInfo[i].driveType = GetDriveType(driveName); - switch (driveType) { - case DRIVE_UNKNOWN: - // The drive type cannot be determined. - break; - case DRIVE_NO_ROOT_DIR: - // The root path is invalid. For example, no volume is mounted at the path. - break; - case DRIVE_REMOVABLE: - // The disk can be removed from the drive. - driveTypeComment = L"Removable"; - break; - case DRIVE_FIXED: - // The disk cannot be removed from the drive. - driveTypeComment = L"Local Disk"; - break; - case DRIVE_REMOTE: - // The drive is a remote (network) drive. - driveTypeComment = L"Network"; - break; - case DRIVE_CDROM: - // The drive is a CD-ROM drive. - driveTypeComment = L"CD-ROM"; - break; - case DRIVE_RAMDISK: - // The drive is a RAM disk. - break; - default: - LOGI("UNKNOWN DRIVE TYPE %d", driveType); - break; - } - - if (driveType == DRIVE_CDROM && !DiskImgLib::Global::GetHasSPTI()) { - /* use "physical" device via ASPI instead */ - LOGI("Not including CD-ROM '%ls' in logical drive list", - driveName); - continue; - } - - WCHAR volNameBuf[256]; - WCHAR fsNameBuf[64]; - const WCHAR* errorComment = NULL; - //DWORD fsFlags; - CString entryName, entryRemarks; - - result = ::GetVolumeInformation(driveName, volNameBuf, - NELEM(volNameBuf), NULL, NULL, NULL /*&fsFlags*/, fsNameBuf, - NELEM(fsNameBuf)); - if (result == FALSE) { - DWORD err = GetLastError(); - if (err == ERROR_UNRECOGNIZED_VOLUME) { - // Win2K: media exists but format not recognized - errorComment = L"Non-Windows format"; - } else if (err == ERROR_NOT_READY) { - // Win2K: device exists but no media loaded - if (isWin9x) { - LOGI("Not showing drive '%ls': not ready", - driveName); - continue; // safer not to show it - } else - errorComment = L"Not ready"; - } else if (err == ERROR_PATH_NOT_FOUND /*Win2K*/ || - err == ERROR_INVALID_DATA /*Win98*/) - { - // Win2K/Win98: device letter not in use - LOGI("GetVolumeInformation '%ls': nothing there", - driveName); - continue; - } else if (err == ERROR_INVALID_PARAMETER) { - // Win2K: device is already open - //LOGI("GetVolumeInformation '%ls': currently open??", - // driveName); - errorComment = L"(currently open?)"; - //continue; - } else if (err == ERROR_ACCESS_DENIED) { - // Win2K: disk is open no-read-sharing elsewhere - errorComment = L"(already open read-write)"; - } else if (err == ERROR_GEN_FAILURE) { - // Win98: floppy format not recognzied - // --> we don't want to access ProDOS floppies via A: in - // Win98, so we skip it here - LOGI("GetVolumeInformation '%ls': general failure", - driveName); - continue; - } else if (err == ERROR_INVALID_FUNCTION) { - // Win2K: CD-ROM with HFS - if (driveType == DRIVE_CDROM) - errorComment = L"Non-Windows format"; - else - errorComment = L"(invalid disc?)"; - } else { - LOGI("GetVolumeInformation '%ls' failed: %ld", - driveName, GetLastError()); - continue; - } - ASSERT(errorComment != NULL); - - entryName.Format(L"(%c:)", 'A' + i); - if (driveTypeComment != NULL) - entryRemarks.Format(L"%ls - %ls", driveTypeComment, - errorComment); - else - entryRemarks.Format(L"%ls", errorComment); - } else { - entryName.Format(L"%ls (%c:)", volNameBuf, 'A' + i); - if (driveTypeComment != NULL) - entryRemarks.Format(L"%ls", driveTypeComment); - else - entryRemarks = ""; - } - - pListView->InsertItem(itemIndex, entryName); - pListView->SetItemText(itemIndex, 1, entryRemarks); - pListView->SetItemData(itemIndex, (DWORD) i + 'A'); -//LOGI("%%%% added logical %d", itemIndex); - itemIndex++; - } else { - LOGI(" (drive %c not available)", i + 'A'); - } - } - - *pItemIndex = itemIndex; - - return true; -} - -bool OpenVolumeDialog::LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex) -{ - /* - * I don't see a clever way to do this in Win2K except to open the first 8 - * or so devices and see what happens. - * - * Win9x isn't much better, though you can be reasonably confident that there - * are at most 4 floppy drives and 4 hard drives. - * - * TODO: check again for a better way - */ - - bool isWin9x = IsWin9x(); - int itemIndex = *pItemIndex; - int i; - - if (isWin9x) { - // fairly arbitrary choices - const int kMaxFloppies = 4; - const int kMaxHardDrives = 8; - - for (i = 0; i < kMaxFloppies; i++) { - CString driveName, remark; - bool result; - - result = HasPhysicalDriveWin9x(i, &remark); - if (result) { - driveName.Format(L"Floppy disk %d", i); - pListView->InsertItem(itemIndex, driveName); - pListView->SetItemText(itemIndex, 1, remark); - pListView->SetItemData(itemIndex, (DWORD) i); -//LOGI("%%%% added floppy %d", itemIndex); - itemIndex++; - } - } - for (i = 0; i < kMaxHardDrives; i++) { - CString driveName, remark; - bool result; - - result = HasPhysicalDriveWin9x(i + 128, &remark); - if (result) { - driveName.Format(L"Hard drive %d", i); - pListView->InsertItem(itemIndex, driveName); - pListView->SetItemText(itemIndex, 1, remark); - pListView->SetItemData(itemIndex, (DWORD) i + 128); -//LOGI("%%%% added HD %d", itemIndex); - itemIndex++; - } - } - } else { - for (i = 0; i < kMaxPhysicalDrives; i++) { - CString driveName, remark; - bool result; - - result = HasPhysicalDriveWin2K(i + 128, &remark); - if (result) { - driveName.Format(L"Physical disk %d", i); - pListView->InsertItem(itemIndex, driveName); - pListView->SetItemText(itemIndex, 1, remark); - pListView->SetItemData(itemIndex, (DWORD) i + 128); // HD volume - itemIndex++; - } - } - } - - if (DiskImgLib::Global::GetHasASPI()) { - LOGI("IGNORING ASPI"); -#if 0 // can we remove this? - DIError dierr; - DiskImgLib::ASPI* pASPI = DiskImgLib::Global::GetASPI(); - ASPIDevice* deviceArray = NULL; - int numDevices; - - dierr = pASPI->GetAccessibleDevices( - ASPI::kDevMaskCDROM | ASPI::kDevMaskHardDrive, - &deviceArray, &numDevices); - if (dierr == kDIErrNone) { - LOGI("Adding %d ASPI CD-ROM devices", numDevices); - for (i = 0; i < numDevices; i++) { - CString driveName, remark; - CString addr, vendor, product; - DWORD aspiAddr; - - addr.Format("ASPI %d:%d:%d", - deviceArray[i].GetAdapter(), - deviceArray[i].GetTarget(), - deviceArray[i].GetLun()); - vendor = deviceArray[i].GetVendorID(); - vendor.TrimRight(); - product = deviceArray[i].GetProductID(); - product.TrimRight(); - - driveName.Format("%s %s", vendor, product); - if (deviceArray[i].GetDeviceType() == ASPIDevice::kTypeCDROM) - remark = "CD-ROM"; - else if (deviceArray[i].GetDeviceType() == ASPIDevice::kTypeDASD) - remark = "Direct-access device"; - if (!deviceArray[i].GetDeviceReady()) - remark += " - Not ready"; - - aspiAddr = (DWORD) 0xaa << 24 | - (DWORD) deviceArray[i].GetAdapter() << 16 | - (DWORD) deviceArray[i].GetTarget() << 8 | - (DWORD) deviceArray[i].GetLun(); - //LOGI("ADDR for '%s' is 0x%08lx", - // (const char*) driveName, aspiAddr); - - pListView->InsertItem(itemIndex, driveName); - pListView->SetItemText(itemIndex, 1, remark); - pListView->SetItemData(itemIndex, aspiAddr); - itemIndex++; - } - } - - delete[] deviceArray; -#endif - } - - *pItemIndex = itemIndex; - return true; -} - -bool OpenVolumeDialog::HasPhysicalDriveWin9x(int unit, CString* pRemark) -{ - HANDLE handle = NULL; - const int VWIN32_DIOC_DOS_INT13 = 4; - const int CARRY_FLAG = 1; - BOOL result; - typedef struct _DIOC_REGISTERS { - DWORD reg_EBX; - DWORD reg_EDX; - DWORD reg_ECX; - DWORD reg_EAX; - DWORD reg_EDI; - DWORD reg_ESI; - DWORD reg_Flags; - } DIOC_REGISTERS, *PDIOC_REGISTERS; - DIOC_REGISTERS reg = {0}; - DWORD lastError, cb; - unsigned char buf[512]; - - if (unit > 4) - return false; // floppy drives only - - handle = CreateFile(L"\\\\.\\vwin32", 0, 0, NULL, - OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (handle == INVALID_HANDLE_VALUE) { - LOGI(" Unable to open vwin32: %ld", ::GetLastError()); - return false; - } - -#if 0 // didn't do what I wanted - reg.reg_EAX = MAKEWORD(0, 0x00); // func 0x00 == reset controller - reg.reg_EDX = MAKEWORD(unit, 0); // specify driver - result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, ®, - sizeof(reg), ®, sizeof(reg), &cb, 0); - LOGI(" DriveReset(drive=0x%02x) result=%d carry=%d", - unit, result, reg.reg_Flags & CARRY_FLAG); -#endif - - reg.reg_EAX = MAKEWORD(1, 0x02); // read 1 sector - reg.reg_EBX = (DWORD) buf; - reg.reg_ECX = MAKEWORD(1, 0); // sector 0 (+1), cylinder 0 - reg.reg_EDX = MAKEWORD(unit, 0); // head - - result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, ®, - sizeof(reg) /*bytes*/, ®, sizeof(reg) /*bytes*/, &cb, 0); - lastError = GetLastError(); - ::CloseHandle(handle); - - if (result == 0 || (reg.reg_Flags & CARRY_FLAG)) { - int ah = HIBYTE(reg.reg_EAX); - LOGI(" DevIoCtrl(unit=%02xh) failed: result=%d lastErr=%d Flags=0x%08lx", - unit, result, lastError, reg.reg_Flags); - LOGI(" AH=%d (EAX=0x%08lx) byte=0x%02x", ah, reg.reg_EAX, buf[0]); - if (ah != 1) { - // failure code 1 means "invalid parameter", drive doesn't exist - // mine returns 128, "timeout", when no disk is in the drive - *pRemark = "Not ready"; - return true; - } else - return false; - } - - *pRemark = "Removable"; - - return true; -} - -bool OpenVolumeDialog::HasPhysicalDriveWin2K(int unit, CString* pRemark) -{ - HANDLE hDevice; // handle to the drive to be examined - DISK_GEOMETRY dg; // disk drive geometry structure - DISK_GEOMETRY_EX dge; // extended geometry request buffer - BOOL result; // results flag - DWORD junk; // discard results - LONGLONG diskSize; // size of the drive, in bytes - CString fileName; - DWORD err; - - /* - * See if the drive is there. - */ - ASSERT(unit >= 128 && unit < 160); // arbitrary max - fileName.Format(L"\\\\.\\PhysicalDrive%d", unit - 128); - - hDevice = ::CreateFile(fileName, // drive to open - 0, // no access to the drive - FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode - NULL, // default security attributes - OPEN_EXISTING, // disposition - 0, // file attributes - NULL); // do not copy file attributes - - if (hDevice == INVALID_HANDLE_VALUE) // cannot open the drive - return false; - - /* - * Try to get the drive geometry. First try with the fancy WinXP call, - * then fall back to the Win2K call if it doesn't exist. - */ - result = ::DeviceIoControl(hDevice, - IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, - NULL, 0, // input buffer - &dge, sizeof(dge), // output buffer + size in bytes - &junk, // # bytes returned - (LPOVERLAPPED) NULL); // synchronous I/O - if (result) { - diskSize = dge.DiskSize.QuadPart; - LOGI(" EX results for device %02xh", unit); - LOGI(" Disk size = %I64d (bytes) = %I64d (MB)", - diskSize, diskSize / (1024*1024)); - if (diskSize > 1024*1024*1024) - pRemark->Format(L"Size is %.2fGB", - (double) diskSize / (1024.0 * 1024.0 * 1024.0)); - else - pRemark->Format(L"Size is %.2fMB", - (double) diskSize / (1024.0 * 1024.0)); - } else { - // Win2K shows ERROR_INVALID_FUNCTION or ERROR_NOT_SUPPORTED - LOGI("IOCTL_DISK_GET_DRIVE_GEOMETRY_EX failed, error was %ld", - GetLastError()); - result = ::DeviceIoControl(hDevice, // device to be queried - IOCTL_DISK_GET_DRIVE_GEOMETRY, // operation to perform - NULL, 0, // no input buffer - &dg, sizeof(dg), // output buffer + size in bytes - &junk, // # bytes returned - (LPOVERLAPPED) NULL); // synchronous I/O - - if (result) { - LOGI(" Results for device %02xh", unit); - LOGI(" Cylinders = %I64d", dg.Cylinders.QuadPart); - LOGI(" Tracks per cylinder = %ld", (ULONG) dg.TracksPerCylinder); - LOGI(" Sectors per track = %ld", (ULONG) dg.SectorsPerTrack); - LOGI(" Bytes per sector = %ld", (ULONG) dg.BytesPerSector); - - diskSize = dg.Cylinders.QuadPart * (ULONG)dg.TracksPerCylinder * - (ULONG)dg.SectorsPerTrack * (ULONG)dg.BytesPerSector; - LOGI("Disk size = %I64d (bytes) = %I64d (MB)", diskSize, - diskSize / (1024 * 1024)); - if (diskSize > 1024*1024*1024) - pRemark->Format(L"Size is %.2fGB", - (double) diskSize / (1024.0 * 1024.0 * 1024.0)); - else - pRemark->Format(L"Size is %.2fMB", - (double) diskSize / (1024.0 * 1024.0)); - } else { - err = GetLastError(); - } - } - - ::CloseHandle(hDevice); - - if (!result) { - LOGI("DeviceIoControl(IOCTL_DISK_GET_DRIVE_GEOMETRY) failed (err=%ld)", - err); - *pRemark = "Not ready"; - } - - return true; -} - -void OpenVolumeDialog::OnListChange(NMHDR*, LRESULT* pResult) -{ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST); - CButton* pButton = (CButton*) GetDlgItem(IDOK); - pButton->EnableWindow(pListView->GetSelectedCount() != 0); - //LOGI("ENABLE %d", pListView->GetSelectedCount() != 0); - - *pResult = 0; -} - -void OpenVolumeDialog::OnListDblClick(NMHDR* pNotifyStruct, LRESULT* pResult) -{ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST); - CButton* pButton = (CButton*) GetDlgItem(IDOK); - - if (pListView->GetSelectedCount() != 0) { - pButton->EnableWindow(); - OnOK(); - } - - *pResult = 0; -} - -void OpenVolumeDialog::OnVolumeFilterSelChange(void) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER); - ASSERT(pCombo != NULL); - LOGI("+++ SELECTION IS NOW %d", pCombo->GetCurSel()); - LoadDriveList(); -} - -void OpenVolumeDialog::OnOK(void) -{ - /* - * Figure out the (zero-based) drive letter. - */ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUME_LIST); - ASSERT(pListView != NULL); - - if (pListView->GetSelectedCount() != 1) { - CString msg, failed; - CheckedLoadString(&failed, IDS_FAILED); - CheckedLoadString(&msg, IDS_VOLUME_SELECT_ONE); - MessageBox(msg, failed, MB_OK); - return; - } - - POSITION posn; - posn = pListView->GetFirstSelectedItemPosition(); - if (posn == NULL) { - ASSERT(false); - return; - } - int num = pListView->GetNextSelectedItem(posn); - DWORD driveID = pListView->GetItemData(num); - UINT formatID = 0; - - if (HIBYTE(HIWORD(driveID)) == 0xaa) { - // TODO: remove this? - fChosenDrive.Format(L"%hs%d:%d:%d\\", - DiskImgLib::kASPIDev, - LOBYTE(HIWORD(driveID)), - HIBYTE(LOWORD(driveID)), - LOBYTE(LOWORD(driveID))); - //ForceReadOnly(true); - } else if (driveID >= 'A' && driveID <= 'Z') { - /* - * Do we want to let them do this? We show some logical drives - * that we don't want them to actually use. - */ - switch (fVolumeInfo[driveID-'A'].driveType) { - case DRIVE_REMOVABLE: - case DRIVE_FIXED: - break; // allow - case DRIVE_CDROM: //formatID = IDS_VOLUME_NO_CDROM; - ForceReadOnly(true); - break; - case DRIVE_REMOTE: formatID = IDS_VOLUME_NO_REMOTE; - break; - case DRIVE_RAMDISK: formatID = IDS_VOLUME_NO_RAMDISK; - break; - case DRIVE_UNKNOWN: - case DRIVE_NO_ROOT_DIR: - default: formatID = IDS_VOLUME_NO_GENERIC; - break; - } - - fChosenDrive.Format(L"%c:\\", driveID); - } else if ((driveID >= 0 && driveID < 4) || - (driveID >= 0x80 && driveID < 0x88)) - { - fChosenDrive.Format(L"%02x:\\", driveID); - } else { - ASSERT(false); - return; - } - - if (formatID != 0) { - CString msg, notAllowed; - - CheckedLoadString(¬Allowed, IDS_NOT_ALLOWED); - CheckedLoadString(&msg, formatID); - MessageBox(msg, notAllowed, MB_OK); - } else { - Preferences* pPreferences = GET_PREFERENCES_WR(); - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_VOLUME_FILTER); - pPreferences->SetPrefLong(kPrVolumeFilter, pCombo->GetCurSel()); - LOGI("SETTING PREF TO %ld", pCombo->GetCurSel()); - - CDialog::OnOK(); - } -} - -void OpenVolumeDialog::ForceReadOnly(bool readOnly) const -{ - CButton* pButton = (CButton*) GetDlgItem(IDC_OPENVOL_READONLY); - ASSERT(pButton != NULL); - - if (readOnly) - pButton->SetCheck(BST_CHECKED); - else - pButton->SetCheck(BST_UNCHECKED); - LOGW("FORCED READ ONLY %d", readOnly); -} diff --git a/ciderpress/app/OpenVolumeDialog.h b/ciderpress/app/OpenVolumeDialog.h deleted file mode 100644 index e99f190..0000000 --- a/ciderpress/app/OpenVolumeDialog.h +++ /dev/null @@ -1,109 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#ifndef APP_OPENVOLUMEDIALOG_H -#define APP_OPENVOLUMEDIALOG_H - -#include -#include "resource.h" - -/* - * A dialog with a list control that we populate with the names of the - * volumes in the system. - */ -class OpenVolumeDialog : public CDialog { -public: - OpenVolumeDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_OPENVOLUMEDLG, pParentWnd), - fChosenDrive(L""), - fAllowROChange(true) - { - Preferences* pPreferences = GET_PREFERENCES_WR(); - fReadOnly = pPreferences->GetPrefBool(kPrOpenVolumeRO); - } - - // Result: drive to open (e.g. "A:\" or "80:\") - CString fChosenDrive; - - // Did the user check "read only"? (sets default and holds return val) - BOOL fReadOnly; - // Set before calling DoModal to disable "read only" checkbox - bool fAllowROChange; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - virtual void OnOK(void) override; - - /* - * Something changed in the list. Update the "OK" button. - */ - afx_msg void OnListChange(NMHDR* pNotifyStruct, LRESULT* pResult); - - /* - * The volume filter drop-down box has changed. - */ - afx_msg void OnVolumeFilterSelChange(void); - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_OPEN_VOLUME); - } - - afx_msg void OnListDblClick(NMHDR* pNotifyStruct, LRESULT* pResult); - - // 0 is default; numbers must match up with pop-up menu order - // order also matters for range test in OnInitDialog - enum { kBoth=0, kLogical=1, kPhysical=2 }; - // common constants - enum { kMaxLogicalDrives = 26, kMaxPhysicalDrives = 8 }; - - /* - * Load the set of logical and physical drives. - */ - void LoadDriveList(void); - - /* - * Determine the logical volumes available in the system and stuff them into - * the list. - */ - bool LoadLogicalDriveList(CListCtrl* pListView, int* pItemIndex); - - /* - * Add a list of physical drives to the list control. - */ - bool LoadPhysicalDriveList(CListCtrl* pListView, int* pItemIndex); - - /* - * Determine whether physical device N exists. - * - * Pass in the Int13 unit number, i.e. 0x00 for the first floppy drive. Win9x - * makes direct access to the hard drive very difficult, so we don't even try. - * - * TODO: remove this entirely? - */ - bool HasPhysicalDriveWin9x(int unit, CString* pRemark); - - /* - * Determine whether physical device N exists. - * - * Pass in the Int13 unit number, i.e. 0x80 for the first hard drive. This - * should not be called with units for floppy drives (e.g. 0x00). - */ - bool HasPhysicalDriveWin2K(int unit, CString* pRemark); - - /* - * Set the state of the "read only" checkbox in the dialog. - */ - void ForceReadOnly(bool readOnly) const; - - struct { - unsigned int driveType; - } fVolumeInfo[kMaxLogicalDrives]; - - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_OPENVOLUMEDIALOG_H*/ diff --git a/ciderpress/app/PasteSpecialDialog.cpp b/ciderpress/app/PasteSpecialDialog.cpp deleted file mode 100644 index 400493f..0000000 --- a/ciderpress/app/PasteSpecialDialog.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for PasteSpecialDialog. - */ -#include "StdAfx.h" -#include "PasteSpecialDialog.h" - -#if 0 -BEGIN_MESSAGE_MAP(PasteSpecialDialog, CDialog) -END_MESSAGE_MAP() - -BOOL -PasteSpecialDialog::OnInitDialog(void) -{ - CString countStr; - CWnd* pWnd; - - countStr.Format(IDS_PASTE_SPECIAL_COUNT, 3); - pWnd = GetDlgItem(IDC_PASTE_SPECIAL_COUNT); - pWnd->SetWindowText(countStr); - - return CDialog::OnInitDialog(); -} -#endif - -void PasteSpecialDialog::DoDataExchange(CDataExchange* pDX) -{ - /* - * Initialize radio control with value from fPasteHow. - */ - - if (!pDX->m_bSaveAndValidate) { - UINT ctrlId; - - if (fPasteHow == kPastePaths) - ctrlId = IDC_PASTE_SPECIAL_PATHS; - else - ctrlId = IDC_PASTE_SPECIAL_NOPATHS; - - CButton* pButton = (CButton*) GetDlgItem(ctrlId); - pButton->SetCheck(BST_CHECKED); - } else { - CButton* pButton = (CButton*) GetDlgItem(IDC_PASTE_SPECIAL_PATHS); - - if (pButton->GetCheck() == BST_CHECKED) - fPasteHow = kPastePaths; - else - fPasteHow = kPasteNoPaths; - } -} diff --git a/ciderpress/app/PasteSpecialDialog.h b/ciderpress/app/PasteSpecialDialog.h deleted file mode 100644 index 8ff6256..0000000 --- a/ciderpress/app/PasteSpecialDialog.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Paste Special dialog. - */ -#ifndef APP_PASTESPECIALDIALOG_H -#define APP_PASTESPECIALDIALOG_H - -#include "resource.h" - -/* - * Simple dialog with a radio button. - */ -class PasteSpecialDialog : public CDialog { -public: - PasteSpecialDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_PASTE_SPECIAL, pParentWnd), - fPasteHow(kPastePaths) - {} - virtual ~PasteSpecialDialog() {} - - /* right now this is boolean, but that may change */ - /* (e.g. "paste clipboard contents into new text file") */ - enum { - kPasteUnknown = 0, - kPastePaths, - kPasteNoPaths, - }; - int fPasteHow; - -protected: - //virtual BOOL OnInitDialog(void); - void DoDataExchange(CDataExchange* pDX) override; - - //DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_PASTESPECIALDIALOG_H*/ diff --git a/ciderpress/app/Preferences.cpp b/ciderpress/app/Preferences.cpp deleted file mode 100644 index e64263f..0000000 --- a/ciderpress/app/Preferences.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Save and restore preferences from the config file. - */ -#include "stdafx.h" -#include "Preferences.h" -#include "NufxArchive.h" -#include "MyApp.h" -#include "../util/UtilLib.h" - -static const WCHAR kDefaultTempPath[] = L"."; - -/* registry section for columns */ -static const WCHAR kColumnSect[] = L"columns"; -/* registry section for file add options */ -static const WCHAR kAddSect[] = L"add"; -/* registry section for extraction options */ -static const WCHAR kExtractSect[] = L"extract"; -/* registry section for view options */ -static const WCHAR kViewSect[] = L"view"; -/* registry section for logical/physical volume operations */ -static const WCHAR kVolumeSect[] = L"volume"; -/* registry section for file-to-disk options */ -//static const WCHAR kConvDiskSect[] = L"conv-disk"; -/* registry section for disk-to-file options */ -static const WCHAR kConvFileSect[] = L"conv-file"; -/* registry section for folders */ -static const WCHAR kFolderSect[] = L"folders"; -/* registry section for preferences on property pages */ -static const WCHAR kPrefsSect[] = L"prefs"; -/* registry section for miscellaneous settings */ -static const WCHAR kMiscSect[] = L"misc"; - - -/* - * Map PrefNum to type and registry string. - * - * To make life easier, we require that the PrefNum (first entry) match the - * offset in the table. That way instead of searching for a match we can just - * index into the table. - */ -const Preferences::PrefMap Preferences::fPrefMaps[kPrefNumLastEntry] = { - /**/ { kPrefNumUnknown, kPTNone, NULL, NULL }, - - { kPrAddIncludeSubFolders, kBool, kAddSect, L"include-sub-folders" }, - { kPrAddStripFolderNames, kBool, kAddSect, L"strip-folder-names" }, - { kPrAddOverwriteExisting, kBool, kAddSect, L"overwrite-existing" }, - { kPrAddTypePreservation, kLong, kAddSect, L"type-preservation" }, - { kPrAddConvEOL, kLong, kAddSect, L"conv-eol" }, - -// { kPrExtractPath, kString, kExtractSect, L"path" }, - { kPrExtractConvEOL, kLong, kExtractSect, L"conv-eol" }, - { kPrExtractConvHighASCII, kBool, kExtractSect, L"conv-high-ascii" }, - { kPrExtractIncludeData, kBool, kExtractSect, L"include-data" }, - { kPrExtractIncludeRsrc, kBool, kExtractSect, L"include-rsrc" }, - { kPrExtractIncludeDisk, kBool, kExtractSect, L"include-disk" }, - { kPrExtractEnableReformat, kBool, kExtractSect, L"enable-reformat" }, - { kPrExtractDiskTo2MG, kBool, kExtractSect, L"disk-to-2mg" }, - { kPrExtractAddTypePreservation, kBool, kExtractSect, L"add-type-preservation" }, - { kPrExtractAddExtension, kBool, kExtractSect, L"add-extension" }, - { kPrExtractStripFolderNames, kBool, kExtractSect, L"strip-folder-names" }, - { kPrExtractOverwriteExisting, kBool, kExtractSect, L"overwrite-existing" }, - -// { kPrViewIncludeDataForks, kBool, kViewSect, L"include-data-forks" }, -// { kPrViewIncludeRsrcForks, kBool, kViewSect, L"include-rsrc-forks" }, -// { kPrViewIncludeDiskImages, kBool, kViewSect, L"include-disk-images" }, -// { kPrViewIncludeComments, kBool, kViewSect, L"include-comments" }, - - { kPrConvFileEmptyFolders, kBool, kConvFileSect, L"preserve-empty-folders" }, - - { kPrOpenArchiveFolder, kString, kFolderSect, L"open-archive" }, - { kPrConvertArchiveFolder, kString, kFolderSect, L"convert-archive" }, - { kPrAddFileFolder, kString, kFolderSect, L"add-file" }, - { kPrExtractFileFolder, kString, kFolderSect, L"extract-file" }, - - { kPrVolumeFilter, kLong, kVolumeSect, L"open-filter" }, - //{ kPrVolumeReadOnly, kBool, kVolumeSect, L"read-only" }, - - { kPrCassetteAlgorithm, kLong, kVolumeSect, L"cassette-algorithm" }, - { kPrOpenWAVFolder, kString, kFolderSect, L"open-wav" }, - - { kPrMimicShrinkIt, kBool, kPrefsSect, L"mimic-shrinkit" }, - { kPrBadMacSHK, kBool, kPrefsSect, L"bad-mac-shk" }, - { kPrReduceSHKErrorChecks, kBool, kPrefsSect, L"reduce-shk-error-checks" }, - { kPrCoerceDOSFilenames, kBool, kPrefsSect, L"coerce-dos-filenames" }, - { kPrSpacesToUnder, kBool, kPrefsSect, L"spaces-to-under" }, - { kPrPasteJunkPaths, kBool, kPrefsSect, L"paste-junk-paths" }, - { kPrBeepOnSuccess, kBool, kPrefsSect, L"beep-on-success" }, - - { kPrQueryImageFormat, kBool, kPrefsSect, L"query-image-format" }, - { kPrOpenVolumeRO, kBool, kPrefsSect, L"open-volume-ro" }, - { kPrOpenVolumePhys0, kBool, kPrefsSect, L"open-volume-phys0" }, - { kPrProDOSAllowLower, kBool, kPrefsSect, L"prodos-allow-lower" }, - { kPrProDOSUseSparse, kBool, kPrefsSect, L"prodos-use-sparse" }, - - { kPrCompressionType, kLong, kPrefsSect, L"compression-type" }, - - { kPrMaxViewFileSize, kLong, kPrefsSect, L"max-view-file-size" }, - { kPrNoWrapText, kBool, kPrefsSect, L"no-wrap-text" }, - - { kPrHighlightHexDump, kBool, kPrefsSect, L"highlight-hex-dump" }, - { kPrHighlightBASIC, kBool, kPrefsSect, L"highlight-basic" }, - { kPrConvHiResBlackWhite, kBool, kPrefsSect, L"conv-hi-res-black-white" }, - { kPrConvDHRAlgorithm, kLong, kPrefsSect, L"dhr-algorithm" }, - { kPrRelaxGfxTypeCheck, kBool, kPrefsSect, L"relax-gfx-type-check" }, - { kPrDisasmOneByteBrkCop, kBool, kPrefsSect, L"disasm-onebytebrkcop" }, - { kPrConvMouseTextToASCII, kBool, kPrefsSect, L"conv-mouse-text-to-ascii" }, - //{ kPrEOLConvRaw, kBool, kPrefsSect, L"eol-conv-raw" }, - { kPrConvTextEOL_HA, kBool, kPrefsSect, L"conv-eol-ha" }, - { kPrConvPascalText, kBool, kPrefsSect, L"conv-pascal-text" }, - { kPrConvPascalCode, kBool, kPrefsSect, L"conv-pascal-code" }, - { kPrConvCPMText, kBool, kPrefsSect, L"conv-cpm-text" }, - { kPrConvApplesoft, kBool, kPrefsSect, L"conv-applesoft" }, - { kPrConvInteger, kBool, kPrefsSect, L"conv-integer" }, - { kPrConvBusiness, kBool, kPrefsSect, L"conv-business" }, - { kPrConvGWP, kBool, kPrefsSect, L"conv-gwp" }, - { kPrConvText8, kBool, kPrefsSect, L"conv-text8" }, - { kPrConvGutenberg, kBool, kPrefsSect, L"conv-gutenberg" }, - { kPrConvAWP, kBool, kPrefsSect, L"conv-awp" }, - { kPrConvADB, kBool, kPrefsSect, L"conv-adb" }, - { kPrConvASP, kBool, kPrefsSect, L"conv-asp" }, - { kPrConvSCAssem, kBool, kPrefsSect, L"conv-scassem" }, - { kPrConvDisasm, kBool, kPrefsSect, L"conv-disasm" }, - { kPrConvHiRes, kBool, kPrefsSect, L"conv-hi-res" }, - { kPrConvDHR, kBool, kPrefsSect, L"conv-dhr" }, - { kPrConvSHR, kBool, kPrefsSect, L"conv-shr" }, - { kPrConvPrintShop, kBool, kPrefsSect, L"conv-print-shop" }, - { kPrConvMacPaint, kBool, kPrefsSect, L"conv-mac-paint" }, - { kPrConvProDOSFolder, kBool, kPrefsSect, L"conv-prodos-folder" }, - { kPrConvResources, kBool, kPrefsSect, L"conv-resources" }, - - { kPrTempPath, kString, kPrefsSect, L"temp-path" }, - { kPrExtViewerExts, kString, kPrefsSect, L"extviewer-exts" }, - - { kPrLastOpenFilterIndex, kLong, kMiscSect, L"open-filter-index" }, - - /**/ { kPrefNumLastRegistry, kPTNone, NULL, NULL }, - - { kPrViewTextTypeFace, kString, NULL, NULL }, - { kPrViewTextPointSize, kLong, NULL, NULL }, - { kPrFileViewerWidth, kLong, NULL, NULL }, - { kPrFileViewerHeight, kLong, NULL, NULL }, - { kPrDiskImageCreateFormat, kLong, NULL, NULL }, -}; - -Preferences::Preferences(void) -{ - LOGI("Initializing Preferences"); - - ScanPrefMaps(); // sanity-check the table - memset(fValues, 0, sizeof(fValues)); - - SetPrefBool(kPrAddIncludeSubFolders, true); - SetPrefBool(kPrAddStripFolderNames, false); - SetPrefBool(kPrAddOverwriteExisting, false); - SetPrefLong(kPrAddTypePreservation, 1); // kPreserveTypes - SetPrefLong(kPrAddConvEOL, 1); // kConvEOLType - - InitFolders(); // set default add/extract folders; overriden by reg - SetPrefLong(kPrExtractConvEOL, 0); // kConvEOLNone - SetPrefBool(kPrExtractConvHighASCII, true); - SetPrefBool(kPrExtractIncludeData, true); - SetPrefBool(kPrExtractIncludeRsrc, false); - SetPrefBool(kPrExtractIncludeDisk, true); - SetPrefBool(kPrExtractEnableReformat, false); - SetPrefBool(kPrExtractDiskTo2MG, false); - SetPrefBool(kPrExtractAddTypePreservation, true); - SetPrefBool(kPrExtractAddExtension, false); - SetPrefBool(kPrExtractStripFolderNames, false); - SetPrefBool(kPrExtractOverwriteExisting, false); - -// SetPrefBool(kPrViewIncludeDataForks, true); -// SetPrefBool(kPrViewIncludeRsrcForks, false); -// SetPrefBool(kPrViewIncludeDiskImages, false); -// SetPrefBool(kPrViewIncludeComments, false); - - SetPrefBool(kPrConvFileEmptyFolders, true); - - // string kPrOpenArchiveFolder - // string kPrAddFileFolder - // string kPrExtractFileFolder - - SetPrefLong(kPrVolumeFilter, 0); - //SetPrefBool(kPrVolumeReadOnly, true); - - SetPrefLong(kPrCassetteAlgorithm, 0); - // string kPrOpenWAVFolder - - SetPrefBool(kPrMimicShrinkIt, false); - SetPrefBool(kPrBadMacSHK, false); - SetPrefBool(kPrReduceSHKErrorChecks, false); - SetPrefBool(kPrCoerceDOSFilenames, false); - SetPrefBool(kPrSpacesToUnder, false); - SetPrefBool(kPrPasteJunkPaths, true); - SetPrefBool(kPrBeepOnSuccess, true); - - SetPrefBool(kPrQueryImageFormat, false); - SetPrefBool(kPrOpenVolumeRO, true); - SetPrefBool(kPrOpenVolumePhys0, false); - SetPrefBool(kPrProDOSAllowLower, false); - SetPrefBool(kPrProDOSUseSparse, true); - - SetPrefLong(kPrCompressionType, DefaultCompressionType()); - - SetPrefLong(kPrMaxViewFileSize, 1024*1024); // 1MB - SetPrefBool(kPrNoWrapText, false); - - SetPrefBool(kPrHighlightHexDump, false); - SetPrefBool(kPrHighlightBASIC, false); - SetPrefBool(kPrConvHiResBlackWhite, false); - SetPrefLong(kPrConvDHRAlgorithm, 1); // latched - SetPrefBool(kPrRelaxGfxTypeCheck, true); - SetPrefBool(kPrDisasmOneByteBrkCop, false); - SetPrefBool(kPrConvMouseTextToASCII, false); - //SetPrefBool(kPrEOLConvRaw, true); - SetPrefBool(kPrConvTextEOL_HA, true); - SetPrefBool(kPrConvPascalText, true); - SetPrefBool(kPrConvPascalCode, true); - SetPrefBool(kPrConvCPMText, true); - SetPrefBool(kPrConvApplesoft, true); - SetPrefBool(kPrConvInteger, true); - SetPrefBool(kPrConvBusiness, true); - SetPrefBool(kPrConvGWP, true); - SetPrefBool(kPrConvText8, true); - SetPrefBool(kPrConvGutenberg, true); - SetPrefBool(kPrConvAWP, true); - SetPrefBool(kPrConvADB, true); - SetPrefBool(kPrConvASP, true); - SetPrefBool(kPrConvSCAssem, true); - SetPrefBool(kPrConvDisasm, true); - SetPrefBool(kPrConvHiRes, true); - SetPrefBool(kPrConvDHR, true); - SetPrefBool(kPrConvSHR, true); - SetPrefBool(kPrConvPrintShop, true); - SetPrefBool(kPrConvMacPaint, true); - SetPrefBool(kPrConvProDOSFolder, true); - SetPrefBool(kPrConvResources, true); - - InitTempPath(); // set default for kPrTempPath - SetPrefString(kPrExtViewerExts, L"gif; jpg; jpeg"); - - SetPrefLong(kPrLastOpenFilterIndex, 0); - - SetPrefString(kPrViewTextTypeFace, L"Courier New"); - SetPrefLong(kPrViewTextPointSize, 10); - long width = 680 + /* exact width for 80-column text */ - ::GetSystemMetrics(SM_CXVSCROLL); - long height = 516; /* exact height for file viewer to show IIgs graphic */ - if (GetSystemMetrics(SM_CXSCREEN) < width) - width = GetSystemMetrics(SM_CXSCREEN); - if (GetSystemMetrics(SM_CYSCREEN) < height) - height = GetSystemMetrics(SM_CYSCREEN); // may overlap system bar - //width = 640; height = 480; - SetPrefLong(kPrFileViewerWidth, width); - SetPrefLong(kPrFileViewerHeight, height); - SetPrefLong(kPrDiskImageCreateFormat, -1); -} - - -/* - * ========================================================================== - * ColumnLayout - * ========================================================================== - */ - -void ColumnLayout::LoadFromRegistry(const WCHAR* section) -{ - WCHAR numBuf[8]; - int i; - - for (i = 0; i < kNumVisibleColumns; i++) { - wsprintf(numBuf, L"%d", i); - - fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, - fColumnWidth[i]); - fColumnWidth[i] = gMyApp.GetProfileInt(section, numBuf, - fColumnWidth[i]); - } - fSortColumn = gMyApp.GetProfileInt(section, L"sort-column", fSortColumn); - fAscending = (gMyApp.GetProfileInt(section, L"ascending", fAscending) != 0); -} - -void ColumnLayout::SaveToRegistry(const WCHAR* section) -{ - WCHAR numBuf[8]; - int i; - - for (i = 0; i < kNumVisibleColumns; i++) { - wsprintf(numBuf, L"%d", i); - - gMyApp.WriteProfileInt(section, numBuf, fColumnWidth[i]); - } - gMyApp.WriteProfileInt(section, L"sort-column", fSortColumn); - gMyApp.WriteProfileInt(section, L"ascending", fAscending); -} - - -/* - * ========================================================================== - * Preferences - * ========================================================================== - */ - -void Preferences::InitTempPath(void) -{ - WCHAR buf[MAX_PATH]; - DWORD len; - CString tempPath; - - len = ::GetTempPath(NELEM(buf), buf); - if (len == 0) { - DWORD err = ::GetLastError(); - LOGI("GetTempPath failed, err=%d", err); - tempPath = kDefaultTempPath; - } else if (len >= NELEM(buf)) { - /* sheesh! */ - LOGI("GetTempPath wants a %d-unit buffer", len); - tempPath = kDefaultTempPath; - } else { - tempPath = buf; - } - - PathName path(tempPath); - LOGD("Temp path is '%ls'", (LPCWSTR) tempPath); - path.SFNToLFN(); - tempPath = path.GetPathName(); - - LOGD("Temp path (long form) is '%ls'", (LPCWSTR) tempPath); - - SetPrefString(kPrTempPath, tempPath); - -// ::GetFullPathName(fTempPath, sizeof(buf), buf, &foo); -// ::SetCurrentDirectory(buf); -// ::GetCurrentDirectory(sizeof(buf2), buf2); -} - -void Preferences::InitFolders(void) -{ - CString path; - - if (GetMyDocuments(&path)) { - SetPrefString(kPrOpenArchiveFolder, path); - SetPrefString(kPrConvertArchiveFolder, path); - SetPrefString(kPrAddFileFolder, path); - SetPrefString(kPrExtractFileFolder, path); - SetPrefString(kPrOpenWAVFolder, path); - } else { - WCHAR buf[MAX_PATH]; - ::GetCurrentDirectory(NELEM(buf), buf); - SetPrefString(kPrOpenArchiveFolder, buf); - SetPrefString(kPrConvertArchiveFolder, buf); - SetPrefString(kPrAddFileFolder, buf); - SetPrefString(kPrExtractFileFolder, buf); - SetPrefString(kPrOpenWAVFolder, buf); - } - - LOGD("Default folder is '%ls'", GetPrefString(kPrExtractFileFolder)); -} - -bool Preferences::GetMyDocuments(CString* pPath) -{ - LPITEMIDLIST pidl = NULL; - LPMALLOC lpMalloc = NULL; - HRESULT hr; - bool result = false; - - hr = ::SHGetMalloc(&lpMalloc); - if (FAILED(hr)) - return NULL; - - hr = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl); - if (FAILED(hr)) { - LOGW("WARNING: unable to get CSIDL_PERSONAL"); - goto bail; - } - - result = (Pidl::GetPath(pidl, pPath) != FALSE); - if (!result) { - LOGW("WARNING: unable to convert CSIDL_PERSONAL to path"); - /* fall through with "result" */ - } - -bail: - lpMalloc->Free(pidl); - lpMalloc->Release(); - return result; -} - -int Preferences::DefaultCompressionType(void) -{ - if (NufxArchive::IsCompressionSupported(kNuThreadFormatLZW2)) - return kNuThreadFormatLZW2; - else - return kNuThreadFormatUncompressed; -} - -bool Preferences::GetPrefBool(PrefNum num) const -{ - if (!ValidateEntry(num, kBool)) - return false; - //return (bool) (fValues[num]); - return (bool) ((long) (fValues[num]) != 0); -} - -void Preferences::SetPrefBool(PrefNum num, bool val) -{ - if (!ValidateEntry(num, kBool)) - return; - fValues[num] = (void*) val; -} - -long Preferences::GetPrefLong(PrefNum num) const -{ - if (!ValidateEntry(num, kLong)) - return -1; - return (long) fValues[num]; -} - -void Preferences::SetPrefLong(PrefNum num, long val) -{ - if (!ValidateEntry(num, kLong)) - return; - fValues[num] = (void*) val; -} - -const WCHAR* Preferences::GetPrefString(PrefNum num) const -{ - if (!ValidateEntry(num, kString)) - return NULL; - return (const WCHAR*) fValues[num]; -} - -void Preferences::SetPrefString(PrefNum num, const WCHAR* str) -{ - if (!ValidateEntry(num, kString)) - return; - free(fValues[num]); - if (str == NULL) { - fValues[num] = NULL; - } else { - fValues[num] = wcsdup(str); - } -} - -void Preferences::FreeStringValues(void) -{ - for (int i = 0; i < kPrefNumLastEntry; i++) { - if (fPrefMaps[i].type == kString) { - delete[] fValues[i]; - } - } -} - -void Preferences::ScanPrefMaps(void) -{ - int i, j; - - /* scan PrefNum */ - for (i = 0; i < kPrefNumLastEntry; i++) { - if (fPrefMaps[i].num != i) { - LOGE("HEY: PrefMaps[%d] has num=%d", i, fPrefMaps[i].num); - ASSERT(false); - break; - } - } - - /* look for duplicate strings */ - for (i = 0; i < kPrefNumLastEntry; i++) { - for (j = i+1; j < kPrefNumLastEntry; j++) { - if (fPrefMaps[i].registryKey == NULL || - fPrefMaps[j].registryKey == NULL) - { - continue; - } - if (wcsicmp(fPrefMaps[i].registryKey, - fPrefMaps[j].registryKey) == 0 && - wcsicmp(fPrefMaps[i].registrySection, - fPrefMaps[j].registrySection) == 0) - { - LOGE("HEY: PrefMaps[%d] and [%d] both have '%ls'/'%ls'", - i, j, fPrefMaps[i].registrySection, - fPrefMaps[i].registryKey); - ASSERT(false); - break; - } - } - } -} - -int Preferences::LoadFromRegistry(void) -{ - CString sval; - bool bval; - long lval; - - LOGI("Loading preferences from registry"); - - fColumnLayout.LoadFromRegistry(kColumnSect); - - int i; - for (i = 0; i < kPrefNumLastRegistry; i++) { - if (fPrefMaps[i].registryKey == NULL) - continue; - - switch (fPrefMaps[i].type) { - case kBool: - bval = GetPrefBool(fPrefMaps[i].num); - SetPrefBool(fPrefMaps[i].num, - GetBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, bval)); - break; - case kLong: - lval = GetPrefLong(fPrefMaps[i].num); - SetPrefLong(fPrefMaps[i].num, - GetInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, lval)); - break; - case kString: - sval = GetPrefString(fPrefMaps[i].num); - SetPrefString(fPrefMaps[i].num, - GetString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, sval)); - break; - default: - LOGW("Invalid type %d on num=%d", fPrefMaps[i].type, i); - ASSERT(false); - break; - } - } - - return 0; -} - -int Preferences::SaveToRegistry(void) -{ - LOGI("Saving preferences to registry"); - - fColumnLayout.SaveToRegistry(kColumnSect); - - int i; - for (i = 0; i < kPrefNumLastRegistry; i++) { - if (fPrefMaps[i].registryKey == NULL) - continue; - - switch (fPrefMaps[i].type) { - case kBool: - WriteBool(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, - GetPrefBool(fPrefMaps[i].num)); - break; - case kLong: - WriteInt(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, - GetPrefLong(fPrefMaps[i].num)); - break; - case kString: - WriteString(fPrefMaps[i].registrySection, fPrefMaps[i].registryKey, - GetPrefString(fPrefMaps[i].num)); - break; - default: - LOGW("Invalid type %d on num=%d", fPrefMaps[i].type, i); - ASSERT(false); - break; - } - } - - return 0; -} diff --git a/ciderpress/app/Preferences.h b/ciderpress/app/Preferences.h deleted file mode 100644 index 75a96b4..0000000 --- a/ciderpress/app/Preferences.h +++ /dev/null @@ -1,351 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Keep track of user preferences. - * - * How to add a new preference item: - * - Add an entry to the PrefNum enum, below. - * - Add a corresponding entry to Preferences::fPrefMaps, adding a new - * section to the registry if appropriate. - * - Add a default value to Preferences::Preferences. If not specified, - * strings will be NULL and numeric values will be zero. - * - * For user-settable preferencers, you must also: - * - Add a UI element to the appropriate preference page. - * - Add the appropriate code for that item. Note in particular the data - * exchange and ON_CONTROL_RANGE values. - * - In Main.cpp, update OnEditPreferences() to init the page. - * - Add a tooltip to the help file. - */ -#ifndef APP_PREFERENCES_H -#define APP_PREFERENCES_H - -#include "MyApp.h" - -class ContentList; - -/* - * Number of visible columns. (We no longer have "invisible" columns, so the - * name is somewhat misleading.) - * - * This is used widely. Update with care. - */ -const int kNumVisibleColumns = 9; - -/* - * Used to save & restore column layout and sorting preferences for - * the ContentList class. - */ -class ColumnLayout { -public: - ColumnLayout(void) { - for (int i = 0; i < kNumVisibleColumns; i++) - fColumnWidth[i] = kWidthDefaulted; - fSortColumn = kNumVisibleColumns; // means "use original order" - fAscending = true; - } - ~ColumnLayout(void) {} - - /* - * Restore column widths. - */ - void LoadFromRegistry(const WCHAR* section); - - /* - * Store column widths. - */ - void SaveToRegistry(const WCHAR* section); - - int GetColumnWidth(int col) const { - ASSERT(col >= 0 && col < kNumVisibleColumns); - return fColumnWidth[col]; - } - void SetColumnWidth(int col, int width) { - ASSERT(col >= 0 && col < kNumVisibleColumns); - ASSERT(width >= 0 || width == kWidthDefaulted); - fColumnWidth[col] = width; - } - - int GetSortColumn(void) const { return fSortColumn; } - void SetSortColumn(int col) { - ASSERT(col >= 0 && col <= kNumVisibleColumns); - fSortColumn = col; - } - bool GetAscending(void) const { return fAscending; } - void SetAscending(bool val) { fAscending = val; } - - /* column width value used to flag "defaulted" status */ - enum { kWidthDefaulted = -1 }; - /* minimium width of column 0 (pathname) */ - enum { kMinCol0Width = 50 }; - -private: - // Create a dummy list control to get default column widths. - void DetermineDefaultWidths(ContentList* pList); - - int fColumnWidth[kNumVisibleColumns]; - int fSortColumn; - bool fAscending; -}; - - -/* - * Preferences type enumeration. - * - * This is logically part of the Preferences object, but it's annoying to - * have to specify the scope resolution operator everywhere. - */ -typedef enum { - /**/ kPrefNumUnknown = 0, - -/* these are saved in the registry */ - - // sticky settings for add file options - kPrAddIncludeSubFolders, // bool - kPrAddStripFolderNames, // bool - kPrAddOverwriteExisting, // bool - kPrAddTypePreservation, // long - kPrAddConvEOL, // long - - // sticky settings for file extraction - //kPrExtractPath, // string - kPrExtractConvEOL, // long - kPrExtractConvHighASCII, // bool - kPrExtractIncludeData, // bool - kPrExtractIncludeRsrc, // bool - kPrExtractIncludeDisk, // bool - kPrExtractEnableReformat, // bool - kPrExtractDiskTo2MG, // bool - kPrExtractAddTypePreservation, // bool - kPrExtractAddExtension, // bool - kPrExtractStripFolderNames, // bool - kPrExtractOverwriteExisting, // bool - -// // view file options -// kPrViewIncludeDataForks, // bool -// kPrViewIncludeRsrcForks, // bool -// kPrViewIncludeDiskImages, // bool -// kPrViewIncludeComments, // bool - - // convert disk image to file archive - //kPrConvFileConvDOSText, // bool - //kPrConvFileConvPascalText, // bool - kPrConvFileEmptyFolders, // bool - - // folders for CFileDialog initialization - kPrOpenArchiveFolder, // string - kPrConvertArchiveFolder, // string - kPrAddFileFolder, // string - kPrExtractFileFolder, // string - - // logical/physical volume prefs - kPrVolumeFilter, // long - //kPrVolumeReadOnly, // bool - - // cassette import/export prefs - kPrCassetteAlgorithm, // long - kPrOpenWAVFolder, // string - - // items from the Preferences propertypages (must be saved/restored) - kPrMimicShrinkIt, // bool - kPrBadMacSHK, // bool - kPrReduceSHKErrorChecks, // bool - kPrCoerceDOSFilenames, // bool - kPrSpacesToUnder, // bool - kPrPasteJunkPaths, // bool - kPrBeepOnSuccess, // bool - - kPrQueryImageFormat, // bool - kPrOpenVolumeRO, // bool - kPrOpenVolumePhys0, // bool - kPrProDOSAllowLower, // bool - kPrProDOSUseSparse, // bool - - kPrCompressionType, // long - - kPrMaxViewFileSize, // long - kPrNoWrapText, // bool - - kPrHighlightHexDump, // bool - kPrHighlightBASIC, // bool - kPrConvHiResBlackWhite, // bool - kPrConvDHRAlgorithm, // long - kPrRelaxGfxTypeCheck, // bool - kPrDisasmOneByteBrkCop, // bool - kPrConvMouseTextToASCII, // bool - //kPrEOLConvRaw, // bool - kPrConvTextEOL_HA, // bool - kPrConvPascalText, // bool - kPrConvPascalCode, // bool - kPrConvCPMText, // bool - kPrConvApplesoft, // bool - kPrConvInteger, // bool - kPrConvBusiness, // bool - kPrConvGWP, // bool - kPrConvText8, // bool - kPrConvGutenberg, // bool - kPrConvAWP, // bool - kPrConvADB, // bool - kPrConvASP, // bool - kPrConvSCAssem, // bool - kPrConvDisasm, // bool - kPrConvHiRes, // bool - kPrConvDHR, // bool - kPrConvSHR, // bool - kPrConvPrintShop, // bool - kPrConvMacPaint, // bool - kPrConvProDOSFolder, // bool - kPrConvResources, // bool - - kPrTempPath, // string - kPrExtViewerExts, // string - - // open file dialog - kPrLastOpenFilterIndex, // long - - /**/ kPrefNumLastRegistry, -/* these are temporary settings, not saved in the registry */ - - // sticky settings for internal file viewer (ViewFilesDialog) - kPrViewTextTypeFace, // string - kPrViewTextPointSize, // long - kPrFileViewerWidth, // long - kPrFileViewerHeight, // long - - // sticky setting for disk image creator - kPrDiskImageCreateFormat, // long - - /**/ kPrefNumLastEntry -} PrefNum; - - -/* - * Container for preferences. - */ -class Preferences { -public: - /* - * There should be only one Preferences object in the - * application, so this should only be run once. - */ - Preferences(void); - ~Preferences(void) { - FreeStringValues(); - } - - /* - * Load preferences from the registry. - */ - int LoadFromRegistry(void); - - /* - * Save preferences to the registry. - */ - int SaveToRegistry(void); - - ColumnLayout* GetColumnLayout(void) { return &fColumnLayout; } - - bool GetPrefBool(PrefNum num) const; - void SetPrefBool(PrefNum num, bool val); - long GetPrefLong(PrefNum num) const; - void SetPrefLong(PrefNum num, long val); - const WCHAR* GetPrefString(PrefNum num) const; - void SetPrefString(PrefNum num, const WCHAR* str); - - -private: - /* - * Get a default value for the temp path. - */ - void InitTempPath(void); - - /* - * Set default values for the various folders. - */ - void InitFolders(void); - - /* - * Get the path to the "My Documents" folder. - */ - bool GetMyDocuments(CString* pPath); - - /* - * Determine the type of compression to use as a default, based on what this - * version of NufxLib supports. - * - * Note this happens *before* the AppInit call, so we should restrict this to - * things that are version-safe for all of NufxLib v2.x. - */ - int DefaultCompressionType(void); - - /* - * Free storage for any string entries. - */ - void FreeStringValues(void); - - /* - * Internal data structure used to manage preferences. - */ - typedef enum { kPTNone, kBool, kLong, kString } PrefType; - typedef struct PrefMap { - PrefNum num; - PrefType type; - const WCHAR* registrySection; - const WCHAR* registryKey; - } PrefMap; - static const PrefMap fPrefMaps[kPrefNumLastEntry]; - - /* - * Do a quick scan of the PrefMaps to identify duplicate, misplaced, and - * missing entries. - */ - void ScanPrefMaps(void); - - // this holds the actual values - void* fValues[kPrefNumLastEntry]; - - // verify that the entry exists and has the expected type - bool ValidateEntry(PrefNum num, PrefType type) const { - if (num <= kPrefNumUnknown || num >= kPrefNumLastEntry) { - ASSERT(false); - return false; - } - if (fPrefMaps[num].type != type) { - ASSERT(false); - return false; - } - return true; - } - - // column widths for ContentList - ColumnLayout fColumnLayout; - - /* - * Registry helpers. - */ - UINT GetInt(const WCHAR* section, const WCHAR* key, int dflt) { - return gMyApp.GetProfileInt(section, key, dflt); - } - bool GetBool(const WCHAR* section, const WCHAR* key, bool dflt) { - return (gMyApp.GetProfileInt(section, key, dflt) != 0); - } - CString GetString(const WCHAR* section, const WCHAR* key, - const WCHAR* dflt) - { - return gMyApp.GetProfileString(section, key, dflt); - } - BOOL WriteInt(const WCHAR* section, const WCHAR* key, int value) { - return gMyApp.WriteProfileInt(section, key, value); - } - BOOL WriteBool(const WCHAR* section, const WCHAR* key, bool value) { - return gMyApp.WriteProfileInt(section, key, value); - } - BOOL WriteString(const WCHAR* section, const WCHAR* key, const WCHAR* value) { - return gMyApp.WriteProfileString(section, key, value); - } -}; - -#endif /*APP_PREFERENCES_H*/ diff --git a/ciderpress/app/PrefsDialog.cpp b/ciderpress/app/PrefsDialog.cpp deleted file mode 100644 index 3171129..0000000 --- a/ciderpress/app/PrefsDialog.cpp +++ /dev/null @@ -1,512 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "PrefsDialog.h" -#include "ChooseDirDialog.h" -#ifdef CAN_UPDATE_FILE_ASSOC -#include "EditAssocDialog.h" -#endif -#include "Main.h" -#include "NufxArchive.h" -#include "resource.h" -#include // need WM_COMMANDHELP - - -/* - * =========================================================================== - * PrefsGeneralPage - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(PrefsGeneralPage, CPropertyPage) - ON_CONTROL_RANGE(BN_CLICKED, IDC_COL_PATHNAME, IDC_COL_ACCESS, OnChangeRange) - ON_BN_CLICKED(IDC_PREF_SHRINKIT_COMPAT, OnChange) - ON_BN_CLICKED(IDC_PREF_REDUCE_SHK_ERROR_CHECKS, OnChange) - ON_BN_CLICKED(IDC_PREF_SHK_BAD_MAC, OnChange) - ON_BN_CLICKED(IDC_PREF_COERCE_DOS, OnChange) - ON_BN_CLICKED(IDC_PREF_SPACES_TO_UNDER, OnChange) - ON_BN_CLICKED(IDC_PREF_PASTE_JUNKPATHS, OnChange) - ON_BN_CLICKED(IDC_PREF_SUCCESS_BEEP, OnChange) - ON_BN_CLICKED(IDC_COL_DEFAULTS, OnDefaults) -#ifdef CAN_UPDATE_FILE_ASSOC - ON_BN_CLICKED(IDC_PREF_ASSOCIATIONS, OnAssociations) -#endif - ON_MESSAGE(WM_HELP, OnHelpInfo) - ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp) -END_MESSAGE_MAP() - - -void PrefsGeneralPage::OnChange(void) -{ - /* - * They clicked on a checkbox, just mark the page as dirty so the "apply" - * button will be enabled. - */ - SetModified(TRUE); -} - -void PrefsGeneralPage::OnChangeRange(UINT nID) -{ - SetModified(TRUE); -} - -void PrefsGeneralPage::OnDefaults(void) -{ - /* - * Since we don't actually set column widths here, we need to tell the main - * window that the defaults button was pushed. It needs to reset all column - * widths to defaults, and then take into account any checking and un-checking - * that was done after "defaults" was pushed. - */ - LOGD("OnDefaults"); - - CButton* pButton; - - fDefaultsPushed = true; - - ASSERT(IDC_COL_ACCESS == IDC_COL_PATHNAME + (kNumVisibleColumns-1)); - - /* assumes that the controls are numbered sequentially */ - for (int i = 0; i < kNumVisibleColumns; i++) { - pButton = (CButton*) GetDlgItem(IDC_COL_PATHNAME+i); - ASSERT(pButton != NULL); - pButton->SetCheck(1); // 0=unchecked, 1=checked, 2=indeterminate - } - - SetModified(TRUE); -} - -#ifdef CAN_UPDATE_FILE_ASSOC -void PrefsGeneralPage::OnAssociations(void) -{ - EditAssocDialog assocDlg; - - assocDlg.fOurAssociations = fOurAssociations; - fOurAssociations = NULL; - - if (assocDlg.DoModal() == IDOK) { - /* - * Make a copy of the changes and mark us as modified so - * the Apply/Cancel buttons behave as expected. (We don't make - * a copy so much as steal the data from the dialog object.) - */ - delete[] fOurAssociations; - fOurAssociations = assocDlg.fOurAssociations; - assocDlg.fOurAssociations = NULL; - SetModified(TRUE); - } -} -#endif - -void PrefsGeneralPage::DoDataExchange(CDataExchange* pDX) -{ - /* - * The various column checkboxes are independent. We still do the xfer - * for "pathname" even though it's disabled. - */ - - fReady = true; - - ASSERT(NELEM(fColumn) == 9); - DDX_Check(pDX, IDC_COL_PATHNAME, fColumn[0]); - DDX_Check(pDX, IDC_COL_TYPE, fColumn[1]); - DDX_Check(pDX, IDC_COL_AUXTYPE, fColumn[2]); - DDX_Check(pDX, IDC_COL_MODDATE, fColumn[3]); - DDX_Check(pDX, IDC_COL_FORMAT, fColumn[4]); - DDX_Check(pDX, IDC_COL_SIZE, fColumn[5]); - DDX_Check(pDX, IDC_COL_RATIO, fColumn[6]); - DDX_Check(pDX, IDC_COL_PACKED, fColumn[7]); - DDX_Check(pDX, IDC_COL_ACCESS, fColumn[8]); - - DDX_Check(pDX, IDC_PREF_SHRINKIT_COMPAT, fMimicShrinkIt); - DDX_Check(pDX, IDC_PREF_SHK_BAD_MAC, fBadMacSHK); - DDX_Check(pDX, IDC_PREF_REDUCE_SHK_ERROR_CHECKS, fReduceSHKErrorChecks); - DDX_Check(pDX, IDC_PREF_COERCE_DOS, fCoerceDOSFilenames); - DDX_Check(pDX, IDC_PREF_SPACES_TO_UNDER, fSpacesToUnder); - DDX_Check(pDX, IDC_PREF_PASTE_JUNKPATHS, fPasteJunkPaths); - DDX_Check(pDX, IDC_PREF_SUCCESS_BEEP, fBeepOnSuccess); -} - - -/* - * =========================================================================== - * PrefsDiskImagePage - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(PrefsDiskImagePage, CPropertyPage) - ON_BN_CLICKED(IDC_PDISK_CONFIRM_FORMAT, OnChange) - ON_BN_CLICKED(IDC_PDISK_OPENVOL_RO, OnChange) - ON_BN_CLICKED(IDC_PDISK_OPENVOL_PHYS0, OnChange) - ON_BN_CLICKED(IDC_PDISK_PRODOS_ALLOWLOWER, OnChange) - ON_BN_CLICKED(IDC_PDISK_PRODOS_USESPARSE, OnChange) - ON_MESSAGE(WM_HELP, OnHelpInfo) - ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp) -END_MESSAGE_MAP() - -BOOL PrefsDiskImagePage::OnInitDialog(void) -{ - //LOGI("OnInit!"); - return CPropertyPage::OnInitDialog(); -} - -void PrefsDiskImagePage::OnChange(void) -{ - LOGD("OnChange"); - SetModified(TRUE); // enable the "apply" button -} - -//void PrefsDiskImagePage::OnChangeRange(UINT nID) -//{ -// LOGD("OnChangeRange id=%d", nID); -// SetModified(TRUE); -//} - - -void PrefsDiskImagePage::DoDataExchange(CDataExchange* pDX) -{ - fReady = true; - DDX_Check(pDX, IDC_PDISK_CONFIRM_FORMAT, fQueryImageFormat); - DDX_Check(pDX, IDC_PDISK_OPENVOL_RO, fOpenVolumeRO); - DDX_Check(pDX, IDC_PDISK_OPENVOL_PHYS0, fOpenVolumePhys0); - DDX_Check(pDX, IDC_PDISK_PRODOS_ALLOWLOWER, fProDOSAllowLower); - DDX_Check(pDX, IDC_PDISK_PRODOS_USESPARSE, fProDOSUseSparse); -} - - -/* - * =========================================================================== - * PrefsCompressionPage - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(PrefsCompressionPage, CPropertyPage) - ON_CONTROL_RANGE(BN_CLICKED, IDC_DEFC_UNCOMPRESSED, IDC_DEFC_BZIP2, OnChangeRange) - ON_MESSAGE(WM_HELP, OnHelpInfo) - ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp) -END_MESSAGE_MAP() - - -BOOL PrefsCompressionPage::OnInitDialog(void) -{ - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatHuffmanSQ)) { - DisableWnd(IDC_DEFC_SQUEEZE); - if (fCompressType == kNuThreadFormatHuffmanSQ) - fCompressType = kNuThreadFormatUncompressed; - } - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatLZW1)) { - DisableWnd(IDC_DEFC_LZW1); - if (fCompressType == kNuThreadFormatLZW1) - fCompressType = kNuThreadFormatUncompressed; - } - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatLZW2)) { - DisableWnd(IDC_DEFC_LZW2); - if (fCompressType == kNuThreadFormatLZW2) { - fCompressType = kNuThreadFormatUncompressed; - } - } - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatLZC12)) { - DisableWnd(IDC_DEFC_LZC12); - if (fCompressType == kNuThreadFormatLZC12) - fCompressType = kNuThreadFormatUncompressed; - } - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatLZC16)) { - DisableWnd(IDC_DEFC_LZC16); - if (fCompressType == kNuThreadFormatLZC16) - fCompressType = kNuThreadFormatUncompressed; - } - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatDeflate)) { - DisableWnd(IDC_DEFC_DEFLATE); - if (fCompressType == kNuThreadFormatDeflate) - fCompressType = kNuThreadFormatUncompressed; - } - if (!NufxArchive::IsCompressionSupported(kNuThreadFormatBzip2)) { - DisableWnd(IDC_DEFC_BZIP2); - if (fCompressType == kNuThreadFormatBzip2) - fCompressType = kNuThreadFormatUncompressed; - } - - /* now invoke DoDataExchange with our modified fCompressType */ - return CPropertyPage::OnInitDialog(); -} - -void PrefsCompressionPage::DisableWnd(int id) -{ - CWnd* pWnd; - pWnd = GetDlgItem(id); - if (pWnd == NULL) { - ASSERT(false); - return; - } - pWnd->EnableWindow(FALSE); -} - -void PrefsCompressionPage::OnChangeRange(UINT nID) -{ - SetModified(TRUE); // enable the "apply" button -} - -void PrefsCompressionPage::DoDataExchange(CDataExchange* pDX) -{ - /* - * Compression types match the NuThreadFormat enum in NufxLib.h, starting - * with IDC_DEFC_UNCOMPRESSED. - */ - - LOGV("OnInit comp!"); - fReady = true; - DDX_Radio(pDX, IDC_DEFC_UNCOMPRESSED, fCompressType); -} - - -/* - * =========================================================================== - * PrefsFviewPage - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(PrefsFviewPage, CPropertyPage) - ON_CONTROL_RANGE(BN_CLICKED, IDC_PVIEW_NOWRAP_TEXT, IDC_PVIEW_HIRES_BW, OnChangeRange) - ON_CONTROL_RANGE(BN_CLICKED, IDC_PVIEW_HITEXT, IDC_PVIEW_TEXT8, OnChangeRange) - ON_EN_CHANGE(IDC_PVIEW_SIZE_EDIT, OnChange) - ON_CBN_SELCHANGE(IDC_PVIEW_DHR_CONV_COMBO, OnChange) - ON_MESSAGE(WM_HELP, OnHelpInfo) - ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp) -END_MESSAGE_MAP() - -BOOL PrefsFviewPage::OnInitDialog(void) -{ - CSpinButtonCtrl* pSpin; - - LOGV("Configuring spin"); - - pSpin = (CSpinButtonCtrl*) GetDlgItem(IDC_PVIEW_SIZE_SPIN); - ASSERT(pSpin != NULL); - - UDACCEL uda; - uda.nSec = 0; - uda.nInc = 64; - pSpin->SetRange(1, 32767); - pSpin->SetAccel(1, &uda); - LOGD("OnInit done!"); - - return CPropertyPage::OnInitDialog(); -} - -void PrefsFviewPage::OnChange(void) -{ - LOGD("OnChange"); - SetModified(TRUE); // enable the "apply" button -} - -void PrefsFviewPage::OnChangeRange(UINT nID) -{ - LOGD("OnChangeRange id=%d", nID); - SetModified(TRUE); -} - -void PrefsFviewPage::DoDataExchange(CDataExchange* pDX) -{ - fReady = true; - //DDX_Check(pDX, IDC_PVIEW_EOL_RAW, fEOLConvRaw); - DDX_Check(pDX, IDC_PVIEW_NOWRAP_TEXT, fNoWrapText); - DDX_Check(pDX, IDC_PVIEW_BOLD_HEXDUMP, fHighlightHexDump); - DDX_Check(pDX, IDC_PVIEW_BOLD_BASIC, fHighlightBASIC); - DDX_Check(pDX, IDC_PVIEW_DISASM_ONEBYTEBRKCOP, fConvDisasmOneByteBrkCop); - DDX_Check(pDX, IDC_PVIEW_MOUSETEXT_TO_ASCII, fConvMouseTextToASCII); - DDX_Check(pDX, IDC_PVIEW_HIRES_BW, fConvHiResBlackWhite); - DDX_CBIndex(pDX, IDC_PVIEW_DHR_CONV_COMBO, fConvDHRAlgorithm); - - DDX_Check(pDX, IDC_PVIEW_HITEXT, fConvTextEOL_HA); - DDX_Check(pDX, IDC_PVIEW_CPMTEXT, fConvCPMText); - DDX_Check(pDX, IDC_PVIEW_PASCALTEXT, fConvPascalText); - DDX_Check(pDX, IDC_PVIEW_PASCALCODE, fConvPascalCode); - DDX_Check(pDX, IDC_PVIEW_APPLESOFT, fConvApplesoft); - DDX_Check(pDX, IDC_PVIEW_INTEGER, fConvInteger); - DDX_Check(pDX, IDC_PVIEW_GWP, fConvGWP); - DDX_Check(pDX, IDC_PVIEW_TEXT8, fConvText8); - DDX_Check(pDX, IDC_PVIEW_AWP, fConvAWP); - DDX_Check(pDX, IDC_PVIEW_ADB, fConvADB); - DDX_Check(pDX, IDC_PVIEW_ASP, fConvASP); - DDX_Check(pDX, IDC_PVIEW_SCASSEM, fConvSCAssem); - DDX_Check(pDX, IDC_PVIEW_DISASM, fConvDisasm); - - DDX_Check(pDX, IDC_PVIEW_HIRES, fConvHiRes); - DDX_Check(pDX, IDC_PVIEW_DHR, fConvDHR); - DDX_Check(pDX, IDC_PVIEW_SHR, fConvSHR); - DDX_Check(pDX, IDC_PVIEW_PRINTSHOP, fConvPrintShop); - DDX_Check(pDX, IDC_PVIEW_MACPAINT, fConvMacPaint); - DDX_Check(pDX, IDC_PVIEW_PRODOSFOLDER, fConvProDOSFolder); - DDX_Check(pDX, IDC_PVIEW_RESOURCES, fConvResources); - DDX_Check(pDX, IDC_PVIEW_RELAX_GFX, fRelaxGfxTypeCheck); - - DDX_Text(pDX, IDC_PVIEW_SIZE_EDIT, fMaxViewFileSizeKB); - DDV_MinMaxUInt(pDX, fMaxViewFileSizeKB, 1, 32767); -} - - -/* - * =========================================================================== - * PrefsFilesPage - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(PrefsFilesPage, CPropertyPage) - ON_EN_CHANGE(IDC_PREF_TEMP_FOLDER, OnChange) - ON_EN_CHANGE(IDC_PREF_EXTVIEWER_EXTS, OnChange) - ON_BN_CLICKED(IDC_PREF_CHOOSE_TEMP_FOLDER, OnChooseFolder) - ON_MESSAGE(WM_HELP, OnHelpInfo) - ON_MESSAGE(WM_COMMANDHELP, OnCommandHelp) -END_MESSAGE_MAP() - - -BOOL PrefsFilesPage::OnInitDialog(void) -{ - fChooseFolderButton.ReplaceDlgCtrl(this, IDC_PREF_CHOOSE_TEMP_FOLDER); - fChooseFolderButton.SetBitmapID(IDB_CHOOSE_FOLDER); - - return CPropertyPage::OnInitDialog(); -} - -void PrefsFilesPage::OnChange(void) -{ - SetModified(TRUE); // enable the "apply" button -} - -void PrefsFilesPage::DoDataExchange(CDataExchange* pDX) -{ - fReady = true; - DDX_Text(pDX, IDC_PREF_TEMP_FOLDER, fTempPath); - DDX_Text(pDX, IDC_PREF_EXTVIEWER_EXTS, fExtViewerExts); - - /* validate the path field */ - if (pDX->m_bSaveAndValidate) { - if (fTempPath.IsEmpty()) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - MessageBox(L"You must specify a path for temp files", - appName, MB_OK); - pDX->Fail(); - } - - // we *could* try to validate the path here... - } -} - -void PrefsFilesPage::OnChooseFolder(void) -{ - /* - * They want to choose the folder from a menu hierarchy. Show them a list. - */ - - ChooseDirDialog chooseDir(this); - CWnd* pEditWnd; - CString editPath; - - /* get the currently-showing text from the edit field */ - pEditWnd = GetDlgItem(IDC_PREF_TEMP_FOLDER); - ASSERT(pEditWnd != NULL); - pEditWnd->GetWindowText(editPath); - - chooseDir.SetPathName(editPath); - if (chooseDir.DoModal() == IDOK) { - const WCHAR* ccp = chooseDir.GetPathName(); - LOGD("New temp path chosen = '%ls'", ccp); - - pEditWnd->SetWindowText(ccp); - - // activate the "apply" button - OnChange(); - } -} - - -/* - * =========================================================================== - * PrefsSheet - * =========================================================================== - */ - -BEGIN_MESSAGE_MAP(PrefsSheet, CPropertySheet) - ON_WM_NCCREATE() - ON_BN_CLICKED(ID_APPLY_NOW, OnApplyNow) - ON_COMMAND(ID_HELP, OnIDHelp) - ON_MESSAGE(WM_HELP, OnHelpInfo) -END_MESSAGE_MAP() - -PrefsSheet::PrefsSheet(CWnd* pParentWnd) : - CPropertySheet(L"Preferences", pParentWnd) -{ - AddPage(&fGeneralPage); - AddPage(&fDiskImagePage); - AddPage(&fFviewPage); - AddPage(&fCompressionPage); - AddPage(&fFilesPage); - - /* this happens automatically with appropriate ID_HELP handlers */ - //m_psh.dwFlags |= PSH_HASHELP; -} - -BOOL PrefsSheet::OnNcCreate(LPCREATESTRUCT cs) -{ - LOGV("PrefsSheet OnNcCreate"); - BOOL val = CPropertySheet::OnNcCreate(cs); - ModifyStyleEx(0, WS_EX_CONTEXTHELP); - return val; -} - -void PrefsSheet::OnApplyNow(void) -{ - BOOL result; - - if (fGeneralPage.fReady) { - //LOGI("Apply to general?"); - result = fGeneralPage.UpdateData(TRUE); - if (!result) - return; - } - if (fDiskImagePage.fReady) { - //LOGI("Apply to disk images?"); - result = fDiskImagePage.UpdateData(TRUE); - if (!result) - return; - } - if (fCompressionPage.fReady) { - //LOGI("Apply to compression?"); - result = fCompressionPage.UpdateData(TRUE); - if (!result) - return; - } - if (fFviewPage.fReady) { - //LOGI("Apply to fview?"); - result = fFviewPage.UpdateData(TRUE); - if (!result) - return; - } - - if (fFilesPage.fReady) { - //LOGI("Apply to fview?"); - result = fFilesPage.UpdateData(TRUE); - if (!result) - return; - } - - /* reset all to "unmodified" state */ - LOGD("All 'applies' were successful"); - ((MainWindow*) AfxGetMainWnd())->ApplyNow(this); - fGeneralPage.SetModified(FALSE); - fGeneralPage.fDefaultsPushed = false; - fDiskImagePage.SetModified(FALSE); - fCompressionPage.SetModified(FALSE); - fFviewPage.SetModified(FALSE); - fFilesPage.SetModified(FALSE); -} - -void PrefsSheet::OnIDHelp(void) -{ - LOGD("PrefsSheet OnIDHelp"); - SendMessage(WM_COMMANDHELP); -} diff --git a/ciderpress/app/PrefsDialog.h b/ciderpress/app/PrefsDialog.h deleted file mode 100644 index 8917780..0000000 --- a/ciderpress/app/PrefsDialog.h +++ /dev/null @@ -1,316 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007, 2008 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Classes to support the Preferences property pages. - */ -#ifndef APP_PREFSDIALOG_H -#define APP_PREFSDIALOG_H - -#include "Preferences.h" -#include "../util/UtilLib.h" -#include "resource.h" -#include "HelpTopics.h" - -/* - * The "general" page, which controls how we display information to the user. - */ -class PrefsGeneralPage : public CPropertyPage -{ -public: - PrefsGeneralPage(void) : - CPropertyPage(IDD_PREF_GENERAL), - fReady(false), - fMimicShrinkIt(FALSE), - fBadMacSHK(FALSE), - fReduceSHKErrorChecks(FALSE), - fCoerceDOSFilenames(FALSE), - fSpacesToUnder(FALSE), - fDefaultsPushed(FALSE), - fOurAssociations(NULL) - {} - virtual ~PrefsGeneralPage(void) { - delete[] fOurAssociations; - } - - bool fReady; - - // fields on this page - BOOL fColumn[kNumVisibleColumns]; - BOOL fMimicShrinkIt; - BOOL fBadMacSHK; - BOOL fReduceSHKErrorChecks; - BOOL fCoerceDOSFilenames; - BOOL fSpacesToUnder; - BOOL fPasteJunkPaths; - BOOL fBeepOnSuccess; - BOOL fDefaultsPushed; - - // initialized if we opened the file associations edit page - bool* fOurAssociations; - -protected: - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnChange(void); - afx_msg void OnChangeRange(UINT); - afx_msg void OnDefaults(void); -#ifdef CAN_UPDATE_FILE_ASSOC - afx_msg void OnAssociations(void); -#endif - afx_msg LONG OnHelpInfo(UINT wParam, LONG lParam) { - return MyApp::HandleHelpInfo((HELPINFO*) lParam); - } - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam) { - MyApp::HandleHelp(this, HELP_TOPIC_PREFS_GENERAL); - return TRUE; - } - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "disk image" page, for selecting disk image preferences. - */ -class PrefsDiskImagePage : public CPropertyPage -{ -public: - PrefsDiskImagePage(void) : - CPropertyPage(IDD_PREF_DISKIMAGE), - fReady(false), - fQueryImageFormat(FALSE), - fOpenVolumeRO(FALSE), - fProDOSAllowLower(FALSE), - fProDOSUseSparse(FALSE) - {} - - bool fReady; - - BOOL fQueryImageFormat; - BOOL fOpenVolumeRO; - BOOL fOpenVolumePhys0; - BOOL fProDOSAllowLower; - BOOL fProDOSUseSparse; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnChange(void); - //afx_msg void OnChangeRange(UINT); - afx_msg LONG OnHelpInfo(UINT wParam, LONG lParam) { - return MyApp::HandleHelpInfo((HELPINFO*) lParam); - } - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam) { - MyApp::HandleHelp(this, HELP_TOPIC_PREFS_DISK_IMAGE); - return TRUE; - } - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "compression" page, which lets the user choose a default compression - * method. - */ -class PrefsCompressionPage : public CPropertyPage -{ -public: - PrefsCompressionPage(void) : - CPropertyPage(IDD_PREF_COMPRESSION), fReady(false) - {} - - bool fReady; - - int fCompressType; // radio button index - -protected: - /* - * Disable compression types not supported by the NufxLib DLL. - */ - virtual BOOL OnInitDialog(void) override; - - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnChangeRange(UINT); - afx_msg LONG OnHelpInfo(UINT wParam, LONG lParam) { - return MyApp::HandleHelpInfo((HELPINFO*) lParam); - } - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam) { - MyApp::HandleHelp(this, HELP_TOPIC_PREFS_COMPRESSION); - return TRUE; - } - -private: - /* - * Disable a window in our dialog. - */ - void DisableWnd(int id); - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "fview" page, for selecting preferences for the internal file viewer. - */ -class PrefsFviewPage : public CPropertyPage -{ -public: - PrefsFviewPage(void) : - CPropertyPage(IDD_PREF_FVIEW), fReady(false) - {} - bool fReady; - - BOOL fEOLConvRaw; - BOOL fNoWrapText; - BOOL fHighlightHexDump; - BOOL fHighlightBASIC; - BOOL fConvDisasmOneByteBrkCop; - BOOL fConvMouseTextToASCII; - BOOL fConvHiResBlackWhite; - int fConvDHRAlgorithm; // drop list - - BOOL fConvTextEOL_HA; - BOOL fConvCPMText; - BOOL fConvPascalText; - BOOL fConvPascalCode; - BOOL fConvApplesoft; - BOOL fConvInteger; - BOOL fConvBusiness; - BOOL fConvGWP; - BOOL fConvText8; - BOOL fConvAWP; - BOOL fConvADB; - BOOL fConvASP; - BOOL fConvSCAssem; - BOOL fConvDisasm; - - BOOL fConvHiRes; - BOOL fConvDHR; - BOOL fConvSHR; - BOOL fConvPrintShop; - BOOL fConvMacPaint; - BOOL fConvProDOSFolder; - BOOL fConvResources; - BOOL fRelaxGfxTypeCheck; - - UINT fMaxViewFileSizeKB; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnChange(void); - afx_msg void OnChangeRange(UINT); - afx_msg LONG OnHelpInfo(UINT wParam, LONG lParam) { - return MyApp::HandleHelpInfo((HELPINFO*) lParam); - } - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam) { - MyApp::HandleHelp(this, HELP_TOPIC_PREFS_FVIEW); - return TRUE; - } - - DECLARE_MESSAGE_MAP() -}; - -/* - * The "compression" page, which lets the user choose a default compression - * method for NuFX archives. - */ -class PrefsFilesPage : public CPropertyPage -{ -public: - PrefsFilesPage(void) : - CPropertyPage(IDD_PREF_FILES), fReady(false) - {} - - bool fReady; - - CString fTempPath; - CString fExtViewerExts; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnChange(void); - afx_msg void OnChooseFolder(void); - afx_msg LONG OnHelpInfo(UINT wParam, LONG lParam) { - return MyApp::HandleHelpInfo((HELPINFO*) lParam); - } - afx_msg LONG OnCommandHelp(UINT wParam, LONG lParam) { - MyApp::HandleHelp(this, HELP_TOPIC_PREFS_FILES); - return TRUE; - } - - MyBitmapButton fChooseFolderButton; - - DECLARE_MESSAGE_MAP() -}; - - -/* - * Property sheet that wraps around the preferences pages. - */ -class PrefsSheet : public CPropertySheet -{ -public: - /* - * Construct the preferences dialog from the individual pages. - */ - PrefsSheet(CWnd* pParentWnd = NULL); - - PrefsGeneralPage fGeneralPage; - PrefsDiskImagePage fDiskImagePage; - PrefsCompressionPage fCompressionPage; - PrefsFviewPage fFviewPage; - PrefsFilesPage fFilesPage; - -protected: - /* - * Enables the context help button. - * - * We don't seem to get a PreCreateWindow or OnInitDialog, but we can - * intercept the WM_NCCREATE message and override the default behavior. - */ - afx_msg BOOL OnNcCreate(LPCREATESTRUCT cs); - - /* - * Handle the "apply" button. We only want to process updates for property - * pages that have been constructed, and they only get constructed when - * the user clicks on them. - * - * We also have to watch out for DDV tests that should prevent the "apply" - * from succeeding, e.g. the file viewer size limit. - */ - afx_msg void OnApplyNow(); - - /* - * Handle a press of the "Help" button by redirecting it back to ourselves - * as a WM_COMMANDHELP message. If we don't do this, the main window ends - * up getting our WM_COMMAND(ID_HELP) message. - * - * We still need to define an ID_HELP WM_COMMAND handler in the main window, - * or the CPropertySheet code refuses to believe that help is enabled for - * the application as a whole. - * - * The PropertySheet object handles the WM_COMMANDHELP message and redirects - * it to the active PropertyPage. Each page must handle WM_COMMANDHELP by - * opening an appropriate chapter in the help file. - */ - afx_msg void OnIDHelp(void); - - /* - * Context help request (question mark button) on something outside of the - * property page, most likely the Apply or Cancel button. - */ - afx_msg LONG OnHelpInfo(UINT wParam, LONG lParam) { - return MyApp::HandleHelpInfo((HELPINFO*) lParam); - } - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_PREFSDIALOG_H*/ diff --git a/ciderpress/app/Print.cpp b/ciderpress/app/Print.cpp deleted file mode 100644 index 15ffddf..0000000 --- a/ciderpress/app/Print.cpp +++ /dev/null @@ -1,737 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for printing. - */ -#include "stdafx.h" -#include "Print.h" -#include "Main.h" -#include "Preferences.h" - - -/* - * ========================================================================== - * PrintStuff - * ========================================================================== - */ - -/*static*/ const WCHAR PrintStuff::kCourierNew[] = L"Courier New"; -/*static*/ const WCHAR PrintStuff::kTimesNewRoman[] = L"Times New Roman"; - -void PrintStuff::InitBasics(CDC* pDC) -{ - ASSERT(pDC != NULL); - ASSERT(fpDC == NULL); - - fpDC = pDC; - - /* make sure we're in MM_TEXT mode */ - pDC->SetMapMode(MM_TEXT); - - /* get device capabilities; logPixels is e.g. 300 */ - fVertRes = pDC->GetDeviceCaps(VERTRES); - fHorzRes = pDC->GetDeviceCaps(HORZRES); - fLogPixelsX = pDC->GetDeviceCaps(LOGPIXELSX); - fLogPixelsY = pDC->GetDeviceCaps(LOGPIXELSY); - LOGI("+++ logPixelsX=%d logPixelsY=%d fHorzRes=%d fVertRes=%d", - fLogPixelsX, fLogPixelsY, fHorzRes, fVertRes); -} - -void PrintStuff::CreateFontByNumLines(CFont* pFont, int numLines) -{ - ASSERT(pFont != NULL); - ASSERT(numLines > 0); - ASSERT(fpDC != NULL); - - /* required height */ - int reqCharHeight; - reqCharHeight = (fVertRes + numLines/2) / numLines; - - /* magic fudge factor */ - int fudge = reqCharHeight / 24; - LOGI(" Reducing reqCharHeight from %d to %d", - reqCharHeight, reqCharHeight - fudge); - reqCharHeight -= fudge; - - pFont->CreateFont(reqCharHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, - DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, - DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, kTimesNewRoman); - /*fpOldFont =*/ fpDC->SelectObject(pFont); -} - -int PrintStuff::StringWidth(const CString& str) -{ - CSize size; - size = fpDC->GetTextExtent(str); - return size.cx; -} - -int PrintStuff::TrimString(CString* pStr, int width, bool addOnLeft) -{ - static const char* kEllipsis = "..."; - CString newStr; - int strWidth; - CSize size; - - size = fpDC->GetTextExtent(kEllipsis); - - if (width < size.cx) { - ASSERT(false); - return width; - } - - newStr = *pStr; - - /* - * Do a linear search. This would probably be better served with a - * binary search or at least a good textmetric-based guess. - */ - strWidth = StringWidth(newStr); - while (strWidth > width) { - if (pStr->IsEmpty()) { - ASSERT(false); - return width; - } - if (addOnLeft) { - *pStr = pStr->Right(pStr->GetLength() -1); - newStr = kEllipsis + *pStr; - } else { - *pStr = pStr->Left(pStr->GetLength() -1); - newStr = *pStr + kEllipsis; - } - - if (!addOnLeft) { - LOGI("Now trying '%ls'", (LPCWSTR) newStr); - } - strWidth = StringWidth(newStr); - } - - *pStr = newStr; - return strWidth; -} - - -/* - * ========================================================================== - * PrintContentList - * ========================================================================== - */ - -void PrintContentList::Setup(CDC* pDC, CWnd* pParent) -{ - /* init base class */ - InitBasics(pDC); - - /* init our stuff */ - CreateFontByNumLines(&fPrintFont, kTargetLinesPerPage); - - fpParentWnd = pParent; - - /* compute text metrics */ - TEXTMETRIC metrics; - pDC->GetTextMetrics(&metrics); - fCharHeight = metrics.tmHeight + metrics.tmExternalLeading; - fLinesPerPage = fVertRes / fCharHeight; - - LOGD("fVertRes=%d, fCharHeight=%d", fVertRes, fCharHeight); - - /* set up our slightly reduced lines per page */ - ASSERT(fLinesPerPage > kHeaderLines+1); - fCLLinesPerPage = fLinesPerPage - kHeaderLines; -} - -void PrintContentList::CalcNumPages(void) -{ - /* set up our local goodies */ - ASSERT(fpContentList != NULL); - int numLines = fpContentList->GetItemCount(); - ASSERT(numLines > 0); - - fNumPages = (numLines + fCLLinesPerPage -1) / fCLLinesPerPage; - ASSERT(fNumPages > 0); - - LOGD("Using numLines=%d, fNumPages=%d, fCLLinesPerPage=%d", - numLines, fNumPages, fCLLinesPerPage); -} - -int -PrintContentList::Print(const ContentList* pContentList) -{ - fpContentList = pContentList; - CalcNumPages(); - - fFromPage = 1; - fToPage = fNumPages; - return StartPrint(); -} - -int PrintContentList::Print(const ContentList* pContentList, int fromPage, - int toPage) -{ - fpContentList = pContentList; - CalcNumPages(); - - fFromPage = fromPage; - fToPage = toPage; - return StartPrint(); -} - -int PrintContentList::StartPrint(void) -{ - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - BOOL bres; - int jobID; - int result = -1; - - ASSERT(fFromPage >= 1); - ASSERT(fToPage <= fNumPages); - - // clear the abort flag - pMain->SetAbortPrinting(false); - - // obstruct input to the main window - fpParentWnd->EnableWindow(FALSE); - - // create a print-cancel dialog -// PrintCancelDialog* pPCD = new PrintCancelDialog; - CancelDialog* pPCD = new CancelDialog; - bres = pPCD->Create(&pMain->fAbortPrinting, IDD_PRINT_CANCEL, fpParentWnd); - if (bres == FALSE) { - LOGI("WARNING: PrintCancelDialog init failed"); - } else { - fpDC->SetAbortProc(pMain->PrintAbortProc); - } - - fDocTitle = pMain->GetPrintTitle(); - - // set up the print job - CString printTitle; - CheckedLoadString(&printTitle, IDS_PRINT_CL_JOB_TITLE); - DOCINFO di; - ::ZeroMemory(&di, sizeof(DOCINFO)); - di.cbSize = sizeof(DOCINFO); - di.lpszDocName = printTitle; - - jobID = fpDC->StartDoc(&di); - if (jobID <= 0) { - LOGI("Got invalid jobID from StartDoc"); - goto bail; - } - LOGI("Got jobID=%d", jobID); - - // do the printing - if (DoPrint() != 0) { - LOGI("Printing was aborted"); - fpDC->AbortDoc(); - } else { - LOGI("Printing was successful"); - fpDC->EndDoc(); - result = 0; - } - -bail: - // destroy print-cancel dialog and restore main window - fpParentWnd->EnableWindow(TRUE); - //fpParentWnd->SetActiveWindow(); - if (pPCD != NULL) - pPCD->DestroyWindow(); - - return result; -} - -int PrintContentList::DoPrint(void) -{ - LOGI("Printing from page=%d to page=%d", fFromPage, fToPage); - - for (int page = fFromPage; page <= fToPage; page++) { - if (fpDC->StartPage() <= 0) { - LOGI("StartPage returned <= 0, returning -1"); - return -1; - } - - DoPrintPage(page); - - // delay so we can test "cancel" button -// { -// MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); -// pMain->EventPause(1000); -// } - - - if (fpDC->EndPage() <= 0) { - LOGI("EndPage returned <= 0, returning -1"); - return -1; - } - } - - return 0; -} - -void PrintContentList::DoPrintPage(int page) -{ - /* - * Column widths, on an arbitrary scale. These will be - * scaled appropriately for the page resolution. - */ - static const struct { - const char* name; - int width; - bool rightJust; - } kColumnWidths[kNumVisibleColumns] = { - { "Pathname", 250, false }, // 200 - { "Type", 40, false }, // 44 - { "Auxtype", 47, false }, // 42 - { "Mod Date", 96, false }, // 99 - { "Format", 52, false }, // 54 - { "Size", 55, true }, // 60 - { "Ratio", 40, true }, // 41 - { "Packed", 55, true }, // 60 - { "Access", 39, false }, // 53 upper, 45 lower - }; - const int kBorderWidth = 3; - - /* normalize */ - float widthMult; - int totalWidth; - - totalWidth = 0; - for (int i = 0; i < NELEM(kColumnWidths); i++) - totalWidth += kColumnWidths[i].width; - - widthMult = (float) fHorzRes / totalWidth; - LOGD("totalWidth=%d, fHorzRes=%d, mult=%.3f", - totalWidth, fHorzRes, widthMult); - - /* - * Calculate some goodies. - */ - int start, end; - start = (page-1) * fCLLinesPerPage; - end = start + fCLLinesPerPage; - if (end >= fpContentList->GetItemCount()) - end = fpContentList->GetItemCount()-1; - - int offset, nextOffset, cellWidth, border; - border = (int) (kBorderWidth * widthMult); - - /* - * Print page header. - */ - fpDC->TextOut(0, 1 * fCharHeight, fDocTitle); - CString pageNum; - pageNum.Format(L"Page %d/%d", page, fNumPages); - int pageNumWidth = StringWidth(pageNum); - fpDC->TextOut(fHorzRes - pageNumWidth, 1 * fCharHeight, pageNum); - - /* - * Print data. - */ - for (int row = -1; row < fCLLinesPerPage && start + row <= end; row++) { - CString text; - offset = 0; - - for (int col = 0; col < kNumVisibleColumns; col++) { - cellWidth = (int) ((float)kColumnWidths[col].width * widthMult); - nextOffset = offset + cellWidth; - if (col != kNumVisibleColumns-1) - cellWidth -= border; - if (col == 0) - cellWidth -= (border*2); // extra border on pathname - - int yOffset; - if (row == -1) { - text = kColumnWidths[col].name; - yOffset = (row+kHeaderLines-1) * fCharHeight; - } else { - text = fpContentList->GetItemText(start + row, col); - yOffset = (row+kHeaderLines) * fCharHeight; - } - - int strWidth; - strWidth = TrimString(&text, cellWidth, col == 0); - - if (kColumnWidths[col].rightJust) - fpDC->TextOut((offset + cellWidth) - strWidth, yOffset, text); - else - fpDC->TextOut(offset, yOffset, text); - - offset = nextOffset; - } - } - - /* - * Add some fancy lines. - */ - CPen penBlack(PS_SOLID, 1, RGB(0, 0, 0)); - CPen* pOldPen = fpDC->SelectObject(&penBlack); - fpDC->MoveTo(0, (int) (fCharHeight * (kHeaderLines - 0.5))); - fpDC->LineTo(fHorzRes, (int) (fCharHeight * (kHeaderLines - 0.5))); - - //fpDC->MoveTo(0, 0); - //fpDC->LineTo(fHorzRes, fVertRes); - //fpDC->MoveTo(fHorzRes-1, 0); - //fpDC->LineTo(0, fVertRes); - - fpDC->SelectObject(pOldPen); -} - - -/* - * ========================================================================== - * PrintRichEdit - * ========================================================================== - */ - -void PrintRichEdit::Setup(CDC* pDC, CWnd* pParent) -{ - /* preflighting can cause this to be initialized twice */ - fpDC = NULL; - - /* init base class */ - InitBasics(pDC); - - if (!fInitialized) { - /* - * Find a nice font for the title area. - */ - const int kPointSize = 10; - int fontHeight; - BOOL result; - - fontHeight = -MulDiv(kPointSize, fLogPixelsY, 72); - - result = fTitleFont.CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, - DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, - DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, kTimesNewRoman); - ASSERT(result); // everybody has Times New Roman - } - - fpParentWnd = pParent; - fInitialized = true; -} - -int PrintRichEdit::PrintPreflight(CRichEditCtrl* pREC, int* pNumPages) -{ - fStartChar = 0; - fEndChar = -1; - fStartPage = 0; - fEndPage = -1; - return StartPrint(pREC, L"(test)", pNumPages, false); -} - -int PrintRichEdit::PrintAll(CRichEditCtrl* pREC, const WCHAR* title) -{ - fStartChar = 0; - fEndChar = -1; - fStartPage = 0; - fEndPage = -1; - return StartPrint(pREC, title, NULL, true); -} - -int PrintRichEdit::PrintPages(CRichEditCtrl* pREC, const WCHAR* title, - int startPage, int endPage) -{ - fStartChar = 0; - fEndChar = -1; - fStartPage = startPage; - fEndPage = endPage; - return StartPrint(pREC, title, NULL, true); -} - -int PrintRichEdit::PrintSelection(CRichEditCtrl* pREC, const WCHAR* title, - long startChar, long endChar) -{ - fStartChar = startChar; - fEndChar = endChar; - fStartPage = 0; - fEndPage = -1; - return StartPrint(pREC, title, NULL, true); -} - -int PrintRichEdit::StartPrint(CRichEditCtrl* pREC, const WCHAR* title, - int* pNumPages, bool doPrint) -{ - CancelDialog* pPCD = NULL; - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - int result; - - /* set up the print cancel dialog */ - if (doPrint) { - BOOL bres; - - /* disable main UI */ - fpParentWnd->EnableWindow(FALSE); - - pPCD = new CancelDialog; - bres = pPCD->Create(&pMain->fAbortPrinting, IDD_PRINT_CANCEL, - fpParentWnd); - - /* set up the DC's print abort callback */ - if (bres != FALSE) - fpDC->SetAbortProc(pMain->PrintAbortProc); - } - - result = DoPrint(pREC, title, pNumPages, doPrint); - - if (doPrint) { - fpParentWnd->EnableWindow(TRUE); - if (pPCD != NULL) - pPCD->DestroyWindow(); - } - - return result; -} - -void PrintRichEdit::PrintPrep(FORMATRANGE* pFR) -{ - CFont* pOldFont; - - /* make sure we're in MM_TEXT mode */ - fpDC->SetMapMode(MM_TEXT); - - TEXTMETRIC metrics; - pOldFont = fpDC->SelectObject(&fTitleFont); - fpDC->GetTextMetrics(&metrics); - fCharHeight = metrics.tmHeight + metrics.tmExternalLeading; - fpDC->SelectObject(pOldFont); - //LOGI("CHAR HEIGHT is %d", fCharHeight); - - /* compute fLeftMargin and fRightMargin */ - ComputeMargins(); - - /* - * Set up the FORMATRANGE values. The Rich Edit stuff likes to have - * measurements in TWIPS, whereas the printer is using DC pixel - * values. fLogPixels_ tells us how many pixels per inch. - */ - memset(pFR, 0, sizeof(FORMATRANGE)); - pFR->hdc = pFR->hdcTarget = fpDC->m_hDC; - - /* - * Set frame for printable area, in TWIPS. The printer DC will set - * its own "reasonable" margins, so the area here is not the entire - * sheet of paper. - */ - pFR->rcPage.left = pFR->rcPage.top = 0; - pFR->rcPage.right = (fHorzRes * kTwipsPerInch) / fLogPixelsX; - pFR->rcPage.bottom = (fVertRes * kTwipsPerInch) / fLogPixelsY; - - long topOffset = (long) ((fCharHeight * 1.5 * kTwipsPerInch) / fLogPixelsY); - pFR->rc.top = pFR->rcPage.top + topOffset; - pFR->rc.bottom = pFR->rcPage.bottom; - pFR->rc.left = pFR->rcPage.left + (fLeftMargin * kTwipsPerInch) / fLogPixelsX; - pFR->rc.right = pFR->rcPage.right - (fRightMargin * kTwipsPerInch) / fLogPixelsX; - - LOGI("PRINTABLE AREA is %d wide x %d high (twips)", - pFR->rc.right - pFR->rc.left, pFR->rc.bottom - pFR->rc.top); - LOGI("FRAME is %d wide x %d high (twips)", - pFR->rcPage.right - pFR->rcPage.left, pFR->rcPage.bottom - pFR->rcPage.top); - - pFR->chrg.cpMin = fStartChar; - pFR->chrg.cpMax = fEndChar; -} - -void PrintRichEdit::ComputeMargins(void) -{ - CFont tmpFont; - CFont* pOldFont; - int char80width, fontHeight, totalMargin; - BOOL result; - - fontHeight = -MulDiv(10, fLogPixelsY, 72); - - result = tmpFont.CreateFont(fontHeight, 0, 0, 0, FW_NORMAL, 0, 0, 0, - DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, - DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, kCourierNew); - ASSERT(result); - - pOldFont = fpDC->SelectObject(&tmpFont); - // in theory we could compute one 'X' * 80; this seems more reliable - WCHAR str[81]; - for (int i = 0; i < 80; i++) - str[i] = 'X'; - str[80] = '\0'; - char80width = StringWidth(str); - fpDC->SelectObject(pOldFont); - - //LOGI("char80 string width=%d", char80width); - - /* - * If there's not enough room on the page, set the margins to zero. - * If the margins required exceed two inches, just set the margin - * to one inch on either side. - */ - totalMargin = fHorzRes - char80width; - if (totalMargin < 0) { - LOGI(" Page not wide enough, setting margins to zero"); - fLeftMargin = fRightMargin = 0; - } else if (totalMargin > fLogPixelsX * 2) { - LOGI(" Page too wide, setting margins to 1 inch"); - fLeftMargin = fRightMargin = fLogPixelsX; - } else { - // try to get leftMargin equal to 1/2" - fLeftMargin = totalMargin / 2; - if (fLeftMargin > fLogPixelsX / 2) - fLeftMargin = fLogPixelsX / 2; - fRightMargin = totalMargin - fLeftMargin -1; - LOGI(" +++ Margins (in %d pixels/inch) are left=%ld right=%ld", - fLogPixelsX, fLeftMargin, fRightMargin); - } -} - -// This was derived from Microsoft KB article 129860. -int PrintRichEdit::DoPrint(CRichEditCtrl* pREC, const WCHAR* title, - int* pNumPages, bool doPrint) -{ - FORMATRANGE fr; - DOCINFO di; - long textLength, textPrinted, lastTextPrinted; - int pageNum; - - LOGI("DoPrint: title='%ls' doPrint=%d", title, doPrint); - LOGI(" startChar=%d endChar=%d startPage=%d endPage=%d", - fStartChar, fEndChar, fStartPage, fEndPage); - - /* - * Get the title font and fill out the FORMATRANGE structure. - */ - PrintPrep(&fr); - - /* fill out a DOCINFO */ - memset(&di, 0, sizeof(di)); - di.cbSize = sizeof(DOCINFO); - di.lpszDocName = title; - - if (doPrint) - fpDC->StartDoc(&di); - - /* - * Here's the really strange part of the affair. The GetTextLength call - * shown in the MSFT KB article doesn't return the correct number of - * characters. The CRichEditView code in MFC uses a GetTextLengthEx - * call, which is documented as being part of the CRichEditCtrl but - * doesn't appear in the header files. Call it the hard way. - * - * If you print a "raw" file with carriage returns, and you use version - * 5.30.23.1200 of "riched20.dll", you get "9609" from GetTextLength and - * "9528" from GetTextLengthEx when the document's length is 9528 and - * there are 81 carriage returns. The value you want to use is 9528, - * because the print code doesn't double-up the count on CRs. - * - * If instead you use version 5.30.23.1215, you get the same answer - * from both calls, and printing works fine. - * - * GetTextLengthEx is part of "riched20.dll". Win9x uses "riched32.dll", - * which doesn't support the call. - */ - GETTEXTLENGTHEX exLenReq; - long basicTextLength, extdTextLength; - basicTextLength = pREC->GetTextLength(); - exLenReq.flags = GTL_PRECISE | GTL_NUMCHARS; -#ifdef _UNICODE - exLenReq.codepage = 1200; // UTF-16 little-endian -#else - exLenReq.codepage = CP_ACP; -#endif - extdTextLength = (long)::SendMessage(pREC->m_hWnd, EM_GETTEXTLENGTHEX, - (WPARAM) &exLenReq, (LPARAM) NULL); - LOGD("RichEdit text length: std=%ld extd=%ld", - basicTextLength, extdTextLength); - - if (fEndChar == -1) { - if (extdTextLength > 0) - textLength = extdTextLength; - else - textLength = basicTextLength; - } else - textLength = fEndChar - fStartChar; - - LOGD(" +++ starting while loop, textLength=%ld", textLength); - pageNum = 0; - lastTextPrinted = -1; - do { - bool skipPage = false; - pageNum++; - LOGD(" +++ while loop: pageNum is %d", pageNum); - - if (fEndPage > 0) { - if (pageNum < fStartPage) - skipPage = true; - if (pageNum > fEndPage) - break; // out of while, ending print - } - - if (doPrint && !skipPage) { - fpDC->StartPage(); - - CFont* pOldFont = fpDC->SelectObject(&fTitleFont); - fpDC->TextOut(0, 0 * fCharHeight, title); - CString pageNumStr; - pageNumStr.Format(L"Page %d", pageNum); - int pageNumWidth = StringWidth(pageNumStr); - fpDC->TextOut(fHorzRes - pageNumWidth, 0 * fCharHeight, pageNumStr); - fpDC->SelectObject(pOldFont); - - CPen penBlack(PS_SOLID, 1, RGB(0, 0, 0)); - CPen* pOldPen = fpDC->SelectObject(&penBlack); - int ycoord = (int) (fCharHeight * 1.25); - fpDC->MoveTo(0, ycoord-1); - fpDC->LineTo(fHorzRes, ycoord-1); - fpDC->MoveTo(0, ycoord); - fpDC->LineTo(fHorzRes, ycoord); - - fpDC->SelectObject(pOldPen); - } - - //LOGI(" +++ calling FormatRange(%d)", doPrint && !skipPage); - //LogHexDump(&fr, sizeof(fr)); - - /* print a page full of RichEdit stuff */ - textPrinted = pREC->FormatRange(&fr, doPrint && !skipPage); - LOGD(" +++ returned from FormatRange (textPrinted=%d)", - textPrinted); - if (textPrinted <= lastTextPrinted) { - /* the earlier StartPage can't be undone, so we'll get an - extra blank page at the very end */ - LOGW("GLITCH: no new text printed (printed=%ld, last=%ld, len=%ld)", - textPrinted, lastTextPrinted, textLength); - pageNum--; // fix page count estimator - break; - } - lastTextPrinted = textPrinted; - - // delay so we can test "cancel" button - //((MainWindow*)::AfxGetMainWnd())->EventPause(1000); - - if (doPrint && !skipPage) { - if (fpDC->EndPage() <= 0) { - /* the "cancel" button was hit */ - LOGI("EndPage returned <= 0 (cancelled)"); - fpDC->AbortDoc(); - return -1; - } - } - - if (textPrinted < textLength) { - fr.chrg.cpMin = textPrinted; - fr.chrg.cpMax = fEndChar; // -1 if nothing selected - } - } while (textPrinted < textLength); - - LOGV(" +++ calling FormatRange(NULL, FALSE)"); - pREC->FormatRange(NULL, FALSE); - LOGV(" +++ returned from final FormatRange"); - - if (doPrint) - fpDC->EndDoc(); - - if (pNumPages != NULL) - *pNumPages = pageNum; - - LOGI("Printing completed (textPrinted=%ld)", textPrinted); - - return 0; -} diff --git a/ciderpress/app/Print.h b/ciderpress/app/Print.h deleted file mode 100644 index 9fa3b1b..0000000 --- a/ciderpress/app/Print.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Goodies needed for printing. - */ -#ifndef APP_PRINT_H -#define APP_PRINT_H - -#include "ContentList.h" -#include "resource.h" - - -/* - * Printing base class. - */ -class PrintStuff { -protected: - PrintStuff(void) : fpDC(NULL) {} - virtual ~PrintStuff(void) {} - - /* get basic goodies, based on the DC */ - virtual void InitBasics(CDC* pDC); - - /* - * Trim a string to the specified number of pixels. If it's too large, - * ellipsis will be added on the left or right. Returns final width. - */ - int TrimString(CString* pStr, int width, bool addOnLeft = false); - - /* - * Fills in blank "pFont" object with font that tries to get us N - * lines per page. - */ - void CreateFontByNumLines(CFont* pFont, int numLines); - - /* - * Returns the width of the string. - */ - int StringWidth(const CString& str); - - static const WCHAR kCourierNew[]; - static const WCHAR kTimesNewRoman[]; - - enum { - kTwipsPerInch = 1440 - }; - - /* the printer DC */ - CDC* fpDC; - - //CFont* fpOldFont; - - /* some stuff gleaned from fpDC */ - int fVertRes; - int fHorzRes; - int fLogPixelsX; - int fLogPixelsY; -}; - - -/* - * Print a content list. - */ -class PrintContentList : public PrintStuff { -public: - PrintContentList(void) : fpContentList(NULL), fCLLinesPerPage(0) {} - virtual ~PrintContentList(void) {} - - /* set the DC and the parent window (for the cancel box) */ - virtual void Setup(CDC* pDC, CWnd* pParent); - - /* - * Initiate printing of the specified list to the configured DC. - * - * Returns 0 if all went well, nonzero on cancellation or failure. - */ - int Print(const ContentList* pContentList); - - int Print(const ContentList* pContentList, int fromPage, int toPage); - - /* this is used to set up the page range selection in print dialog */ - static int GetLinesPerPage(void) { - return kTargetLinesPerPage - kHeaderLines; - } - -private: - /* - * Compute the number of pages in fpContentList. - */ - void CalcNumPages(void); - - /* - * Kick off the print job. - */ - int StartPrint(void); - - /* - * Print all pages. - * - * Returns 0 on success, nonzero on failure. - */ - int DoPrint(void); - - /* - * Print page N of the content list, where N is a 1-based count. - */ - void DoPrintPage(int page); - - enum { - kHeaderLines = 4, // lines of header stuff on each page - kTargetLinesPerPage = 64, // use fLinesPerPage for actual value - }; - - CWnd* fpParentWnd; - CFont fPrintFont; - int fLinesPerPage; - int fCharHeight; - int fEllipsisWidth; - - const ContentList* fpContentList; - CString fDocTitle; - int fCLLinesPerPage; // fLinesPerPage - kHeaderLines - int fNumPages; - int fFromPage; - int fToPage; -}; - - -/* - * Print the contents of a RichEdit control. - */ -class PrintRichEdit : public PrintStuff { -public: - PrintRichEdit(void) : fInitialized(false), fpParentWnd(NULL) {} - virtual ~PrintRichEdit(void) {} - - /* set the DC and the parent window (for the cancel box) */ - virtual void Setup(CDC* pDC, CWnd* pParent); - - /* - * Pre-flight the print process to get the number of pages. - */ - int PrintPreflight(CRichEditCtrl* pREC, int* pNumPages); - - /* - * Print all pages. - */ - int PrintAll(CRichEditCtrl* pREC, const WCHAR* title); - - /* - * Print a range of pages. - */ - int PrintPages(CRichEditCtrl* pREC, const WCHAR* title, int startPage, - int endPage); - - /* - * Print the selected area. - */ - int PrintSelection(CRichEditCtrl* pREC, const WCHAR* title, long startChar, - long endChar); - -private: - /* - * Start the printing process by posting a print-cancel dialog. - */ - int StartPrint(CRichEditCtrl* pREC, const WCHAR* title, - int* pNumPages, bool doPrint); - - /* - * Do some prep work before printing. - */ - void PrintPrep(FORMATRANGE* pFR); - - /* - * Compute the size of the left and right margins, based on the width of 80 - * characters of 10-point Courier New on the current printer. - * - * Sets fLeftMargin and fRightMargin, in printer DC pixels. - */ - void ComputeMargins(void); - - /* - * Send the contents of the rich edit control to the printer DC. - */ - int DoPrint(CRichEditCtrl* pREC, const WCHAR* title, int* pNumPages, - bool doPrint); - - bool fInitialized; - CFont fTitleFont; - int fCharHeight; - int fLeftMargin; - int fRightMargin; - - CWnd* fpParentWnd; - int fStartChar; - int fEndChar; - int fStartPage; - int fEndPage; -}; - -#endif /*APP_PRINT_H*/ diff --git a/ciderpress/app/ProgressCounterDialog.h b/ciderpress/app/ProgressCounterDialog.h deleted file mode 100644 index dcc4087..0000000 --- a/ciderpress/app/ProgressCounterDialog.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Show the progress of something that has no definite bound. Because we - * don't know when we need to stop, we just count upward. - */ -#ifndef APP_PROGRESSCOUNTERDIALOG_H -#define APP_PROGRESSCOUNTERDIALOG_H - -#include "resource.h" - -/* - * Modeless dialog; must be allocated on the heap. - */ -class ProgressCounterDialog : public CancelDialog { -public: - BOOL Create(const CString& descr, CWnd* pParentWnd = NULL) { - fpParentWnd = pParentWnd; - fDescr = descr; - fCountFormat = L"%d"; - fCancel = false; - - /* disable the parent window before we're created */ - if (pParentWnd != NULL) - pParentWnd->EnableWindow(FALSE); - return CancelDialog::Create(&fCancel, IDD_PROGRESS_COUNTER, - pParentWnd); - } - - /* enable the parent window before we're destroyed */ - virtual BOOL DestroyWindow(void) { - if (fpParentWnd != NULL) - fpParentWnd->EnableWindow(TRUE); - return ModelessDialog::DestroyWindow(); - } - - /* set a format string, e.g. "Processing file %d" */ - void SetCounterFormat(const CString& fmt) { fCountFormat = fmt; } - - /* set the current count */ - void SetCount(int count) { - CString msg; - msg.Format(fCountFormat, count); - GetDlgItem(IDC_PROGRESS_COUNTER_COUNT)->SetWindowText(msg); - } - - /* get the status of the "cancelled" flag */ - bool GetCancel(void) const { return fCancel; } - -private: - BOOL OnInitDialog(void) override { - CancelDialog::OnInitDialog(); - - CWnd* pWnd = GetDlgItem(IDC_PROGRESS_COUNTER_DESC); - pWnd->SetWindowText(fDescr); - pWnd = GetDlgItem(IDC_PROGRESS_COUNTER_COUNT); - pWnd->SetWindowText(L""); - pWnd->SetFocus(); // get focus off of the Cancel button - return FALSE; // accept our focus - } - - CWnd* fpParentWnd; - CString fDescr; - CString fCountFormat; - bool fCancel; -}; - -#endif /*APP_PROGRESSCOUNTERDIALOG_H*/ diff --git a/ciderpress/app/RecompressOptionsDialog.cpp b/ciderpress/app/RecompressOptionsDialog.cpp deleted file mode 100644 index 97949de..0000000 --- a/ciderpress/app/RecompressOptionsDialog.cpp +++ /dev/null @@ -1,76 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "RecompressOptionsDialog.h" -#include "NufxArchive.h" - -//BEGIN_MESSAGE_MAP(UseSelectionDialog, CDialog) -// ON_WM_HELPINFO() -// //ON_COMMAND(IDHELP, OnHelp) -//END_MESSAGE_MAP() - - -BOOL RecompressOptionsDialog::OnInitDialog(void) -{ - fCompressionIdx = LoadComboBox((NuThreadFormat) fCompressionType); - - return UseSelectionDialog::OnInitDialog(); -} - -int RecompressOptionsDialog::LoadComboBox(NuThreadFormat fmt) -{ - static const struct { - NuThreadFormat format; - const WCHAR* name; - } kComboStrings[] = { - { kNuThreadFormatUncompressed, L"No compression" }, - { kNuThreadFormatHuffmanSQ, L"Squeeze" }, - { kNuThreadFormatLZW1, L"Dynamic LZW/1" }, - { kNuThreadFormatLZW2, L"Dynamic LZW/2" }, - { kNuThreadFormatLZC12, L"12-bit LZC" }, - { kNuThreadFormatLZC16, L"16-bit LZC" }, - { kNuThreadFormatDeflate, L"Deflate" }, - { kNuThreadFormatBzip2, L"Bzip2" }, - }; - - CComboBox* pCombo; - int idx, comboIdx; - int retIdx = 0; - - pCombo = (CComboBox*) GetDlgItem(IDC_RECOMP_COMP); - ASSERT(pCombo != NULL); - - for (idx = comboIdx = 0; idx < NELEM(kComboStrings); idx++) { - if (NufxArchive::IsCompressionSupported(kComboStrings[idx].format)) { - pCombo->AddString(kComboStrings[idx].name); - pCombo->SetItemData(comboIdx, kComboStrings[idx].format); - - if (kComboStrings[idx].format == fmt) - retIdx = comboIdx; - - comboIdx++; - } - } - - return retIdx; -} - -void RecompressOptionsDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_CBIndex(pDX, IDC_RECOMP_COMP, fCompressionIdx); - - if (pDX->m_bSaveAndValidate) { - CComboBox* pCombo; - pCombo = (CComboBox*) GetDlgItem(IDC_RECOMP_COMP); - ASSERT(pCombo != NULL); - - fCompressionType = pCombo->GetItemData(fCompressionIdx); - LOGI("DDX got type=%d from combo index %d", - fCompressionType, fCompressionIdx); - } - - UseSelectionDialog::DoDataExchange(pDX); -} diff --git a/ciderpress/app/RecompressOptionsDialog.h b/ciderpress/app/RecompressOptionsDialog.h deleted file mode 100644 index 71ff01d..0000000 --- a/ciderpress/app/RecompressOptionsDialog.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Options for recompressing files. This is derived from the "use selection" - * dialog. - */ -#ifndef APP_RECOMPESSOPTIONSDIALOG_H -#define APP_RECOMPESSOPTIONSDIALOG_H - -#include "UseSelectionDialog.h" -#include "../nufxlib/NufxLib.h" -#include "resource.h" - -/* - * Straightforward confirmation plus a drop-list. - */ -class RecompressOptionsDialog : public UseSelectionDialog { -public: - RecompressOptionsDialog(int selCount, CWnd* pParentWnd = NULL) : - UseSelectionDialog(selCount, pParentWnd, IDD_RECOMPRESS_OPTS) - { - fCompressionType = 0; - } - virtual ~RecompressOptionsDialog(void) {} - - // maps directly to NuThreadFormat enum - int fCompressionType; - -private: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * Load strings into the combo box. Only load formats supported by the - * NufxLib DLL. - * - * Returns the combo box index for the format matching "fmt". - */ - int LoadComboBox(NuThreadFormat fmt); - - int fCompressionIdx; // drop list index - - //DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_RECOMPESSOPTIONSDIALOG_H*/ diff --git a/ciderpress/app/Registry.cpp b/ciderpress/app/Registry.cpp deleted file mode 100644 index f8de594..0000000 --- a/ciderpress/app/Registry.cpp +++ /dev/null @@ -1,736 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Windows Registry operations. - */ -#include "stdafx.h" -#ifdef CAN_UPDATE_FILE_ASSOC -#include "Registry.h" -#include "Main.h" -#include "MyApp.h" - -#define kCompanyName L"faddenSoft" - -#if 0 -#define kRegAppName L"CiderPress" -#define kRegExeName L"CiderPress.exe" - -static const WCHAR kRegKeyCPKVersions[] = L"vrs"; -static const WCHAR kRegKeyCPKExpire[] = L"epr"; - -/* - * Application path. Add two keys: - * - * (default) = FullPathName - * Full pathname of the executable file. - * Path = Path - * The $PATH that will be in effect when the program starts (but only if - * launched from the Windows explorer). - */ -static const WCHAR kAppKeyBase[] = - L"HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" kRegExeName; - -/* - * Local settings. App stuff goes in the per-user key, registration info is - * in the per-machine key. - */ -static const WCHAR kMachineSettingsBaseKey[] = - L"HKEY_LOCAL_MACHINE\\SOFTWARE\\" kCompanyName L"\\" kRegAppName; -static const WCHAR kUserSettingsBaseKey[] = - L"HKEY_CURRENT_USER\\Software\\" kCompanyName L"\\" kRegAppName; - -static const WCHAR kRegKeyCPKStr[] = L"CPK"; -#endif - -/* - * ProgID fields. - * - * See http://msdn.microsoft.com/en-us/library/windows/desktop/cc144152%28v=vs.85%29.aspx - */ - -static const WCHAR kDefaultIcon[] = L"DefaultIcon"; -static const WCHAR kFriendlyTypeName[] = L"FriendlyTypeName"; -static const WCHAR kInfoTip[] = L"InfoTip"; - -static const WCHAR kShellOpenCommand[] = L"\\shell\\open\\command"; - - -/* - * ProgID key names. - * - * We used to open HKEY_CLASSES_ROOT, which provides a "merged" view of - * HKEY_LOCAL_MACHINE\Software\Classes and HKEY_CURRENT_USER\Software\Classes. - * The HKLM entries provided defaults for all users on the machine, while the - * HKCU entries were specific to the current user. - * - * It appears that Windows no longer likes it when executables other than the - * app installer (which can run privileged) mess with HKLM, so we just work - * with HKCU directly now. - */ -static const WCHAR kProgIdKeyNuFX[] = L"CiderPress.NuFX.4"; -static const WCHAR kProgIdKeyDiskImage[] = L"CiderPress.DiskImage.4"; -static const WCHAR kProgIdKeyBinaryII[] = L"CiderPress.BinaryII.4"; - -/* file associations go here under HKCU */ -static const WCHAR kFileAssocBase[] = L"Software\\Classes"; - -/* - * Table of file type associations. They will appear in the UI in the same - * order that they appear here, so try to maintain alphabetical order. - */ -const MyRegistry::FileTypeAssoc MyRegistry::kFileTypeAssoc[] = { - { L".2MG", kProgIdKeyDiskImage }, - { L".APP", kProgIdKeyDiskImage }, - { L".BNY", kProgIdKeyBinaryII }, - { L".BQY", kProgIdKeyBinaryII }, - { L".BSE", kProgIdKeyNuFX }, - { L".BXY", kProgIdKeyNuFX }, - { L".D13", kProgIdKeyDiskImage }, - { L".DDD", kProgIdKeyDiskImage }, - { L".DO", kProgIdKeyDiskImage }, - { L".DSK", kProgIdKeyDiskImage }, - { L".FDI", kProgIdKeyDiskImage }, - { L".HDV", kProgIdKeyDiskImage }, - { L".IMG", kProgIdKeyDiskImage }, - { L".NIB", kProgIdKeyDiskImage }, - { L".PO", kProgIdKeyDiskImage }, - { L".SDK", kProgIdKeyDiskImage }, - { L".SEA", kProgIdKeyNuFX }, - { L".SHK", kProgIdKeyNuFX }, -// { L".DC", kProgIdKeyDiskImage }, -// { L".DC6", kProgIdKeyDiskImage }, -// { L".GZ", kProgIdKeyDiskImage }, -// { L".NB2", kProgIdKeyDiskImage }, -// { L".RAW", kProgIdKeyDiskImage }, -// { L".ZIP", kProgIdKeyDiskImage }, -}; - -#if 0 -static const struct { - const char* user; - const char* reg; -} gBadKeys[] = { - { "Nimrod Bonehead", "CP1-68C069-62CC9444" }, - { "Connie Tan", "CP1-877B2C-A428FFD6" }, -}; -#endif - - -/* - * ========================================================================== - * One-time install/uninstall - * ========================================================================== - */ - -void MyRegistry::OneTimeInstall(void) const -{ - /* start by stomping on our ProgIDs */ - LOGI(" Removing ProgIDs"); - RegDeleteKeyHKCU(kProgIdKeyNuFX); - RegDeleteKeyHKCU(kProgIdKeyDiskImage); - RegDeleteKeyHKCU(kProgIdKeyBinaryII); - - /* configure the ProgIDs */ - FixBasicSettings(); - - HKEY hClassesKey = NULL; - if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) { - return; - } - - /* configure extensions */ - for (int i = 0; i < NELEM(kFileTypeAssoc); i++) { - HKEY hExtKey; - LSTATUS res = RegOpenKeyEx(hClassesKey, kFileTypeAssoc[i].ext, 0, - KEY_READ, &hExtKey); - if (res == ERROR_SUCCESS) { - LOGI(" Found existing HKCU\\%ls\\'%ls', leaving alone", - kFileAssocBase, kFileTypeAssoc[i].ext); - RegCloseKey(hExtKey); - } else if (res == ERROR_FILE_NOT_FOUND) { - OwnExtension(kFileTypeAssoc[i].ext, kFileTypeAssoc[i].progId); - } else { - LOGW(" Got error %lu opening HKCU\\%ls\\'%ls', leaving alone", - res, kFileAssocBase, kFileTypeAssoc[i].ext); - } - } - - RegCloseKey(hClassesKey); -} - -void MyRegistry::OneTimeUninstall(void) const -{ - /* drop any associations we hold */ - int i; - for (i = 0; i < NELEM(kFileTypeAssoc); i++) { - CString ext, handler; - bool ours; - - GetFileAssoc(i, &ext, &handler, &ours); - if (ours) { - DisownExtension(ext); - } - } - - /* remove our ProgIDs */ - LOGI(" Removing ProgIDs"); - RegDeleteKeyHKCU(kProgIdKeyNuFX); - RegDeleteKeyHKCU(kProgIdKeyDiskImage); - RegDeleteKeyHKCU(kProgIdKeyBinaryII); -} - - -/* - * ========================================================================== - * Shareware registration logic - * ========================================================================== - */ - -/* [removed] */ - -/* - * ========================================================================== - * Windows shell game - * ========================================================================== - */ - -const WCHAR* MyRegistry::GetAppRegistryKey(void) const -{ - return kCompanyName; -} - -bool MyRegistry::IsOurProgId(const WCHAR* progIdKeyName) const -{ - return (wcsicmp(progIdKeyName, kProgIdKeyNuFX) == 0 || - wcsicmp(progIdKeyName, kProgIdKeyDiskImage) == 0 || - wcsicmp(progIdKeyName, kProgIdKeyBinaryII) == 0); -} - -void MyRegistry::FixBasicSettings(void) const -{ - /* - * Fix the basic registry settings, e.g. our ProgID classes. - * - * We don't overwrite values that already exist. We want to hold on to the - * installer's settings, which should get whacked if the program is - * uninstalled or reinstalled. This is here for "installer-less" environments - * and to cope with registry damage. - */ - - const WCHAR* exeName = gMyApp.GetExeFileName(); - ASSERT(exeName != NULL && wcslen(exeName) > 0); - - LOGI("Fixing any missing file type ProgID entries in registry"); - - ConfigureProgId(kProgIdKeyNuFX, L"NuFX Archive (CiderPress)", exeName, 1); - ConfigureProgId(kProgIdKeyBinaryII, L"Binary II (CiderPress)", exeName, 2); - ConfigureProgId(kProgIdKeyDiskImage, L"Disk Image (CiderPress)", exeName, 3); -} - -void MyRegistry::ConfigureProgId(const WCHAR* progIdKeyName, const WCHAR* descr, - const WCHAR* exeName, int iconIdx) const -{ - LOGI(" ConfigureProgId '%ls' for '%ls'", progIdKeyName, exeName); - - HKEY hClassesKey = NULL; - HKEY hAppKey = NULL; - - if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) { - LOGW(" ConfigureProgId failed to open HKCU"); - return; - } - - DWORD disp; - LONG result; - if ((result = RegCreateKeyEx(hClassesKey, progIdKeyName, 0, REG_NONE, - REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, - &hAppKey, &disp)) == ERROR_SUCCESS) - { - if (disp == REG_CREATED_NEW_KEY) { - LOGD(" Created new key for %ls", progIdKeyName); - } else if (disp == REG_OPENED_EXISTING_KEY) { - LOGD(" Opened existing key for %ls", progIdKeyName); - } else { - LOGD(" Odd RegCreateKeyEx result 0x%lx", disp); - } - - ConfigureProgIdCommand(hAppKey, descr, exeName); - - // Configure default entry and "friendly" type name. The friendly - // name takes precedence (as tested on Win7), but the default entry - // is set for backward compatibility. - if (RegSetValueEx(hAppKey, L"", 0, REG_SZ, (const BYTE*) descr, - (wcslen(descr)+1) * sizeof(WCHAR)) != ERROR_SUCCESS) - { - LOGW(" WARNING: unable to set ProgID default to '%ls'", descr); - } - if (RegSetValueEx(hAppKey, kFriendlyTypeName, 0, REG_SZ, - (const BYTE*) descr, - (wcslen(descr)+1) * sizeof(WCHAR)) != ERROR_SUCCESS) - { - LOGW(" WARNING: unable to set ProgID friendly name to '%ls'", descr); - } - //if (RegSetValueEx(hAppKey, kInfoTip, 0, REG_SZ, - // (const BYTE*) infoTip, - // (wcslen(infoTip)+1) * sizeof(WCHAR)) != ERROR_SUCCESS) - //{ - // LOGW(" WARNING: unable to set ProgID info tip to '%ls'", infoTip); - //} - - // Configure the default icon field. This is a DefaultIcon entry - // with a (Default) name. - HKEY hIconKey = NULL; - if (RegCreateKeyEx(hAppKey, kDefaultIcon, 0, REG_NONE, - REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, - &hIconKey, NULL) == ERROR_SUCCESS) - { - DWORD type, size; - unsigned char buf[512]; - long res; - - size = sizeof(buf); // size in bytes - res = RegQueryValueEx(hIconKey, L"", NULL, &type, buf, &size); - if (res == ERROR_SUCCESS && size > 1) { - LOGI(" Icon for '%ls' already exists, not altering", - progIdKeyName); - } else { - CString iconStr; - iconStr.Format(L"%ls,%d", exeName, iconIdx); - - if (RegSetValueEx(hIconKey, L"", 0, REG_SZ, - (const BYTE*)(LPCWSTR) iconStr, - wcslen(iconStr) * sizeof(WCHAR)) == ERROR_SUCCESS) - { - LOGI(" Set icon for '%ls' to '%ls'", progIdKeyName, - (LPCWSTR) iconStr); - } else { - LOGW(" WARNING: unable to set DefaultIcon for '%ls' to '%ls'", - progIdKeyName, (LPCWSTR) iconStr); - } - } - - RegCloseKey(hIconKey); - } else { - LOGW(" WARNING: couldn't set up DefaultIcon for '%ls'", progIdKeyName); - } - } else { - LOGW(" WARNING: couldn't create ProgId='%ls' (err=%ld)", - progIdKeyName, result); - } - - RegCloseKey(hAppKey); - RegCloseKey(hClassesKey); -} - -void MyRegistry::ConfigureProgIdCommand(HKEY hAppKey, const WCHAR* descr, - const WCHAR* exeName) const -{ - HKEY hShellKey, hOpenKey, hCommandKey; - DWORD dw; - - ASSERT(hAppKey != NULL); - ASSERT(descr != NULL); - ASSERT(exeName != NULL); - hShellKey = hOpenKey = hCommandKey = NULL; - - // TODO: I believe we can do this with a single call -- it should create - // the intermediate path elements for us. - if (RegCreateKeyEx(hAppKey, L"shell", 0, REG_NONE, - REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, - &hShellKey, &dw) == ERROR_SUCCESS) - { - if (RegCreateKeyEx(hShellKey, L"open", 0, REG_NONE, - REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, - &hOpenKey, &dw) == ERROR_SUCCESS) - { - if (RegCreateKeyEx(hOpenKey, L"command", 0, REG_NONE, - REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, - &hCommandKey, &dw) == ERROR_SUCCESS) - { - DWORD type, size; - WCHAR buf[MAX_PATH+8]; - long res; - - size = sizeof(buf); // size in bytes - res = RegQueryValueEx(hCommandKey, L"", NULL, &type, (LPBYTE) buf, - &size); - if (res == ERROR_SUCCESS && size > 1) { - LOGI(" Command already exists, not altering ('%ls')", buf); - } else { - CString openCmd; - - openCmd.Format(L"\"%ls\" \"%%1\"", exeName); - if (RegSetValueEx(hCommandKey, L"", 0, REG_SZ, - (LPBYTE)(LPCWSTR) openCmd, - wcslen(openCmd) * sizeof(WCHAR)) == ERROR_SUCCESS) - { - LOGI(" Set command to '%ls'", (LPCWSTR) openCmd); - } else { - LOGW(" WARNING: unable to set open cmd '%ls'", - (LPCWSTR) openCmd); - } - } - RegCloseKey(hCommandKey); - } - RegCloseKey(hOpenKey); - } - RegCloseKey(hShellKey); - } -} - - -int MyRegistry::GetNumFileAssocs(void) const -{ - return NELEM(kFileTypeAssoc); -} - -void MyRegistry::GetFileAssoc(int idx, CString* pExt, CString* pHandler, - bool* pOurs) const -{ - /* - * We check to see if the file extension is associated with one of our - * ProgID keys. We don't bother to check whether the ProgID keys - * are still associated with CiderPress, since nobody should be - * messing with those. - * - * BUG: we should be checking to see what the shell actually does to - * take into account the overrides that users can set. - */ - - ASSERT(idx >= 0 && idx < NELEM(kFileTypeAssoc)); - - *pExt = kFileTypeAssoc[idx].ext; - *pHandler = L""; - *pOurs = false; - - HKEY hClassesKey = NULL; - if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) { - LOGW("GetFileAssoc failed to open HKCU"); - return; - } - - CString progIdKeyName; - HKEY hExtKey = NULL; - - long res = RegOpenKeyEx(hClassesKey, *pExt, 0, KEY_READ, &hExtKey); - if (res == ERROR_SUCCESS) { - WCHAR buf[MAX_PATH]; // somewhat arbitrary - DWORD size = sizeof(buf); // size in bytes - DWORD type; - - res = RegQueryValueEx(hExtKey, L"", NULL, &type, (LPBYTE)buf, &size); - if (res == ERROR_SUCCESS) { - LOGD(" GetFileAssoc %d got '%ls'", idx, buf); - progIdKeyName = buf; - - if (GetAssocAppName(progIdKeyName, pHandler) != 0) - *pHandler = progIdKeyName; - } else { - LOGW("RegQueryValueEx failed on '%ls'", (LPCWSTR) *pExt); - } - } else { - LOGW(" RegOpenKeyEx failed on '%ls'", (LPCWSTR) *pExt); - } - - if (!pHandler->IsEmpty()) { - *pOurs = IsOurProgId(progIdKeyName); - } - - RegCloseKey(hExtKey); - RegCloseKey(hClassesKey); -} - -int MyRegistry::GetAssocAppName(const CString& progIdKeyName, CString* pCmd) const -{ - HKEY hAppKey = NULL; - int result = -1; - - HKEY hClassesKey = NULL; - if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) { - LOGW("GetAssocAppName failed to open HKCU"); - return result; - } - - CString keyName = progIdKeyName + kShellOpenCommand; - - long res = RegOpenKeyEx(hClassesKey, keyName, 0, KEY_READ, &hAppKey); - if (res == ERROR_SUCCESS) { - WCHAR buf[260]; - DWORD type; - DWORD size = sizeof(buf); // size in bytes - - res = RegQueryValueEx(hAppKey, L"", NULL, &type, (LPBYTE) buf, &size); - if (res == ERROR_SUCCESS) { - CString cmd(buf); - int pos; - - /* cut it down to just the EXE name */ - ReduceToToken(&cmd); - - pos = cmd.ReverseFind('\\'); - if (pos != -1 && pos != cmd.GetLength()-1) { - cmd = cmd.Right(cmd.GetLength() - pos -1); - } - - *pCmd = cmd; - result = 0; - } else { - LOGW("Unable to open %ls for '%ls'", (LPCWSTR) kShellOpenCommand, - (LPCWSTR) progIdKeyName); - } - } else { - CString errBuf; - GetWin32ErrorString(res, &errBuf); - - LOGW("Unable to open ProgId key '%ls' (%ls)", - (LPCWSTR) keyName, (LPCWSTR) errBuf); - } - - RegCloseKey(hAppKey); - RegCloseKey(hClassesKey); - return result; -} - -void MyRegistry::ReduceToToken(CString* pStr) const -{ - WCHAR* argv[1]; - int argc = 1; - WCHAR* mangle = wcsdup(*pStr); - - VectorizeString(mangle, argv, &argc); - - if (argc == 1) - *pStr = argv[0]; - - free(mangle); -} - -int MyRegistry::SetFileAssoc(int idx, bool wantIt) const -{ - /* - * Set the state of a file association. There are four possibilities: - * - * - We own it, we want to keep owning it: do nothing. - * - We don't own it, we want to keep not owning it: do nothing. - * - We own it, we don't want it anymore: remove ".xxx" entry. - * - We don't own it, we want to own it: remove ".xxx" entry and replace it. - * - * Returns 0 on success, nonzero on failure. - */ - - const WCHAR* ext; - bool weOwnIt; - int result = 0; - - ASSERT(idx >= 0 && idx < NELEM(kFileTypeAssoc)); - - ext = kFileTypeAssoc[idx].ext; - weOwnIt = GetAssocState(ext); - LOGI("SetFileAssoc: ext='%ls' own=%d want=%d", ext, weOwnIt, wantIt); - - if (weOwnIt && !wantIt) { - /* reset it */ - LOGI(" SetFileAssoc: clearing '%ls'", ext); - result = DisownExtension(ext); - } else if (!weOwnIt && wantIt) { - /* take it */ - LOGI(" SetFileAssoc: taking '%ls'", ext); - result = OwnExtension(ext, kFileTypeAssoc[idx].progId); - } else { - LOGI(" SetFileAssoc: do nothing with '%ls'", ext); - /* do nothing */ - } - - return 0; -} - -bool MyRegistry::GetAssocState(const WCHAR* ext) const -{ - WCHAR buf[260]; - HKEY hExtKey = NULL; - int res; - bool result = false; - - HKEY hClassesKey = NULL; - if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) { - LOGW("GetAssocState failed to open HKCU"); - return result; - } - - res = RegOpenKeyEx(hClassesKey, ext, 0, KEY_READ, &hExtKey); - if (res == ERROR_SUCCESS) { - DWORD type; - DWORD size = sizeof(buf); // size in bytes - res = RegQueryValueEx(hExtKey, L"", NULL, &type, (LPBYTE) buf, &size); - if (res == ERROR_SUCCESS && type == REG_SZ) { - /* compare it to known ProgID values */ - LOGD(" Found '%ls', testing '%ls'", ext, buf); - if (IsOurProgId((WCHAR*)buf)) - result = true; - } - RegCloseKey(hExtKey); - } - RegCloseKey(hClassesKey); - - return result; -} - -int MyRegistry::DisownExtension(const WCHAR* ext) const -{ - ASSERT(ext != NULL); - ASSERT(ext[0] == '.'); - if (ext == NULL || wcslen(ext) < 2) - return -1; - - if (RegDeleteKeyHKCU(ext) == ERROR_SUCCESS) { - LOGI(" HKCU\\%ls\\%ls subtree deleted", kFileAssocBase, ext); - } else { - LOGW(" Failed deleting HKCU\\%ls\\'%ls'", kFileAssocBase, ext); - return -1; - } - - return 0; -} - -int MyRegistry::OwnExtension(const WCHAR* ext, const WCHAR* progIdKeyName) const -{ - ASSERT(ext != NULL); - ASSERT(ext[0] == '.'); - if (ext == NULL || wcslen(ext) < 2) - return -1; - - HKEY hClassesKey = NULL; - HKEY hExtKey = NULL; - int result = -1; - - if (OpenHKCUSoftwareClasses(&hClassesKey) != ERROR_SUCCESS) { - goto bail; - } - - // delete the old key (which might be a hierarchy) - DWORD res = RegDeleteKeyNT(hClassesKey, ext); - if (res == ERROR_SUCCESS) { - LOGI(" HKCU\\%ls\\%ls subtree deleted", kFileAssocBase, ext); - } else if (res == ERROR_FILE_NOT_FOUND) { - LOGI(" No HKCU\\%ls\\%ls subtree to delete", kFileAssocBase, ext); - } else { - LOGW(" Failed deleting HKCU\\%ls\\'%ls'", kFileAssocBase, ext); - goto bail; - } - - // set the new key - DWORD dw; - if (RegCreateKeyEx(hClassesKey, ext, 0, REG_NONE, - REG_OPTION_NON_VOLATILE, KEY_WRITE|KEY_READ, NULL, - &hExtKey, &dw) == ERROR_SUCCESS) - { - // default entry gets the ProgID key name - res = RegSetValueEx(hExtKey, L"", 0, REG_SZ, (LPBYTE) progIdKeyName, - (wcslen(progIdKeyName)+1) * sizeof(WCHAR)); - if (res == ERROR_SUCCESS) { - LOGI(" Set '%ls' to '%ls'", ext, progIdKeyName); - result = 0; - } else { - LOGW("Failed setting '%ls' to '%ls' (res=%d)", - ext, progIdKeyName, res); - goto bail; - } - } - -bail: - RegCloseKey(hExtKey); - RegCloseKey(hClassesKey); - return result; -} - -DWORD MyRegistry::OpenHKCUSoftwareClasses(HKEY* phKey) const -{ - DWORD result = RegOpenKeyEx(HKEY_CURRENT_USER, kFileAssocBase, 0, - KEY_READ, phKey); - if (result != ERROR_SUCCESS) { - LOGW("Unable to open HKEY_CURRENT_USER \\ '%ls' for reading", - (LPCWSTR) kFileAssocBase); - } - - return result; -} - -DWORD MyRegistry::RegDeleteKeyHKCU(const WCHAR* partialKeyName) const -{ - HKEY hClassesKey; - DWORD result; - - result = OpenHKCUSoftwareClasses(&hClassesKey); - if (result != ERROR_SUCCESS) { return result; } - - result = RegDeleteKeyNT(hClassesKey, partialKeyName); - RegCloseKey(hClassesKey); - if (result != ERROR_SUCCESS) { - LOGW("RegDeleteKeyNT failed (err=%lu)", result); - } - return result; -} - -// (This comes from the MSDN sample sources.) -// -// Recursively delete a key and any sub-keys. -// -// The sample code makes no attempt to check or recover from partial -// deletions. -// -// A registry key that is opened by an application can be deleted -// without error by another application in both Windows 95 and -// Windows NT. This is by design. -// -#define MAX_KEY_LENGTH 256 // not in any header I can find ++ATM -DWORD MyRegistry::RegDeleteKeyNT(HKEY hStartKey, LPCTSTR pKeyName) const -{ - DWORD dwRtn, dwSubKeyLength; - LPTSTR pSubKey = NULL; - TCHAR szSubKey[MAX_KEY_LENGTH]; // (256) this should be dynamic. - HKEY hKey; - - LOGD("RegDeleteKeyNT %p '%ls'", hStartKey, (LPCWSTR) pKeyName); - - // Do not allow NULL or empty key name - if ( pKeyName && lstrlen(pKeyName)) - { - if( (dwRtn=RegOpenKeyEx(hStartKey,pKeyName, - 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &hKey )) == ERROR_SUCCESS) - { - while (dwRtn == ERROR_SUCCESS ) - { - dwSubKeyLength = MAX_KEY_LENGTH; - dwRtn=RegEnumKeyEx( - hKey, - 0, // always index zero, because we're deleting it - szSubKey, - &dwSubKeyLength, - NULL, - NULL, - NULL, - NULL - ); - - if(dwRtn == ERROR_NO_MORE_ITEMS) - { - dwRtn = RegDeleteKey(hStartKey, pKeyName); - break; - } - else if(dwRtn == ERROR_SUCCESS) - dwRtn=RegDeleteKeyNT(hKey, szSubKey); - } - RegCloseKey(hKey); - // Do not save return code because error - // has already occurred - } - } - else - dwRtn = ERROR_BADKEY; - - return dwRtn; -} -#endif diff --git a/ciderpress/app/Registry.h b/ciderpress/app/Registry.h deleted file mode 100644 index 47a3b16..0000000 --- a/ciderpress/app/Registry.h +++ /dev/null @@ -1,193 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * A class representing the system registry. - */ -#ifndef APP_REGISTRY_H -#define APP_REGISTRY_H - -#ifdef CAN_UPDATE_FILE_ASSOC - -/* - * All access to the registry (except for GetProfileInt/GetProfileString) - * should go through this. - */ -class MyRegistry { -public: - MyRegistry(void) {} - ~MyRegistry(void) {} - - /* - typedef enum RegStatus { - kRegUnknown = 0, - kRegNotSet, // unregistered - kRegExpired, // unregistered, expired - kRegValid, // registration present and valid - kRegInvalid, // registration present, but invalid (!) - kRegFailed, // error occurred during registration - } RegStatus; - */ - - /* - * This is called immediately after installation finishes. - * - * We want to snatch up any unused file type associations. We define them - * as "unused" if the entry does not exist in the registry at all. A more - * thorough installer would also verify that the ProgID actually existed - * and "steal" any apparent orphans, but we can let the user do that manually. - */ - void OneTimeInstall(void) const; - - /* - * Remove things that the standard uninstall script won't. - * - * We want to un-set any of our file associations. We don't really need to - * clean up the ".xxx" entries, because removing their ProgID entries is enough - * to fry their little brains, but it's probably the right thing to do. - * - * We definitely want to strip out our ProgIDs. - */ - void OneTimeUninstall(void) const; - - /* - int GetRegistration(CString* pUser, CString* pCompany, - CString* pReg, CString* pVersions, CString* pExpire); - int SetRegistration(const CString& user, const CString& company, - const CString& reg, const CString& versions, const CString& expire); - RegStatus CheckRegistration(CString* pResult); - bool IsValidRegistrationKey(const CString& user, - const CString& company, const CString& reg); - */ - - /* - * Return the application's registry key. This is used as the argument to - * CWinApp::SetRegistryKey(). The GetProfile{Int,String} calls combine this - * (in m_pszRegistryKey) with the app name (in m_pszProfileName) and - * prepends "HKEY_CURRENT_USER\Software\". - */ - const WCHAR* GetAppRegistryKey(void) const; - - // Fix basic settings, e.g. HKCU ProgID classes. - void FixBasicSettings(void) const; - - /* - * Return the number of file type associations. - */ - int GetNumFileAssocs(void) const; - - /* - * Return information on a file association. - * - * For the given index, return the extension, the ProgID key, and an - * indication of whether or not we believe this is ours. If nothing - * is associated with this extension, *pHandler receives an empty string. - */ - void GetFileAssoc(int idx, CString* pExt, CString* pHandler, - bool* pOurs) const; - - /* - * Sets the state of a file association. - */ - int SetFileAssoc(int idx, bool wantIt) const; - - //static uint16_t ComputeStringCRC(const char* str); - -private: - typedef struct FileTypeAssoc { - const WCHAR* ext; // e.g. ".SHK" - const WCHAR* progId; // e.g. "CiderPress.NuFX.4" - } FileTypeAssoc; - - static const FileTypeAssoc kFileTypeAssoc[]; - - /* - * See if a ProgID key is one we recognize. - */ - bool IsOurProgId(const WCHAR* progIdKeyName) const; - - /* - * Set up the registry goodies for one ProgID. - */ - void ConfigureProgId(const WCHAR* progIdKeyName, const WCHAR* descr, - const WCHAR* exeName, int iconIdx) const; - - /* - * Puts the "Open" command in "...\shell\open\command". - */ - void ConfigureProgIdCommand(HKEY hAppKey, const WCHAR* descr, - const WCHAR* exeName) const; - - /* - * Given a ProgID, determine the application's name. The executable - * path will be stripped. - * - * This requires burrowing down into - * HKEY_CURRENT_USER\\shell\open\command\. - * - * This does not currently take into account the Windows shell stuff, i.e. - * HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\<.ext> - * (and I'm not really sure we should). - * - * TODO: if we don't find an association in HKCU, do a lookup in HKCR. - * That could get confusing if there's an older CiderPress association - * lurking in HKLM, but it's at least as confusing to see "no - * association" when there's clearly an association. - */ - int GetAssocAppName(const CString& progIdKeyName, CString* pCmd) const; - - /* - * Reduce a compound string to just its first token. - */ - void ReduceToToken(CString* pStr) const; - - /* - * Determine whether or not the filetype described by "ext" is one that we - * currently manage. - * - * Returns "true" if so, "false" if not. Returns "false" on any errors - * encountered. - */ - bool GetAssocState(const WCHAR* ext) const; - - /* - * Drop ownership of a file extension. We assume we own it. - * - * Returns 0 on success, -1 on error. - */ - int DisownExtension(const WCHAR* ext) const; - - /* - * Take ownership of a file extension. - * - * Returns 0 on success, -1 on error. - */ - int OwnExtension(const WCHAR* ext, const WCHAR* progIdKeyName) const; - - /* - * Open HKEY_CURRENT_USER\Software\Classes for reading. - */ - DWORD OpenHKCUSoftwareClasses(HKEY* phKey) const; - - /* - * Recursively delete a key in the HKEY_CURRENT_USER\Software\Classes - * hierarchy. - */ - DWORD RegDeleteKeyHKCU(const WCHAR* partialKeyName) const; - - DWORD RegDeleteKeyNT(HKEY hStartKey, LPCWSTR pKeyName) const; - - /* key validation */ - //static uint16_t CalcCRC16(uint16_t seed, - // const uint8_t* ptr, int count); - - static char* StripStrings(const char* str1, const char* str2); - void ComputeKey(const char* chBuf, int salt, long* pKeyLo, long* pKeyHi); - int VerifyKey(const char* user, const char* company, const char* key); -}; - -#endif - -#endif /*APP_REGISTRY_H*/ diff --git a/ciderpress/app/RenameEntryDialog.cpp b/ciderpress/app/RenameEntryDialog.cpp deleted file mode 100644 index 9475e02..0000000 --- a/ciderpress/app/RenameEntryDialog.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "RenameEntryDialog.h" - -BEGIN_MESSAGE_MAP(RenameEntryDialog, CDialog) - ON_WM_HELPINFO() - ON_COMMAND(IDHELP, OnHelp) - ON_BN_CLICKED(IDC_RENAME_SKIP, OnSkip) -END_MESSAGE_MAP() - - -BOOL RenameEntryDialog::OnInitDialog(void) -{ - ASSERT(fBasePath.IsEmpty()); - fOldFile = fOldName; - fFssepStr = fFssep; - - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_RENAME_PATHSEP); - pEdit->SetReadOnly(!fCanChangeFssep); - pEdit->LimitText(1); - - /* if they can't rename the full path, only give them the file name */ - if (fCanRenameFullPath || fFssep == '\0') { - fNewName = fOldName; - // fBasePath is empty - } else { - int offset; - - offset = fOldName.ReverseFind(fFssep); - if (offset < fOldName.GetLength()) { - fBasePath = fOldName.Left(offset); - fNewName = fOldName.Right(fOldName.GetLength() - (offset+1)); - } else { - /* weird -- filename ended with an fssep? */ - ASSERT(false); // debugbreak - // fBasePath is empty - fNewName = fOldName; - } - } - - /* do the DoDataExchange stuff */ - CDialog::OnInitDialog(); - - /* select the editable text and set the focus */ - pEdit = (CEdit*) GetDlgItem(IDC_RENAME_NEW); - ASSERT(pEdit != NULL); - pEdit->SetSel(0, -1); - pEdit->SetFocus(); - - return FALSE; // we set the focus -} - -void RenameEntryDialog::DoDataExchange(CDataExchange* pDX) -{ - CString msg, failed; - - CheckedLoadString(&failed, IDS_MB_APP_NAME); - - /* fNewName must come last, or the focus will be set on the wrong field - when we return after failure */ - DDX_Text(pDX, IDC_RENAME_OLD, fOldFile); - DDX_Text(pDX, IDC_RENAME_PATHSEP, fFssepStr); - DDX_Text(pDX, IDC_RENAME_NEW, fNewName); - - /* validate the path field */ - if (pDX->m_bSaveAndValidate) { - if (fNewName.IsEmpty()) { - msg = "You must specify a new name."; - goto fail; - } - - msg = fpArchive->TestPathName(fpEntry, fBasePath, fNewName, fFssep); - if (!msg.IsEmpty()) - goto fail; - - if (fFssepStr.IsEmpty()) - fFssep = '\0'; - else - fFssep = (char) fFssepStr.GetAt(0); // could be '\0', that's okay - } - - return; - -fail: - ASSERT(!msg.IsEmpty()); - MessageBox(msg, failed, MB_OK); - pDX->Fail(); - return; -} - -void RenameEntryDialog::OnSkip(void) -{ - /* - * User pressed the "skip" button, which causes us to bail with a result that - * skips the rename but continues with the series. - */ - EndDialog(IDIGNORE); -} diff --git a/ciderpress/app/RenameEntryDialog.h b/ciderpress/app/RenameEntryDialog.h deleted file mode 100644 index 3e9c337..0000000 --- a/ciderpress/app/RenameEntryDialog.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Rename an archive entry. - */ -#ifndef APP_RENAMEENTRYDIALOG_H -#define APP_RENAMEENTRYDIALOG_H - -#include "GenericArchive.h" -#include "resource.h" - -/* - * Rename an entry in an archive (as opposed to renaming a file on the local - * hard drive). - * - * We use the GenericArchive to verify that the name is valid before we - * shut the dialog. - * - * We should probably dim the "Skip" button when there aren't any more entries - * to rename. This requires that the caller tell us when we're on the last - * one. - */ -class RenameEntryDialog : public CDialog { -public: - RenameEntryDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_RENAME_ENTRY, pParentWnd) - { - fFssep = '='; - //fNewNameLimit = 0; - fpArchive = NULL; - fpEntry = NULL; - fCanRenameFullPath = false; - fCanChangeFssep = false; - } - virtual ~RenameEntryDialog(void) {} - - void SetCanRenameFullPath(bool val) { fCanRenameFullPath = val; } - void SetCanChangeFssep(bool val) { fCanChangeFssep = val; } - - CString fOldName; - char fFssep; - CString fNewName; - //int fNewNameLimit; // max #of chars accepted, or 0 - const GenericArchive* fpArchive; - const GenericEntry* fpEntry; - -protected: - // overrides - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnSkip(void); - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_RENAME_ENTRY); - } - -private: - //CString fOldPath; // pathname component, or empty if canRenFull - CString fOldFile; // filename component, or full name if ^^^ - CString fBasePath; - CString fFssepStr; - bool fCanRenameFullPath; - bool fCanChangeFssep; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_RENAMEENTRYDIALOG_H*/ diff --git a/ciderpress/app/RenameVolumeDialog.cpp b/ciderpress/app/RenameVolumeDialog.cpp deleted file mode 100644 index a40dcd8..0000000 --- a/ciderpress/app/RenameVolumeDialog.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "RenameVolumeDialog.h" -#include "DiskFSTree.h" -#include "DiskArchive.h" - -BEGIN_MESSAGE_MAP(RenameVolumeDialog, CDialog) - ON_NOTIFY(TVN_SELCHANGED, IDC_RENAMEVOL_TREE, OnSelChanged) - ON_BN_CLICKED(IDHELP, OnHelp) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - -BOOL RenameVolumeDialog::OnInitDialog(void) -{ - /* do the DoDataExchange stuff */ - CDialog::OnInitDialog(); - - CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_RENAMEVOL_TREE); - DiskImgLib::DiskFS* pDiskFS = fpArchive->GetDiskFS(); - - ASSERT(pTree != NULL); - - fDiskFSTree.fIncludeSubdirs = false; - fDiskFSTree.fExpandDepth = -1; - if (!fDiskFSTree.BuildTree(pDiskFS, pTree)) { - LOGI("Tree load failed!"); - OnCancel(); - } - - int count = pTree->GetCount(); - LOGI("ChooseAddTargetDialog tree has %d items", count); - - /* select the default text and set the focus */ - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_RENAMEVOL_NEW); - ASSERT(pEdit != NULL); - pEdit->SetSel(0, -1); - pEdit->SetFocus(); - - return FALSE; // we set the focus -} - -void RenameVolumeDialog::DoDataExchange(CDataExchange* pDX) -{ - CString msg, failed; - //DiskImgLib::DiskFS* pDiskFS = fpArchive->GetDiskFS(); - - CheckedLoadString(&failed, IDS_MB_APP_NAME); - - /* put fNewName last so it gets the focus after failure */ - DDX_Text(pDX, IDC_RENAMEVOL_NEW, fNewName); - - /* validate the path field */ - if (pDX->m_bSaveAndValidate) { - /* - * Make sure they chose a volume that can be modified. - */ - CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_RENAMEVOL_TREE); - CString errMsg, appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - HTREEITEM selected; - selected = pTree->GetSelectedItem(); - if (selected == NULL) { - errMsg = "Please select a disk to rename."; - MessageBox(errMsg, appName, MB_OK); - pDX->Fail(); - return; - } - - DiskFSTree::TargetData* pTargetData; - pTargetData = (DiskFSTree::TargetData*) pTree->GetItemData(selected); - if (!pTargetData->selectable) { - errMsg = "You can't rename that volume."; - MessageBox(errMsg, appName, MB_OK); - pDX->Fail(); - return; - } - ASSERT(pTargetData->kind == DiskFSTree::kTargetDiskFS); - - /* - * Verify that the new name is okay. (Do this *after* checking the - * volume above to avoid spurious complaints about unsupported - * filesystems.) - */ - if (fNewName.IsEmpty()) { - msg = "You must specify a new name."; - goto fail; - } - msg = fpArchive->TestVolumeName(pTargetData->pDiskFS, fNewName); - if (!msg.IsEmpty()) - goto fail; - - - /* - * Looks good. Fill in the answer. - */ - fpChosenDiskFS = pTargetData->pDiskFS; - } - - return; - -fail: - ASSERT(!msg.IsEmpty()); - MessageBox(msg, failed, MB_OK); - pDX->Fail(); - return; -} - -void RenameVolumeDialog::OnSelChanged(NMHDR* pnmh, LRESULT* pResult) -{ - CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_RENAMEVOL_TREE); - HTREEITEM selected; - CString newText; - - selected = pTree->GetSelectedItem(); - if (selected != NULL) { - DiskFSTree::TargetData* pTargetData; - pTargetData = (DiskFSTree::TargetData*) pTree->GetItemData(selected); - if (pTargetData->selectable) { - newText = pTargetData->pDiskFS->GetBareVolumeName(); - } else { - newText = ""; - } - } - - CEdit* pEdit = (CEdit*) GetDlgItem(IDC_RENAMEVOL_NEW); - ASSERT(pEdit != NULL); - pEdit->SetWindowText(newText); - pEdit->SetSel(0, -1); - - *pResult = 0; -} diff --git a/ciderpress/app/RenameVolumeDialog.h b/ciderpress/app/RenameVolumeDialog.h deleted file mode 100644 index ff06360..0000000 --- a/ciderpress/app/RenameVolumeDialog.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Declarations for "rename volume" dialog. - * - * Show a tree with possible volumes and sub-volumes, and ask the user to - * enter the desired name (or volume number). - * - * We need to have the tree, rather than just clicking on an entry in the file - * list, because we want to be able to change names and volume numbers on - * disks with no files. - */ -#ifndef APP_RENAMEVOLUME_H -#define APP_RENAMEVOLUME_H - -#include "DiskFSTree.h" -#include "resource.h" - -class DiskArchive; - -/* - * Get a pointer to the DiskFS that we're altering, and a valid string for - * the new volume name. - */ -class RenameVolumeDialog : public CDialog { -public: - RenameVolumeDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_RENAME_VOLUME, pParentWnd) - { - fpArchive = NULL; - } - virtual ~RenameVolumeDialog(void) {} - - const DiskArchive* fpArchive; - CString fNewName; - DiskImgLib::DiskFS* fpChosenDiskFS; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * Get a notification whenever the selection changes. Use it to stuff a - * default value into the edit box. - */ - afx_msg void OnSelChanged(NMHDR* pnmh, LRESULT* pResult); - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_RENAME_VOLUME); - } - - DiskFSTree fDiskFSTree; - -private: - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_RENAMEVOLUME_H*/ diff --git a/ciderpress/app/Squeeze.cpp b/ciderpress/app/Squeeze.cpp deleted file mode 100644 index 2ae386e..0000000 --- a/ciderpress/app/Squeeze.cpp +++ /dev/null @@ -1,403 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of SQueeze (RLE+Huffman) compression. - * - * This was ripped fairly directly from Squeeze.c in NufxLib. Because - * there's relatively little code, and providing direct access to the - * compression functions already in NuLib is a little unwieldy, I've just - * cut & pasted the necessary pieces here. - */ -#include "stdafx.h" -#include "Squeeze.h" -#include "NufxArchive.h" - -#define kSqBufferSize 8192 /* must hold full SQ header, and % 128 */ - -#define kNuSQMagic 0xff76 /* magic value for file header */ -#define kNuSQRLEDelim 0x90 /* RLE delimiter */ -#define kNuSQEOFToken 256 /* distinguished stop symbol */ -#define kNuSQNumVals 257 /* 256 symbols + stop */ - - -/* - * =========================================================================== - * Unsqueeze - * =========================================================================== - */ - -/* - * State during uncompression. - */ -typedef struct USQState { - unsigned long dataInBuffer; - unsigned char* dataPtr; - int bitPosn; - int bits; - - /* - * Decoding tree; first "nodeCount" values are populated. Positive - * values are indices to another node in the tree, negative values - * are literals (+1 because "negative zero" doesn't work well). - */ - int nodeCount; - struct { - short child[2]; /* left/right kids, must be signed 16-bit */ - } decTree[kNuSQNumVals-1]; -} USQState; - - -/* - * Decode the next symbol from the Huffman stream. - */ -static NuError USQDecodeHuffSymbol(USQState* pUsqState, int* pVal) -{ - short val = 0; - int bits, bitPosn; - - bits = pUsqState->bits; /* local copy */ - bitPosn = pUsqState->bitPosn; - - do { - if (++bitPosn > 7) { - /* grab the next byte and use that */ - bits = *pUsqState->dataPtr++; - bitPosn = 0; - if (!pUsqState->dataInBuffer--) - return kNuErrBufferUnderrun; - - val = pUsqState->decTree[val].child[1 & bits]; - } else { - /* still got bits; shift right and use it */ - val = pUsqState->decTree[val].child[1 & (bits >>= 1)]; - } - } while (val >= 0); - - /* val is negative literal; add one to make it zero-based then negate it */ - *pVal = -(val + 1); - - pUsqState->bits = bits; - pUsqState->bitPosn = bitPosn; - - return kNuErrNone; -} - - -/* - * Read two bytes of signed data out of the buffer. - */ -static inline NuError USQReadShort(USQState* pUsqState, short* pShort) -{ - if (pUsqState->dataInBuffer < 2) - return kNuErrBufferUnderrun; - - *pShort = *pUsqState->dataPtr++; - *pShort |= (*pUsqState->dataPtr++) << 8; - pUsqState->dataInBuffer -= 2; - - return kNuErrNone; -} - -/* - * Wrapper for fread(). Note the arguments resemble read(2) rather - * than fread(3S). - */ -static NuError SQRead(FILE* fp, void* buf, size_t nbyte) -{ - size_t result; - - ASSERT(buf != NULL); - ASSERT(nbyte > 0); - ASSERT(fp != NULL); - - errno = 0; - result = fread(buf, 1, nbyte, fp); - if (result != nbyte) - return errno ? (NuError)errno : kNuErrFileRead; - return kNuErrNone; -} - -NuError UnSqueeze(FILE* fp, unsigned long realEOF, ExpandBuffer* outExp, - bool fullSqHeader, int blockSize) -{ - /* - * Because we have a stop symbol, knowing the uncompressed length of - * the file is not essential. - */ - - NuError err = kNuErrNone; - USQState usqState; - unsigned long compRemaining, getSize; - unsigned short magic, fileChecksum, checksum; // fullSqHeader only - short nodeCount; - int i, inrep; - unsigned char* tmpBuf = NULL; - unsigned char lastc = 0; - - tmpBuf = (unsigned char*) malloc(kSqBufferSize); - if (tmpBuf == NULL) { - err = kNuErrMalloc; - goto bail; - } - - usqState.dataInBuffer = 0; - usqState.dataPtr = tmpBuf; - - compRemaining = realEOF; - if ((fullSqHeader && compRemaining < 8) || - (!fullSqHeader && compRemaining < 3)) - { - err = kNuErrBadData; - LOGI("too short to be valid SQ data"); - goto bail; - } - - /* - * Round up to the nearest 128-byte boundary. We need to read - * everything out of the file in case this is a streaming archive. - * Because the compressed data has an embedded stop symbol, it's okay - * to "overrun" the expansion code. - */ - if (blockSize != 0) { - compRemaining = - ((compRemaining + blockSize-1) / blockSize) * blockSize; - } - - /* want to grab up to kSqBufferSize bytes */ - if (compRemaining > kSqBufferSize) - getSize = kSqBufferSize; - else - getSize = compRemaining; - - /* - * Grab a big chunk. "compRemaining" is the amount of compressed - * data left in the file, usqState.dataInBuffer is the amount of - * compressed data left in the buffer. - * - * For BNY, we want to read 128-byte blocks. - */ - if (getSize) { - ASSERT(getSize <= kSqBufferSize); - err = SQRead(fp, usqState.dataPtr, getSize); - if (err != kNuErrNone) { - LOGI("failed reading compressed data (%ld bytes)", getSize); - goto bail; - } - usqState.dataInBuffer += getSize; - if (getSize > compRemaining) - compRemaining = 0; - else - compRemaining -= getSize; - } - - /* reset dataPtr */ - usqState.dataPtr = tmpBuf; - - /* - * Read the header. We assume that the header will fit in the - * compression buffer ( sq allowed 300+ for the filename, plus - * 257*2 for the tree, plus misc). - */ - ASSERT(kSqBufferSize > 1200); - if (fullSqHeader) { - err = USQReadShort(&usqState, (short*)&magic); - if (err != kNuErrNone) - goto bail; - if (magic != kNuSQMagic) { - err = kNuErrBadData; - LOGI("bad magic number in SQ block"); - goto bail; - } - - err = USQReadShort(&usqState, (short*)&fileChecksum); - if (err != kNuErrNone) - goto bail; - - checksum = 0; - - /* skip over the filename */ - while (*usqState.dataPtr++ != '\0') - usqState.dataInBuffer--; - usqState.dataInBuffer--; - } - - err = USQReadShort(&usqState, &nodeCount); - if (err != kNuErrNone) - goto bail; - if (nodeCount < 0 || nodeCount >= kNuSQNumVals) { - err = kNuErrBadData; - LOGI("invalid decode tree in SQ (%d nodes)", nodeCount); - goto bail; - } - usqState.nodeCount = nodeCount; - - /* initialize for possibly empty tree (only happens on an empty file) */ - usqState.decTree[0].child[0] = -(kNuSQEOFToken+1); - usqState.decTree[0].child[1] = -(kNuSQEOFToken+1); - - /* read the nodes, ignoring "read errors" until we're done */ - for (i = 0; i < nodeCount; i++) { - err = USQReadShort(&usqState, &usqState.decTree[i].child[0]); - err = USQReadShort(&usqState, &usqState.decTree[i].child[1]); - } - if (err != kNuErrNone) { - err = kNuErrBadData; - LOGI("SQ data looks truncated at tree"); - goto bail; - } - - usqState.bitPosn = 99; /* force an immediate read */ - - /* - * Start pulling data out of the file. We have to Huffman-decode - * the input, and then feed that into an RLE expander. - * - * A completely lopsided (and broken) Huffman tree could require - * 256 tree descents, so we want to try to ensure we have at least 256 - * bits in the buffer. Otherwise, we could get a false buffer underrun - * indication back from DecodeHuffSymbol. - * - * The SQ sources actually guarantee that a code will fit entirely - * in 16 bits, but there's no reason not to use the larger value. - */ - inrep = false; - while (1) { - int val; - - if (usqState.dataInBuffer < 65 && compRemaining) { - /* - * Less than 256 bits, but there's more in the file. - * - * First thing we do is slide the old data to the start of - * the buffer. - */ - if (usqState.dataInBuffer) { - ASSERT(tmpBuf != usqState.dataPtr); - memmove(tmpBuf, usqState.dataPtr, usqState.dataInBuffer); - } - usqState.dataPtr = tmpBuf; - - /* - * Next we read as much as we can. - */ - if (kSqBufferSize - usqState.dataInBuffer < compRemaining) - getSize = kSqBufferSize - usqState.dataInBuffer; - else - getSize = compRemaining; - - ASSERT(getSize <= kSqBufferSize); - //LOGI("Reading from offset=%ld (compRem=%ld)", - // ftell(fp), compRemaining); - err = SQRead(fp, usqState.dataPtr + usqState.dataInBuffer, - getSize); - if (err != kNuErrNone) { - LOGI("failed reading compressed data (%ld bytes, err=%d)", - getSize, err); - goto bail; - } - usqState.dataInBuffer += getSize; - if (getSize > compRemaining) - compRemaining = 0; - else - compRemaining -= getSize; - - ASSERT(compRemaining < 32767*65536); - ASSERT(usqState.dataInBuffer <= kSqBufferSize); - } - - err = USQDecodeHuffSymbol(&usqState, &val); - if (err != kNuErrNone) { - LOGI("failed decoding huff symbol"); - goto bail; - } - - if (val == kNuSQEOFToken) - break; - - /* - * Feed the symbol into the RLE decoder. - */ - if (inrep) { - /* - * Last char was RLE delim, handle this specially. We use - * --val instead of val-- because we already emitted the - * first occurrence of the char (right before the RLE delim). - */ - if (val == 0) { - /* special case -- just an escaped RLE delim */ - lastc = kNuSQRLEDelim; - val = 2; - } - while (--val) { - /*if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, &lastc, 1);*/ - if (outExp != NULL) - outExp->Putc(lastc); - if (fullSqHeader) { - checksum += lastc; - } - } - inrep = false; - } else { - /* last char was ordinary */ - if (val == kNuSQRLEDelim) { - /* set a flag and catch the count the next time around */ - inrep = true; - } else { - lastc = val; - /*if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, &lastc, 1);*/ - if (outExp != NULL) - outExp->Putc(lastc); - if (fullSqHeader) { - checksum += lastc; - } - } - } - - } - - if (inrep) { - err = kNuErrBadData; - LOGI("got stop symbol when run length expected"); - goto bail; - } - - if (fullSqHeader) { - /* verify the checksum stored in the SQ file */ - if (checksum != fileChecksum) { - err = kNuErrBadDataCRC; - LOGI("expected 0x%04x, got 0x%04x (SQ)", fileChecksum, checksum); - goto bail; - } else { - LOGI("--- SQ checksums match (0x%04x)", checksum); - } - } - - /* - * Gobble up any unused bytes in the last 128-byte block. There - * shouldn't be more than that left over. - */ - if (compRemaining > kSqBufferSize) { - err = kNuErrBadData; - LOGI("wow: found %ld bytes left over", compRemaining); - goto bail; - } - if (compRemaining) { - LOGI("+++ slurping up last %ld bytes", compRemaining); - err = SQRead(fp, tmpBuf, compRemaining); - if (err != kNuErrNone) { - LOGI("failed reading leftovers"); - goto bail; - } - } - -bail: - //if (outfp != NULL) - // fflush(outfp); - free(tmpBuf); - return err; -} diff --git a/ciderpress/app/Squeeze.h b/ciderpress/app/Squeeze.h deleted file mode 100644 index e532cca..0000000 --- a/ciderpress/app/Squeeze.h +++ /dev/null @@ -1,20 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of SQueeze compression. - */ -#ifndef APP_SQUEEZE_H -#define APP_SQUEEZE_H - -/* - * Expand "SQ" format. Archive file should already be seeked. - * - * If "outExp" is NULL, no output is produced (useful for "test" mode). - */ -NuError UnSqueeze(FILE* fp, unsigned long realEOF, ExpandBuffer* outExp, - bool fullSqHeader, int blockSize); - -#endif /*APP_SQUEEZE_H*/ diff --git a/ciderpress/app/StdAfx.cpp b/ciderpress/app/StdAfx.cpp deleted file mode 100644 index 29f4e9c..0000000 --- a/ciderpress/app/StdAfx.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// stdafx.cpp : source file that includes just the standard includes -// diskimg.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/ciderpress/app/StdAfx.h b/ciderpress/app/StdAfx.h deleted file mode 100644 index f833e50..0000000 --- a/ciderpress/app/StdAfx.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#if !defined(AFX_STDAFX_H__1CB7B33E_42BF_4A98_B814_4198EA8ACC57__INCLUDED_) -#define AFX_STDAFX_H__1CB7B33E_42BF_4A98_B814_4198EA8ACC57__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers -#define VC_EXTRALEAN - -// enable file association editing -#define CAN_UPDATE_FILE_ASSOC - -#include "targetver.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "../diskimg/DiskImg.h" -#include "../util/UtilLib.h" -#include "Main.h" - -// TODO: reference additional headers your program requires here - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_STDAFX_H__1CB7B33E_42BF_4A98_B814_4198EA8ACC57__INCLUDED_) diff --git a/ciderpress/app/SubVolumeDialog.cpp b/ciderpress/app/SubVolumeDialog.cpp deleted file mode 100644 index dfb0b46..0000000 --- a/ciderpress/app/SubVolumeDialog.cpp +++ /dev/null @@ -1,55 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for the sub-volume selection dialog. - * - * This just picks a sub-volume. Image format overrides and blocks vs. - * sectors should be chosen elsewhere. - */ -#include "stdafx.h" -#include "SubVolumeDialog.h" -#include "resource.h" -#include "../reformat/Charset.h" - - -BEGIN_MESSAGE_MAP(SubVolumeDialog, CDialog) - ON_LBN_DBLCLK(IDC_SUBV_LIST, OnItemDoubleClicked) -END_MESSAGE_MAP() - - -BOOL SubVolumeDialog::OnInitDialog(void) -{ - ASSERT(fpDiskFS != NULL); - - CListBox* pListBox = (CListBox*) GetDlgItem(IDC_SUBV_LIST); - ASSERT(pListBox != NULL); - -// if (pListBox->SetTabStops(12) != TRUE) { -// ASSERT(false); -// } - - DiskFS::SubVolume* pSubVol = fpDiskFS->GetNextSubVolume(NULL); - ASSERT(pSubVol != NULL); // shouldn't be here otherwise - while (pSubVol != NULL) { - CString volumeId(Charset::ConvertMORToUNI(pSubVol->GetDiskFS()->GetVolumeID())); - pListBox->AddString(volumeId); // makes a copy of the string - - pSubVol = fpDiskFS->GetNextSubVolume(pSubVol); - } - - return CDialog::OnInitDialog(); -} - -void SubVolumeDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_LBIndex(pDX, IDC_SUBV_LIST, fListBoxIndex); -} - -void SubVolumeDialog::OnItemDoubleClicked(void) -{ - // Accept a double-click as an "OK". - OnOK(); -} diff --git a/ciderpress/app/SubVolumeDialog.h b/ciderpress/app/SubVolumeDialog.h deleted file mode 100644 index 5e0b96f..0000000 --- a/ciderpress/app/SubVolumeDialog.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Sub-volume selection dialog. - */ -#ifndef APP_SUBVOLUMEDIALOG_H -#define APP_SUBVOLUMEDIALOG_H - -#include "resource.h" -#include "../diskimg/DiskImg.h" -using namespace DiskImgLib; - -/* - * Display the sub-volume selection dialog, which is primarily a list box - * with the sub-volumes listed in it. - */ -class SubVolumeDialog : public CDialog { -public: - SubVolumeDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_SUBV, pParentWnd) - { - fListBoxIndex = 0; - } - virtual ~SubVolumeDialog(void) {} - - void Setup(DiskFS* pDiskFS) { fpDiskFS = pDiskFS; } - - /* so long as we don't sort the list, this number is enough */ - int fListBoxIndex; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg void OnItemDoubleClicked(void); - -private: - DiskFS* fpDiskFS; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_SUBVOLUMEDIALOG_H*/ diff --git a/ciderpress/app/Tools.cpp b/ciderpress/app/Tools.cpp deleted file mode 100644 index 2e6aaef..0000000 --- a/ciderpress/app/Tools.cpp +++ /dev/null @@ -1,2404 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Some items from the "tools" menu. - * - * [ There's a lot of cutting and pasting going on in here. Some of this - * stuff needs to get refactored. ++ATM 20040729 ] - */ -#include "StdAfx.h" -#include "Main.h" -#include "DiskEditDialog.h" -#include "ImageFormatDialog.h" -#include "DiskConvertDialog.h" -#include "ChooseDirDialog.h" -#include "DoneOpenDialog.h" -#include "OpenVolumeDialog.h" -#include "DiskEditOpenDialog.h" -#include "VolumeCopyDialog.h" -#include "CreateImageDialog.h" -#include "DiskArchive.h" -#include "EOLScanDialog.h" -#include "TwoImgPropsDialog.h" -#include // need chsize() for TwoImgProps - - -int MainWindow::TryDiskImgOverride(DiskImg* pImg, const WCHAR* fileSource, - DiskImg::FSFormat defaultFormat, int* pDisplayFormat, bool allowUnknown, - CString* pErrMsg) -{ - ImageFormatDialog imf; - - *pErrMsg = ""; - imf.InitializeValues(pImg); - imf.fFileSource = fileSource; - imf.fAllowUnknown = allowUnknown; - if (pDisplayFormat == NULL) - imf.SetQueryDisplayFormat(false); - - /* don't show "unknown format" if we have a default value */ - if (defaultFormat != DiskImg::kFormatUnknown && - imf.fFSFormat == DiskImg::kFormatUnknown) - { - imf.fFSFormat = defaultFormat; - } - - LOGI(" On entry, sectord=%d format=%d", - imf.fSectorOrder, imf.fFSFormat); - - if (imf.DoModal() != IDOK) { - LOGI(" User bailed on IMF dialog"); - return IDCANCEL; - } - - LOGI(" On exit, sectord=%d format=%d", - imf.fSectorOrder, imf.fFSFormat); - - if (pDisplayFormat != NULL) - *pDisplayFormat = imf.fDisplayFormat; - if (imf.fSectorOrder != pImg->GetSectorOrder() || - imf.fFSFormat != pImg->GetFSFormat()) - { - LOGI("Initial values overridden, forcing img format"); - DIError dierr; - dierr = pImg->OverrideFormat(pImg->GetPhysicalFormat(), imf.fFSFormat, - imf.fSectorOrder); - if (dierr != kDIErrNone) { - pErrMsg->Format(L"Unable to access disk image using selected" - L" parameters. Error: %hs.", - DiskImgLib::DIStrError(dierr)); - // fall through to "return IDOK" - } - } - - return IDOK; -} - - -/* - * ========================================================================== - * Disk Editor - * ========================================================================== - */ - -void MainWindow::OnToolsDiskEdit(void) -{ - DIError dierr; - DiskImg img; - CString loadName, saveFolder; - CString failed, errMsg; - DiskEditOpenDialog diskEditOpen(this); - /* create three, show one */ - BlockEditDialog blockEdit(this); - SectorEditDialog sectorEdit(this); - NibbleEditDialog nibbleEdit(this); - DiskEditDialog* pEditDialog; - int displayFormat; - bool readOnly = true; - - /* flush current archive in case that's what we're planning to edit */ - OnFileSave(); - - CheckedLoadString(&failed, IDS_FAILED); - - diskEditOpen.fArchiveOpen = false; - if (fpOpenArchive != NULL && - fpOpenArchive->GetArchiveKind() == GenericArchive::kArchiveDiskImage) - { - diskEditOpen.fArchiveOpen = true; - } - - if (diskEditOpen.DoModal() != IDOK) - goto bail; - - /* - * Choose something to open, based on "fOpenWhat". - */ - if (diskEditOpen.fOpenWhat == DiskEditOpenDialog::kOpenFile) { - CString openFilters, saveFolder; - - openFilters = kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - /* for now, everything is read-only */ - dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - loadName = dlg.GetPathName(); - readOnly = true; // add to file dialog - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - } else if (diskEditOpen.fOpenWhat == DiskEditOpenDialog::kOpenVolume) { - OpenVolumeDialog dlg(this); - int result; - - result = dlg.DoModal(); - if (result != IDOK) - goto bail; - - loadName = dlg.fChosenDrive; - readOnly = (dlg.fReadOnly != 0); - } else if (diskEditOpen.fOpenWhat == DiskEditOpenDialog::kOpenCurrent) { - // get values from currently open archive - assert(fpOpenArchive != NULL); - loadName = fpOpenArchive->GetPathName(); - readOnly = fpOpenArchive->IsReadOnly(); - } else { - LOGI("GLITCH: unexpected fOpenWhat %d", diskEditOpen.fOpenWhat); - ASSERT(false); - goto bail; - } - - LOGI("Disk editor what=%d name='%ls' ro=%d", - diskEditOpen.fOpenWhat, (LPCWSTR) loadName, readOnly); - - -#if 1 - { - CWaitCursor waitc; - /* open the image file and analyze it */ - CStringA loadNameA(loadName); - dierr = img.OpenImage(loadNameA, PathProposal::kLocalFssep, true); - } -#else - /* quick test of memory-buffer-based interface */ - FILE* tmpfp; - char* phatbuf; - long length; - - tmpfp = fopen(loadName, "rb"); - ASSERT(tmpfp != NULL); - fseek(tmpfp, 0, SEEK_END); - length = ftell(tmpfp); - rewind(tmpfp); - LOGI(" PHATBUF %d", length); - phatbuf = new char[length]; - if (fread(phatbuf, length, 1, tmpfp) != 1) - LOGI("FREAD FAILED %d", errno); - fclose(tmpfp); - dierr = img.OpenImage(phatbuf, length, true); -#endif - - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); - goto bail; - } - -#if 0 - { - /* - * TEST - set custom entry to match Sheila NIB image. We have to - * do this here so that the disk routines can analyze the disk - * correctly. We need a way to enter these parameters in the - * disk editor and then re-analyze the image. (Not to mention a way - * to flip in and out of block/sector/nibble mode.) - */ - DiskImg::NibbleDescr sheilaDescr = - { - "H.A.L. Labs (Sheila)", - 16, - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - true, // verify checksum - true, // verify track - 2, // epilog verify count - { 0xd5, 0xaa, 0xda }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - true, // verify checksum - 2, // epilog verify count - DiskImg::kNibbleEnc62, - DiskImg::kNibbleSpecialNone, - }; - /* same thing, but for original 13-sector Zork */ - DiskImg::NibbleDescr zork13Descr = - { - "Zork 13-sector", - 13, - { 0xd5, 0xaa, 0xb5 }, { 0xde, 0xaa, 0xeb }, - 0x00, - false, - false, - 0, - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x1f, - true, - 0, - DiskImg::kNibbleEnc53, - DiskImg::kNibbleSpecialNone, - }; - - img.SetCustomNibbleDescr(&zork13Descr); - } -#endif - - - if (img.AnalyzeImage() != kDIErrNone) { - errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", - (LPCWSTR) loadName); - MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); - goto bail; - } - - if (img.ShowAsBlocks()) - displayFormat = ImageFormatDialog::kShowAsBlocks; - else - displayFormat = ImageFormatDialog::kShowAsSectors; - - /* if they can't do anything but view nibbles, don't demand an fs format */ - bool allowUnknown; - allowUnknown = false; - if (!img.GetHasSectors() && !img.GetHasBlocks() && img.GetHasNibbles()) - allowUnknown = true; - - /* - * If requested (or necessary), verify the format. - */ - if (img.GetFSFormat() == DiskImg::kFormatUnknown || - img.GetSectorOrder() == DiskImg::kSectorOrderUnknown || - fPreferences.GetPrefBool(kPrQueryImageFormat)) - { - if (TryDiskImgOverride(&img, loadName, DiskImg::kFormatUnknown, - &displayFormat, allowUnknown, &errMsg) != IDOK) - { - goto bail; - } - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - } - - - /* select edit dialog type, based on blocks vs. sectors */ - if (displayFormat == ImageFormatDialog::kShowAsSectors) - pEditDialog = §orEdit; - else if (displayFormat == ImageFormatDialog::kShowAsBlocks) - pEditDialog = &blockEdit; - else - pEditDialog = &nibbleEdit; - - /* - * Create an appropriate DiskFS object and hand it to the edit dialog. - */ - DiskFS* pDiskFS; - pDiskFS = img.OpenAppropriateDiskFS(true); - if (pDiskFS == NULL) { - LOGI("HEY: OpenAppropriateDiskFS failed!"); - goto bail; - } - - pDiskFS->SetScanForSubVolumes(DiskFS::kScanSubEnabled); - - { - CWaitCursor wait; // big ProDOS volumes can be slow - dierr = pDiskFS->Initialize(&img, DiskFS::kInitFull); - } - if (dierr != kDIErrNone) { - errMsg.Format(L"Warning: error during disk scan: %hs.", - DiskImgLib::DIStrError(dierr)); - MessageBox(errMsg, failed, MB_OK | MB_ICONEXCLAMATION); - /* keep going */ - } - pEditDialog->Setup(pDiskFS, loadName); - (void) pEditDialog->DoModal(); - - delete pDiskFS; - - /* - * FUTURE: if we edited the file we have open in the contentlist, - * we need to post a warning and/or close it in the contentlist. - * Or maybe just re-open it? Allow that as an option. - */ - -bail: -#if 0 - delete phatbuf; -#endif - return; -} - - -/* - * ========================================================================== - * Disk Converter - * ========================================================================== - */ - -void MainWindow::OnToolsDiskConv(void) -{ - DIError dierr; - CString openFilters, errMsg; - CString loadName, saveName, saveFolder; - DiskImg srcImg, dstImg; - DiskConvertDialog convDlg(this); - CString storageName; - CStringA saveNameA, storageNameA, loadNameA; - - /* flush current archive in case that's what we're planning to convert */ - OnFileSave(); - - dstImg.SetNuFXCompressionType(fPreferences.GetPrefLong(kPrCompressionType)); - - /* - * Select the image to convert. - */ - openFilters = kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - /* for now, everything is read-only */ - dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - dlg.m_ofn.lpstrTitle = L"Select image to convert"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - loadName = dlg.GetPathName(); - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - /* open the image file and analyze it */ - loadNameA = loadName; - dierr = srcImg.OpenImage(loadNameA, PathProposal::kLocalFssep, true); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - if (srcImg.AnalyzeImage() != kDIErrNone) { - errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", - (LPCWSTR) loadName); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* - * If confirm image format is set, or we can't figure out the sector - * ordering, prompt the user. - */ - if (srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown || - fPreferences.GetPrefBool(kPrQueryImageFormat)) - { - if (TryDiskImgOverride(&srcImg, loadName, DiskImg::kFormatGenericProDOSOrd, - NULL, false, &errMsg) != IDOK) - { - goto bail; - } - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - } - - /* - * If this is a ProDOS volume, use the disk volume name as the default - * value for "storageName" (which is used for NuFX archives and DC42). - */ - if (srcImg.GetFSFormat() == DiskImg::kFormatProDOS) { - CWaitCursor waitc; - DiskFS* pDiskFS = srcImg.OpenAppropriateDiskFS(); - // use "headerOnly", which gets the volume name - dierr = pDiskFS->Initialize(&srcImg, DiskFS::kInitHeaderOnly); - if (dierr == kDIErrNone) { - storageName = pDiskFS->GetVolumeName(); // note: ASCII only - } - delete pDiskFS; - } else { - /* use filename as storageName (exception for DiskCopy42 later) */ - storageName = PathName::FilenameOnly(loadName, '\\'); - } - LOGI(" Using '%ls' as storageName", (LPCWSTR) storageName); - - /* transfer the DOS volume num, if one was set */ - dstImg.SetDOSVolumeNum(srcImg.GetDOSVolumeNum()); - LOGI("DOS volume number set to %d", dstImg.GetDOSVolumeNum()); - - DiskImg::FSFormat origFSFormat; - origFSFormat = srcImg.GetFSFormat(); - - /* - * The converter always tries to read and write images as if they were - * ProDOS blocks. This way the only sector ordering changes are caused by - * differences in the sector ordering, rather than differences in the - * assumed filesystem types (which may not be knowable). - */ - dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); - if (dierr != kDIErrNone) { - errMsg.Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* - * Put up a dialog to figure out what we want to do with this image. - * - * We have a fair amount of faith that this will not pick impossible - * combinations. If we do, we will fail later on in CreateImage. - */ - convDlg.Init(&srcImg); - if (convDlg.DoModal() != IDOK) { - LOGI(" User bailed out of convert dialog"); - goto bail; - } - - /* - * Examine their choices. - */ - DiskImg::OuterFormat outerFormat; - DiskImg::FileFormat fileFormat; - DiskImg::PhysicalFormat physicalFormat; - DiskImg::SectorOrder sectorOrder; - - if (DetermineImageSettings(convDlg.fConvertIdx, (convDlg.fAddGzip != 0), - &outerFormat, &fileFormat, &physicalFormat, §orOrder) != 0) - { - goto bail; - } - - const DiskImg::NibbleDescr* pNibbleDescr; - pNibbleDescr = srcImg.GetNibbleDescr(); - if (pNibbleDescr == NULL && DiskImg::IsNibbleFormat(physicalFormat)) { - /* - * We're writing to a nibble format, so we have to decide how the - * disk should be formatted. The source doesn't specify it, so we - * use generic 13- or 16-sector, defaulting to the latter when in - * doubt. - */ - if (srcImg.GetHasSectors() && srcImg.GetNumSectPerTrack() == 13) { - pNibbleDescr = DiskImg::GetStdNibbleDescr( - DiskImg::kNibbleDescrDOS32Std); - } else { - pNibbleDescr = DiskImg::GetStdNibbleDescr( - DiskImg::kNibbleDescrDOS33Std); - } - } - LOGI(" NibbleDescr is 0x%08lx (%hs)", (long) pNibbleDescr, - pNibbleDescr != NULL ? pNibbleDescr->description : "---"); - - if (srcImg.GetFileFormat() == DiskImg::kFileFormatTrackStar && - fileFormat != DiskImg::kFileFormatTrackStar) - { - /* converting from TrackStar to anything else */ - CString msg, appName; - CheckedLoadString(&msg, IDS_TRACKSTAR_TO_OTHER_WARNING); - CheckedLoadString(&appName, IDS_MB_APP_NAME); - if (MessageBox(msg, appName, MB_OKCANCEL | MB_ICONWARNING) != IDOK) { - LOGI(" User bailed after trackstar-to-other warning"); - goto bail; - } - } else if (srcImg.GetFileFormat() == DiskImg::kFileFormatFDI && - fileFormat != DiskImg::kFileFormatTrackStar && - srcImg.GetNumBlocks() != 1600) - { - /* converting from 5.25" FDI to anything but TrackStar */ - CString msg, appName; - CheckedLoadString(&msg, IDS_FDI_TO_OTHER_WARNING); - CheckedLoadString(&appName, IDS_MB_APP_NAME); - if (MessageBox(msg, appName, MB_OKCANCEL | MB_ICONWARNING) != IDOK) { - LOGI(" User bailed after fdi-to-other warning"); - goto bail; - } - } else if (srcImg.GetHasNibbles() && DiskImg::IsSectorFormat(physicalFormat)) - { - /* converting from nibble to non-nibble format */ - CString msg, appName; - CheckedLoadString(&msg, IDS_NIBBLE_TO_SECTOR_WARNING); - CheckedLoadString(&appName, IDS_MB_APP_NAME); - if (MessageBox(msg, appName, MB_OKCANCEL | MB_ICONWARNING) != IDOK) { - LOGI(" User bailed after nibble-to-sector warning"); - goto bail; - } - } else if (srcImg.GetHasNibbles() && - DiskImg::IsNibbleFormat(physicalFormat) && - srcImg.GetPhysicalFormat() != physicalFormat) - { - /* converting between differing nibble formats */ - CString msg, appName; - CheckedLoadString(&msg, IDS_DIFFERENT_NIBBLE_WARNING); - CheckedLoadString(&appName, IDS_MB_APP_NAME); - if (MessageBox(msg, appName, MB_OKCANCEL | MB_ICONWARNING) != IDOK) { - LOGI(" User bailed after differing-nibbles warning"); - goto bail; - } - } - - /* - * If the source is a UNIDOS volume and the target format is DiskCopy 4.2, - * use DOS sector ordering instead of ProDOS block ordering. For some - * reason the disks come out that way. - */ - if (origFSFormat == DiskImg::kFormatUNIDOS && - fileFormat == DiskImg::kFileFormatDiskCopy42) - { - LOGI(" Switching to DOS sector ordering for UNIDOS/DiskCopy42"); - sectorOrder = DiskImg::kSectorOrderDOS; - } - if (origFSFormat != DiskImg::kFormatProDOS && - fileFormat == DiskImg::kFileFormatDiskCopy42) - { - LOGI(" Nuking storage name for non-ProDOS DiskCopy42 image"); - storageName = L""; // want to use "-not a mac disk" for non-ProDOS - } - - /* - * Pick file to save into. - */ - { - CFileDialog saveDlg(FALSE, convDlg.fExtension, NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - L"All Files (*.*)|*.*||", this); - - CString saveFolder; - CString title = L"New disk image (."; - title += convDlg.fExtension; - title += L")"; - - saveDlg.m_ofn.lpstrTitle = title; - saveDlg.m_ofn.lpstrInitialDir = - fPreferences.GetPrefString(kPrConvertArchiveFolder); - - if (saveDlg.DoModal() != IDOK) { - LOGI(" User bailed out of image save dialog"); - goto bail; - } - - saveFolder = saveDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(saveDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrConvertArchiveFolder, saveFolder); - - saveName = saveDlg.GetPathName(); - } - LOGI("File will be saved to '%ls'", (LPCWSTR) saveName); - - /* DiskImgLib does not like it if file already exists */ - errMsg = RemoveFile(saveName); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* - * Create the image file. Adjust the number of tracks if we're - * copying to or from TrackStar or FDI images. - */ - int dstNumTracks; - int dstNumBlocks; - bool isPartial; - dstNumTracks = srcImg.GetNumTracks(); - dstNumBlocks = srcImg.GetNumBlocks(); - isPartial = false; - - if (srcImg.GetFileFormat() == DiskImg::kFileFormatTrackStar && - fileFormat != DiskImg::kFileFormatTrackStar && - srcImg.GetNumTracks() == 40) - { - /* from TrackStar to other */ - dstNumTracks = 35; - dstNumBlocks = 280; - isPartial = true; - } - if (srcImg.GetFileFormat() == DiskImg::kFileFormatFDI && - fileFormat != DiskImg::kFileFormatFDI && - srcImg.GetNumTracks() != 35 && srcImg.GetNumBlocks() != 1600) - { - /* from 5.25" FDI to other */ - dstNumTracks = 35; - dstNumBlocks = 280; - isPartial = true; - } - - if (srcImg.GetFileFormat() != DiskImg::kFileFormatTrackStar && - fileFormat == DiskImg::kFileFormatTrackStar && - dstNumTracks == 35) - { - /* from other to TrackStar */ - isPartial = true; - } - - saveNameA = saveName; - storageNameA = storageName; - - if (srcImg.GetHasNibbles() && - DiskImg::IsNibbleFormat(physicalFormat) && - physicalFormat == srcImg.GetPhysicalFormat()) - { - /* - * For nibble-to-nibble with the same track format, copy it as - * a collection of tracks. - */ - dierr = dstImg.CreateImage((LPCSTR) saveNameA, - (LPCSTR) storageNameA, - outerFormat, - fileFormat, - physicalFormat, - pNibbleDescr, - sectorOrder, - DiskImg::kFormatGenericProDOSOrd, - dstNumTracks, srcImg.GetNumSectPerTrack(), - false /* must format */); - } else if (srcImg.GetHasBlocks()) { - /* - * For general case, copy as a block image, converting in and out of - * nibbles as needed. - */ - dierr = dstImg.CreateImage((LPCSTR) saveNameA, - (LPCSTR) storageNameA, - outerFormat, - fileFormat, - physicalFormat, - pNibbleDescr, - sectorOrder, - DiskImg::kFormatGenericProDOSOrd, - dstNumBlocks, - false /* only needed for nibble?? */); - } else if (srcImg.GetHasSectors()) { - /* - * We should only get here when converting to/from D13. We have to - * special-case this because this was originally written to support - * block copying as the lowest common denominator. D13 screwed - * everything up. :-) - */ - dierr = dstImg.CreateImage((LPCSTR) saveNameA, - (LPCSTR) storageNameA, - outerFormat, - fileFormat, - physicalFormat, - pNibbleDescr, - sectorOrder, - DiskImg::kFormatGenericProDOSOrd, // needs to match above - dstNumTracks, srcImg.GetNumSectPerTrack(), - false /* only need for dest=nibble? */); - } else { - /* - * Generally speaking, we don't allow the user to make choices that - * would get us here. In particular, the UI should not allow the - * user to convert directly between nibble formats when the source - * image doesn't have a recognizeable block format. - */ - ASSERT(false); - dierr = kDIErrInternal; - } - if (dierr != kDIErrNone) { - errMsg.Format(L"Couldn't create disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* - * Do the actual copy, either as blocks or tracks. - */ - dierr = CopyDiskImage(&dstImg, &srcImg, false, isPartial, NULL); - if (dierr != kDIErrNone) { - errMsg.Format(L"Copy failed: %hs.", DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - dierr = srcImg.CloseImage(); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: srcImg close failed (err=%d)\n", dierr); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - dierr = dstImg.CloseImage(); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: dstImg close failed (err=%d)\n", dierr); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - SuccessBeep(); - - /* - * We're done. Give them the opportunity to open the disk image they - * just created. - */ - { - DoneOpenDialog doneOpen(this); - - if (doneOpen.DoModal() == IDOK) { - LOGI(" At user request, opening '%ls'", (LPCWSTR) saveName); - - DoOpenArchive(saveName, convDlg.fExtension, - kFilterIndexDiskImage, false); - } - } - -bail: - return; -} - -int MainWindow::DetermineImageSettings(int convertIdx, bool addGzip, - DiskImg::OuterFormat* pOuterFormat, DiskImg::FileFormat* pFileFormat, - DiskImg::PhysicalFormat* pPhysicalFormat, - DiskImg::SectorOrder* pSectorOrder) -{ - if (addGzip) - *pOuterFormat = DiskImg::kOuterFormatGzip; - else - *pOuterFormat = DiskImg::kOuterFormatNone; - - switch (convertIdx) { - case DiskConvertDialog::kConvDOSRaw: - *pFileFormat = DiskImg::kFileFormatUnadorned; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderDOS; - break; - case DiskConvertDialog::kConvDOS2MG: - *pFileFormat = DiskImg::kFileFormat2MG; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderDOS; - break; - case DiskConvertDialog::kConvProDOSRaw: - *pFileFormat = DiskImg::kFileFormatUnadorned; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderProDOS; - break; - case DiskConvertDialog::kConvProDOS2MG: - *pFileFormat = DiskImg::kFileFormat2MG; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderProDOS; - break; - case DiskConvertDialog::kConvNibbleRaw: - *pFileFormat = DiskImg::kFileFormatUnadorned; - *pPhysicalFormat = DiskImg::kPhysicalFormatNib525_6656; - *pSectorOrder = DiskImg::kSectorOrderPhysical; - break; - case DiskConvertDialog::kConvNibble2MG: - *pFileFormat = DiskImg::kFileFormat2MG; - *pPhysicalFormat = DiskImg::kPhysicalFormatNib525_6656; - *pSectorOrder = DiskImg::kSectorOrderPhysical; - break; - case DiskConvertDialog::kConvD13: - *pFileFormat = DiskImg::kFileFormatUnadorned; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderDOS; - break; - case DiskConvertDialog::kConvDiskCopy42: - *pFileFormat = DiskImg::kFileFormatDiskCopy42; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderProDOS; - break; - case DiskConvertDialog::kConvTrackStar: - *pFileFormat = DiskImg::kFileFormatTrackStar; - *pPhysicalFormat = DiskImg::kPhysicalFormatNib525_Var; - *pSectorOrder = DiskImg::kSectorOrderPhysical; - break; - case DiskConvertDialog::kConvNuFX: - *pFileFormat = DiskImg::kFileFormatNuFX; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderProDOS; - break; - case DiskConvertDialog::kConvSim2eHDV: - *pFileFormat = DiskImg::kFileFormatSim2eHDV; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderProDOS; - break; - case DiskConvertDialog::kConvDDD: - *pFileFormat = DiskImg::kFileFormatDDD; - *pPhysicalFormat = DiskImg::kPhysicalFormatSectors; - *pSectorOrder = DiskImg::kSectorOrderDOS; - break; - default: - ASSERT(false); - LOGI(" WHOA: invalid conv type %d", convertIdx); - return -1; - } - - return 0; -} - -static inline int MIN(int val1, int val2) -{ - return (val1 < val2) ? val1 : val2; -} - -DIError MainWindow::CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk, - bool partial, ProgressCancelDialog* pPCDialog) -{ - /* - * This originally just did a block copy. Nibble track copies were added - * later, and sector copies were added even later. - */ - - DIError dierr = kDIErrNone; - CString errMsg; - unsigned char* dataBuf = NULL; - - if (pSrcImg->GetHasNibbles() && pDstImg->GetHasNibbles() && - pSrcImg->GetPhysicalFormat() == pDstImg->GetPhysicalFormat()) - { - /* - * Copy as a series of nibble tracks. - * - * NOTE: we could do better here for 6384 to ".app", but in - * practice nobody cares anyway. - */ - if (!partial) { - ASSERT(pSrcImg->GetNumTracks() == pDstImg->GetNumTracks()); - } - - //unsigned char trackBuf[kTrackAllocSize]; - long trackLen; - int numTracks; - - dataBuf = new unsigned char[kTrackAllocSize]; - if (dataBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - numTracks = MIN(pSrcImg->GetNumTracks(), pDstImg->GetNumTracks()); - LOGI("Nibble track copy (%d tracks)", numTracks); - for (int track = 0; track < numTracks; track++) { - dierr = pSrcImg->ReadNibbleTrack(track, dataBuf, &trackLen); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: read on track %d failed (err=%d)\n", - track, dierr); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - dierr = pDstImg->WriteNibbleTrack(track, dataBuf, trackLen); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: write on track %d failed (err=%d)\n", - track, dierr); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* these aren't slow enough that we need progress updating */ - } - } else if (!pSrcImg->GetHasBlocks() || !pDstImg->GetHasBlocks()) { - /* - * Do a sector copy, for D13 images (which can't be accessed as blocks). - */ - if (!partial) { - ASSERT(pSrcImg->GetNumTracks() == pDstImg->GetNumTracks()); - ASSERT(pSrcImg->GetNumSectPerTrack() == pDstImg->GetNumSectPerTrack()); - } - - long numTracks, numSectPerTrack; - int numBadSectors = 0; - - dataBuf = new unsigned char[256]; // one sector - if (dataBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - numTracks = MIN(pSrcImg->GetNumTracks(), pDstImg->GetNumTracks()); - numSectPerTrack = MIN(pSrcImg->GetNumSectPerTrack(), - pDstImg->GetNumSectPerTrack()); - LOGI("Sector copy (%d tracks / %d sectors)", - numTracks, numSectPerTrack); - for (int track = 0; track < numTracks; track++) { - for (int sector = 0; sector < numSectPerTrack; sector++) { - dierr = pSrcImg->ReadTrackSector(track, sector, dataBuf); - if (dierr != kDIErrNone) { - LOGI("Bad sector T=%d S=%d", track, sector); - numBadSectors++; - dierr = kDIErrNone; - memset(dataBuf, 0, 256); - } - dierr = pDstImg->WriteTrackSector(track, sector, dataBuf); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: write of T=%d S=%d failed (err=%d)\n", - track, sector, dierr); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - } - - /* these aren't slow enough that we need progress updating */ - } - - if (!bulk && numBadSectors != 0) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - errMsg.Format(L"Skipped %ld unreadable sector%ls.", numBadSectors, - numBadSectors == 1 ? L"" : L"s"); - MessageBox(errMsg, appName, MB_OK | MB_ICONWARNING); - } - } else { - /* - * Do a block copy, copying multiple blocks at a time for performance. - */ - if (!partial) { - ASSERT(pSrcImg->GetNumBlocks() == pDstImg->GetNumBlocks()); - } - - //unsigned char blkBuf[512]; - long numBadBlocks = 0; - long numBlocks; - int blocksPerRead; - - numBlocks = MIN(pSrcImg->GetNumBlocks(), pDstImg->GetNumBlocks()); - if (numBlocks <= 2880) - blocksPerRead = 9; // better granularity (one floppy track) - else - blocksPerRead = 64; // 32K per read; max seems to be 64K? - - dataBuf = new unsigned char[blocksPerRead * 512]; - if (dataBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - LOGD("--- BLOCK COPY (%ld blocks, %d per)", - numBlocks, blocksPerRead); - for (long block = 0; block < numBlocks; ) { - long blocksThisTime = blocksPerRead; - if (block + blocksThisTime > numBlocks) - blocksThisTime = numBlocks - block; - - dierr = pSrcImg->ReadBlocks(block, blocksThisTime, dataBuf); - if (dierr != kDIErrNone) { - if (blocksThisTime != 1) { - /* - * Media with errors. Drop to one block per read. - */ - LOGW(" Bad sector encountered at %ld(%ld), slowing", - block, blocksThisTime); - blocksThisTime = blocksPerRead = 1; - continue; // retry this block - } - numBadBlocks++; - dierr = kDIErrNone; - memset(dataBuf, 0, 512); - } - dierr = pDstImg->WriteBlocks(block, blocksThisTime, dataBuf); - if (dierr != kDIErrNone) { - if (dierr != kDIErrWriteProtected) { - errMsg.Format(L"ERROR: write of block %ld failed: %hs\n", - block, DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - } - goto bail; - } - - /* if we have a cancel dialog, keep it lively */ - if (pPCDialog != NULL && (block % 18) == 0) { - int status; - PeekAndPump(); - LONGLONG bigBlock = block; - bigBlock = bigBlock * ProgressCancelDialog::kProgressResolution; - status = pPCDialog->SetProgress((int)(bigBlock / numBlocks)); - if (status == IDCANCEL) { - dierr = kDIErrCancelled; // pretend it came from DiskImg - goto bail; - } - } else if (bulk && (block % 512) == 0) { - PeekAndPump(); - } - - block += blocksThisTime; - } - - if (!bulk && numBadBlocks != 0) { - CString appName; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - errMsg.Format(L"Skipped %ld unreadable block%ls.", numBadBlocks, - numBadBlocks == 1 ? L"" : L"s"); - MessageBox(errMsg, appName, MB_OK | MB_ICONWARNING); - } - } - -bail: - delete[] dataBuf; - return dierr; -} - - -/* - * ========================================================================== - * Bulk disk convert - * ========================================================================== - */ - -/* - * Sub-class the generic libutil CancelDialog class. - */ -class BulkConvCancelDialog : public CancelDialog { -public: - BOOL Create(CWnd* pParentWnd = NULL) { - fAbortOperation = false; - return CancelDialog::Create(&fAbortOperation, - IDD_BULKCONV, pParentWnd); - } - - void SetCurrentFile(const WCHAR* fileName) { - CWnd* pWnd = GetDlgItem(IDC_BULKCONV_PATHNAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fileName); - } - - bool fAbortOperation; - -private: - void OnOK(void) { - LOGD("Ignoring BulkConvCancelDialog OnOK"); - } - - MainWindow* GetMainWindow(void) const { - return (MainWindow*)::AfxGetMainWnd(); - } -}; - -void MainWindow::OnToolsBulkDiskConv(void) -{ - const int kFileNameBufSize = 32768; - DiskConvertDialog convDlg(this); - ChooseDirDialog chooseDirDlg(this); - BulkConvCancelDialog* pCancelDialog = new BulkConvCancelDialog; // on heap - CString openFilters, errMsg; - CString saveFolder, targetDir; - int nameCount; - - /* flush current archive in case that's what we're planning to convert */ - OnFileSave(); - - /* - * Select the set of images to convert. - */ - openFilters = kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - dlg.m_ofn.lpstrFile = new WCHAR[kFileNameBufSize]; - dlg.m_ofn.lpstrFile[0] = dlg.m_ofn.lpstrFile[1] = '\0'; - dlg.m_ofn.nMaxFile = kFileNameBufSize; - dlg.m_ofn.Flags |= OFN_HIDEREADONLY; // open all images as read-only - dlg.m_ofn.Flags |= OFN_ALLOWMULTISELECT; - dlg.m_ofn.lpstrTitle = L"Select images to convert"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - /* count up the number of entries */ - POSITION posn; - posn = dlg.GetStartPosition(); - nameCount = 0; - while (posn != NULL) { - CString pathName; - pathName = dlg.GetNextPathName(posn); - nameCount++; - } - LOGD("BulkConv got nameCount=%d", nameCount); - - /* - * Choose the target directory. - * - * We use the "convert archive" folder by default. - */ - chooseDirDlg.SetPathName(fPreferences.GetPrefString(kPrConvertArchiveFolder)); - if (chooseDirDlg.DoModal() != IDOK) - goto bail; - - targetDir = chooseDirDlg.GetPathName(); - fPreferences.SetPrefString(kPrConvertArchiveFolder, targetDir); - - /* - * Put up a dialog to select the target conversion format. - * - * It is up to the user to select a format that matches the selected - * files. If it doesn't (e.g. converting an 800K floppy to DDD format), - * the process will fail later on. - */ - convDlg.Init(nameCount); - if (convDlg.DoModal() != IDOK) { - LOGI(" User bailed out of convert dialog"); - goto bail; - } - - /* initialize cancel dialog, and disable main window */ - EnableWindow(FALSE); - if (pCancelDialog->Create(this) == FALSE) { - LOGE("Cancel dialog init failed?!"); - ASSERT(false); - goto bail; - } - - /* - * Loop through all selected files and convert them one at a time. - */ - posn = dlg.GetStartPosition(); - while (posn != NULL) { - CString pathName; - pathName = dlg.GetNextPathName(posn); - LOGI(" BulkConv: source path='%ls'", (LPCWSTR) pathName); - - pCancelDialog->SetCurrentFile(PathName::FilenameOnly(pathName, '\\')); - PeekAndPump(); - if (pCancelDialog->fAbortOperation) - break; - BulkConvertImage(pathName, targetDir, convDlg, &errMsg); - - if (!errMsg.IsEmpty()) { - /* show error message, do OK/Cancel */ - /* do we need to delete the output file on failure? In general - we can't, because we could have failed because the file - already existed. */ - CString failed; - int res; - - CheckedLoadString(&failed, IDS_FAILED); - errMsg += "\n\nSource file: "; - errMsg += pathName; - errMsg += "\n\nClick OK to skip this and continue, or Cancel to " - "stop now."; - res = pCancelDialog->MessageBox(errMsg, - failed, MB_OKCANCEL | MB_ICONERROR); - if (res != IDOK) - goto bail; - } - } - - if (!pCancelDialog->fAbortOperation) - SuccessBeep(); - -bail: - // restore the main window to prominence - EnableWindow(TRUE); - //SetActiveWindow(); - if (pCancelDialog != NULL) - pCancelDialog->DestroyWindow(); - - delete[] dlg.m_ofn.lpstrFile; - return; -} - -void MainWindow::BulkConvertImage(const WCHAR* pathName, const WCHAR* targetDir, - const DiskConvertDialog& convDlg, CString* pErrMsg) -{ - /* - * [Much of this is copy & pasted from OnToolsDiskConv(). This needs to get - * refactored.] - */ - - DIError dierr; - CString saveName; - DiskImg srcImg, dstImg; - CString storageName; - PathName srcPath(pathName); - CString fileName, ext; - CStringA saveNameA, storageNameA; - - *pErrMsg = L""; - - dstImg.SetNuFXCompressionType( - fPreferences.GetPrefLong(kPrCompressionType)); - - /* open the image file and analyze it */ - CStringA pathNameA(pathName); - dierr = srcImg.OpenImage(pathNameA, PathProposal::kLocalFssep, true); - if (dierr != kDIErrNone) { - pErrMsg->Format(L"Unable to open disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - if (srcImg.AnalyzeImage() != kDIErrNone) { - pErrMsg->Format(L"The file doesn't seem to hold a valid disk image."); - goto bail; - } - -#if 0 // don't feel like posting this UI - /* - * If we can't figure out the sector ordering, prompt the user. Don't - * go into it if they have "confirm format" selected, since that would be - * annoying. If they need to confirm it, they can use the one-at-a-time - * interface. - */ - if (srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - if (TryDiskImgOverride(&srcImg, pathName, DiskImg::kFormatGenericProDOSOrd, - NULL, pErrMsg) != IDOK) - { - *pErrMsg = "Image conversion cancelled."; - } - if (!pErrMsg->IsEmpty()) - goto bail; - } -#else - if (srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - *pErrMsg = L"Could not determine the disk image sector ordering. You " - L"may need to change the file extension."; - goto bail; - } -#endif - - /* transfer the DOS volume num, if one was set */ - dstImg.SetDOSVolumeNum(srcImg.GetDOSVolumeNum()); - LOGI("DOS volume number set to %d", dstImg.GetDOSVolumeNum()); - - DiskImg::FSFormat origFSFormat; - origFSFormat = srcImg.GetFSFormat(); - - /* - * The converter always tries to read and write images as if they were - * ProDOS blocks. This way the only sector ordering changes are caused by - * differences in the sector ordering, rather than differences in the - * assumed filesystem types (which may not be knowable). - */ - dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); - if (dierr != kDIErrNone) { - pErrMsg->Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* - * Examine their choices. - */ - DiskImg::OuterFormat outerFormat; - DiskImg::FileFormat fileFormat; - DiskImg::PhysicalFormat physicalFormat; - DiskImg::SectorOrder sectorOrder; - - if (DetermineImageSettings(convDlg.fConvertIdx, (convDlg.fAddGzip != 0), - &outerFormat, &fileFormat, &physicalFormat, §orOrder) != 0) - { - *pErrMsg = L"Odd: couldn't configure image settings"; - goto bail; - } - - const DiskImg::NibbleDescr* pNibbleDescr; - pNibbleDescr = srcImg.GetNibbleDescr(); - if (pNibbleDescr == NULL && DiskImg::IsNibbleFormat(physicalFormat)) { - /* - * We're writing to a nibble format, so we have to decide how the - * disk should be formatted. The source doesn't specify it, so we - * use generic 13- or 16-sector, defaulting to the latter when in - * doubt. - */ - if (srcImg.GetHasSectors() && srcImg.GetNumSectPerTrack() == 13) { - pNibbleDescr = DiskImg::GetStdNibbleDescr( - DiskImg::kNibbleDescrDOS32Std); - } else { - pNibbleDescr = DiskImg::GetStdNibbleDescr( - DiskImg::kNibbleDescrDOS33Std); - } - } - LOGI(" NibbleDescr is 0x%08lx (%hs)", (long) pNibbleDescr, - pNibbleDescr != NULL ? pNibbleDescr->description : "---"); - - /* - * Create the new filename based on the old filename. - */ - saveName = targetDir; - if (saveName.Right(1) != '\\') - saveName += '\\'; - fileName = srcPath.GetFileName(); - ext = srcPath.GetExtension(); // extension, including '.' - if (ext.CompareNoCase(L".gz") == 0) { - /* got a .gz, see if there's anything else in front of it */ - CString tmpName, ext2; - tmpName = srcPath.GetPathName(); - tmpName = tmpName.Left(tmpName.GetLength() - ext.GetLength()); - PathName tmpPath(tmpName); - ext2 = tmpPath.GetExtension(); - if (ext2.GetLength() >= 2 && ext2.GetLength() <= 4) - ext = ext2 + ext; - - saveName += fileName.Left(fileName.GetLength() - ext.GetLength()); - } else { - if (ext.GetLength() < 2 || ext.GetLength() > 4) { - /* no meaningful extension */ - saveName += fileName; - } else { - saveName += fileName.Left(fileName.GetLength() - ext.GetLength()); - } - } - storageName = PathName::FilenameOnly(saveName, '\\'); // grab this for SHK name - saveName += '.'; - saveName += convDlg.fExtension; - LOGI(" Bulk converting '%ls' to '%ls'", pathName, (LPCWSTR) saveName); - - /* - * If this is a ProDOS volume, use the disk volume name as the default - * value for "storageName" (which is only used for NuFX archives). - */ - if (srcImg.GetFSFormat() == DiskImg::kFormatProDOS) { - CWaitCursor waitc; - DiskFS* pDiskFS = srcImg.OpenAppropriateDiskFS(); - // set "headerOnly" since we only need the volume name - dierr = pDiskFS->Initialize(&srcImg, DiskFS::kInitHeaderOnly); - if (dierr == kDIErrNone) { - storageName = pDiskFS->GetVolumeName(); // note: ASCII only - } - delete pDiskFS; - } else { - /* just use storageName as set earlier, unless target is DiskCopy42 */ - if (fileFormat == DiskImg::kFileFormatDiskCopy42) - storageName = L""; // want to use "not a mac disk" for non-ProDOS - } - LOGI(" Using '%ls' as storageName", (LPCWSTR) storageName); - - /* - * If the source is a UNIDOS volume and the target format is DiskCopy 4.2, - * use DOS sector ordering instead of ProDOS block ordering. For some - * reason the disks come out that way. - */ - if (origFSFormat == DiskImg::kFormatUNIDOS && - fileFormat == DiskImg::kFileFormatDiskCopy42) - { - LOGI(" Switching to DOS sector ordering for UNIDOS/DiskCopy42"); - sectorOrder = DiskImg::kSectorOrderDOS; - } - - /* - * Create the image file. Adjust the number of tracks if we're - * copying to or from a TrackStar image. - */ - int dstNumTracks; - int dstNumBlocks; - bool isPartial; - dstNumTracks = srcImg.GetNumTracks(); - dstNumBlocks = srcImg.GetNumBlocks(); - isPartial = false; - - if (srcImg.GetFileFormat() == DiskImg::kFileFormatTrackStar && - fileFormat != DiskImg::kFileFormatTrackStar && - srcImg.GetNumTracks() == 40) - { - /* from TrackStar to other */ - dstNumTracks = 35; - dstNumBlocks = 280; - isPartial = true; - } - if (srcImg.GetFileFormat() == DiskImg::kFileFormatFDI && - fileFormat != DiskImg::kFileFormatTrackStar && - srcImg.GetNumTracks() != 35 && srcImg.GetNumBlocks() != 1600) - { - /* from 5.25" FDI to other */ - dstNumTracks = 35; - dstNumBlocks = 280; - isPartial = true; - } - - if (srcImg.GetFileFormat() != DiskImg::kFileFormatTrackStar && - fileFormat == DiskImg::kFileFormatTrackStar && - dstNumTracks == 35) - { - /* other to TrackStar */ - isPartial = true; - } - - saveNameA = saveName; - storageNameA = storageName; - if (srcImg.GetHasNibbles() && - DiskImg::IsNibbleFormat(physicalFormat) && - physicalFormat == srcImg.GetPhysicalFormat()) - { - /* for nibble-to-nibble with the same track format, copy it - as collection of tracks */ - dierr = dstImg.CreateImage((LPCSTR) saveNameA, - (LPCSTR) storageNameA, - outerFormat, - fileFormat, - physicalFormat, - pNibbleDescr, - sectorOrder, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumTracks(), srcImg.GetNumSectPerTrack(), - false /* must format */); - } else if (srcImg.GetHasBlocks()) { - /* for general case, create as a block image */ - ASSERT(srcImg.GetHasBlocks()); - dierr = dstImg.CreateImage((LPCSTR) saveNameA, - (LPCSTR) storageNameA, - outerFormat, - fileFormat, - physicalFormat, - pNibbleDescr, - sectorOrder, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false /* only need for nibble? */); - } else if (srcImg.GetHasSectors()) { - /* - * We should only get here when converting to/from D13. We have to - * special-case this because this was originally written to support - * block copying as the lowest common denominator. D13 screwed - * everything up. :-) - */ - dierr = dstImg.CreateImage((LPCSTR) saveNameA, - (LPCSTR) storageNameA, - outerFormat, - fileFormat, - physicalFormat, - pNibbleDescr, - sectorOrder, - DiskImg::kFormatGenericProDOSOrd, // needs to match above - dstNumTracks, srcImg.GetNumSectPerTrack(), - false /* only need for dest=nibble? */); - } else { - /* e.g. unrecognizeable nibble to blocks */ - *pErrMsg = L"Could not convert to requested format."; - goto bail; - } - if (dierr != kDIErrNone) { - if (dierr == kDIErrInvalidCreateReq) - *pErrMsg = L"Could not convert to requested format."; - else - pErrMsg->Format(L"Couldn't construct disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* - * Do the actual copy, either as blocks or tracks. - */ - dierr = CopyDiskImage(&dstImg, &srcImg, true, isPartial, NULL); - if (dierr != kDIErrNone) - goto bail; - - dierr = dstImg.CloseImage(); - if (dierr != kDIErrNone) { - pErrMsg->Format(L"ERROR: dstImg close failed (err=%d)\n", dierr); - goto bail; - } - - dierr = srcImg.CloseImage(); - if (dierr != kDIErrNone) { - pErrMsg->Format(L"ERROR: srcImg close failed (err=%d)\n", dierr); - goto bail; - } - -bail: - return; -} - - -/* - * ========================================================================== - * SST Merge - * ========================================================================== - */ - -const int kSSTNumTracks = 35; -const int kSSTNumSectPerTrack = 16; -const int kSSTTrackLen = 6656; - -void MainWindow::OnToolsSSTMerge(void) -{ - const int kBadCountThreshold = 3072; - DiskImg srcImg0, srcImg1; - CString appName, saveName, saveFolder, errMsg; - uint8_t* trackBuf = NULL; - long badCount; - - // no need to flush -- can't really open raw SST images - - CFileDialog saveDlg(FALSE, L"nib", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - L"All Files (*.*)|*.*||", this); - - CheckedLoadString(&appName, IDS_MB_APP_NAME); - - trackBuf = new uint8_t[kSSTNumTracks * kSSTTrackLen]; - if (trackBuf == NULL) - goto bail; - - /* - * Open the two images and verify that they are what they seem. - */ - badCount = 0; - if (SSTOpenImage(0, &srcImg0) != 0) - goto bail; - if (SSTLoadData(0, &srcImg0, trackBuf, &badCount) != 0) - goto bail; - LOGI("FOUND %ld bad bytes in part 0", badCount); - if (badCount > kBadCountThreshold) { - CheckedLoadString(&errMsg, IDS_BAD_SST_IMAGE); - if (MessageBox(errMsg, appName, MB_OKCANCEL | MB_ICONWARNING) != IDOK) - goto bail; - } - - badCount = 0; - if (SSTOpenImage(1, &srcImg1) != 0) - goto bail; - if (SSTLoadData(1, &srcImg1, trackBuf, &badCount) != 0) - goto bail; - LOGI("FOUND %ld bad bytes in part 1", badCount); - if (badCount > kBadCountThreshold) { - CheckedLoadString(&errMsg, IDS_BAD_SST_IMAGE); - if (MessageBox(errMsg, appName, MB_OKCANCEL | MB_ICONWARNING) != IDOK) - goto bail; - } - - /* - * Realign the tracks and OR 0x80 to everything. - */ - SSTProcessTrackData(trackBuf); - - /* - * Pick the output file and write the buffer to it. - */ - saveDlg.m_ofn.lpstrTitle = L"Save .NIB disk image as..."; - saveDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - if (saveDlg.DoModal() != IDOK) { - LOGI(" User bailed out of image save dialog"); - goto bail; - } - saveFolder = saveDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(saveDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - saveName = saveDlg.GetPathName(); - LOGI("File will be saved to '%ls'", (LPCWSTR) saveName); - - /* remove the file if it exists */ - errMsg = RemoveFile(saveName); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - FILE* fp; - fp = _wfopen(saveName, L"wb"); - if (fp == NULL) { - errMsg.Format(L"Unable to create '%ls': %hs.", - (LPCWSTR) saveName, strerror(errno)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - if (fwrite(trackBuf, kSSTNumTracks * kSSTTrackLen, 1, fp) != 1) { - errMsg.Format(L"Failed while writing to new image file: %hs.", - strerror(errno)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - fclose(fp); - goto bail; - } - - fclose(fp); - - SuccessBeep(); - - /* - * We're done. Give them the opportunity to open the disk image they - * just created. - */ - { - DoneOpenDialog doneOpen(this); - - if (doneOpen.DoModal() == IDOK) { - LOGI(" At user request, opening '%ls'", (LPCWSTR) saveName); - - DoOpenArchive(saveName, L"nib", kFilterIndexDiskImage, false); - } - } - -bail: - delete[] trackBuf; - return; -} - -int MainWindow::SSTOpenImage(int seqNum, DiskImg* pDiskImg) -{ - DIError dierr; - int result = -1; - CString openFilters, errMsg; - CString loadName, saveFolder; - CStringA loadNameA; - - /* - * Select the image to convert. - */ - openFilters = kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - if (seqNum == 0) - dlg.m_ofn.lpstrTitle = L"Select first SST image"; - else - dlg.m_ofn.lpstrTitle = L"Select second SST image"; - dlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - loadName = dlg.GetPathName(); - - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - /* open the image file and analyze it */ - loadNameA = loadName; - dierr = pDiskImg->OpenImage(loadNameA, PathProposal::kLocalFssep, true); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - if (pDiskImg->AnalyzeImage() != kDIErrNone) { - errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", - (LPCWSTR) loadName); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* - * If confirm image format is set, or we can't figure out the sector - * ordering, prompt the user. - */ - if (pDiskImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown || - fPreferences.GetPrefBool(kPrQueryImageFormat)) - { - if (TryDiskImgOverride(pDiskImg, loadName, - DiskImg::kFormatGenericDOSOrd, NULL, false, &errMsg) != IDOK) - { - goto bail; - } - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - } - - if (pDiskImg->GetFSFormat() != DiskImg::kFormatUnknown && - !DiskImg::IsGenericFormat(pDiskImg->GetFSFormat())) - { - errMsg = L"This disk image appears to have a valid filesystem. SST" - L" images are just raw track dumps."; - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - if (pDiskImg->GetNumTracks() != kSSTNumTracks || - pDiskImg->GetNumSectPerTrack() != kSSTNumSectPerTrack) - { - errMsg = L"ERROR: only 5.25\" floppy disk images can be SST inputs."; - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* use DOS filesystem sector ordering */ - dierr = pDiskImg->OverrideFormat(pDiskImg->GetPhysicalFormat(), - DiskImg::kFormatGenericDOSOrd, pDiskImg->GetSectorOrder()); - if (dierr != kDIErrNone) { - errMsg = L"ERROR: internal failure: format override failed."; - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - result = 0; - -bail: - return result; -} - -int MainWindow::SSTLoadData(int seqNum, DiskImg* pDiskImg, uint8_t* trackBuf, - long* pBadCount) -{ - DIError dierr; - uint8_t sctBuf[256]; - int track, sector; - long bufOffset; - - for (track = 0; track < kSSTNumTracks; track++) { - int virtualTrack = track + (seqNum * kSSTNumTracks); - bufOffset = SSTGetBufOffset(virtualTrack); - //LOGI("USING offset=%ld (track=%d / %d)", - // bufOffset, track, virtualTrack); - - if (virtualTrack & 0x01) { - /* odd-numbered track, sectors 15-4 */ - for (sector = 15; sector >= 4; sector--) { - dierr = pDiskImg->ReadTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) { - LOGI("ERROR: on track=%d sector=%d", - track, sector); - return -1; - } - - *pBadCount += SSTCountBadBytes(sctBuf, 256); - - memcpy(trackBuf + bufOffset, sctBuf, 256); - bufOffset += 256; - } - } else { - for (sector = 13; sector >= 0; sector--) { - dierr = pDiskImg->ReadTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) { - LOGI("ERROR: on track=%d sector=%d", - track, sector); - return -1; - } - - *pBadCount += SSTCountBadBytes(sctBuf, 256); - - memcpy(trackBuf + bufOffset, sctBuf, 256); - bufOffset += 256; - } - } - } - - return 0; -} - -long MainWindow::SSTGetBufOffset(int track) -{ - assert(track >= 0 && track < kSSTNumTracks*2); - - long offset; - - if (track & 0x01) { - /* odd, use start of data */ - offset = (track / 2) * kSSTTrackLen; - } else { - /* even, start of data plus 12 sectors */ - offset = (track / 2) * kSSTTrackLen + 12 * 256; - } - - assert(offset >= 0 && offset < kSSTTrackLen * kSSTNumTracks); - - return offset; -} - -long MainWindow::SSTCountBadBytes(const unsigned char* sctBuf, int count) -{ - long badCount = 0; - unsigned char uch; - - while (count--) { - uch = (*sctBuf) | 0x80; - if (uch >= 0x80 && uch <= 0x92) - badCount++; - sctBuf++; - } - - return badCount; -} - -void MainWindow::SSTProcessTrackData(unsigned char* trackBuf) -{ - unsigned char* trackPtr; - int track; - - for (track = 0, trackPtr = trackBuf; track < kSSTNumTracks; - track++, trackPtr += kSSTTrackLen) - { - bool inRun; - int start, longestStart; - int count7f, longest = -1; - int i; - - inRun = false; - for (i = 0; i < kSSTTrackLen; i++) { - if (trackPtr[i] == 0x7f) { - if (inRun) { - count7f++; - } else { - count7f = 1; - start = i; - inRun = true; - } - } else { - if (inRun) { - if (count7f > longest) { - longest = count7f; - longestStart = start; - } - inRun = false; - } else { - /* do nothing */ - } - } - - trackPtr[i] |= 0x80; - } - - - if (longest == -1) { - LOGI("HEY: couldn't find any 0x7f in track %d", - track); - } else { - LOGI("Found run of %d at %d in track %d", - longest, longestStart, track); - - int bkpt = longestStart + longest; - assert(bkpt < kSSTTrackLen); - - char oneTrack[kSSTTrackLen]; - memcpy(oneTrack, trackPtr, kSSTTrackLen); - - /* copy it back so sync bytes are at end of track */ - memcpy(trackPtr, oneTrack + bkpt, kSSTTrackLen - bkpt); - memcpy(trackPtr + (kSSTTrackLen - bkpt), oneTrack, bkpt); - } - } -} - - -/* - * ========================================================================== - * Volume Copier - * ========================================================================== - */ - -void MainWindow::OnToolsVolumeCopierVolume(void) -{ - VolumeCopier(false); -} - -void MainWindow::OnToolsVolumeCopierFile(void) -{ - VolumeCopier(true); -} - -void MainWindow::VolumeCopier(bool openFile) -{ - VolumeCopyDialog copyDlg(this); - DiskImg srcImg; - //DiskFS* pDiskFS = NULL; - DIError dierr; - CString failed, errMsg, msg; - CString deviceName; - bool readOnly = false; - int result; - - /* flush current archive in case that's what we're planning to edit */ - OnFileSave(); - - CheckedLoadString(&failed, IDS_FAILED); - - if (!openFile) { - /* - * Select the volume to manipulate. - */ - OpenVolumeDialog openVolDlg(this); - //openVolDlg.fReadOnly = false; - //openVolDlg.fAllowROChange = true; - result = openVolDlg.DoModal(); - if (result != IDOK) - goto bail; - deviceName = openVolDlg.fChosenDrive; - readOnly = (openVolDlg.fReadOnly != 0); - } else { - /* - * Open a disk image file instead. - */ - CString openFilters; - openFilters = kOpenDiskImage; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog fileDlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - //dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - fileDlg.m_ofn.Flags &= ~(OFN_READONLY); - fileDlg.m_ofn.lpstrTitle = L"Select disk image file"; - fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (fileDlg.DoModal() != IDOK) - goto bail; - deviceName = fileDlg.GetPathName(); - readOnly = (fileDlg.GetReadOnlyPref() != 0); - } - - /* - * Open the disk image and figure out what it is. - */ - { - CWaitCursor waitc; - - DiskImg::SetAllowWritePhys0(false); - - CStringA deviceNameA(deviceName); - dierr = srcImg.OpenImage(deviceNameA, '\0', readOnly); - if (dierr == kDIErrAccessDenied) { - if (openFile) { - errMsg.Format(L"Unable to open '%ls': %hs (try opening the file" - L" with 'Read Only' checked).", - (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); - } else if (!IsWin9x() && !openFile) { - errMsg.Format(L"Unable to open '%ls': %hs (make sure you have" - L" administrator privileges).", - (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); - } else { - errMsg.Format(L"Unable to open '%ls': %hs.", - (LPCWSTR) deviceName, DiskImgLib::DIStrError(dierr)); - } - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } else if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open '%ls': %hs.", (LPCWSTR) deviceName, - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* analyze it to get #of blocks and determine the FS */ - if (srcImg.AnalyzeImage() != kDIErrNone) { - errMsg.Format(L"There isn't a valid disk image here?!?"); - MessageBox(errMsg, failed, MB_OK|MB_ICONSTOP); - goto bail; - } - } - - /* - * If requested (or necessary), verify the format. - */ - if (srcImg.GetFSFormat() == DiskImg::kFormatUnknown || - srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown || - fPreferences.GetPrefBool(kPrQueryImageFormat)) - { - if (TryDiskImgOverride(&srcImg, deviceName, DiskImg::kFormatUnknown, - NULL, true, &errMsg) != IDOK) - { - goto bail; - } - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - } - - /* - * Hand the DiskImg object off to the volume copier dialog. - */ - copyDlg.fpDiskImg = &srcImg; - copyDlg.fPathName = deviceName; - (void) copyDlg.DoModal(); - - /* - * The volume copier could have modified our open file. If it has, - * we need to close and reopen the archive. - */ - srcImg.CloseImage(); // could interfere with volume reopen - if (fNeedReopen) { - PeekAndPump(); // clear out dialog - ReopenArchive(); - } - -bail: - return; -} - - -/* - * ========================================================================== - * Disk image creator - * ========================================================================== - */ - -void MainWindow::OnToolsDiskImageCreator(void) -{ - CreateImageDialog createDlg(this); - DiskArchive* pNewArchive = NULL; - - createDlg.fDiskFormatIdx = - fPreferences.GetPrefLong(kPrDiskImageCreateFormat); - if (createDlg.fDiskFormatIdx < 0) - createDlg.fDiskFormatIdx = CreateImageDialog::kFmtProDOS; - - /* - * Ask the user what sort of disk they'd like to create. - */ - if (createDlg.DoModal() != IDOK) - return; - - fPreferences.SetPrefLong(kPrDiskImageCreateFormat, createDlg.fDiskFormatIdx); - - /* - * Set up the options struct. We set base.sectorOrder later. - */ - assert(createDlg.fNumBlocks > 0); - - DiskArchive::NewOptions options; - memset(&options, 0, sizeof(options)); - switch (createDlg.fDiskFormatIdx) { - case CreateImageDialog::kFmtBlank: - options.base.format = DiskImg::kFormatUnknown; - options.blank.numBlocks = createDlg.fNumBlocks; - break; - case CreateImageDialog::kFmtProDOS: - options.base.format = DiskImg::kFormatProDOS; - options.prodos.numBlocks = createDlg.fNumBlocks; - options.prodos.volName = createDlg.fVolName_ProDOS; - break; - case CreateImageDialog::kFmtPascal: - options.base.format = DiskImg::kFormatPascal; - options.pascalfs.numBlocks = createDlg.fNumBlocks; - options.pascalfs.volName = createDlg.fVolName_Pascal; - break; - case CreateImageDialog::kFmtHFS: - options.base.format = DiskImg::kFormatMacHFS; - options.hfs.numBlocks = createDlg.fNumBlocks; - options.hfs.volName = createDlg.fVolName_HFS; - break; - case CreateImageDialog::kFmtDOS32: - options.base.format = DiskImg::kFormatDOS32; - options.dos.volumeNum = createDlg.fDOSVolumeNum; - options.dos.allocDOSTracks = (createDlg.fAllocTracks_DOS != 0); - options.dos.numTracks = 35; - options.dos.numSectors = 13; - break; - case CreateImageDialog::kFmtDOS33: - options.base.format = DiskImg::kFormatDOS33; - options.dos.volumeNum = createDlg.fDOSVolumeNum; - options.dos.allocDOSTracks = (createDlg.fAllocTracks_DOS != 0); - if (createDlg.fNumBlocks <= 400) { - ASSERT(createDlg.fNumBlocks % 8 == 0); - options.dos.numTracks = createDlg.fNumBlocks / 8; - options.dos.numSectors = 16; - } else if (createDlg.fNumBlocks <= 800) { - ASSERT(createDlg.fNumBlocks % 16 == 0); - options.dos.numTracks = createDlg.fNumBlocks / 16; - options.dos.numSectors = 32; - options.dos.allocDOSTracks = false; - } else { - ASSERT(false); - return; - } - break; - default: - LOGI("Invalid fDiskFormatIdx %d from CreateImageDialog", - createDlg.fDiskFormatIdx); - ASSERT(false); - return; - } - - /* - * Select the file to store it in. - */ - CString filename, saveFolder, errStr; - int filterIndex = 1; - CString formats; - - if (createDlg.fDiskFormatIdx == CreateImageDialog::kFmtDOS32) { - formats = L"13-sector disk (*.d13)|*.d13|"; - } else { - formats = L"ProDOS-ordered image (*.po)|*.po|"; - if (createDlg.fNumBlocks == 280) { - formats += L"DOS-ordered image (*.do)|*.do|"; - filterIndex = 2; - } - } - formats += L"|"; - - CFileDialog saveDlg(FALSE, L"po", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - formats, this); - saveDlg.m_ofn.lpstrTitle = L"New Disk Image"; - saveDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - saveDlg.m_ofn.nFilterIndex = filterIndex; - - if (saveDlg.DoModal() != IDOK) { - LOGI(" User cancelled xfer from image create dialog"); - return; - } - - saveFolder = saveDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(saveDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - filename = saveDlg.GetPathName(); - LOGI(" Will xfer to file '%ls' (filterIndex=%d)", - (LPCWSTR) filename, saveDlg.m_ofn.nFilterIndex); - - if (createDlg.fDiskFormatIdx == CreateImageDialog::kFmtDOS32) { - options.base.sectorOrder = DiskImg::kSectorOrderDOS; - } else { - if (saveDlg.m_ofn.nFilterIndex == 2) - options.base.sectorOrder = DiskImg::kSectorOrderDOS; - else - options.base.sectorOrder = DiskImg::kSectorOrderProDOS; - } - - /* remove file if it already exists */ - CString errMsg; - errMsg = RemoveFile(filename); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - return; - } - - pNewArchive = new DiskArchive; - - /* create the new archive, showing a "busy" message */ - { - ExclusiveModelessDialog* pWaitDlg = new ExclusiveModelessDialog; - pWaitDlg->Create(IDD_FORMATTING, this); - pWaitDlg->CenterWindow(); - PeekAndPump(); // redraw - CWaitCursor waitc; - - errStr = pNewArchive->New(filename, &options); - - pWaitDlg->DestroyWindow(); - //PeekAndPump(); // redraw - } - - delete pNewArchive; // close it, either way - if (!errStr.IsEmpty()) { - ShowFailureMsg(this, errStr, IDS_FAILED); - (void) _wunlink(filename); - } else { - LOGI("Disk image created successfully"); -#if 0 - SuccessBeep(); - - /* give them the opportunity to open the new disk image */ - DoneOpenDialog doneOpen(this); - - if (doneOpen.DoModal() == IDOK) { - LOGI(" At user request, opening '%ls'", filename); - - DoOpenArchive(filename, "dsk", kFilterIndexDiskImage, false); - } -#else - if (createDlg.fDiskFormatIdx != CreateImageDialog::kFmtBlank) - DoOpenArchive(filename, L"dsk", kFilterIndexDiskImage, false); -#endif - } -} - - -/* - * ========================================================================== - * EOL scanner - * ========================================================================== - */ - -void MainWindow::OnToolsEOLScanner(void) -{ - CString fileName, saveFolder, errMsg; - - CString openFilters; - openFilters = kOpenAll; - openFilters += kOpenEnd; - CFileDialog fileDlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - fileDlg.m_ofn.Flags |= OFN_HIDEREADONLY; - //fileDlg.m_ofn.Flags &= ~(OFN_READONLY); - fileDlg.m_ofn.lpstrTitle = L"Select file to scan"; - fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (fileDlg.DoModal() != IDOK) - return; - fileName = fileDlg.GetPathName(); - - saveFolder = fileDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(fileDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - LOGI("Scanning '%ls'", (LPCWSTR) fileName); - - FILE* fp = _wfopen(fileName, L"rb"); - if (fp == NULL) { - errMsg.Format(L"Unable to open '%ls': %hs.", - (LPCWSTR) fileName, strerror(errno)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - return; - } - - long numCR, numLF, numCRLF, numHAChars, numChars; - bool lastCR; - int ic; - - /* - * Plow through the file, counting up characters. - */ - numCR = numLF = numCRLF = numChars = numHAChars = 0; - lastCR = false; - while (true) { - ic = getc(fp); - if (ic == EOF) - break; - - if ((ic & 0x80) != 0) - numHAChars++; - - if (ic == '\r') { - lastCR = true; - numCR++; - } else if (ic == '\n') { - if (lastCR) { - numCR--; - numCRLF++; - lastCR = false; - } else { - numLF++; - } - } else { - lastCR = false; - } - numChars++; - } - fclose(fp); - - LOGD("Got CR=%ld LF=%ld CRLF=%ld (numChars=%ld)", - numCR, numLF, numCRLF, numChars); - - EOLScanDialog output; - output.fCountCR = numCR; - output.fCountLF = numLF; - output.fCountCRLF = numCRLF; - output.fCountChars = numChars; - output.fCountHighASCII = numHAChars; - (void) output.DoModal(); -} - - -/* - * ========================================================================== - * 2MG disk image properties editor - * ========================================================================== - */ - -void MainWindow::OnToolsTwoImgProps(void) -{ - CString fileName, saveFolder, errMsg; - CString openFilters; - - /* flush current archive in case that's what we're planning to edit */ - OnFileSave(); - - /* - * Select the file to open. - */ - openFilters = L"2MG Disk Images (.2mg .2img)|*.2mg;*.2img|"; - openFilters += kOpenAll; - openFilters += kOpenEnd; - CFileDialog fileDlg(TRUE, L"2mg", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - fileDlg.m_ofn.Flags |= OFN_HIDEREADONLY; - //fileDlg.m_ofn.Flags &= ~(OFN_READONLY); - fileDlg.m_ofn.lpstrTitle = L"Select file to edit"; - fileDlg.m_ofn.lpstrInitialDir = fPreferences.GetPrefString(kPrOpenArchiveFolder); - - if (fileDlg.DoModal() != IDOK) - return; - fileName = fileDlg.GetPathName(); - - saveFolder = fileDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(fileDlg.m_ofn.nFileOffset); - fPreferences.SetPrefString(kPrOpenArchiveFolder, saveFolder); - - /* - * Open it up. - */ - bool changed; - changed = EditTwoImgProps(fileName); - - if (changed && IsOpenPathName(fileName)) { - PeekAndPump(); // clear out dialog - ReopenArchive(); - } -} - -bool MainWindow::EditTwoImgProps(const WCHAR* fileName) -{ - TwoImgPropsDialog dialog; - TwoImgHeader header; - FILE* fp = NULL; - bool dirty = false; - CString errMsg; - long totalLength; - bool readOnly = false; - - LOGI("EditTwoImgProps '%ls'", fileName); - fp = _wfopen(fileName, L"r+b"); - if (fp == NULL) { - int firstError = errno; - fp = _wfopen(fileName, L"rb"); - if (fp == NULL) { - errMsg.Format(L"Unable to open '%ls': %hs.", - fileName, strerror(firstError)); - goto bail; - } else { - readOnly = true; - } - } - - fseek(fp, 0, SEEK_END); - totalLength = ftell(fp); - rewind(fp); - - if (header.ReadHeader(fp, totalLength) != 0) { - errMsg.Format(L"Unable to process 2MG header in '%ls'" - L" (are you sure this is in 2MG format?).", - (LPCWSTR) fileName); - goto bail; - } - - dialog.Setup(&header, readOnly); - if (dialog.DoModal() == IDOK) { - long result; - //header.SetCreatorChunk("fubar", 5); - header.DumpHeader(); - - rewind(fp); - if (header.WriteHeader(fp) != 0) { - errMsg = L"Unable to write 2MG header"; - goto bail; - } - - /* - * Clip off the footer. They might have had one before but don't - * have one now. If they do have one now we'll add it back in a - * second. - */ - result = fseek(fp, header.fDataOffset + header.fDataLen, SEEK_SET); - if (result < 0) { - errMsg = L"Unable to seek to end of 2MG file"; - goto bail; - } - dirty = true; - - if (::chsize(fileno(fp), ftell(fp)) != 0) { - errMsg = L"Unable to truncate 2MG file before writing footer"; - goto bail; - } - - if (header.fCmtLen || header.fCreatorLen) { - if (header.WriteFooter(fp) != 0) { - errMsg = L"Unable to write 2MG footer"; - goto bail; - } - } - - LOGI("2MG success!"); - } - -bail: - if (fp != NULL) - fclose(fp); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - } - - return dirty; -} diff --git a/ciderpress/app/TwoImgPropsDialog.cpp b/ciderpress/app/TwoImgPropsDialog.cpp deleted file mode 100644 index 427d259..0000000 --- a/ciderpress/app/TwoImgPropsDialog.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "StdAfx.h" -#include "TwoImgPropsDialog.h" - -BEGIN_MESSAGE_MAP(TwoImgPropsDialog, CDialog) - ON_BN_CLICKED(IDC_TWOIMG_LOCKED, OnChange) - ON_BN_CLICKED(IDC_TWOIMG_DOSVOLSET, OnChange) - ON_EN_CHANGE(IDC_TWOIMG_DOSVOLNUM, OnChange) - ON_EN_CHANGE(IDC_TWOIMG_COMMENT, OnChange) - ON_WM_HELPINFO() -END_MESSAGE_MAP() - - -BOOL TwoImgPropsDialog::OnInitDialog(void) -{ - CWnd* pWnd; - CEdit* pEdit; - CString tmpStr; - - ASSERT(fpHeader != NULL); - - /* - * Set up the static fields. - */ - pWnd = GetDlgItem(IDC_TWOIMG_CREATOR); - tmpStr.Format(L"'%hs'", fpHeader->GetCreatorStr()); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_TWOIMG_VERSION); - tmpStr.Format(L"%d", fpHeader->fVersion); - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_TWOIMG_FORMAT); - switch (fpHeader->fImageFormat) { - case TwoImgHeader::kImageFormatDOS: tmpStr = L"DOS order sectors"; break; - case TwoImgHeader::kImageFormatProDOS: tmpStr = L"ProDOS order sectors"; break; - case TwoImgHeader::kImageFormatNibble: tmpStr = L"Raw nibbles"; break; - default: tmpStr = L"Unknown"; break; - } - pWnd->SetWindowText(tmpStr); - - pWnd = GetDlgItem(IDC_TWOIMG_BLOCKS); - tmpStr.Format(L"%d", fpHeader->fNumBlocks); - pWnd->SetWindowText(tmpStr); - - /* - * Restrict the edit field. - */ - pEdit = (CEdit*) GetDlgItem(IDC_TWOIMG_DOSVOLNUM); - pEdit->LimitText(3); // 1-254 - - /* - * Disable the "Save" button. - */ - pWnd = GetDlgItem(IDOK); - pWnd->EnableWindow(FALSE); - - /* for read-only mode, all buttons are disabled */ - if (fReadOnly) { - GetDlgItem(IDC_TWOIMG_LOCKED)->EnableWindow(FALSE); - GetDlgItem(IDC_TWOIMG_DOSVOLSET)->EnableWindow(FALSE); - GetDlgItem(IDC_TWOIMG_COMMENT)->EnableWindow(FALSE); - GetDlgItem(IDC_TWOIMG_DOSVOLNUM)->EnableWindow(FALSE); - - GetWindowText(tmpStr); - tmpStr += " (read-only)"; - SetWindowText(tmpStr); - } - - return CDialog::OnInitDialog(); -} - -void TwoImgPropsDialog::DoDataExchange(CDataExchange* pDX) -{ - BOOL locked, dosVolSet; - CString comment; - int dosVolNum; - - if (pDX->m_bSaveAndValidate) { - DDX_Check(pDX, IDC_TWOIMG_LOCKED, locked); - DDX_Check(pDX, IDC_TWOIMG_DOSVOLSET, dosVolSet); - DDX_Text(pDX, IDC_TWOIMG_COMMENT, comment); - DDX_Text(pDX, IDC_TWOIMG_DOSVOLNUM, dosVolNum); - - LOGI("GOT dosVolNum = %d", dosVolNum); - - fpHeader->fFlags &= ~(TwoImgHeader::kFlagLocked); - if (locked) - fpHeader->fFlags |= TwoImgHeader::kFlagLocked; - - fpHeader->fFlags &= ~(TwoImgHeader::kDOSVolumeMask); - fpHeader->fFlags &= ~(TwoImgHeader::kDOSVolumeSet); - if (dosVolSet) { - fpHeader->fFlags |= TwoImgHeader::kDOSVolumeSet; - fpHeader->fFlags |= (dosVolNum & TwoImgHeader::kDOSVolumeMask); - - CString appStr, errMsg; - if (dosVolNum < 1 || dosVolNum > 254) { - CheckedLoadString(&appStr, IDS_MB_APP_NAME); - CheckedLoadString(&errMsg, IDS_VALID_VOLNAME_DOS); - MessageBox(errMsg, appStr, MB_OK); - pDX->Fail(); - } else { - fpHeader->SetDOSVolumeNum(dosVolNum); - } - } - - - if (!comment.IsEmpty()) { - CStringA commentA(comment); - fpHeader->SetComment(commentA); - } else { - fpHeader->SetComment(NULL); - } - } else { - CWnd* pWnd; - - locked = (fpHeader->fFlags & TwoImgHeader::kFlagLocked) != 0; - dosVolSet = (fpHeader->fFlags & TwoImgHeader::kDOSVolumeSet) != 0; - comment = fpHeader->GetComment(); - if (dosVolSet) - dosVolNum = fpHeader->GetDOSVolumeNum(); - else - dosVolNum = TwoImgHeader::kDefaultVolumeNum; - - DDX_Check(pDX, IDC_TWOIMG_LOCKED, locked); - DDX_Check(pDX, IDC_TWOIMG_DOSVOLSET, dosVolSet); - DDX_Text(pDX, IDC_TWOIMG_COMMENT, comment); - DDX_Text(pDX, IDC_TWOIMG_DOSVOLNUM, dosVolNum); - - /* set initial state of dos volume number edit field */ - if (!fReadOnly) { - pWnd = GetDlgItem(IDC_TWOIMG_DOSVOLNUM); - pWnd->EnableWindow(dosVolSet); - } - } -} - -void TwoImgPropsDialog::OnChange(void) -{ - CButton* pButton; - UINT checked; - - ASSERT(!fReadOnly); - - GetDlgItem(IDOK)->EnableWindow(TRUE); - - pButton = (CButton*) GetDlgItem(IDC_TWOIMG_DOSVOLSET); - checked = pButton->GetCheck(); - GetDlgItem(IDC_TWOIMG_DOSVOLNUM)->EnableWindow(checked == BST_CHECKED); -} diff --git a/ciderpress/app/TwoImgPropsDialog.h b/ciderpress/app/TwoImgPropsDialog.h deleted file mode 100644 index 29960a6..0000000 --- a/ciderpress/app/TwoImgPropsDialog.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * TwoImg properties edit dialog. - */ -#ifndef APP_TWOIMGPROPSDIALOG_H -#define APP_TWOIMGPROPSDIALOG_H - -#include "resource.h" -#include "../diskimg/TwoImg.h" - - -/* - * Dialog with a bunch of controls that map to fields in the TwoImg file - * header. We want to keep the "Save" button ("OK") dimmed until we have - * something to write. - */ -class TwoImgPropsDialog : public CDialog { -public: - TwoImgPropsDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_TWOIMG_PROPS, pParentWnd), - fpHeader(NULL), fReadOnly(false) - {} - - - void Setup(TwoImgHeader* pHeader, bool readOnly) { - fpHeader = pHeader; - fReadOnly = readOnly; - } - -protected: - // overrides - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - - /* - * If they changed anything, enable the "save" button. - */ - afx_msg void OnChange(void); - - TwoImgHeader* fpHeader; - bool fReadOnly; - //bool fModified; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_TWOIMGPROPSDIALOG_H*/ diff --git a/ciderpress/app/UseSelectionDialog.cpp b/ciderpress/app/UseSelectionDialog.cpp deleted file mode 100644 index 698fc27..0000000 --- a/ciderpress/app/UseSelectionDialog.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#include "stdafx.h" -#include "UseSelectionDialog.h" - -BEGIN_MESSAGE_MAP(UseSelectionDialog, CDialog) - ON_WM_HELPINFO() - //ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - - -BOOL UseSelectionDialog::OnInitDialog(void) -{ - CString str; - CString selStr; - CWnd* pWnd; - - CDialog::OnInitDialog(); - - /* grab the radio button with the selection count */ - pWnd = GetDlgItem(IDC_USE_SELECTED); - ASSERT(pWnd != NULL); - - /* set the string using a string table entry */ - if (fSelectedCount == 1) { - CheckedLoadString(&str, fSelCountID); - pWnd->SetWindowText(str); - } else { - CheckedLoadString(&str, fSelCountsID); - selStr.Format((LPCWSTR) str, fSelectedCount); - pWnd->SetWindowText(selStr); - - if (fSelectedCount == 0) - pWnd->EnableWindow(FALSE); - } - - /* set the other strings */ - CheckedLoadString(&str, fTitleID); - SetWindowText(str); - - pWnd = GetDlgItem(IDC_USE_ALL); - ASSERT(pWnd != NULL); - CheckedLoadString(&str, fAllID); - pWnd->SetWindowText(str); - - pWnd = GetDlgItem(IDOK); - ASSERT(pWnd != NULL); - CheckedLoadString(&str, fOkLabelID); - pWnd->SetWindowText(str); - - return TRUE; -} - -void UseSelectionDialog::DoDataExchange(CDataExchange* pDX) -{ - DDX_Radio(pDX, IDC_USE_SELECTED, fFilesToAction); -} diff --git a/ciderpress/app/UseSelectionDialog.h b/ciderpress/app/UseSelectionDialog.h deleted file mode 100644 index f13c9c1..0000000 --- a/ciderpress/app/UseSelectionDialog.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Acknowledge and clarify a request to do something, e.g. delete files. - */ -#ifndef APP_USESELECTIONDIALOG_H -#define APP_USESELECTIONDIALOG_H - -#include "resource.h" - -/* - * Straightforward confirmation. - */ -class UseSelectionDialog : public CDialog { -public: - UseSelectionDialog(int selCount, CWnd* pParentWnd = NULL, int rsrcID = IDD_USE_SELECTION) : - CDialog(rsrcID, pParentWnd), fSelectedCount(selCount) - { - // init values; these should be overridden before DoModal - fFilesToAction = 0; - } - virtual ~UseSelectionDialog(void) {} - - // set up dialog parameters; must be called before DoModal - void Setup(int titleID, int okLabelID, int countID, int countsID, - int allID) - { - fTitleID = titleID; - fOkLabelID = okLabelID; - fSelCountID = countID; - fSelCountsID = countsID; - fAllID = allID; - } - - enum { kActionSelection = 0, kActionAll = 1 }; - int fFilesToAction; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void DoDataExchange(CDataExchange* pDX) override; - - afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo) { - return MyApp::HandleHelpInfo(lpHelpInfo); - } - -private: - int fSelectedCount; - - /* dialog parameters */ - int fTitleID; - int fOkLabelID; - int fSelCountID; - int fSelCountsID; - int fAllID; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_USESELECTIONDIALOG_H*/ diff --git a/ciderpress/app/ViewFilesDialog.cpp b/ciderpress/app/ViewFilesDialog.cpp deleted file mode 100644 index 57482fc..0000000 --- a/ciderpress/app/ViewFilesDialog.cpp +++ /dev/null @@ -1,1294 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for the "view files" dialog box. - */ -#include "stdafx.h" -#include "ViewFilesDialog.h" -#include "Main.h" -#include "Print.h" -#include "../util/UtilLib.h" - - -/* - * =========================================================================== - * ViewFilesDialog - * =========================================================================== - */ - -static const UINT gFindReplaceID = RegisterWindowMessage(FINDMSGSTRING); - -BEGIN_MESSAGE_MAP(ViewFilesDialog, CDialog) - ON_WM_CREATE() - ON_WM_DESTROY() - ON_WM_SIZE() - ON_WM_GETMINMAXINFO() - ON_REGISTERED_MESSAGE(gFindReplaceID, OnFindDialogMessage) - ON_COMMAND(IDC_FVIEW_NEXT, OnFviewNext) - ON_COMMAND(IDC_FVIEW_PREV, OnFviewPrev) - ON_COMMAND(IDC_FVIEW_FONT, OnFviewFont) - ON_COMMAND(IDC_FVIEW_PRINT, OnFviewPrint) - ON_COMMAND(IDC_FVIEW_FIND, OnFviewFind) - ON_BN_CLICKED(IDC_FVIEW_DATA, OnFviewData) - ON_BN_CLICKED(IDC_FVIEW_RSRC, OnFviewRsrc) - ON_BN_CLICKED(IDC_FVIEW_CMMT, OnFviewCmmt) - ON_COMMAND(IDC_FVIEW_FMT_BEST, OnFviewFmtBest) - ON_COMMAND(IDC_FVIEW_FMT_HEX, OnFviewFmtHex) - ON_COMMAND(IDC_FVIEW_FMT_RAW, OnFviewFmtRaw) - ON_CBN_SELCHANGE(IDC_FVIEW_FORMATSEL, OnFormatSelChange) - ON_COMMAND(IDHELP, OnHelp) -END_MESSAGE_MAP() - -BOOL ViewFilesDialog::OnInitDialog(void) -{ - LOGD("Now in VFD OnInitDialog!"); - - ASSERT(fpSelSet != NULL); - - /* delete dummy control and insert our own with modded styles */ - CRichEditCtrl* pEdit = (CRichEditCtrl*)GetDlgItem(IDC_FVIEW_EDITBOX); - ASSERT(pEdit != NULL); - CRect rect; - pEdit->GetWindowRect(&rect); - pEdit->DestroyWindow(); - ScreenToClient(&rect); - - DWORD styles = ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY | - WS_BORDER | WS_VSCROLL | WS_VISIBLE | WS_TABSTOP | - ES_NOHIDESEL; - if (fNoWrapText) - styles |= ES_AUTOHSCROLL | WS_HSCROLL; - fEditCtrl.Create(styles, rect, this, IDC_FVIEW_EDITBOX); - fEditCtrl.SetFocus(); - /* - * HEY: I think we can do this with pEdit->ShowScrollBar(SB_BOTH) !! - * Could also use GetWindowLong/SetWindowLong to change the window style; - * probably need a SetWindowPos to cause changes to flush. - */ - - - /* - * We want to adjust the size of the window to match the last size used. - * However, if we do this after creating the edit dialog but before it - * actually has data to display, then when we stuff data into it the - * scroll bar goodies are all out of whack. - */ - fFirstResize = true; -#if 0 - const Preferences* pPreferences = GET_PREFERENCES(); - long width = pPreferences->GetFileViewerWidth(); - long height = pPreferences->GetFileViewerHeight(); - CRect fullRect; - GetWindowRect(&fullRect); - LOGI(" VFD pre-size %dx%d", fullRect.Width(), fullRect.Height()); - fullRect.right = fullRect.left + width; - fullRect.bottom = fullRect.top + height; - MoveWindow(fullRect, FALSE); -#endif - - // This invokes UpdateData, which calls DoDataExchange, which leads to - // the StreamIn call. So don't do this until everything else is ready. - CDialog::OnInitDialog(); - - LOGD("VFD OnInitDialog done"); - return FALSE; // don't let Windows set the focus -} - -int ViewFilesDialog::OnCreate(LPCREATESTRUCT lpcs) -{ - LOGD("VFD OnCreate"); - - HICON hIcon; - hIcon = ::AfxGetApp()->LoadIcon(IDI_FILE_VIEWER); - SetIcon(hIcon, TRUE); - - GetClientRect(&fLastWinSize); - - CRect initRect(fLastWinSize); - initRect.left = initRect.right - ::GetSystemMetrics(SM_CXVSCROLL); - initRect.top = initRect.bottom - ::GetSystemMetrics(SM_CYHSCROLL); - fGripper.Create(WS_CHILD | WS_VISIBLE | - SBS_SIZEBOX | SBS_SIZEBOXBOTTOMRIGHTALIGN | SBS_SIZEGRIP, - initRect, this, AFX_IDW_SIZE_BOX); - - LOGD("VFD OnCreate done"); - return 0; -} - -void ViewFilesDialog::OnDestroy(void) -{ - Preferences* pPreferences = GET_PREFERENCES_WR(); - CRect rect; - GetWindowRect(&rect); - - pPreferences->SetPrefLong(kPrFileViewerWidth, rect.Width()); - pPreferences->SetPrefLong(kPrFileViewerHeight, rect.Height()); - - CDialog::OnDestroy(); -} - -void ViewFilesDialog::OnOK(void) -{ - if (fBusy) - MessageBeep(-1); - else { - CRect rect; - - GetWindowRect(&rect); - LOGD(" VFD size now %dx%d", rect.Width(), rect.Height()); - - CDialog::OnOK(); - } -} - -void ViewFilesDialog::OnCancel(void) -{ - if (fBusy) - MessageBeep(-1); - else - CDialog::OnCancel(); -} - -void ViewFilesDialog::OnGetMinMaxInfo(MINMAXINFO* pMMI) -{ - pMMI->ptMinTrackSize.x = 664; - pMMI->ptMinTrackSize.y = 200; -} - -void ViewFilesDialog::OnSize(UINT nType, int cx, int cy) -{ - CDialog::OnSize(nType, cx, cy); - - //LOGI("Dialog: old size %d,%d", - // fLastWinSize.Width(), fLastWinSize.Height()); - LOGD("Dialog: new size %d,%d", cx, cy); - - if (fLastWinSize.Width() == cx && fLastWinSize.Height() == cy) { - LOGD("VFD OnSize: no change"); - return; - } - - int deltaX, deltaY; - deltaX = cx - fLastWinSize.Width(); - deltaY = cy - fLastWinSize.Height(); - //LOGI("Delta is %d,%d", deltaX, deltaY); - - ShiftControls(deltaX, deltaY); - - GetClientRect(&fLastWinSize); -} - -void ViewFilesDialog::ShiftControls(int deltaX, int deltaY) -{ - HDWP hdwp; - - /* - * Use deferred reposn so that they don't end up drawing on top of each - * other and getting all weird. - * - * IMPORTANT: the DeferWindowPos stuff changes the tab order of the - * items in the window. The controls must be added in the reverse - * order in which they appear in the window. - */ - hdwp = BeginDeferWindowPos(15); - hdwp = MoveControl(hdwp, this, AFX_IDW_SIZE_BOX, deltaX, deltaY); - hdwp = MoveControl(hdwp, this, IDHELP, deltaX, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_FONT, deltaX, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_PRINT, deltaX, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_FIND, deltaX, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_FMT_RAW, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_FMT_HEX, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_FMT_BEST, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_PREV, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_NEXT, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_CMMT, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_RSRC, 0, deltaY); - hdwp = MoveControl(hdwp, this, IDC_FVIEW_DATA, 0, deltaY); - hdwp = MoveStretchControl(hdwp, this, IDC_FVIEW_FORMATSEL, 0, deltaY, deltaX, 0); - hdwp = StretchControl(hdwp, this, IDC_FVIEW_EDITBOX, deltaX, deltaY); - hdwp = MoveControl(hdwp, this, IDOK, deltaX, deltaY); - if (!EndDeferWindowPos(hdwp)) { - LOGI("EndDeferWindowPos failed"); - } - - /* - * Work around buggy CRichEdit controls. The inner edit area is only - * resized when the box is shrunk, not when it's expanded, and the - * results are inconsistent between Win98 and Win2K. - * - * Set the internal size equal to the size of the entire edit box. - * This should be large enough to make everything work right, but small - * enough to avoid funky scrolling behavior. (If you want to set this - * more precisely, don't forget that scroll bars are not part of the - * edit control client area, and their sizes must be factored in.) - */ - CRect rect; - CRichEditCtrl* pEdit = (CRichEditCtrl*) GetDlgItem(IDC_FVIEW_EDITBOX); - ASSERT(pEdit != NULL); - //pEdit->GetClientRect(&rect); - pEdit->GetWindowRect(&rect); - //GetClientRect(&rect); - rect.left = 2; - rect.top = 2; - pEdit->SetRect(&rect); -} - - -void ViewFilesDialog::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - - if (pDX->m_bSaveAndValidate) { - LOGD("COPY OUT"); - } else { - LOGD("COPY IN"); - OnFviewNext(); - } -} - -static void DumpBitmapInfo(HBITMAP hBitmap) -{ - BITMAP info; - CBitmap* pBitmap = CBitmap::FromHandle(hBitmap); - int gotten; - - gotten = pBitmap->GetObject(sizeof(info), &info); - - LOGD("DumpBitmapInfo: gotten=%d of %d", gotten, sizeof(info)); - LOGD(" bmType = %d", info.bmType); - LOGD(" bmWidth=%d, bmHeight=%d", info.bmWidth, info.bmHeight); - LOGD(" bmWidthBytes=%d", info.bmWidthBytes); - LOGD(" bmPlanes=%d", info.bmPlanes); - LOGD(" bmBitsPixel=%d", info.bmBitsPixel); - LOGD(" bmPits = 0x%p", info.bmBits); -} - -bool ViewFilesDialog::IsSourceEmpty(const GenericEntry* pEntry, - ReformatHolder::ReformatPart part) -{ - switch (part) { - case ReformatHolder::ReformatPart::kPartData: - return pEntry->GetDataForkLen() == 0; - case ReformatHolder::ReformatPart::kPartRsrc: - return pEntry->GetRsrcForkLen() == 0; - case ReformatHolder::ReformatPart::kPartCmmt: - return !pEntry->GetHasNonEmptyComment(); - default: - return false; - } -} - -void ViewFilesDialog::DisplayText(const WCHAR* fileName, bool zeroSourceLen) -{ - CWaitCursor wait; // streaming of big files can take a little while - bool errFlg; - bool emptyFlg = false; - bool editHadFocus = false; - - ASSERT(fpOutput != NULL); - ASSERT(fileName != NULL); - - errFlg = fpOutput->GetOutputKind() == ReformatOutput::kOutputErrorMsg; - - ASSERT(fpOutput->GetOutputKind() != ReformatOutput::kOutputUnknown); - - CRichEditCtrl* pEdit = (CRichEditCtrl*) GetDlgItem(IDC_FVIEW_EDITBOX); - ASSERT(pEdit != NULL); - - /* retain the selection even if we lose focus [can't do this in OnInit] */ - pEdit->SetOptions(ECOOP_OR, ECO_SAVESEL); - -#if 0 - /* - * Start by trashing anything that's there. Not strictly necessary, - * but it prevents the control from trying to maintain the old stuff - * in an undo buffer. (Not entirely sure if a stream-in operation is - * undoable, but it costs very little to be certain.) - * - * UPDATE: I turned this off because it was dinging the speaker (?!). - * Might be doing that because it's in read-only mode. - */ - pEdit->SetSel(0, -1); - pEdit->Clear(); - pEdit->EmptyUndoBuffer(); -#endif - - /* - * There's a redraw flash that goes away if you change the input - * focus to something other than the edit ctrl. (Move between large - * files; it looks like you can see the text being selected and - * hightlighted. The control doesn't have an "always highlight" flag - * set, so if the focus is on a different control it doesn't light up.) - * - * Since we're currently forcing the focus to be on the edit ctrl later - * on, we just jam it to something else here. If nothing has the focus, - * as can happen if we click on "resource fork" and then Alt-N to a - * file without a resource fork, we force the focus to return to the - * edit window. - * - * NOTE: this would look a little better if we used the Prev/Next - * buttons to hold the temporary focus, but we need to know which key - * the user hit. We could also create a bogus control, move it into - * negative space where it will be invisible, and use that as a "focus - * holder". - * - * TODO: on Win7 you can sometimes see a blue flash. Not sure if it - * relates to this or some other aspect of the redraw. - */ - CWnd* pFocusWnd = GetFocus(); - if (pFocusWnd == NULL || pFocusWnd->m_hWnd == pEdit->m_hWnd) { - editHadFocus = true; - GetDlgItem(IDOK)->SetFocus(); - } - - /* - * The text color isn't getting reset when we reload the control. I - * can't find a "set default text color" call, so I'm reformatting - * part of the buffer. - * - * Here's the weird part: it doesn't seem to matter what color I - * set it to under Win2K. It reverts to black so long as I do anything - * here. Under Win98, it uses the new color. - */ - //if (0) - { - CHARFORMAT cf; - cf.cbSize = sizeof(CHARFORMAT); - cf.dwMask = CFM_COLOR; - cf.crTextColor = RGB(0, 0, 0); - pEdit->SetSel(0, 1); // must select at least one char - pEdit->SetSelectionCharFormat(cf); - } - - /* - * Add the appropriate data. If the "bitmap" flag is set, use the - * MyDIBitmap pointer instead. - */ - if (fpOutput->GetOutputKind() == ReformatOutput::kOutputBitmap) { - CClientDC dcScreen(this); - HBITMAP hBitmap; - - if (fpRichEditOle == NULL) { - /* can't do this in OnInitDialog -- m_pWnd isn't initialized */ - fpRichEditOle = pEdit->GetIRichEditOle(); - ASSERT(fpRichEditOle != NULL); - } - - //FILE* fp = fopen("C:/test/output.bmp", "wb"); - //if (fp != NULL) { - // pDib->WriteToFile(fp); - // fclose(fp); - //} - - hBitmap = fpOutput->GetDIB()->ConvertToDDB(dcScreen.m_hDC); - if (hBitmap == NULL) { - LOGW("ConvertToDDB failed!"); - pEdit->SetWindowText(L"Internal error."); - errFlg = true; - } else { - //DumpBitmapInfo(hBitmap); - //DumpBitmapInfo(pDib->GetHandle()); - - LOGD("Inserting bitmap"); - pEdit->SetWindowText(L""); - CImageDataObject::InsertBitmap(fpRichEditOle, hBitmap); - - /* RichEditCtrl has it now */ - ::DeleteObject(hBitmap); - } - } else { - /* - * Stream the data in, using the appropriate format. Since we don't - * have the "replace selection" flag set, this replaces everything - * that's currently in there. - * - * We can't use SetWindowText() unless we're willing to forgo viewing - * of binary files in "raw" form. There doesn't seem to be any other - * difference between the two approaches. - */ - const char* textBuf; - long textLen; - int streamFormat; - - textBuf = fpOutput->GetTextBuf(); - textLen = fpOutput->GetTextLen(); - streamFormat = SF_TEXT; - if (fpOutput->GetOutputKind() == ReformatOutput::kOutputRTF) - streamFormat = SF_RTF; - if (fpOutput->GetTextLen() == 0) { - if (zeroSourceLen) { - textBuf = "(file is empty)"; - EnableFormatSelection(FALSE); - } else { - textBuf = "(converted output is empty)"; - } - textLen = strlen(textBuf); - emptyFlg = true; - } - if (fpOutput->GetOutputKind() == ReformatOutput::kOutputErrorMsg) - EnableFormatSelection(FALSE); - - /* make sure the control will hold everything we throw at it */ - pEdit->LimitText(textLen+1); - LOGI("Streaming %ld bytes (kind=%d)", - textLen, fpOutput->GetOutputKind()); - - /* clear this early to avoid loading onto yellow */ - if (errFlg) - pEdit->SetBackgroundColor(FALSE, RGB(255, 255, 0)); - else if (emptyFlg) - pEdit->SetBackgroundColor(FALSE, RGB(192, 192, 192)); - else - pEdit->SetBackgroundColor(TRUE, 0); - - RichEditXfer xfer(textBuf, textLen); - EDITSTREAM es; - es.dwCookie = (DWORD) &xfer; - es.dwError = 0; - es.pfnCallback = RichEditXfer::EditStreamCallback; - long count; - count = pEdit->StreamIn(streamFormat, es); - LOGI("StreamIn returned count=%ld dwError=%d", count, es.dwError); - - if (es.dwError != 0) { - /* a -16 error can happen if the type is RTF but contents are not */ - char errorText[256]; - - _snprintf(errorText, sizeof(errorText), - "ERROR: failed while loading data (err=0x%08lx)\n" - "(File contents might be too big for Windows to display)\n", - es.dwError); - RichEditXfer errXfer(errorText, strlen(errorText)); - es.dwCookie = (DWORD) &errXfer; - es.dwError = 0; - - count = pEdit->StreamIn(SF_TEXT, es); - LOGI("Error StreamIn returned count=%ld dwError=%d", count, es.dwError); - - errFlg = true; - } - - //pEdit->SetSel(0, 0); - } - - /* move us back to the top */ - pEdit->LineScroll(-pEdit->GetFirstVisibleLine()); - - /* just in case it's trying to hold on to something */ - pEdit->EmptyUndoBuffer(); - - /* work around bug that creates unnecessary scroll bars */ - pEdit->SetScrollRange(SB_VERT, 0, 0, TRUE); - pEdit->SetScrollRange(SB_HORZ, 0, 0, TRUE); - - /* display the entire message in the user's selected font */ - if (!fpOutput->GetMultipleFontsFlag()) { - // adjust the font, stripping default boldness from SF_TEXT - NewFontSelected(fpOutput->GetOutputKind() != ReformatOutput::kOutputRTF); - } - - /* enable/disable the scroll bars */ - //pEdit->EnableScrollBar(SB_BOTH, ESB_DISABLE_BOTH); - - if (errFlg) - pEdit->SetBackgroundColor(FALSE, RGB(255, 255, 0)); - else if (emptyFlg) - pEdit->SetBackgroundColor(FALSE, RGB(192, 192, 192)); - else - pEdit->SetBackgroundColor(TRUE, 0); - - /* - * Work around a Windows bug that prevents the scroll bars from - * being displayed immediately. This makes them appear, but the - * vertical scroll bar comes out funky on short files (fixed with - * the SetScrollRange call above). - * - * Best guess: when the edit box is resized, it chooses the scroll bar - * configuration based on the currently-loaded data. If you resize it - * and *then* add data, you're stuck with the previous scroll bar - * values. This doesn't quite make sense though... - * - * This works: - * - Set up dialog. - * - Load data. - * - Do minor twiddling. - * - Resize box significantly. - * - * This works: - * - (box already has data in it) - * - Load new data. - * - Do minor twiddling. - * - * This doesn't: - * - Set up dialog - * - Resize box significantly. - * - Load data. - * - Do minor twiddling. - * - * There might be some first-time setup issues in here. Hard to say. - * Anything related to RichEdit controls is extremely fragile, and must - * be tested with a variety of inputs, preference settings, and under - * at least Win98 and Win2K (which are *very* different). - * - * TODO: re-evaluate all this without worrying about Win9x - */ - if (fFirstResize) { - /* adjust the size of the window to match the last size used */ - const Preferences* pPreferences = GET_PREFERENCES(); - long width = pPreferences->GetPrefLong(kPrFileViewerWidth); - long height = pPreferences->GetPrefLong(kPrFileViewerHeight); - CRect fullRect; - GetWindowRect(&fullRect); - //LOGI(" VFD pre-size %dx%d", fullRect.Width(), fullRect.Height()); - fullRect.right = fullRect.left + width; - fullRect.bottom = fullRect.top + height; - MoveWindow(fullRect, TRUE); - - editHadFocus = true; // force focus on edit box - - fFirstResize = false; - } else { - /* this should be enough */ - ShiftControls(0, 0); - } - - if (fpOutput->GetOutputKind() == ReformatOutput::kOutputBitmap) { - /* get the cursor off of the image */ - pEdit->SetSel(-1, -1); - - /* - * Tall Super Hi-Res graphics (e.g. Paintworks PNT) cause the edit - * control to scroll to the bottom. Move it back to the top. - * - * SetScrollInfo just moves the scrollbar without changing the - * view position. - */ - pEdit->SendMessage(WM_VSCROLL, SB_TOP, 0); - } - - /* - * We want the focus to be on the text window so keyboard selection - * commands work. However, it's also nice to be able to arrow through - * the format selection box. - */ - if (editHadFocus) - pEdit->SetFocus(); - - fTitle = fileName; - //if (fpOutput->GetOutputKind() == ReformatOutput::kOutputText || - // fpOutput->GetOutputKind() == ReformatOutput::kOutputRTF || - // fpOutput->GetOutputKind() == ReformatOutput::kOutputCSV || - // fpOutput->GetOutputKind() == ReformatOutput::kOutputBitmap || - // fpOutput->GetOutputKind() == ReformatOutput::kOutputRaw) - //{ - // not for error messages - fTitle += _T(" ["); - fTitle += fpOutput->GetFormatDescr(); - fTitle += _T("]"); - //} else if (fpOutput->GetOutputKind() == ReformatOutput::kOutputRaw) { - // fTitle += _T(" [Raw]"); - //} - - CString winTitle = _T("File Viewer - "); - winTitle += fTitle; - SetWindowText(winTitle); - - /* - * Enable or disable the next/prev buttons. - */ - CButton* pButton; - pButton = (CButton*) GetDlgItem(IDC_FVIEW_PREV); - pButton->EnableWindow(fpSelSet->IterHasPrev()); - pButton = (CButton*) GetDlgItem(IDC_FVIEW_NEXT); - pButton->EnableWindow(fpSelSet->IterHasNext()); -} - -void ViewFilesDialog::OnFviewNext(void) -{ - // handles "next" button; also called from DoDataExchange - ReformatHolder::ReformatPart part; - ReformatHolder::ReformatID id; - int result; - - if (fBusy) { - LOGI("BUSY!"); - return; - } - - fBusy = true; - - if (!fpSelSet->IterHasNext()) { - ASSERT(false); - return; - } - - /* - * Get the pieces of the file. - */ - SelectionEntry* pSelEntry = fpSelSet->IterNext(); - GenericEntry* pEntry = pSelEntry->GetEntry(); - result = ReformatPrep(pEntry); -#if 0 - { - // for debugging -- simulate failure - result = -1; - delete fpHolder; - fpHolder = NULL; - delete fpOutput; - fpOutput = NULL; - } -#endif - - fBusy = false; - if (result != 0) { - ASSERT(fpHolder == NULL); - ASSERT(fpOutput == NULL); - return; - } - - /* - * Format a piece. - */ - ConfigurePartButtons(pSelEntry->GetEntry()); - part = GetSelectedPart(); - id = ConfigureFormatSel(part); - Reformat(pSelEntry->GetEntry(), part, id); - - DisplayText(pSelEntry->GetEntry()->GetDisplayName(), - IsSourceEmpty(pSelEntry->GetEntry(), part)); -} - -void ViewFilesDialog::OnFviewPrev(void) -{ - ReformatHolder::ReformatPart part; - ReformatHolder::ReformatID id; - int result; - - if (fBusy) { - LOGI("BUSY!"); - return; - } - - fBusy = true; - - if (!fpSelSet->IterHasPrev()) { - ASSERT(false); - return; - } - - /* - * Get the pieces of the file. - */ - SelectionEntry* pSelEntry = fpSelSet->IterPrev(); - GenericEntry* pEntry = pSelEntry->GetEntry(); - result = ReformatPrep(pEntry); - - fBusy = false; - if (result != 0) { - ASSERT(fpHolder == NULL); - ASSERT(fpOutput == NULL); - return; - } - - /* - * Format a piece. - */ - ConfigurePartButtons(pEntry); - part = GetSelectedPart(); - id = ConfigureFormatSel(part); - Reformat(pEntry, part, id); - - DisplayText(pEntry->GetDisplayName(), IsSourceEmpty(pEntry, part)); -} - -void ViewFilesDialog::ConfigurePartButtons(const GenericEntry* pEntry) -{ - int id = 0; - - CButton* pDataWnd = (CButton*) GetDlgItem(IDC_FVIEW_DATA); - CButton* pRsrcWnd = (CButton*) GetDlgItem(IDC_FVIEW_RSRC); - CButton* pCmmtWnd = (CButton*) GetDlgItem(IDC_FVIEW_CMMT); - ASSERT(pDataWnd != NULL && pRsrcWnd != NULL && pCmmtWnd != NULL); - - /* figure out what was checked before, ignoring if it's not available */ - if (pDataWnd->GetCheck() == BST_CHECKED && pEntry->GetHasDataFork()) - id = IDC_FVIEW_DATA; - else if (pRsrcWnd->GetCheck() == BST_CHECKED && pEntry->GetHasRsrcFork()) - id = IDC_FVIEW_RSRC; - else if (pCmmtWnd->GetCheck() == BST_CHECKED && pEntry->GetHasComment()) - id = IDC_FVIEW_CMMT; - - /* couldn't keep previous check, find a new one */ - if (id == 0) { - if (pEntry->GetHasDataFork()) - id = IDC_FVIEW_DATA; - else if (pEntry->GetHasRsrcFork()) - id = IDC_FVIEW_RSRC; - else if (pEntry->GetHasComment()) - id = IDC_FVIEW_CMMT; - // else leave it set to 0 - } - - /* set up the dialog */ - pDataWnd->SetCheck(BST_UNCHECKED); - pRsrcWnd->SetCheck(BST_UNCHECKED); - pCmmtWnd->SetCheck(BST_UNCHECKED); - - if (pEntry == NULL) { - pDataWnd->EnableWindow(FALSE); - pRsrcWnd->EnableWindow(FALSE); - pCmmtWnd->EnableWindow(FALSE); - } else { - pDataWnd->EnableWindow(pEntry->GetHasDataFork()); - pRsrcWnd->EnableWindow(pEntry->GetHasRsrcFork()); - pCmmtWnd->EnableWindow(pEntry->GetHasComment()); - } - - if (id == IDC_FVIEW_RSRC) - pRsrcWnd->SetCheck(BST_CHECKED); - else if (id == IDC_FVIEW_CMMT) - pCmmtWnd->SetCheck(BST_CHECKED); - else - pDataWnd->SetCheck(BST_CHECKED); -} - -ReformatHolder::ReformatPart ViewFilesDialog::GetSelectedPart(void) -{ - CButton* pDataWnd = (CButton*) GetDlgItem(IDC_FVIEW_DATA); - CButton* pRsrcWnd = (CButton*) GetDlgItem(IDC_FVIEW_RSRC); - CButton* pCmmtWnd = (CButton*) GetDlgItem(IDC_FVIEW_CMMT); - ASSERT(pDataWnd != NULL && pRsrcWnd != NULL && pCmmtWnd != NULL); - - if (pDataWnd->GetCheck() == BST_CHECKED) - return ReformatHolder::kPartData; - else if (pRsrcWnd->GetCheck() == BST_CHECKED) - return ReformatHolder::kPartRsrc; - else if (pCmmtWnd->GetCheck() == BST_CHECKED) - return ReformatHolder::kPartCmmt; - else { - assert(false); - return ReformatHolder::kPartData; - } -} - -int ViewFilesDialog::ReformatPrep(GenericEntry* pEntry) -{ - CWaitCursor waitc; // can be slow reading data from floppy - MainWindow* pMainWindow = GET_MAIN_WINDOW(); - int result; - - delete fpHolder; - fpHolder = NULL; - - result = pMainWindow->GetFileParts(pEntry, &fpHolder); - if (result != 0) { - LOGI("GetFileParts(prev) failed!"); - ASSERT(fpHolder == NULL); - return -1; - } - - /* set up the ReformatHolder */ - MainWindow::ConfigureReformatFromPreferences(fpHolder); - - /* prep for applicability test */ - fpHolder->SetSourceAttributes( - pEntry->GetFileType(), - pEntry->GetAuxType(), - MainWindow::ReformatterSourceFormat(pEntry->GetSourceFS()), - pEntry->GetFileNameExtensionMOR()); - - /* figure out which reformatters apply to this file */ - LOGD("Testing reformatters"); - fpHolder->TestApplicability(); - - return 0; -} - -int ViewFilesDialog::Reformat(const GenericEntry* pEntry, - ReformatHolder::ReformatPart part, ReformatHolder::ReformatID id) -{ - CWaitCursor waitc; - - delete fpOutput; - fpOutput = NULL; - - /* run the best one */ - fpOutput = fpHolder->Apply(part, id); - -//bail: - if (fpOutput != NULL) { - // success -- do some sanity checks - switch (fpOutput->GetOutputKind()) { - case ReformatOutput::kOutputText: - case ReformatOutput::kOutputRTF: - case ReformatOutput::kOutputCSV: - case ReformatOutput::kOutputErrorMsg: - ASSERT(fpOutput->GetTextBuf() != NULL); - ASSERT(fpOutput->GetDIB() == NULL); - break; - case ReformatOutput::kOutputBitmap: - ASSERT(fpOutput->GetDIB() != NULL); - ASSERT(fpOutput->GetTextBuf() == NULL); - break; - case ReformatOutput::kOutputRaw: - // text buf might be NULL - ASSERT(fpOutput->GetDIB() == NULL); - break; - } - return 0; - } else { - /* shouldn't get here; handle it if we do */ - static const char* kFailMsg = "Internal error\r\n"; - fpOutput = new ReformatOutput; - fpOutput->SetTextBuf((char*) kFailMsg, strlen(kFailMsg), false); - fpOutput->SetOutputKind(ReformatOutput::kOutputErrorMsg); - return -1; - } -} - -ReformatHolder::ReformatID ViewFilesDialog::ConfigureFormatSel( - ReformatHolder::ReformatPart part) -{ - //ReformatHolder::ReformatID prevID = ReformatHolder::kReformatUnknown; - ReformatHolder::ReformatID returnID = ReformatHolder::kReformatRaw; - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_FVIEW_FORMATSEL); - - LOGD("--- ConfigureFormatSel"); - - //int sel; - //sel = pCombo->GetCurSel(); - //if (sel != CB_ERR) - // prevID = (ReformatHolder::ReformatID) pCombo->GetItemData(sel); - //LOGI(" prevID = %d", prevID); - - EnableFormatSelection(TRUE); - pCombo->ResetContent(); - - /* - * Fill out the combo box with the reformatter entries. - * - * There's probably a way to do this that doesn't involve abusing - * enums, but this'll do for now. - */ - int applyIdx, idIdx; - bool preferred = true; - int comboIdx; - for (applyIdx = ReformatHolder::kApplicMAX; - applyIdx > ReformatHolder::kApplicNot; /*no incr*/) - { - if (applyIdx == ReformatHolder::kApplicMAX) - goto skip; - if (applyIdx == ReformatHolder::kApplicUnknown) - goto skip; - - int testApplies; - - testApplies = applyIdx; - if (preferred) - testApplies |= ReformatHolder::kApplicPreferred; - - for (idIdx = 0; idIdx < ReformatHolder::kReformatMAX; idIdx++) { - if (idIdx == ReformatHolder::kReformatUnknown) - continue; - - ReformatHolder::ReformatApplies applies; - - applies = fpHolder->GetApplic(part, - (ReformatHolder::ReformatID) idIdx); - if ((int) applies == testApplies) { - /* match! */ - CString str; - //LOGI("MATCH at %d (0x%02x)", idIdx, testApplies); - str.Format(L"%ls", ReformatHolder::GetReformatName( - (ReformatHolder::ReformatID) idIdx)); - comboIdx = pCombo->AddString(str); - pCombo->SetItemData(comboIdx, idIdx); - - /* define initial selection as best item */ - if (comboIdx == 0) - pCombo->SetCurSel(comboIdx); - - //if (idIdx == (int) prevID && - // applyIdx == ReformatHolder::kApplicAlways) - //{ - // LOGI(" Found 'always' prevID, selecting"); - // pCombo->SetCurSel(comboIdx); - //} - } - } - -skip: - if (!preferred) - applyIdx--; - preferred = !preferred; - } - - /* return whatever we now have selected */ - int sel = pCombo->GetCurSel(); - LOGD(" At end, sel is %d", sel); - if (sel != CB_ERR) - returnID = (ReformatHolder::ReformatID) pCombo->GetItemData(sel); - - return returnID; -} - -void ViewFilesDialog::OnFormatSelChange(void) -{ - /* - * The user has changed entries in the format selection drop box. - * - * Also called from the "quick change" buttons. - */ - - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_FVIEW_FORMATSEL); - ASSERT(pCombo != NULL); - LOGD("+++ SELECTION IS NOW %d", pCombo->GetCurSel()); - - SelectionEntry* pSelEntry = fpSelSet->IterCurrent(); - GenericEntry* pEntry = pSelEntry->GetEntry(); - ReformatHolder::ReformatPart part; - ReformatHolder::ReformatID id; - - part = GetSelectedPart(); - id = (ReformatHolder::ReformatID) pCombo->GetItemData(pCombo->GetCurSel()); - Reformat(pEntry, part, id); - - DisplayText(pEntry->GetDisplayName(), IsSourceEmpty(pEntry, part)); -} - -void ViewFilesDialog::OnFviewData(void) -{ - ForkSelectCommon(ReformatHolder::kPartData); -} - -void ViewFilesDialog::OnFviewRsrc(void) -{ - ForkSelectCommon(ReformatHolder::kPartRsrc); -} - -void ViewFilesDialog::OnFviewCmmt(void) -{ - ForkSelectCommon(ReformatHolder::kPartCmmt); -} - -void ViewFilesDialog::ForkSelectCommon(ReformatHolder::ReformatPart part) -{ - GenericEntry* pEntry; - ReformatHolder::ReformatID id; - - LOGI("Switching to file part=%d", part); - ASSERT(fpHolder != NULL); - ASSERT(fpSelSet != NULL); - ASSERT(fpSelSet->IterCurrent() != NULL); - pEntry = fpSelSet->IterCurrent()->GetEntry(); - ASSERT(pEntry != NULL); - - id = ConfigureFormatSel(part); - - Reformat(pEntry, part, id); - DisplayText(pEntry->GetDisplayName(), IsSourceEmpty(pEntry, part)); -} - -void ViewFilesDialog::OnFviewFmtBest(void) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_FVIEW_FORMATSEL); - pCombo->SetCurSel(0); // best is always at the top - OnFormatSelChange(); - pCombo->SetFocus(); -} - -void ViewFilesDialog::OnFviewFmtHex(void) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_FVIEW_FORMATSEL); - int sel = FindByVal(pCombo, ReformatHolder::kReformatHexDump); - if (sel < 0) - sel = 0; - pCombo->SetCurSel(sel); - OnFormatSelChange(); - pCombo->SetFocus(); -} - -void ViewFilesDialog::OnFviewFmtRaw(void) -{ - CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_FVIEW_FORMATSEL); - int sel = FindByVal(pCombo, ReformatHolder::kReformatRaw); - if (sel < 0) - sel = 0; - pCombo->SetCurSel(sel); - OnFormatSelChange(); - pCombo->SetFocus(); -} - -void ViewFilesDialog::EnableFormatSelection(BOOL enable) -{ - GetDlgItem(IDC_FVIEW_FORMATSEL)->EnableWindow(enable); - GetDlgItem(IDC_FVIEW_FMT_BEST)->EnableWindow(enable); - GetDlgItem(IDC_FVIEW_FMT_HEX)->EnableWindow(enable); - GetDlgItem(IDC_FVIEW_FMT_RAW)->EnableWindow(enable); -} - -int ViewFilesDialog::FindByVal(CComboBox* pCombo, DWORD val) -{ - int count = pCombo->GetCount(); - int i; - - for (i = 0; i < count; i++) { - if (pCombo->GetItemData(i) == val) - return i; - } - return -1; -} - -void ViewFilesDialog::OnFviewFont(void) -{ - // Choose a font, then apply the choice to all of the text in the box. - - LOGFONT logFont; - CFont font; - - /* - * Create a LOGFONT structure with the desired default characteristics, - * then use that to initialize the font dialog. - */ - CreateSimpleFont(&font, this, fTypeFace, fPointSize); - font.GetLogFont(&logFont); - - CFontDialog fontDlg(&logFont); - fontDlg.m_cf.Flags &= ~(CF_EFFECTS); - - if (fontDlg.DoModal() == IDOK) { - //fontDlg.GetCurrentFont(&logFont); - fTypeFace = fontDlg.GetFaceName(); - fPointSize = fontDlg.GetSize() / 10; - LOGI("Now using %d-point '%ls'", fPointSize, (LPCWSTR) fTypeFace); - - NewFontSelected(false); - } - -} - -void ViewFilesDialog::NewFontSelected(bool resetBold) -{ - CRichEditCtrl* pEdit = (CRichEditCtrl*) GetDlgItem(IDC_FVIEW_EDITBOX); - ASSERT(pEdit != NULL); - - CHARFORMAT cf; - cf.cbSize = sizeof(CHARFORMAT); - cf.dwMask = CFM_FACE | CFM_SIZE; - if (resetBold) { - cf.dwMask |= CFM_BOLD; - cf.dwEffects = 0; - } - ::lstrcpy(cf.szFaceName, fTypeFace); - cf.yHeight = fPointSize * 20; // in twips - pEdit->SetSel(0, -1); // select all - pEdit->SetSelectionCharFormat(cf); - pEdit->SetSel(0, 0); // unselect -} - - -void ViewFilesDialog::OnFviewPrint(void) -{ - MainWindow* pMainWindow = GET_MAIN_WINDOW(); - CPrintDialog dlg(FALSE); // use CPrintDialogEx for Win2K? CPageSetUpDialog? - PrintRichEdit pre; - CDC dc; - int numPages; - - dlg.m_pd.nFromPage = dlg.m_pd.nMinPage = 1; - dlg.m_pd.nToPage = dlg.m_pd.nMaxPage = 1; - - /* - * Getting the expected number of pages requires a print test-run. - * However, if we use GetDefaults to get the DC, the call to DoModal - * returns immediately with IDCANCEL. So, we do our pre-flighting - * in a separate DC with a separate print dialog object. - */ - { - CPrintDialog countDlg(FALSE); - CDC countDC; - - dlg.m_pd.hDevMode = pMainWindow->fhDevMode; - dlg.m_pd.hDevNames = pMainWindow->fhDevNames; - - if (countDlg.GetDefaults() == TRUE) { - CWaitCursor waitc; - - if (countDC.Attach(countDlg.GetPrinterDC()) != TRUE) { - ASSERT(false); - } - pre.Setup(&countDC, this); - pre.PrintPreflight(&fEditCtrl, &numPages); - LOGI("Default printer generated %d pages", numPages); - - dlg.m_pd.nToPage = dlg.m_pd.nMaxPage = numPages; - } - - pMainWindow->fhDevMode = dlg.m_pd.hDevMode; - pMainWindow->fhDevNames = dlg.m_pd.hDevNames; - } - - long startChar, endChar; - fEditCtrl.GetSel(/*ref*/startChar, /*ref*/endChar); - - if (endChar != startChar) { - LOGI("GetSel returned start=%ld end=%ld", startChar, endChar); - dlg.m_pd.Flags &= ~(PD_NOSELECTION); - } - - dlg.m_pd.hDevMode = pMainWindow->fhDevMode; - dlg.m_pd.hDevNames = pMainWindow->fhDevNames; - dlg.m_pd.Flags |= PD_USEDEVMODECOPIESANDCOLLATE; - dlg.m_pd.Flags &= ~(PD_NOPAGENUMS); - - - /* - * Show them the print dialog. - */ - if (dlg.DoModal() != IDOK) - return; - - /* - * Grab the chosen printer and prep ourselves. - */ - if (dc.Attach(dlg.GetPrinterDC()) != TRUE) { - CString msg; - CheckedLoadString(&msg, IDS_PRINTER_NOT_USABLE); - ShowFailureMsg(this, msg, IDS_FAILED); - return; - } - pre.Setup(&dc, this); - - /* - * Do the printing. - */ - if (dlg.PrintRange()) - pre.PrintPages(&fEditCtrl, fTitle, dlg.GetFromPage(), dlg.GetToPage()); - else if (dlg.PrintSelection()) - pre.PrintSelection(&fEditCtrl, fTitle, startChar, endChar); - else // dlg.PrintAll() - pre.PrintAll(&fEditCtrl, fTitle); - - pMainWindow->fhDevMode = dlg.m_pd.hDevMode; - pMainWindow->fhDevNames = dlg.m_pd.hDevNames; -} - -void ViewFilesDialog::OnFviewFind(void) -{ - // User hit the "Find..." button. Open the modeless Find dialog. - DWORD flags = 0; - - if (fpFindDialog != NULL) - return; - - if (fFindDown) - flags |= FR_DOWN; - if (fFindMatchCase) - flags |= FR_MATCHCASE; - if (fFindMatchWholeWord) - flags |= FR_WHOLEWORD; - - fpFindDialog = new CFindReplaceDialog; - fpFindDialog->Create(TRUE, // "find" only - fFindLastStr, // default string to search for - NULL, // default string to replace - flags, // flags - this); // parent -} - -LRESULT ViewFilesDialog::OnFindDialogMessage(WPARAM wParam, LPARAM lParam) -{ - /* - * Handle activity in the modeless "find" dialog. - */ - - assert(fpFindDialog != NULL); - - fFindDown = (fpFindDialog->SearchDown() != 0); - fFindMatchCase = (fpFindDialog->MatchCase() != 0); - fFindMatchWholeWord = (fpFindDialog->MatchWholeWord() != 0); - - if (fpFindDialog->IsTerminating()) { - LOGI("VFD find dialog closing"); - fpFindDialog = NULL; - return 0; - } - - if (fpFindDialog->FindNext()) { - fFindLastStr = fpFindDialog->GetFindString(); - FindNext(fFindLastStr, fFindDown, fFindMatchCase, fFindMatchWholeWord); - } else { - LOGI("Unexpected find dialog activity"); - } - - return 0; -} - - -void ViewFilesDialog::FindNext(const WCHAR* str, bool down, bool matchCase, - bool wholeWord) -{ - LOGI("FindText '%ls' d=%d c=%d w=%d", str, down, matchCase, wholeWord); - - FINDTEXTEX findTextEx = { 0 }; - CHARRANGE selChrg; - DWORD flags = 0; - long start, result; - - if (down) - flags |= FR_DOWN; - if (matchCase) - flags |= FR_MATCHCASE; - if (wholeWord) - flags |= FR_WHOLEWORD; - - fEditCtrl.GetSel(selChrg); - if (selChrg.cpMin == selChrg.cpMax) - start = selChrg.cpMin; // start at caret - else - start = selChrg.cpMin +1; // start past selection - LOGD(" selection is %ld,%ld; start=%ld", - selChrg.cpMin, selChrg.cpMax, start); - - findTextEx.lpstrText = str; - findTextEx.chrg.cpMin = start; - if (down) { - findTextEx.chrg.cpMax = -1; - } else { - findTextEx.chrg.cpMax = 0; - } - LOGV(" using cpMin=%ld cpMax=%ld", - findTextEx.chrg.cpMin, findTextEx.chrg.cpMax); - - result = fEditCtrl.FindText(flags, &findTextEx); - if (result == -1) { - LOGD(" not found, wrapping and retrying"); - /* didn't find it, wrap around to start/end and retry */ - if (down) { - findTextEx.chrg.cpMin = 0; - findTextEx.chrg.cpMax = -1; - } else { - findTextEx.chrg.cpMin = fEditCtrl.GetTextLength(); - findTextEx.chrg.cpMax = 0; - } - findTextEx.lpstrText = str; - result = fEditCtrl.FindText(flags, &findTextEx); - } - - LOGD(" result=%ld min=%ld max=%ld", result, - findTextEx.chrgText.cpMin, findTextEx.chrgText.cpMax); - if (result != -1) { - /* select the text we found */ - fEditCtrl.SetSel(findTextEx.chrgText); - } else { - /* remove selection, leaving caret at start of sel */ - selChrg.cpMax = selChrg.cpMin; - fEditCtrl.SetSel(selChrg); - - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - pMain->FailureBeep(); - } -} diff --git a/ciderpress/app/ViewFilesDialog.h b/ciderpress/app/ViewFilesDialog.h deleted file mode 100644 index e658758..0000000 --- a/ciderpress/app/ViewFilesDialog.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Class for the "view files" dialog box. - */ -#ifndef APP_VIEWFILESDIALOG_H -#define APP_VIEWFILESDIALOG_H - -#include "GenericArchive.h" -#include "resource.h" - -class MainWindow; - -/* - * Implementation of the "view files" dialog box. - * - * The default window size is actually defined over in Preferences.cpp. - * The window size is a "sticky" pref (i.e. not stored in registry). - */ -class ViewFilesDialog : public CDialog { -public: - ViewFilesDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_FILE_VIEWER, pParentWnd), - fpSelSet(NULL), - fpHolder(NULL), - fpOutput(NULL), - fPointSize(0), - fNoWrapText(false), - fBusy(false), - fFirstResize(false), - fpRichEditOle(NULL), - fpFindDialog(NULL), - fFindDown(true), - fFindMatchCase(false), - fFindMatchWholeWord(false) - {} - virtual ~ViewFilesDialog(void) { - delete fpHolder; - delete fpOutput; - // Windows will handle destruction of fpFindDialog (child window) - } - - void SetSelectionSet(SelectionSet* pSelSet) { fpSelSet = pSelSet; } - - CString GetTextTypeFace(void) const { return fTypeFace; } - void SetTextTypeFace(const WCHAR* name) { fTypeFace = name; } - int GetTextPointSize(void) const { return fPointSize; } - void SetTextPointSize(int size) { fPointSize = size; } - //bool GetNoWrapText(void) const { return fNoWrapText; } - void SetNoWrapText(bool val) { fNoWrapText = val; } - -protected: - /* - * Window creation. Stuff the desired text into the RichEdit box. - */ - virtual BOOL OnInitDialog(void) override; - - /* - * Override OnOK/OnCancel so we don't bail out while we're in the middle of - * loading something. It would actually be kind of nice to be able to do - * so, so someday we should make the "cancel" button work, or perhaps allow - * prev/next to skip over the thing being loaded. "TO DO" - */ - virtual void OnOK(void) override; - virtual void OnCancel(void) override; - - virtual void DoDataExchange(CDataExchange* pDX) override; - - /* - * Window creation stuff. Set the icon and the "gripper". - */ - afx_msg int OnCreate(LPCREATESTRUCT lpcs); - - /* - * Window is going away. Save the current size. - */ - afx_msg void OnDestroy(void); - - /* - * When the window resizes, we have to tell the edit box to expand, and - * rearrange the controls inside it. - */ - afx_msg void OnSize(UINT nType, int cx, int cy); - - /* - * Restrict the minimum window size to something reasonable. - */ - afx_msg void OnGetMinMaxInfo(MINMAXINFO* pMMI); - - afx_msg void OnFviewNext(void); - afx_msg void OnFviewPrev(void); - afx_msg void OnFviewFont(void); - afx_msg void OnFviewPrint(void); - afx_msg void OnFviewFind(void); - afx_msg void OnFviewData(void); - afx_msg void OnFviewRsrc(void); - afx_msg void OnFviewCmmt(void); - afx_msg void OnFviewFmtBest(void); - afx_msg void OnFviewFmtHex(void); - afx_msg void OnFviewFmtRaw(void); - afx_msg void OnFormatSelChange(void); - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_FILE_VIEWER); - } - //afx_msg void OnFviewWrap(void); - afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam); - -private: - /* - * Adjust the positions and sizes of the controls. - * - * This relies on MinMaxInfo to guarantee that nothing falls off an edge. - */ - void ShiftControls(int deltaX, int deltaY); - - //void MoveControl(int id, int deltaX, int deltaY); - //void StretchControl(int id, int deltaX, int deltaY); - void NewFontSelected(bool resetBold); - - /* - * Determines whether the specified part is an empty fork/comment. - */ - bool IsSourceEmpty(const GenericEntry* pEntry, - ReformatHolder::ReformatPart part); - - /* - * Display a buffer of text in the RichEdit control. - * - * The RichEdit dialog will hold its own copy of the data, so "pHolder" can - * be safely destroyed after this returns. - * - * "fileName" is for display only. "zeroSourceLen" allows the function to - * tell the difference between an empty file and a non-empty file that - * generated empty output. - */ - void DisplayText(const WCHAR* fileName, bool zeroSourceLen); - - /* - * Set up the fpHolder. Does not reformat the data, just loads the source - * material and runs the applicability tests. - * - * Returns 0 on success, -1 on failure. - */ - int ReformatPrep(GenericEntry* pEntry); - - /* - * Reformat a file. - * - * Returns 0 if the file was reformatted, -1 if not - */ - int Reformat(const GenericEntry* pEntry, - ReformatHolder::ReformatPart part, ReformatHolder::ReformatID id); - - /* - * Configure the radio buttons that determine which part to view, enabling - * only those that make sense. - * - * Try to keep the previously-set button set. - * - * If "pEntry" is NULL, all buttons are disabled (useful for first-time - * initialization). - */ - void ConfigurePartButtons(const GenericEntry* pEntry); - - /* - * Figure out which part of the file is selected (data/rsrc/comment). - * - * If no part is selected, throws up its hands and returns kPartData. - */ - ReformatHolder::ReformatPart GetSelectedPart(void); - - void ForkSelectCommon(ReformatHolder::ReformatPart part); - - /* - * Set up the entries in the drop box based on the "applicable" array in - * fpHolder. The set of values is different for each part of the file. - * - * Returns the default reformatter ID. This is always entry #0. - */ - ReformatHolder::ReformatID ConfigureFormatSel( - ReformatHolder::ReformatPart part); - - /* - * Return the combo box index for the entry whose "data" field matches "val". - * - * Returns -1 if the entry couldn't be found. - */ - int FindByVal(CComboBox* pCombo, DWORD val); - - /* - * Enable or disable all of the format selection buttons. - */ - void EnableFormatSelection(BOOL enable); - - /* - * Find the next ocurrence of the specified string. - */ - void FindNext(const WCHAR* str, bool down, bool matchCase, - bool wholeWord); - - // pointer to main window, so we can ask for text to view - //MainWindow* fpMainWindow; - - // stuff to display - SelectionSet* fpSelSet; - - // edit control - CRichEditCtrl fEditCtrl; - - // currently loaded file - ReformatHolder* fpHolder; - - // most recent conversion - ReformatOutput* fpOutput; - - // current title of window - CString fTitle; - - // used to display a "gripper" in the bottom right of the dialog - CGripper fGripper; - - // last size of the window, so we can shift things around - CRect fLastWinSize; - - // font characteristics - CString fTypeFace; // name of font - int fPointSize; // size, in points - - // do we want to scroll or wrap? - bool fNoWrapText; - - // the message pump in the progress updater can cause NufxLib reentrancy - // (alternate solution: disable the window while we load stuff) - bool fBusy; - - // this is *really* annoying - bool fFirstResize; - - // used for stuffing images in; points at something inside RichEdit ctrl - IRichEditOle* fpRichEditOle; - - CFindReplaceDialog* fpFindDialog; - CString fFindLastStr; - bool fFindDown; - bool fFindMatchCase; - bool fFindMatchWholeWord; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_VIEWFILESDIALOG_H*/ diff --git a/ciderpress/app/VolumeCopyDialog.cpp b/ciderpress/app/VolumeCopyDialog.cpp deleted file mode 100644 index a4f2ffb..0000000 --- a/ciderpress/app/VolumeCopyDialog.cpp +++ /dev/null @@ -1,824 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Dialog that allows copying volumes or sub-volumes to and from files on - * disk. - * - * NOTE: we probably shouldn't allow copying volumes that start at block 0 - * and are equal to the DiskImgLib limit on sizes (e.g. 8GB). We'd be - * copying a partial volume, which doesn't make sense. - */ -#include "stdafx.h" -#include "VolumeCopyDialog.h" -#include "Main.h" -#include "../reformat/Charset.h" - - -BEGIN_MESSAGE_MAP(VolumeCopyDialog, CDialog) - ON_COMMAND(IDHELP, OnHelp) - ON_COMMAND(IDC_VOLUEMCOPYSEL_TOFILE, OnCopyToFile) - ON_COMMAND(IDC_VOLUEMCOPYSEL_FROMFILE, OnCopyFromFile) - ON_NOTIFY(LVN_ITEMCHANGED, IDC_VOLUMECOPYSEL_LIST, OnListChange) - ON_MESSAGE(WMU_DIALOG_READY, OnDialogReady) -END_MESSAGE_MAP() - - -/* - * Sub-class the generic libutil CancelDialog class. - */ -class VolumeXferProgressDialog : public ProgressCancelDialog { -public: - BOOL Create(CWnd* pParentWnd = NULL) { - fAbortOperation = false; - return ProgressCancelDialog::Create(&fAbortOperation, - IDD_VOLUMECOPYPROG, IDC_VOLUMECOPYPROG_PROGRESS, pParentWnd); - } - - void SetCurrentFiles(const WCHAR* fromName, const WCHAR* toName) { - CWnd* pWnd = GetDlgItem(IDC_VOLUMECOPYPROG_FROM); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fromName); - pWnd = GetDlgItem(IDC_VOLUMECOPYPROG_TO); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(toName); - } - -private: - void OnOK(void) override { - LOGI("Ignoring VolumeXferProgressDialog OnOK"); - } - - MainWindow* GetMainWindow(void) const { - return (MainWindow*)::AfxGetMainWnd(); - } - - bool fAbortOperation; -}; - - -BOOL VolumeCopyDialog::OnInitDialog(void) -{ - /* - * Scan the source image. - */ - - CRect rect; - - //this->GetWindowRect(&rect); - //LOGI("RECT is %d, %d, %d, %d", rect.left, rect.top, rect.bottom, rect.right); - - ASSERT(fpDiskImg != NULL); - ScanDiskInfo(false); - - CDialog::OnInitDialog(); // does DDX init - - CButton* pButton; - pButton = (CButton*) GetDlgItem(IDC_VOLUEMCOPYSEL_FROMFILE); - pButton->EnableWindow(FALSE); - pButton = (CButton*) GetDlgItem(IDC_VOLUEMCOPYSEL_TOFILE); - pButton->EnableWindow(FALSE); - - CString newTitle; - GetWindowText(newTitle); - newTitle += " - "; - newTitle += fPathName; - SetWindowText(newTitle); - - /* - * Prep the listview control. - * - * Columns: - * [icon] Volume name | Format | Size (MB/GB) | Block count - */ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUMECOPYSEL_LIST); - ASSERT(pListView != NULL); - ListView_SetExtendedListViewStyleEx(pListView->m_hWnd, - LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); - - int width1, width2, width3; - //CRect rect; - - pListView->GetClientRect(&rect); - width1 = pListView->GetStringWidth(L"XXVolume NameXXmmmmm"); - width2 = pListView->GetStringWidth(L"XXFormatXXmmmmmmmmmm"); - width3 = pListView->GetStringWidth(L"XXSizeXXmmm"); - //width4 = pListView->GetStringWidth("XXBlock CountXX"); - - pListView->InsertColumn(0, L"Volume Name", LVCFMT_LEFT, width1); - pListView->InsertColumn(1, L"Format", LVCFMT_LEFT, width2); - pListView->InsertColumn(2, L"Size", LVCFMT_LEFT, width3); - pListView->InsertColumn(3, L"Block Count", LVCFMT_LEFT, - rect.Width() - (width1+width2+width3) - - ::GetSystemMetrics(SM_CXVSCROLL) ); - - /* add images for list; this MUST be loaded before header images */ - LoadListImages(); - pListView->SetImageList(&fListImageList, LVSIL_SMALL); - - LoadList(); - - CenterWindow(); - - int cc = PostMessage(WMU_DIALOG_READY, 0, 0); - ASSERT(cc != 0); - - return TRUE; -} - -void VolumeCopyDialog::OnOK(void) -{ - /* - * We need to make sure we throw out the DiskFS we created before the modal - * dialog exits. This is necessary because we rely on an external DiskImg, - * and create DiskFS objects that point to it. - */ - Cleanup(); - CDialog::OnOK(); -} - -void VolumeCopyDialog::OnCancel(void) -{ - Cleanup(); - CDialog::OnCancel(); -} - -void VolumeCopyDialog::Cleanup(void) -{ - LOGD(" VolumeCopyDialog is done, cleaning up DiskFS"); - delete fpDiskFS; - fpDiskFS = NULL; -} - -void VolumeCopyDialog::OnListChange(NMHDR*, LRESULT* pResult) -{ - //CRect rect; - //this->GetWindowRect(&rect); - //LOGI("RECT is %d, %d, %d, %d", rect.left, rect.top, rect.bottom, rect.right); - - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUMECOPYSEL_LIST); - ASSERT(pListView != NULL); - CButton* pButton; - UINT selectedCount; - - selectedCount = pListView->GetSelectedCount(); - - pButton = (CButton*) GetDlgItem(IDC_VOLUEMCOPYSEL_TOFILE); - pButton->EnableWindow(selectedCount != 0); - - if (!fpDiskImg->GetReadOnly()) { - pButton = (CButton*) GetDlgItem(IDC_VOLUEMCOPYSEL_FROMFILE); - pButton->EnableWindow(selectedCount != 0); - } - - *pResult = 0; -} - -void VolumeCopyDialog::ScanDiskInfo(bool scanTop) -{ - /* - * The top-level disk image should already have been analyzed and the - * format overridden (if necessary). We don't want to do it in here the - * first time around because the "override" dialog screws up placement - * of our dialog box. I guess opening windows from inside OnInitDialog - * isn't expected. Annoying. [Um, maybe we could call CenterWindow?? - * Actually, now I'm a little concerned about modal dialogs coming and - * going while we're in OnInitDialog, because MainWindow is disabled and - * we're not yet enabled. ++ATM] - */ - - const Preferences* pPreferences = GET_PREFERENCES(); - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - DIError dierr; - CString errMsg, failed; - - assert(fpDiskImg != NULL); - assert(fpDiskFS == NULL); - - if (scanTop) { - DiskImg::FSFormat oldFormat; - oldFormat = fpDiskImg->GetFSFormat(); - - /* check to see if the top-level FS has changed */ - fpDiskImg->AnalyzeImageFS(); - - /* - * If requested (or necessary), verify the format. We only do this - * if we think the format has changed. This is possible, e.g. if - * somebody drops an MS-DOS volume into the first partition of a - * CFFA disk. - */ - if (oldFormat != fpDiskImg->GetFSFormat() && - (fpDiskImg->GetFSFormat() == DiskImg::kFormatUnknown || - fpDiskImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown || - pPreferences->GetPrefBool(kPrQueryImageFormat))) - { - // ignore them if they hit "cancel" - (void) pMain->TryDiskImgOverride(fpDiskImg, fPathName, - DiskImg::kFormatUnknown, NULL, true, &errMsg); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - return; - } - } - } - - /* - * Creating the "busy" window here is problematic, because we get called - * from OnInitDialog, at which point our window isn't yet established. - * Since we're modal, we disabled MainWindow, which means that when the - * "busy" box goes away there's no CiderPress window to take control. As - * a result we get a nasty flash. - * - * The only way around this is to defer the destruction of the modeless - * dialog until after we become visible. - */ - bool deferDestroy = false; - if (!IsWindowVisible() || !IsWindowEnabled()) { - LOGI(" Deferring destroy on wait dialog"); - deferDestroy = true; - } else { - LOGI(" Not deferring destroy on wait dialog"); - } - - fpWaitDlg = new ExclusiveModelessDialog; - fpWaitDlg->Create(IDD_LOADING, this); - fpWaitDlg->CenterWindow(pMain); - pMain->PeekAndPump(); - - CWaitCursor waitc; - - /* - * Create an appropriate DiskFS object. We only need to do this to get - * the sub-volume info, which is unfortunate since it can be slow. - */ - fpDiskFS = fpDiskImg->OpenAppropriateDiskFS(true); - if (fpDiskFS == NULL) { - LOGI("HEY: OpenAppropriateDiskFS failed!"); - /* this is fatal, but there's no easy way to die */ - /* (could we do a DestroyWindow from here?) */ - /* at any rate, with "allowUnknown" set, this shouldn't happen */ - } else { - fpDiskFS->SetScanForSubVolumes(DiskFS::kScanSubContainerOnly); - - dierr = fpDiskFS->Initialize(fpDiskImg, DiskFS::kInitFull); - if (dierr != kDIErrNone) { - CString appName, msg; - CheckedLoadString(&appName, IDS_MB_APP_NAME); - msg.Format(L"Warning: error during disk scan: %hs.", - DiskImgLib::DIStrError(dierr)); - fpWaitDlg->MessageBox(msg, appName, MB_OK | MB_ICONEXCLAMATION); - /* keep going */ - } - } - - if (!deferDestroy && fpWaitDlg != NULL) { - fpWaitDlg->DestroyWindow(); - fpWaitDlg = NULL; - } - - return; -} - -LONG VolumeCopyDialog::OnDialogReady(UINT, LONG) -{ - if (fpWaitDlg != NULL) { - LOGI("OnDialogReady found active window, destroying"); - fpWaitDlg->DestroyWindow(); - fpWaitDlg = NULL; - } - return 0; -} - -void VolumeCopyDialog::LoadList(void) -{ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUMECOPYSEL_LIST); - ASSERT(pListView != NULL); - int itemIndex = 0; - - CString unknown = "(unknown)"; - - pListView->DeleteAllItems(); - if (fpDiskFS == NULL) { - /* can only happen if imported volume is unrecognizeable */ - return; - } - - AddToList(pListView, fpDiskImg, fpDiskFS, &itemIndex); - - DiskImgLib::DiskFS::SubVolume* pSubVolume; - pSubVolume = fpDiskFS->GetNextSubVolume(NULL); - while (pSubVolume != NULL) { - if (pSubVolume->GetDiskFS() == NULL) { - LOGI("WARNING: sub-volume DiskFS is NULL?!"); - assert(false); - } else { - AddToList(pListView, pSubVolume->GetDiskImg(), - pSubVolume->GetDiskFS(), &itemIndex); - } - pSubVolume = fpDiskFS->GetNextSubVolume(pSubVolume); - } -} - -void VolumeCopyDialog::AddToList(CListCtrl* pListView, DiskImg* pDiskImg, - DiskFS* pDiskFS, int* pIndex) -{ - CString sizeStr, blocksStr; - - assert(pListView != NULL); - assert(pDiskImg != NULL); - assert(pDiskFS != NULL); - assert(pIndex != NULL); - - long numBlocks = pDiskImg->GetNumBlocks(); - - CString volName(Charset::ConvertMORToUNI(pDiskFS->GetVolumeName())); - CString format = DiskImg::ToString(pDiskImg->GetFSFormat()); - blocksStr.Format(L"%ld", pDiskImg->GetNumBlocks()); - if (numBlocks > 1024*1024*2) - sizeStr.Format(L"%.2fGB", (double) numBlocks / (1024.0*1024.0*2.0)); - else if (numBlocks > 1024*2) - sizeStr.Format(L"%.2fMB", (double) numBlocks / (1024.0*2.0)); - else - sizeStr.Format(L"%.2fKB", (double) numBlocks / 2.0); - - /* add entry; first entry is the whole volume */ - pListView->InsertItem(*pIndex, volName, - *pIndex == 0 ? kListIconVolume : kListIconSubVolume); - pListView->SetItemText(*pIndex, 1, format); - pListView->SetItemText(*pIndex, 2, sizeStr); - pListView->SetItemText(*pIndex, 3, blocksStr); - pListView->SetItemData(*pIndex, (DWORD) pDiskFS); - (*pIndex)++; -} - -bool VolumeCopyDialog::GetSelectedDisk(DiskImg** ppDiskImg, DiskFS** ppDiskFS) -{ - CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_VOLUMECOPYSEL_LIST); - ASSERT(pListView != NULL); - - ASSERT(ppDiskImg != NULL); - ASSERT(ppDiskFS != NULL); - - if (pListView->GetSelectedCount() != 1) - return false; - - POSITION posn; - posn = pListView->GetFirstSelectedItemPosition(); - if (posn == NULL) { - ASSERT(false); - return false; - } - int num = pListView->GetNextSelectedItem(posn); - DWORD data = pListView->GetItemData(num); - - *ppDiskFS = (DiskFS*) data; - assert(*ppDiskFS != NULL); - *ppDiskImg = (*ppDiskFS)->GetDiskImg(); - return true; -} - -void VolumeCopyDialog::OnCopyToFile(void) -{ - VolumeXferProgressDialog* pProgressDialog = NULL; - Preferences* pPreferences = GET_PREFERENCES_WR(); - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - DiskImg::FSFormat originalFormat = DiskImg::kFormatUnknown; - DiskImg* pSrcImg = NULL; - DiskFS* pSrcFS = NULL; - DiskImg dstImg; - DIError dierr; - CString errMsg, saveName, msg; - int result; - - result = GetSelectedDisk(&pSrcImg, &pSrcFS); - if (!result) - return; - assert(pSrcImg != NULL); - assert(pSrcFS != NULL); - - CString srcName(Charset::ConvertMORToUNI(pSrcFS->GetVolumeName())); - - /* force the format to be generic ProDOS-ordered blocks */ - originalFormat = pSrcImg->GetFSFormat(); - dierr = pSrcImg->OverrideFormat(pSrcImg->GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, pSrcImg->GetSectorOrder()); - if (dierr != kDIErrNone) { - errMsg.Format(L"Internal error: couldn't switch to generic ProDOS: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - LOGI("Logical volume '%ls' has %d 512-byte blocks", - (LPCWSTR) srcName, pSrcImg->GetNumBlocks()); - - /* - * Select file to write blocks to. - */ - { - CFileDialog saveDlg(FALSE, L"po", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN|OFN_HIDEREADONLY, - L"All Files (*.*)|*.*||", this); - - CString saveFolder; - - saveDlg.m_ofn.lpstrTitle = L"New disk image (.po)"; - saveDlg.m_ofn.lpstrInitialDir = - pPreferences->GetPrefString(kPrOpenArchiveFolder); - - if (saveDlg.DoModal() != IDOK) { - LOGI(" User bailed out of image save dialog"); - goto bail; - } - - saveFolder = saveDlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(saveDlg.m_ofn.nFileOffset); - pPreferences->SetPrefString(kPrOpenArchiveFolder, saveFolder); - - saveName = saveDlg.GetPathName(); - } - LOGI("File will be saved to '%ls'", (LPCWSTR) saveName); - - /* DiskImgLib does not like it if file already exists */ - errMsg = pMain->RemoveFile(saveName); - if (!errMsg.IsEmpty()) { - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* - * Create a block image with the expected number of blocks. - */ - int dstNumBlocks; - dstNumBlocks = pSrcImg->GetNumBlocks(); - - { - ExclusiveModelessDialog* pWaitDlg = new ExclusiveModelessDialog; - pWaitDlg->Create(IDD_FORMATTING, this); - pWaitDlg->CenterWindow(pMain); - pMain->PeekAndPump(); // redraw - CWaitCursor waitc; - - CStringA saveNameA(saveName); - dierr = dstImg.CreateImage(saveNameA, NULL, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - NULL, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - dstNumBlocks, - true /* don't need to erase contents */); - - pWaitDlg->DestroyWindow(); - //pMain->PeekAndPump(); // redraw - } - if (dierr != kDIErrNone) { - errMsg.Format(L"Couldn't create disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* initialize cancel dialog, and disable main window */ - pProgressDialog = new VolumeXferProgressDialog; - EnableWindow(FALSE); - if (pProgressDialog->Create(this) == FALSE) { - LOGI("Progress dialog init failed?!"); - ASSERT(false); - goto bail; - } - pProgressDialog->SetCurrentFiles(srcName, saveName); - - time_t startWhen, endWhen; - startWhen = time(NULL); - - /* - * Do the actual block copy. - */ - dierr = pMain->CopyDiskImage(&dstImg, pSrcImg, false, false, pProgressDialog); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) { - CheckedLoadString(&errMsg, IDS_OPERATION_CANCELLED); - ShowFailureMsg(pProgressDialog, errMsg, IDS_CANCELLED); - // remove the partially-written file - dstImg.CloseImage(); - (void) _wunlink(saveName); - } else { - errMsg.Format(L"Copy failed: %hs.", DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); - } - goto bail; - } - - dierr = dstImg.CloseImage(); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: dstImg close failed (err=%d)\n", dierr); - ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); - goto bail; - } - - /* put elapsed time in the debug log */ - endWhen = time(NULL); - float elapsed; - if (endWhen == startWhen) - elapsed = 1.0; - else - elapsed = (float) (endWhen - startWhen); - msg.Format(L"Copied %ld blocks in %ld seconds (%.2fKB/sec)", - pSrcImg->GetNumBlocks(), (long) (endWhen - startWhen), - (pSrcImg->GetNumBlocks() / 2.0) / elapsed); - LOGI("%ls", (LPCWSTR) msg); -#ifdef _DEBUG - pProgressDialog->MessageBox(msg, L"DEBUG: elapsed time", MB_OK); -#endif - - pMain->SuccessBeep(); - - -bail: - // restore the dialog window to prominence - EnableWindow(TRUE); - //SetActiveWindow(); - if (pProgressDialog != NULL) - pProgressDialog->DestroyWindow(); - - /* un-override the source disk */ - if (originalFormat != DiskImg::kFormatUnknown) { - dierr = pSrcImg->OverrideFormat(pSrcImg->GetPhysicalFormat(), - originalFormat, pSrcImg->GetSectorOrder()); - if (dierr != kDIErrNone) { - LOGI("ERROR: couldn't un-override source image (dierr=%d)", dierr); - // not much else to do; should be okay - } - } - return; -} - -void VolumeCopyDialog::OnCopyFromFile(void) -{ - VolumeXferProgressDialog* pProgressDialog = NULL; - Preferences* pPreferences = GET_PREFERENCES_WR(); - MainWindow* pMain = (MainWindow*)::AfxGetMainWnd(); - //DiskImg::FSFormat originalFormat = DiskImg::kFormatUnknown; - CString openFilters; - CString loadName, errMsg, warning; - DiskImg* pDstImg = NULL; - DiskFS* pDstFS = NULL; - DiskImg srcImg; - DIError dierr; - int result; - bool needReload = false; - bool isPartial = false; - - CheckedLoadString(&warning, IDS_WARNING); - - /* - * Get the DiskImg and DiskFS pointers for the selected partition out of - * the control. The storage for these is part of fpDiskFS, which holds - * the tree of subvolumes. - */ - result = GetSelectedDisk(&pDstImg, &pDstFS); - if (!result) - return; - - CString targetName(Charset::ConvertMORToUNI(pDstFS->GetVolumeName())); - - - /* - * Select the image to copy from. - */ - openFilters = MainWindow::kOpenDiskImage; - openFilters += MainWindow::kOpenAll; - openFilters += MainWindow::kOpenEnd; - CFileDialog dlg(TRUE, L"dsk", NULL, OFN_FILEMUSTEXIST, openFilters, this); - - /* source file gets opened read-only */ - dlg.m_ofn.Flags |= OFN_HIDEREADONLY; - dlg.m_ofn.lpstrTitle = L"Select image to copy from"; - dlg.m_ofn.lpstrInitialDir = pPreferences->GetPrefString(kPrOpenArchiveFolder); - - if (dlg.DoModal() != IDOK) - goto bail; - loadName = dlg.GetPathName(); - - { - CWaitCursor waitc; - CString saveFolder; - - /* open the image file and analyze it */ - CStringA loadNameA(loadName); - dierr = srcImg.OpenImage(loadNameA, PathProposal::kLocalFssep, true); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open disk image: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - if (srcImg.AnalyzeImage() != kDIErrNone) { - errMsg.Format(L"The file '%ls' doesn't seem to hold a valid disk image.", - (LPCWSTR) loadName); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - // save our folder choice in the preferences file - saveFolder = dlg.m_ofn.lpstrFile; - saveFolder = saveFolder.Left(dlg.m_ofn.nFileOffset); - pPreferences->SetPrefString(kPrOpenArchiveFolder, saveFolder); - } - - /* - * Require that the input be block-addressable. This isn't really the - * right test, because it's conceivable that somebody would want to put - * a nibble image onto a disk volume. I can't think of a good reason - * to do this -- you can't just splat a fixed-track-length .NIB file - * onto a 5.25" disk, assuming you could get the drive to work on a PC - * in the first place -- so I'm going to take the simple way out. The - * right test is to verify that the EOF on the input is the same as the - * EOF on the output. - */ - if (!srcImg.GetHasBlocks()) { - errMsg = L"The disk image must be block-oriented. Nibble images" - L" cannot be copied."; - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* force source volume to generic ProDOS blocks */ - dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); - if (dierr != kDIErrNone) { - errMsg.Format(L"Internal error: couldn't switch source to generic ProDOS: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - LOGI("Source image '%ls' has %d 512-byte blocks", - (LPCWSTR) loadName, srcImg.GetNumBlocks()); - - LOGI("Target volume has %d 512-byte blocks", pDstImg->GetNumBlocks()); - - if (srcImg.GetNumBlocks() > pDstImg->GetNumBlocks()) { - errMsg.Format(L"Error: the disk image file has %ld blocks, but the" - L" target volume holds %ld blocks. The target must" - L" have more space than the input file.", - srcImg.GetNumBlocks(), pDstImg->GetNumBlocks()); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - if (pDstImg->GetNumBlocks() >= DiskImgLib::kVolumeMaxBlocks) { - // TODO: re-evaluate this limitation - errMsg.Format(L"Error: for safety reasons, copying disk images to" - L" larger volumes is not supported when the target" - L" is 8GB or larger."); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - if (srcImg.GetNumBlocks() != pDstImg->GetNumBlocks()) { - //errMsg.LoadString(IDS_WARNING); - errMsg.Format(L"The disk image file has %ld blocks, but the target" - L" volume holds %ld blocks. The leftover space may be" - L" wasted, and non-ProDOS volumes may not be identified" - L" correctly. Do you wish to continue?", - srcImg.GetNumBlocks(), pDstImg->GetNumBlocks()); - result = MessageBox(errMsg, warning, MB_OKCANCEL | MB_ICONQUESTION); - if (result != IDOK) { - LOGI("User chickened out of oversized disk copy"); - goto bail; - } - isPartial = true; - } - - //errMsg.LoadString(IDS_WARNING); - errMsg.Format(L"You are about to overwrite volume %ls with the" - L" contents of '%ls'. This will destroy all data on" - L" %ls. Are you sure you wish to continue?", - (LPCWSTR) targetName, (LPCWSTR) loadName, (LPCWSTR) targetName); - result = MessageBox(errMsg, warning, MB_OKCANCEL | MB_ICONEXCLAMATION); - if (result != IDOK) { - LOGI("User chickened out of disk copy"); - goto bail; - } - - /* force the target disk image to be generic ProDOS-ordered blocks */ - dierr = pDstImg->OverrideFormat(pDstImg->GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, pDstImg->GetSectorOrder()); - if (dierr != kDIErrNone) { - errMsg.Format(L"Internal error: couldn't switch target to generic ProDOS: %hs.", - DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - - /* from here on out, before we exit we must re-analyze this volume */ - needReload = true; - - // redraw main to erase previous dialog - pMain->PeekAndPump(); - - /* initialize cancel dialog, and disable dialog */ - pProgressDialog = new VolumeXferProgressDialog; - EnableWindow(FALSE); - if (pProgressDialog->Create(this) == FALSE) { - LOGI("Progress dialog init failed?!"); - ASSERT(false); - return; - } -// if (pDstFS == NULL) -// pProgressDialog->SetCurrentFiles(loadName, "target"); -// else - pProgressDialog->SetCurrentFiles(loadName, targetName); - - /* - * We want to delete fpDiskFS now, but we can't because it's holding the - * storage for the DiskImg/DiskFS pointers in the subvolume list. We - * flush it to ensure that it won't try to write to the disk after the - * copy completes. - */ - fpDiskFS->Flush(DiskImg::kFlushAll); - - time_t startWhen, endWhen; - startWhen = time(NULL); - - /* - * Do the actual block copy. - */ - dierr = pMain->CopyDiskImage(pDstImg, &srcImg, false, isPartial, - pProgressDialog); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) { - CheckedLoadString(&errMsg, IDS_OPERATION_CANCELLED); - ShowFailureMsg(pProgressDialog, errMsg, IDS_CANCELLED); - } else { - errMsg.Format(L"Copy failed: %hs.", DiskImgLib::DIStrError(dierr)); - ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); - } - goto bail; - } - - dierr = srcImg.CloseImage(); - if (dierr != kDIErrNone) { - errMsg.Format(L"ERROR: srcImg close failed (err=%d)\n", dierr); - ShowFailureMsg(pProgressDialog, errMsg, IDS_FAILED); - goto bail; - } - - endWhen = time(NULL); - float elapsed; - if (endWhen == startWhen) - elapsed = 1.0; - else - elapsed = (float) (endWhen - startWhen); - errMsg.Format(L"Copied %ld blocks in %ld seconds (%.2fKB/sec)", - srcImg.GetNumBlocks(), (long) (endWhen - startWhen), - (srcImg.GetNumBlocks() / 2.0) / elapsed); - LOGI("%ls", (LPCWSTR) errMsg); -#ifdef _DEBUG - pProgressDialog->MessageBox(errMsg, L"DEBUG: elapsed time", MB_OK); -#endif - - pMain->SuccessBeep(); - - /* - * If a DiskFS insists on privately caching stuff (e.g. libhfs), we could - * end up corrupting the image we just wrote. We use SetAllReadOnly() to - * ensure that nothing will be written before we delete the DiskFS. - */ - assert(!fpDiskImg->GetReadOnly()); - fpDiskFS->SetAllReadOnly(true); - delete fpDiskFS; - fpDiskFS = NULL; - assert(fpDiskImg->GetReadOnly()); - fpDiskImg->SetReadOnly(false); - -bail: - // restore the dialog window to prominence - EnableWindow(TRUE); - //SetActiveWindow(); - if (pProgressDialog != NULL) - pProgressDialog->DestroyWindow(); - - /* - * Force a reload. We need to reload the disk information, then reload - * the list contents. - * - * By design, anything that would require un-overriding the format of - * the target DiskImg requires reloading it completely. Sort of heavy- - * handed, but it's reliable. - */ - if (needReload) { - LOGI("RELOAD dialog"); - ScanDiskInfo(true); // reopens fpDiskFS - LoadList(); - - /* will we need to reopen the currently-open file list archive? */ - if (pMain->IsOpenPathName(fPathName)) - pMain->SetReopenFlag(); - } - return; -} diff --git a/ciderpress/app/VolumeCopyDialog.h b/ciderpress/app/VolumeCopyDialog.h deleted file mode 100644 index 8734071..0000000 --- a/ciderpress/app/VolumeCopyDialog.h +++ /dev/null @@ -1,122 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Dialog that allows copying volumes or sub-volumes to and from files on - * disk. Handy for backing up and restoring floppy disks and CFFA partitions. - */ -#ifndef APP_VOLUMECOPYDIALOG_H -#define APP_VOLUMECOPYDIALOG_H - -#include -#include "../diskimg/DiskImg.h" -#include "resource.h" - -/* - * A dialog with a list control that we populate with the names of the - * volumes in the system. - */ -class VolumeCopyDialog : public CDialog { -public: - VolumeCopyDialog(CWnd* pParentWnd = NULL) : - CDialog(IDD_VOLUMECOPYSEL, pParentWnd), - fpDiskImg(NULL), - fpDiskFS(NULL), - fpWaitDlg(NULL) - {} - ~VolumeCopyDialog(void) { assert(fpDiskFS == NULL); } - - /* disk image to work with; we don't own it */ - DiskImgLib::DiskImg* fpDiskImg; - /* path name of input disk image or volume; mainly for display */ - CString fPathName; - -protected: - virtual BOOL OnInitDialog(void) override; - virtual void OnOK(void) override; - virtual void OnCancel(void) override; - - void Cleanup(void); - - enum { WMU_DIALOG_READY = WM_USER+2 }; - - /* - * Something changed in the list. Update the buttons. - */ - afx_msg void OnListChange(NMHDR* pNotifyStruct, LRESULT* pResult); - - /* - * User pressed the "copy to file" button. Copy the selected partition out to - * a file on disk. - */ - afx_msg void OnCopyToFile(void); - - /* - * User pressed the "copy from file" button. Copy a file over the selected - * partition. We may need to reload the main window after this completes. - */ - afx_msg void OnCopyFromFile(void); - - afx_msg void OnHelp(void) { - MyApp::HandleHelp(this, HELP_TOPIC_VOLUME_COPIER); - } - - /* - * When the focus changes, e.g. after dialog construction completes, see if - * we have a modeless dialog lurking about. - */ - afx_msg LONG OnDialogReady(UINT, LONG); - - /* - * (Re-)scan the disk image and any sub-volumes. - */ - void ScanDiskInfo(bool scanTop); - - /* - * (Re-)load the volume and sub-volumes into the list. - * - * We currently only look at the first level of sub-volumes. We're not - * really set up to display a hierarchy in the list view. Very few people - * will ever need to access a sub-sub-volume in this way, so it's not - * worth sorting it out. - */ - void LoadList(void); - - /* - * Create an entry for a diskimg/diskfs pair. - */ - void AddToList(CListCtrl* pListView, DiskImgLib::DiskImg* pDiskImg, - DiskImgLib::DiskFS* pDiskFS, int* pIndex); - - /* - * Recover the DiskImg and DiskFS pointers for the volume or sub-volume - * currently selected in the list. - * - * Returns "true" on success, "false" on failure. - */ - bool GetSelectedDisk(DiskImgLib::DiskImg** ppDstImg, - DiskImgLib::DiskFS** ppDiskFS); - - // Load images to be used in the list. Apparently this must be called - // before we try to load any header images. - void LoadListImages(void) { - if (!fListImageList.Create(IDB_VOL_PICS, 16, 1, CLR_DEFAULT)) - LOGI("GLITCH: list image create failed"); - fListImageList.SetBkColor(::GetSysColor(COLOR_WINDOW)); - } - enum { // defs for IDB_VOL_PICS - kListIconVolume = 0, - kListIconSubVolume = 1, - }; - CImageList fListImageList; - - DiskImgLib::DiskFS* fpDiskFS; - - ModelessDialog* fpWaitDlg; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*APP_VOLUMECOPYDIALOG_H*/ diff --git a/ciderpress/app/app.vcxproj b/ciderpress/app/app.vcxproj deleted file mode 100644 index bb54ec3..0000000 --- a/ciderpress/app/app.vcxproj +++ /dev/null @@ -1,324 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - MFCProj - {B023611B-7086-46E1-847B-3B21C4732384} - 8.1 - - - - Application - v120_xp - Dynamic - Unicode - - - Application - v120_xp - Dynamic - Unicode - - - - - - - - - - - - - <_ProjectFileVersion>12.0.30501.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - CiderPress - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - CiderPress - - - - MaxSpeed - OnlyExplicitInline - WIN32;NDEBUGX;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - MultiThreadedDLL - true - - stdafx.h - $(IntDir)$(TargetName).pch - $(IntDir) - $(IntDir) - $(IntDir)vc$(PlatformToolsetVersion).pdb - true - Level3 - true - true - false - - - $(OutDir)$(TargetName)$(TargetExt) - true - $(OutDir)$(TargetName).pdb - - - Windows - MachineX86 - - htmlhelp.lib - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/app.tlb - - - - Copy HtmlHelp file - postbuild.bat "$(ProjectDir)" "$(OutDir)" - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - - stdafx.h - $(IntDir)$(TargetName).pch - $(IntDir) - $(IntDir) - $(IntDir)vc$(PlatformToolsetVersion).pdb - true - Level3 - true - EditAndContinue - true - false - - - $(OutDir)$(TargetName)$(TargetExt) - true - true - $(OutDir)$(TargetName).pdb - Windows - MachineX86 - - htmlhelp.lib - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/app.tlb - - - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - postbuild.bat "$(ProjectDir)" "$(OutDir)" - - - Copy HtmlHelp file - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {0cfe6fad-0126-4e99-8625-c807d1d2aaf4} - false - - - {c48ae53b-3dcb-43b1-9207-b7c5b6bb78af} - - - {18bcf397-397e-460c-a1dc-3e26798966e4} - false - - - {04bfae2a-7ab3-4b63-b4ab-42ff1d6ad3c5} - false - - - {b66109f4-217b-43c0-86aa-eb55657e5ac0} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - Create - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/ciderpress/app/app.vcxproj.filters b/ciderpress/app/app.vcxproj.filters deleted file mode 100644 index 7617344..0000000 --- a/ciderpress/app/app.vcxproj.filters +++ /dev/null @@ -1,415 +0,0 @@ - - - - - {ce11a4a3-b5b9-4a09-ab8b-9c92c728f3f5} - cpp;c;cxx;rc;def;r;odl;idl;hpj;bat - - - {ca3f0358-99e0-4dc7-b460-b7b929dec101} - h;hpp;hxx;hm;inl - - - {7687a6e0-1b1c-4442-b2da-c49022f435ea} - ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe - - - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/ciderpress/app/app.vcxproj.user b/ciderpress/app/app.vcxproj.user deleted file mode 100644 index ef5ff2a..0000000 --- a/ciderpress/app/app.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ciderpress/app/postbuild.bat b/ciderpress/app/postbuild.bat deleted file mode 100755 index 05172c8..0000000 --- a/ciderpress/app/postbuild.bat +++ /dev/null @@ -1,3 +0,0 @@ -@echo off -copy %1\help\CiderPress.chm %2 -copy %1\..\dist\nlist.data.txt %2 diff --git a/ciderpress/app/resource.h b/ciderpress/app/resource.h deleted file mode 100644 index 952ac54..0000000 --- a/ciderpress/app/resource.h +++ /dev/null @@ -1,568 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by CiderPress.rc -// -#define IDR_MAINFRAME 102 -#define IDD_ABOUTDLG 105 -#define IDB_FSLOGO 106 -#define IDR_TOOLBAR1 107 -#define IDI_FILE_NUFX 108 -#define IDI_FILE_BINARY2 109 -#define IDI_FILE_DISKIMAGE 110 -#define IDB_HDRBAR 110 -#define IDD_PREF_GENERAL 115 -#define IDD_PREF_COMPRESSION 116 -#define IDR_RIGHTCLICKMENU 117 -#define IDD_FILE_VIEWER 118 -#define IDI_FILE_VIEWER 120 -#define IDD_PREF_FVIEW 121 -#define IDB_LIST_PICS 123 -#define IDD_DISKEDIT 127 -#define IDD_DECONF 128 -#define IDD_SUBV 131 -#define IDD_DEFILE 132 -#define IDD_PREF_FILES 133 -#define IDB_NEW_FOLDER 139 -#define IDB_CHOOSE_FOLDER 140 -#define IDD_EXTRACT_FILES 142 -#define IDD_ACTION_PROGRESS 143 -#define IDD_CONFIRM_OVERWRITE 144 -#define IDD_RENAME_OVERWRITE 145 -#define IDD_USE_SELECTION 147 -#define IDD_RENAME_ENTRY 148 -#define IDD_COMMENT_EDIT 149 -#define IDD_RECOMPRESS_OPTS 150 -#define IDD_PRINT_CANCEL 152 -#define IDD_ASSOCIATIONS 153 -#define IDD_REGISTRATION 157 -#define IDD_DISKCONV 158 -#define IDD_DONEOPEN 159 -#define IDD_PROPS_EDIT 160 -#define IDD_CONVFILE_OPTS 161 -#define IDD_CONVDISK_OPTS 162 -#define IDD_BULKCONV 163 -#define IDD_OPENVOLUMEDLG 164 -#define IDD_VOLUMECOPYPROG 166 -#define IDD_DISKEDIT_OPENWHICH 167 -#define IDD_VOLUMECOPYSEL 169 -#define IDB_VOL_PICS 170 -#define IDD_FORMATTING 171 -#define IDD_CREATEIMAGE 172 -#define IDD_CHOOSE_ADD_TARGET 173 -#define IDB_TREE_PICS 174 -#define IDD_ARCHIVEINFO_NUFX 175 -#define IDD_ARCHIVEINFO_DISK 176 -#define IDD_ARCHIVEINFO_BNY 177 -#define IDD_PREF_DISKIMAGE 178 -#define IDD_CREATE_SUBDIR 179 -#define IDD_RENAME_VOLUME 180 -#define IDD_EOLSCAN 181 -#define IDD_TWOIMG_PROPS 182 -#define IDD_LOADING 183 -#define IDD_IMPORTCASSETTE 184 -#define IDD_CASSIMPTARGET 186 -#define IDD_ADD_CLASH 187 -#define IDD_ARCHIVEINFO_ACU 188 -#define IDD_IMPORT_BAS 189 -#define IDD_PASTE_SPECIAL 190 -#define IDD_PROGRESS_COUNTER 191 -#define IDD_ARCHIVEINFO_APPLESINGLE 192 -#define IDC_NUFXLIB_VERS_TEXT 1001 -#define IDC_CONTENT_LIST 1002 -#define IDC_COL_PATHNAME 1005 -#define IDC_COL_TYPE 1006 -#define IDC_COL_AUXTYPE 1007 -#define IDC_COL_MODDATE 1008 -#define IDC_COL_FORMAT 1009 -#define IDC_COL_SIZE 1010 -#define IDC_COL_RATIO 1011 -#define IDC_COL_PACKED 1012 -#define IDC_COL_ACCESS 1013 -#define IDC_COL_DEFAULTS 1014 -#define IDC_DEFC_UNCOMPRESSED 1016 -#define IDC_DEFC_SQUEEZE 1017 -#define IDC_DEFC_LZW1 1018 -#define IDC_DEFC_LZW2 1019 -#define IDC_DEFC_LZC12 1020 -#define IDC_DEFC_LZC16 1021 -#define IDC_DEFC_DEFLATE 1022 -#define IDC_DEFC_BZIP2 1023 -#define IDC_PVIEW_NOWRAP_TEXT 1025 -#define IDC_PVIEW_BOLD_HEXDUMP 1026 -#define IDC_PVIEW_BOLD_BASIC 1027 -#define IDC_PVIEW_DISASM_ONEBYTEBRKCOP 1028 -#define IDC_PVIEW_HIRES_BW 1029 -#define IDC_PVIEW_DHR_CONV_COMBO 1030 -#define IDC_PVIEW_MOUSETEXT_TO_ASCII 1031 -#define IDC_PVIEW_HITEXT 1036 -#define IDC_PVIEW_PASCALTEXT 1037 -#define IDC_PVIEW_APPLESOFT 1038 -#define IDC_PVIEW_INTEGER 1039 -#define IDC_PVIEW_HIRES 1040 -#define IDC_PVIEW_DHR 1041 -#define IDC_PVIEW_SHR 1042 -#define IDC_PVIEW_AWP 1043 -#define IDC_PVIEW_PRODOSFOLDER 1044 -#define IDC_PVIEW_RESOURCES 1045 -#define IDC_PVIEW_RELAX_GFX 1046 -#define IDC_PVIEW_ADB 1047 -#define IDC_PVIEW_SCASSEM 1048 -#define IDC_PVIEW_ASP 1049 -#define IDC_PVIEW_MACPAINT 1050 -#define IDC_PVIEW_PASCALCODE 1051 -#define IDC_PVIEW_CPMTEXT 1052 -#define IDC_PVIEW_GWP 1053 -#define IDC_PVIEW_DISASM 1054 -#define IDC_PVIEW_PRINTSHOP 1055 -#define IDC_PVIEW_TEXT8 1056 -#define IDC_PVIEW_SIZE_EDIT 1060 -#define IDC_PVIEW_SIZE_SPIN 1061 -#define IDC_DISKEDIT_DOREAD 1063 -#define IDC_DISKEDIT_DOWRITE 1064 -#define IDC_DISKEDIT_TRACK 1065 -#define IDC_DISKEDIT_TRACKSPIN 1066 -#define IDC_DISKEDIT_SECTOR 1067 -#define IDC_DISKEDIT_SECTORSPIN 1068 -#define IDC_DISKEDIT_OPENFILE 1069 -#define IDC_DISKEDIT_EDIT 1070 -#define IDC_DISKEDIT_PREV 1071 -#define IDC_DISKEDIT_NEXT 1072 -#define IDC_STEXT_SECTOR 1073 -#define IDC_STEXT_TRACK 1074 -#define IDC_DISKEDIT_DONE 1077 -#define IDC_DISKEDIT_HEX 1078 -#define IDC_DISKEDIT_SUBVOLUME 1081 -#define IDC_DECONF_FSFORMAT 1090 -#define IDC_DECONF_SECTORORDER 1091 -#define IDC_DECONF_PHYSICAL 1092 -#define IDC_DECONF_FILEFORMAT 1093 -#define IDC_DECONF_SOURCE 1094 -#define IDC_DISKIMG_VERS_TEXT 1095 -#define IDC_FVIEW_EDITBOX 1101 -#define IDC_SELECTED_COUNT 1102 -#define IDC_ABOUT_CREDITS 1111 -#define IDC_DECONF_HELP 1112 -#define IDC_SUBV_LIST 1114 -#define IDC_DEFILE_FILENAME 1115 -#define IDC_DEFILE_RSRC 1116 -#define IDC_CIDERPRESS_VERS_TEXT 1117 -#define IDC_PREF_TEMP_FOLDER 1118 -#define IDC_PREF_CHOOSE_TEMP_FOLDER 1128 -#define IDC_FVIEW_FONT 1129 -#define IDC_FVIEW_NEXT 1130 -#define IDC_FVIEW_PREV 1131 -#define IDC_EXT_PATH 1136 -#define IDC_EXT_CONVEOLTEXT 1137 -#define IDC_EXT_CONVEOLALL 1138 -#define IDC_EXT_STRIP_FOLDER 1142 -#define IDC_EXT_OVERWRITE_EXIST 1143 -#define IDC_EXT_SELECTED 1144 -#define IDC_EXT_ALL 1145 -#define IDC_EXT_REFORMAT 1147 -#define IDC_EXT_DATAFORK 1148 -#define IDC_EXT_RSRCFORK 1149 -#define IDC_EXT_CONVEOLNONE 1151 -#define IDC_EXT_CHOOSE_FOLDER 1152 -#define IDC_PROG_ARC_NAME 1153 -#define IDC_PROG_FILE_NAME 1154 -#define IDC_PROG_VERB 1155 -#define IDC_PROG_TOFROM 1156 -#define IDC_PROG_PROGRESS 1157 -#define IDC_OVWR_YES 1161 -#define IDC_OVWR_YESALL 1162 -#define IDC_OVWR_NO 1163 -#define IDC_OVWR_NOALL 1164 -#define IDC_OVWR_NEW_INFO 1166 -#define IDC_OVWR_RENAME 1167 -#define IDC_OVWR_EXIST_NAME 1168 -#define IDC_OVWR_EXIST_INFO 1169 -#define IDC_OVWR_NEW_NAME 1170 -#define IDC_RENOVWR_SOURCE_NAME 1171 -#define IDC_RENOVWR_ORIG_NAME 1172 -#define IDC_RENOVWR_NEW_NAME 1173 -#define IDC_SELECT_ACCEPT 1175 -#define IDC_ADDFILES_PREFIX 1177 -#define IDC_ADDFILES_INCLUDE_SUBFOLDERS 1180 -#define IDC_ADDFILES_STRIP_FOLDER 1181 -#define IDC_ADDFILES_NOPRESERVE 1182 -#define IDC_ADDFILES_PRESERVE 1183 -#define IDC_ADDFILES_PRESERVEPLUS 1184 -#define IDC_ADDFILES_STATIC1 1186 -#define IDC_ADDFILES_STATIC2 1187 -#define IDC_ADDFILES_STATIC3 1188 -#define IDC_ADDFILES_OVERWRITE 1189 -#define IDC_PREF_SHRINKIT_COMPAT 1190 -#define IDC_USE_SELECTED 1192 -#define IDC_USE_ALL 1193 -#define IDC_RENAME_OLD 1194 -#define IDC_RENAME_NEW 1195 -#define IDC_RENAME_PATHSEP 1196 -#define IDC_COMMENT_EDIT 1198 -#define IDC_COMMENT_DELETE 1199 -#define IDC_RECOMP_COMP 1201 -#define IDC_PREF_ASSOCIATIONS 1202 -#define IDC_ASSOCIATION_LIST 1209 -#define IDC_REG_COMPANY_NAME 1210 -#define IDC_REG_EXPIRES 1211 -#define IDC_ABOUT_ENTER_REG 1212 -#define IDC_REGENTER_USER 1213 -#define IDC_REGENTER_COMPANY 1214 -#define IDC_REGENTER_REG 1215 -#define IDC_REG_USER_NAME 1216 -#define IDC_ZLIB_VERS_TEXT 1218 -#define IDC_EXT_CONVHIGHASCII 1219 -#define IDC_EXT_DISKIMAGE 1220 -#define IDC_EXT_DISK_2MG 1221 -#define IDC_EXT_ADD_PRESERVE 1222 -#define IDC_EXT_ADD_EXTEN 1223 -#define IDC_EXT_CONFIG_PRESERVE 1224 -#define IDC_EXT_CONFIG_CONVERT 1225 -#define IDC_PREF_COERCE_DOS 1226 -#define IDC_PREF_SPACES_TO_UNDER 1227 -#define IDC_REGENTER_USERCRC 1228 -#define IDC_REGENTER_COMPCRC 1229 -#define IDC_REGENTER_REGCRC 1230 -#define IDC_RENAME_SKIP 1232 -#define IDC_DECONF_VIEWASBLOCKS 1233 -#define IDC_DECONF_VIEWASSECTORS 1234 -#define IDC_DECONF_VIEWASNIBBLES 1235 -#define IDC_DECONF_OUTERFORMAT 1236 -#define IDC_DECONF_VIEWAS 1237 -#define IDC_IMAGE_TYPE 1238 -#define IDC_DISKCONV_DOS 1239 -#define IDC_DISKCONV_DOS2MG 1240 -#define IDC_DISKCONV_PRODOS 1241 -#define IDC_DISKCONV_PRODOS2MG 1242 -#define IDC_DISKCONV_NIB 1243 -#define IDC_DISKCONV_NIB2MG 1244 -#define IDC_DISKCONV_D13 1245 -#define IDC_DISKCONV_DC42 1246 -#define IDC_DISKCONV_SDK 1247 -#define IDC_DISKCONV_TRACKSTAR 1248 -#define IDC_DISKCONV_HDV 1249 -#define IDC_DISKCONV_DDD 1250 -#define IDC_DISKCONV_GZIP 1251 -#define IDC_DISKEDIT_NIBBLE_PARMS 1252 -#define IDC_PROPS_PATHNAME 1255 -#define IDC_PROPS_FILETYPE 1256 -#define IDC_PROPS_AUXTYPE 1257 -#define IDC_PROPS_ACCESS_R 1258 -#define IDC_PROPS_ACCESS_W 1259 -#define IDC_PROPS_ACCESS_N 1260 -#define IDC_PROPS_ACCESS_D 1261 -#define IDC_PROPS_ACCESS_I 1262 -#define IDC_PROPS_ACCESS_B 1263 -#define IDC_PROPS_MODWHEN 1266 -#define IDC_PROPS_TYPEDESCR 1267 -#define IDC_CONVFILE_PRESERVEDIR 1270 -#define IDC_CONVDISK_140K 1271 -#define IDC_CONVDISK_800K 1273 -#define IDC_CONVDISK_1440K 1274 -#define IDC_CONVDISK_5MB 1275 -#define IDC_CONVDISK_16MB 1276 -#define IDC_CONVDISK_20MB 1277 -#define IDC_CONVDISK_32MB 1278 -#define IDC_CONVDISK_SPECIFY 1279 -#define IDC_IMAGE_SIZE_TEXT 1289 -#define IDC_BULKCONV_PATHNAME 1290 -#define IDC_PREF_EXTVIEWER_EXTS 1292 -#define IDC_VOLUME_LIST 1295 -#define IDC_OPENVOL_READONLY 1296 -#define IDC_VOLUMECOPYPROG_FROM 1297 -#define IDC_VOLUMECOPYPROG_TO 1298 -#define IDC_VOLUMECOPYPROG_PROGRESS 1299 -#define IDC_CONVDISK_SPECIFY_EDIT 1302 -#define IDC_CONVDISK_COMPUTE 1303 -#define IDC_DEOW_FILE 1303 -#define IDC_CONVDISK_SPACEREQ 1304 -#define IDC_DEOW_VOLUME 1304 -#define IDC_DEOW_CURRENT 1305 -#define IDC_CONVDISK_VOLNAME 1307 -#define IDC_VOLUME_FILTER 1307 -#define IDC_VOLUMECOPYSEL_LIST 1309 -#define IDC_VOLUEMCOPYSEL_TOFILE 1310 -#define IDC_VOLUEMCOPYSEL_FROMFILE 1311 -#define IDC_CREATEFS_DOS32 1312 -#define IDC_CREATEFS_DOS33 1313 -#define IDC_CREATEFS_PRODOS 1314 -#define IDC_CREATEFS_PASCAL 1315 -#define IDC_CREATEFS_HFS 1316 -#define IDC_CREATEFS_BLANK 1317 -#define IDC_CREATEFSDOS_ALLOCDOS 1321 -#define IDC_CREATEFSDOS_VOLNUM 1322 -#define IDC_CREATEFSPRODOS_VOLNAME 1323 -#define IDC_CREATEFSPASCAL_VOLNAME 1324 -#define IDC_ASPI_VERS_TEXT 1330 -#define IDC_PREF_SUCCESS_BEEP 1331 -#define IDC_ADD_TARGET_TREE 1333 -#define IDC_AIDISK_SUBVOLSEL 1334 -#define IDC_AIDISK_NOTES 1335 -#define IDC_AI_FILENAME 1336 -#define IDC_AIBNY_RECORDS 1337 -#define IDC_AINUFX_FORMAT 1338 -#define IDC_AINUFX_RECORDS 1339 -#define IDC_AINUFX_MASTERVERSION 1340 -#define IDC_AINUFX_CREATEWHEN 1341 -#define IDC_AINUFX_MODIFYWHEN 1342 -#define IDC_AINUFX_JUNKSKIPPED 1343 -#define IDC_AIDISK_OUTERFORMAT 1344 -#define IDC_AIDISK_FILEFORMAT 1345 -#define IDC_AIDISK_PHYSICALFORMAT 1346 -#define IDC_AIDISK_SECTORORDER 1347 -#define IDC_AIDISK_FSFORMAT 1348 -#define IDC_AIDISK_FILECOUNT 1349 -#define IDC_AIDISK_CAPACITY 1350 -#define IDC_AIDISK_FREESPACE 1351 -#define IDC_AIDISK_DAMAGED 1352 -#define IDC_AIDISK_WRITEABLE 1354 -#define IDC_PDISK_CONFIRM_FORMAT 1359 -#define IDC_PDISK_PRODOS_ALLOWLOWER 1360 -#define IDC_PDISK_PRODOS_USESPARSE 1361 -#define IDC_FVIEW_PRINT 1363 -#define IDC_CREATESUBDIR_BASE 1364 -#define IDC_CREATESUBDIR_NEW 1365 -#define IDC_RENAMEVOL_TREE 1366 -#define IDC_RENAMEVOL_NEW 1367 -#define IDC_ADDFILES_CONVEOLNONE 1368 -#define IDC_ADDFILES_CONVEOLTEXT 1369 -#define IDC_ADDFILES_CONVEOLALL 1370 -#define IDC_ADDFILES_STATIC4 1371 -#define IDC_PROPS_CREATEWHEN 1372 -#define IDC_EOLSCAN_CR 1374 -#define IDC_EOLSCAN_LF 1375 -#define IDC_EOLSCAN_CRLF 1376 -#define IDC_EOLSCAN_CHARS 1377 -#define IDC_PREF_PASTE_JUNKPATHS 1378 -#define IDC_EXT_CONVEOLTYPE 1379 -#define IDC_ADDFILES_CONVEOLTYPE 1380 -#define IDC_TWOIMG_LOCKED 1381 -#define IDC_TWOIMG_DOSVOLSET 1382 -#define IDC_TWOIMG_DOSVOLNUM 1383 -#define IDC_TWOIMG_COMMENT 1384 -#define IDC_TWOIMG_CREATOR 1385 -#define IDC_TWOIMG_VERSION 1386 -#define IDC_TWOIMG_FORMAT 1387 -#define IDC_TWOIMG_BLOCKS 1388 -#define IDC_FVIEW_DATA 1395 -#define IDC_FVIEW_RSRC 1396 -#define IDC_FVIEW_CMMT 1397 -#define IDC_FVIEW_FORMATSEL 1399 -#define IDC_FVIEW_FMT_HEX 1400 -#define IDC_FVIEW_FMT_RAW 1401 -#define IDC_FVIEW_FMT_BEST 1403 -#define IDC_PDISK_OPENVOL_RO 1405 -#define IDC_EOLSCAN_HIGHASCII 1407 -#define IDC_CASSETTE_LIST 1414 -#define IDC_IMPORT_CHUNK 1416 -#define IDC_CASSETTE_ALG 1418 -#define IDC_CASSETTE_INPUT 1419 -#define IDC_CASSIMPTARG_FILENAME 1420 -#define IDC_CASSIMPTARG_BAS 1421 -#define IDC_CASSIMPTARG_INT 1422 -#define IDC_CASSIMPTARG_BIN 1423 -#define IDC_CASSIMPTARG_BINADDR 1424 -#define IDC_CASSIMPTARG_RANGE 1425 -#define IDC_CLASH_RENAME 1426 -#define IDC_CLASH_SKIP 1427 -#define IDC_CLASH_WINNAME 1428 -#define IDC_CLASH_STORAGENAME 1429 -#define IDC_PREF_REDUCE_SHK_ERROR_CHECKS 1430 -#define IDC_IMPORT_BAS_RESULTS 1431 -#define IDC_IMPORT_BAS_SAVEAS 1432 -#define IDC_FVIEW_FIND 1438 -#define IDC_CREATEFSHFS_VOLNAME 1440 -#define IDC_PROPS_HFS_FILETYPE 1441 -#define IDC_PROPS_HFS_AUXTYPE 1442 -#define IDC_PROPS_HFS_MODE 1444 -#define IDC_PROPS_HFS_LABEL 1445 -#define IDC_PASTE_SPECIAL_COUNT 1446 -#define IDC_PASTE_SPECIAL_PATHS 1447 -#define IDC_PASTE_SPECIAL_NOPATHS 1448 -#define IDC_PROGRESS_COUNTER_COUNT 1449 -#define IDC_PROGRESS_COUNTER_DESC 1450 -#define IDC_PDISK_OPENVOL_PHYS0 1451 -#define IDC_PREF_SHK_BAD_MAC 1453 -#define IDS_READONLY 2021 -#define IDS_FAILED 2023 -#define IDS_ERROR 2024 -#define IDS_NOT_ALLOWED 2025 -#define IDS_WARNING 2026 -#define IDS_CANCELLED 2027 -#define IDS_SELECTED_COUNT 2041 -#define IDS_SELECTED_COUNTS_FMT 2042 -#define IDS_BLOCK 2045 -#define IDS_DEFILE_FIND_FAILED 2047 -#define IDS_DEFILE_OPEN_FAILED 2049 -#define IDS_DISKEDIT_NOREADTS 2057 -#define IDS_DISKEDIT_NOREADBLOCK 2058 -#define IDS_DISKEDIT_FIRDFAILED 2059 -#define IDS_EXT_SELECTED_COUNT 2060 -#define IDS_EXT_SELECTED_COUNTS_FMT 2061 -#define IDS_INDIC_RSRC 2062 -#define IDS_INDIC_DISK 2063 -#define IDS_INDIC_COMMENT 2064 -#define IDS_INDIC_DATA 2065 -#define IDS_NOW_ADDING 2066 -#define IDS_ADDING_AS 2067 -#define IDS_DEL_SELECTED_COUNTS_FMT 2068 -#define IDS_DEL_SELECTED_COUNT 2069 -#define IDS_DEL_ALL_FILES 2070 -#define IDS_DEL_OK 2071 -#define IDS_DEL_TITLE 2072 -#define IDS_TEST_SELECTED_COUNTS_FMT 2073 -#define IDS_TEST_SELECTED_COUNT 2074 -#define IDS_TEST_ALL_FILES 2075 -#define IDS_TEST_OK 2076 -#define IDS_TEST_TITLE 2077 -#define IDS_NOW_TESTING 2078 -#define IDS_MB_APP_NAME 2079 -#define IDS_NO_COMMENT_ADD 2080 -#define IDS_EDIT_COMMENT 2081 -#define IDS_DEL_COMMENT_OK 2082 -#define IDS_RECOMP_SELECTED_COUNT 2083 -#define IDS_RECOMP_SELECTED_COUNTS_FMT 2084 -#define IDS_RECOMP_ALL_FILES 2085 -#define IDS_RECOMP_OK 2086 -#define IDS_RECOMP_TITLE 2087 -#define IDS_NOW_EXPANDING 2088 -#define IDS_NOW_COMPRESSING 2089 -#define IDS_PRINT_CL_JOB_TITLE 2090 -#define IDS_REG_EXPIRED 2091 -#define IDS_REG_FAILURE 2092 -#define IDS_REG_INVALID 2093 -#define IDS_REG_EVAL_REM 2094 -#define IDS_REG_BAD_ENTRY 2095 -#define IDS_OPEN_AS_NUFX 2096 -#define IDS_ABOUT_UNREGISTERED 2097 -#define IDS_CDESC_140K 2098 -#define IDS_CDESC_800K 2099 -#define IDS_CDESC_BLOCKS 2100 -#define IDS_CDEC_140K_13 2101 -#define IDS_BAD_SST_IMAGE 2102 -#define IDS_NIBBLE_TO_SECTOR_WARNING 2103 -#define IDS_CDEC_RAWNIB 2104 -#define IDS_DISKEDITMSG_EMPTY 2105 -#define IDS_DISKEDITMSG_SPARSE 2106 -#define IDS_DISKEDITMSG_BADSECTOR 2107 -#define IDS_DISKEDITMSG_BADBLOCK 2108 -#define IDS_DISKEDITMSG_BADTRACK 2109 -#define IDS_CONVFILE_SELECTED_COUNT 2110 -#define IDS_CONVFILE_SELECTED_COUNTS_FMT 2111 -#define IDS_CONVFILE_ALL_FILES 2112 -#define IDS_CONVFILE_OK 2113 -#define IDS_CONVFILE_TITLE 2114 -#define IDS_CONVDISK_SELECTED_COUNT 2115 -#define IDS_CONVDISK_SELECTED_COUNTS_FMT 2116 -#define IDS_CONVDISK_ALL_FILES 2117 -#define IDS_CONVDISK_OK 2118 -#define IDS_CONVDISK_TITLE 2119 -#define IDS_CONVDISK_SPACEREQ 2120 -#define IDS_CDESC_40TRACK 2121 -#define IDS_TRACKSTAR_TO_OTHER_WARNING 2122 -#define IDS_DIFFERENT_NIBBLE_WARNING 2123 -#define IDS_VOLUME_NO_REMOTE 2124 -#define IDS_VOLUME_NO_CDROM 2125 -#define IDS_VOLUME_NO_RAMDISK 2126 -#define IDS_VOLUME_NO_GENERIC 2127 -#define IDS_VOLUME_NO_CDRIVE 2128 -#define IDS_VOLUME_SELECT_ONE 2129 -#define IDS_OPERATION_CANCELLED 2130 -#define IDS_NOT_READY 2131 -#define IDS_ASPI_NOT_LOADED 2132 -#define IDS_NOW_DELETING 2133 -#define IDS_VALID_FILENAME_PRODOS 2134 -#define IDS_VALID_FILENAME_DOS 2135 -#define IDS_VALID_FILENAME_PASCAL 2136 -#define IDS_VALID_VOLNAME_PRODOS 2137 -#define IDS_VALID_VOLNAME_DOS 2138 -#define IDS_VALID_VOLNAME_PASCAL 2139 -#define IDS_CLIPBOARD_REGFAILED 2140 -#define IDS_CLIPBOARD_OPENFAILED 2141 -#define IDS_CLIPBOARD_NOITEMS 2142 -#define IDS_CLIPBOARD_ALLOCFAILED 2143 -#define IDS_CLIPBOARD_NOTFOUND 2144 -#define IDS_CLIPBOARD_READFAILURE 2145 -#define IDS_CLIPBOARD_WRITEFAILURE 2146 -#define IDS_CLIPBOARD_WIN9XMAX 2147 -#define IDS_PRINTER_NOT_USABLE 2148 -#define IDS_NLIST_DATA_FAILED 2149 -#define IDS_PROPS_DOS_TYPE_CHANGE 2150 -#define IDS_FDI_TO_OTHER_WARNING 2151 -#define IDS_VALID_VOLNAME_HFS 2152 -#define IDS_VALID_FILENAME_HFS 2153 -#define IDS_PASTE_SPECIAL_COUNT 2154 -#define IDS_NO_FORKS_SPECIFIED 2155 -#define IDM_FILE_NEW_ARCHIVE 40001 -#define IDM_FILE_OPEN 40002 -#define IDM_FILE_CLOSE 40003 -#define IDM_FILE_PRINT 40004 -#define IDM_ACTIONS_ADD_FILES 40005 -#define IDM_FILE_EXIT 40006 -#define IDM_ACTIONS_ADD_DISKS 40007 -#define IDM_ACTIONS_EXTRACT 40008 -#define IDM_ACTIONS_VIEW 40009 -#define IDM_ACTIONS_TEST 40010 -#define IDM_ACTIONS_DELETE 40011 -#define IDM_EDIT_SELECT_ALL 40014 -#define IDM_ACTIONS_INVERT_SELECTION 40015 -#define IDM_EDIT_PREFERENCES 40016 -#define IDM_HELP_CONTENTS 40017 -#define IDM_HELP_ORDERING 40018 -#define IDM_HELP_ABOUT 40019 -#define IDM_ACTIONS_RECOMPRESS 40020 -#define IDM_SORT_PATHNAME 40025 -#define IDM_SORT_TYPE 40026 -#define IDM_SORT_AUXTYPE 40027 -#define IDM_SORT_MODDATE 40028 -#define IDM_SORT_FORMAT 40029 -#define IDM_SORT_SIZE 40030 -#define IDM_SORT_RATIO 40031 -#define IDM_SORT_PACKED 40032 -#define IDM_SORT_ACCESS 40033 -#define IDM_SORT_ORIGINAL 40034 -#define IDM_CONVERT_TOBSC 40039 -#define IDM_CONVERT_FROMBSC 40040 -#define ID_INDICATOR_COMPLETE 40043 -#define IDM_TOOLS_DISKEDIT 40044 -#define IDM_HELP_WEBSITE 40046 -#define IDM_EDIT_INVERT_SELECTION 40048 -#define IDM_ACTIONS_RENAME 40050 -#define IDM_ACTIONS_EDIT_COMMENT 40051 -#define IDM_TOOLS_DISKCONV 40054 -#define IDM_TOOLS_SST_MERGE 40055 -#define IDM_ACTIONS_OPENASDISK 40063 -#define IDM_ACTIONS_EDIT_PROPS 40064 -#define IDM_ACTIONS_CONV_DISK 40065 -#define IDM_ACTIONS_CONV_FILE 40066 -#define IDM_TOOLS_BULKDISKCONV 40067 -#define IDM_FILE_OPEN_VOLUME 40068 -#define IDM_TOOLS_IMAGECREATOR 40072 -#define IDM_TOOLS_VOLUMECOPIER_FILE 40074 -#define IDM_TOOLS_VOLUMECOPIER_VOLUME 40075 -#define IDM_FILE_ARCHIVEINFO 40079 -#define IDM_ACTIONS_CREATE_SUBDIR 40080 -#define IDM_ACTIONS_RENAME_VOLUME 40081 -#define IDM_TOOLS_EOLSCANNER 40083 -#define IDM_FILE_FLUSH 40084 -#define IDM_FILE_SAVE 40085 -#define IDM_EDIT_COPY 40086 -#define IDM_EDIT_PASTE 40087 -#define IDM_TOOLS_TWOMGPROPS 40088 -#define IDM_TOOLS_TWOIMGPROPS 40089 -#define IDM_ACTIONS_CONV_TOWAV 40094 -#define IDM_ACTIONS_CONV_FROMWAV 40095 -#define IDM_ACTIONS_IMPORT_BAS 40097 -#define IDM_FILE_REOPEN 40098 -#define IDM_EDIT_FIND 40100 -#define IDM_EDIT_PASTE_SPECIAL 40101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 196 -#define _APS_NEXT_COMMAND_VALUE 40102 -#define _APS_NEXT_CONTROL_VALUE 1455 -#define _APS_NEXT_SYMED_VALUE 102 -#endif -#endif diff --git a/ciderpress/app/resource.hm b/ciderpress/app/resource.hm deleted file mode 100644 index 63005db..0000000 --- a/ciderpress/app/resource.hm +++ /dev/null @@ -1,19 +0,0 @@ -// Microsoft Developer Studio generated Help ID include file. -// Used by CiderPress.rc -// -#define HIDC_ABOUT_CREDITS 0x80690457 // IDD_ABOUTDLG -#define HIDC_DISKEDIT_DONE 0x807f0435 // IDD_DISKEDIT -#define HIDC_DISKEDIT_DOREAD 0x807f0427 // IDD_DISKEDIT -#define HIDC_DISKEDIT_DOWRITE 0x807f0428 // IDD_DISKEDIT -#define HIDC_DISKEDIT_EDIT 0x807f042e // IDD_DISKEDIT -#define HIDC_DISKEDIT_HEX 0x807f0436 // IDD_DISKEDIT -#define HIDC_DISKEDIT_NEXT 0x807f0430 // IDD_DISKEDIT -#define HIDC_DISKEDIT_OPENFILE 0x807f042d // IDD_DISKEDIT -#define HIDC_DISKEDIT_PREV 0x807f042f // IDD_DISKEDIT -#define HIDC_DISKEDIT_SECTOR 0x807f042b // IDD_DISKEDIT -#define HIDC_DISKEDIT_SUBVOLUME 0x807f0439 // IDD_DISKEDIT -#define HIDC_DISKEDIT_TRACK 0x807f0429 // IDD_DISKEDIT -#define HIDC_STATIC 0x8074ffff // IDD_PREF_COMPRESSION -#define HIDC_STEXT_SECTOR 0x807f0431 // IDD_DISKEDIT -#define HIDC_STEXT_TRACK 0x807f0432 // IDD_DISKEDIT -#define HIDHELP 0x807f0009 // IDD_DISKEDIT diff --git a/ciderpress/app/targetver.h b/ciderpress/app/targetver.h deleted file mode 100644 index f3d0a90..0000000 --- a/ciderpress/app/targetver.h +++ /dev/null @@ -1,18 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ - -// Visual C++ 2013 yells if you don't define these values. By default -// they're set to the highest version, but we want to support WinXP, so -// we use that definition. -// -// The symbolic names for the constants are defined in SDKDDKVer.h, but -// that also sets the default values, so the auto-generated "targetver.h" -// recommends doing it this way. Web docs confirm that it should be done -// numerically. -#include -#define WINVER 0x0501 -#define _WIN32_WINNT 0x0501 -#include diff --git a/ciderpress/diskimg/ASPI.cpp b/ciderpress/diskimg/ASPI.cpp deleted file mode 100644 index 5bd91d5..0000000 --- a/ciderpress/diskimg/ASPI.cpp +++ /dev/null @@ -1,615 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * ASPI I/O functions. - * - * Some notes on ASPI stuff: - * - The Nero ASPI provides an interface for IDE hard drives. It also - * throws in a couple of mystery devices on a host adapter at the end. - * It has "unknown" device type and doesn't respond to SCSI device - * inquiries, so it's easy to ignore. - * - The Win98 generic ASPI only finds CD-ROM drives on the IDE bus. - */ -#include "StdAfx.h" -#if defined(_WIN32) && defined (WANT_ASPI) - -#include "DiskImgPriv.h" -#include "SCSIDefs.h" -#include "CP_wnaspi32.h" -#include "ASPI.h" - - -/* - * Initialize ASPI. - */ -DIError ASPI::Init(void) -{ - DWORD aspiStatus; - static const char* kASPIDllName = "wnaspi32.dll"; - - /* - * Try to load the DLL. - */ - fhASPI = ::LoadLibrary(kASPIDllName); - if (fhASPI == NULL) { - DWORD lastErr = ::GetLastError(); - if (lastErr == ERROR_MOD_NOT_FOUND) { - LOGI("ASPI DLL '%s' not found", kASPIDllName); - } else { - LOGI("ASPI LoadLibrary(%s) failed (err=%ld)", - kASPIDllName, GetLastError()); - } - return kDIErrGeneric; - } - - GetASPI32SupportInfo = (DWORD(*)(void))::GetProcAddress(fhASPI, "GetASPI32SupportInfo"); - SendASPI32Command = (DWORD(*)(LPSRB))::GetProcAddress(fhASPI, "SendASPI32Command"); - GetASPI32DLLVersion = (DWORD(*)(void))::GetProcAddress(fhASPI, "GetASPI32DLLVersion"); - if (GetASPI32SupportInfo == NULL || SendASPI32Command == NULL) { - LOGI("ASPI functions not found in dll"); - ::FreeLibrary(fhASPI); - fhASPI = NULL; - return kDIErrGeneric; - } - - if (GetASPI32DLLVersion != NULL) { - fASPIVersion = GetASPI32DLLVersion(); - LOGI(" ASPI version is %d.%d.%d.%d", - fASPIVersion & 0x0ff, - (fASPIVersion >> 8) & 0xff, - (fASPIVersion >> 16) & 0xff, - (fASPIVersion >> 24) & 0xff); - } else { - LOGI("ASPI WARNING: couldn't find GetASPI32DLLVersion interface"); - } - - /* - * Successfully loaded the library. Start it up and see if it works. - */ - aspiStatus = GetASPI32SupportInfo(); - if (HIBYTE(LOWORD(aspiStatus)) != SS_COMP) { - LOGI("ASPI loaded but not working (status=%d)", - HIBYTE(LOWORD(aspiStatus))); - ::FreeLibrary(fhASPI); - fhASPI = NULL; - return kDIErrASPIFailure; - } - - fHostAdapterCount = LOBYTE(LOWORD(aspiStatus)); - LOGI("ASPI loaded successfully, hostAdapterCount=%d", - fHostAdapterCount); - - return kDIErrNone; -} - -/* - * Destructor. Unload the ASPI DLL. - */ -ASPI::~ASPI(void) -{ - if (fhASPI != NULL) { - LOGI("Unloading ASPI DLL"); - ::FreeLibrary(fhASPI); - fhASPI = NULL; - } -} - - -/* - * Issue an ASPI host adapter inquiry request for the specified adapter. - * - * Pass in a pointer to a struct that receives the result. - */ -DIError ASPI::HostAdapterInquiry(unsigned char adapter, AdapterInfo* pAdapterInfo) -{ - SRB_HAInquiry req; - DWORD result; - - assert(adapter >= 0 && adapter < kMaxAdapters); - - memset(&req, 0, sizeof(req)); - req.SRB_Cmd = SC_HA_INQUIRY; - req.SRB_HaId = adapter; - - result = SendASPI32Command(&req); - if (result != SS_COMP) { - LOGI("ASPI(SC_HA_INQUIRY on %d) failed with result=0x%lx", - adapter, result); - return kDIErrASPIFailure; - } - - pAdapterInfo->adapterScsiID = req.HA_SCSI_ID; - memcpy(pAdapterInfo->managerID, req.HA_ManagerId, - sizeof(pAdapterInfo->managerID)-1); - pAdapterInfo->managerID[sizeof(pAdapterInfo->managerID)-1] = '\0'; - memcpy(pAdapterInfo->identifier, req.HA_Identifier, - sizeof(pAdapterInfo->identifier)-1); - pAdapterInfo->identifier[sizeof(pAdapterInfo->identifier)-1] = '\0'; - pAdapterInfo->maxTargets = req.HA_Unique[3]; - pAdapterInfo->bufferAlignment = - (unsigned short) req.HA_Unique[1] << 8 | req.HA_Unique[0]; - - return kDIErrNone; -} - -/* - * Issue an ASPI query on device type. - */ -DIError ASPI::GetDeviceType(unsigned char adapter, unsigned char target, - unsigned char lun, unsigned char* pType) -{ - SRB_GDEVBlock req; - DWORD result; - - assert(adapter >= 0 && adapter < kMaxAdapters); - assert(target >= 0 && target < kMaxTargets); - assert(lun >= 0 && lun < kMaxLuns); - assert(pType != NULL); - - memset(&req, 0, sizeof(req)); - req.SRB_Cmd = SC_GET_DEV_TYPE; - req.SRB_HaId = adapter; - req.SRB_Target = target; - req.SRB_Lun = lun; - - result = SendASPI32Command(&req); - if (result != SS_COMP) - return kDIErrASPIFailure; - - *pType = req.SRB_DeviceType; - - return kDIErrNone; -} - -/* - * Return a printable string for the given device type. - */ -const char* ASPI::DeviceTypeToString(unsigned char deviceType) -{ - switch (deviceType) { - case kScsiDevTypeDASD: return "Disk device"; - case kScsiDevTypeSEQD: return "Tape device"; - case kScsiDevTypePRNT: return "Printer"; - case kScsiDevTypePROC: return "Processor"; - case kScsiDevTypeWORM: return "Write-once read-multiple"; - case kScsiDevTypeCDROM: return "CD-ROM device"; - case kScsiDevTypeSCAN: return "Scanner device"; - case kScsiDevTypeOPTI: return "Optical memory device"; - case kScsiDevTypeJUKE: return "Medium changer device"; - case kScsiDevTypeCOMM: return "Communications device"; - case kScsiDevTypeUNKNOWN: return "Unknown or no device type"; - default: return "Invalid type"; - } -} - -/* - * Issue a SCSI device inquiry and return the interesting parts. - */ -DIError ASPI::DeviceInquiry(unsigned char adapter, unsigned char target, - unsigned char lun, Inquiry* pInquiry) -{ - DIError dierr; - SRB_ExecSCSICmd srb; - CDB6Inquiry* pCDB; - unsigned char buf[96]; // enough to hold everything of interest, and more - CDB_InquiryData* pInqData = (CDB_InquiryData*) buf; - - assert(sizeof(CDB6Inquiry) == 6); - - memset(&srb, 0, sizeof(srb)); - srb.SRB_Cmd = SC_EXEC_SCSI_CMD; - srb.SRB_HaId = adapter; - srb.SRB_Target = target; - srb.SRB_Lun = lun; - srb.SRB_Flags = SRB_DIR_IN; - srb.SRB_BufLen = sizeof(buf); - srb.SRB_BufPointer = buf; - srb.SRB_SenseLen = SENSE_LEN; - srb.SRB_CDBLen = sizeof(*pCDB); - - pCDB = (CDB6Inquiry*) srb.CDBByte; - pCDB->operationCode = kScsiOpInquiry; - pCDB->allocationLength = sizeof(buf); - - // Don't set pCDB->logicalUnitNumber. It's only there for SCSI-1 - // devices. SCSI-2 uses an IDENTIFY command; I gather ASPI is doing - // this for us. - - dierr = ExecSCSICommand(&srb); - if (dierr != kDIErrNone) - return dierr; - - memcpy(pInquiry->vendorID, pInqData->vendorId, - sizeof(pInquiry->vendorID)-1); - pInquiry->vendorID[sizeof(pInquiry->vendorID)-1] = '\0'; - memcpy(pInquiry->productID, pInqData->productId, - sizeof(pInquiry->productID)-1); - pInquiry->productID[sizeof(pInquiry->productID)-1] = '\0'; - pInquiry->productRevision[0] = pInqData->productRevisionLevel[0]; - pInquiry->productRevision[1] = pInqData->productRevisionLevel[1]; - pInquiry->productRevision[2] = pInqData->productRevisionLevel[2]; - pInquiry->productRevision[3] = pInqData->productRevisionLevel[3]; - - return kDIErrNone; -} - - -/* - * Get the capacity of a SCSI block device. - */ -DIError ASPI::GetDeviceCapacity(unsigned char adapter, unsigned char target, - unsigned char lun, unsigned long* pLastBlock, unsigned long* pBlockSize) -{ - DIError dierr; - SRB_ExecSCSICmd srb; - CDB10* pCDB; - CDB_ReadCapacityData dataBuf; - - assert(sizeof(dataBuf) == 8); // READ CAPACITY returns two longs - assert(sizeof(CDB10) == 10); - - memset(&srb, 0, sizeof(srb)); - srb.SRB_Cmd = SC_EXEC_SCSI_CMD; - srb.SRB_HaId = adapter; - srb.SRB_Target = target; - srb.SRB_Lun = lun; - srb.SRB_Flags = SRB_DIR_IN; - srb.SRB_BufLen = sizeof(dataBuf); - srb.SRB_BufPointer = (unsigned char*)&dataBuf; - srb.SRB_SenseLen = SENSE_LEN; - srb.SRB_CDBLen = sizeof(*pCDB); - - pCDB = (CDB10*) srb.CDBByte; - pCDB->operationCode = kScsiOpReadCapacity; - // rest of CDB is zero - - dierr = ExecSCSICommand(&srb); - if (dierr != kDIErrNone) - return dierr; - - *pLastBlock = - (unsigned long) dataBuf.logicalBlockAddr0 << 24 | - (unsigned long) dataBuf.logicalBlockAddr1 << 16 | - (unsigned long) dataBuf.logicalBlockAddr2 << 8 | - (unsigned long) dataBuf.logicalBlockAddr3; - *pBlockSize = - (unsigned long) dataBuf.bytesPerBlock0 << 24 | - (unsigned long) dataBuf.bytesPerBlock1 << 16 | - (unsigned long) dataBuf.bytesPerBlock2 << 8 | - (unsigned long) dataBuf.bytesPerBlock3; - return kDIErrNone; -} - -/* - * Test to see if a device is ready. - * - * Returns "true" if the device is ready, "false" if not. - */ -DIError ASPI::TestUnitReady(unsigned char adapter, unsigned char target, - unsigned char lun, bool* pReady) -{ - DIError dierr; - SRB_ExecSCSICmd srb; - CDB6* pCDB; - - assert(sizeof(CDB6) == 6); - - memset(&srb, 0, sizeof(srb)); - srb.SRB_Cmd = SC_EXEC_SCSI_CMD; - srb.SRB_HaId = adapter; - srb.SRB_Target = target; - srb.SRB_Lun = lun; - srb.SRB_Flags = 0; //SRB_DIR_IN; - srb.SRB_BufLen = 0; - srb.SRB_BufPointer = NULL; - srb.SRB_SenseLen = SENSE_LEN; - srb.SRB_CDBLen = sizeof(*pCDB); - - pCDB = (CDB6*) srb.CDBByte; - pCDB->operationCode = kScsiOpTestUnitReady; - // rest of CDB is zero - - dierr = ExecSCSICommand(&srb); - if (dierr != kDIErrNone) { - const CDB_SenseData* pSense = (const CDB_SenseData*) srb.SenseArea; - - if (srb.SRB_TargStat == kScsiStatCheckCondition && - pSense->senseKey == kScsiSenseNotReady) - { - // expect pSense->additionalSenseCode to be - // kScsiAdSenseNoMediaInDevice; no need to check it really. - LOGI(" ASPI TestUnitReady: drive %d:%d:%d is NOT ready", - adapter, target, lun); - } else { - LOGI(" ASPI TestUnitReady failed, status=0x%02x sense=0x%02x ASC=0x%02x", - srb.SRB_TargStat, pSense->senseKey, - pSense->additionalSenseCode); - } - *pReady = false; - } else { - const CDB_SenseData* pSense = (const CDB_SenseData*) srb.SenseArea; - LOGI(" ASPI TestUnitReady: drive %d:%d:%d is ready", - adapter, target, lun); - //LOGI(" status=0x%02x sense=0x%02x ASC=0x%02x", - // srb.SRB_TargStat, pSense->senseKey, pSense->additionalSenseCode); - *pReady = true; - } - - return kDIErrNone; -} - -/* - * Read one or more blocks from the device. - * - * The block size is going to be whatever the device's native size is - * (possibly modified by extents, but we'll ignore that). For a CD-ROM - * this means 2048-byte blocks. - */ -DIError ASPI::ReadBlocks(unsigned char adapter, unsigned char target, - unsigned char lun, long startBlock, short numBlocks, long blockSize, - void* buf) -{ - SRB_ExecSCSICmd srb; - CDB10* pCDB; - - //LOGI(" ASPI ReadBlocks start=%ld num=%d (size=%d)", - // startBlock, numBlocks, blockSize); - - assert(sizeof(CDB10) == 10); - assert(startBlock >= 0); - assert(numBlocks > 0); - assert(buf != NULL); - - memset(&srb, 0, sizeof(srb)); - srb.SRB_Cmd = SC_EXEC_SCSI_CMD; - srb.SRB_HaId = adapter; - srb.SRB_Target = target; - srb.SRB_Lun = lun; - srb.SRB_Flags = SRB_DIR_IN; - srb.SRB_BufLen = numBlocks * blockSize; - srb.SRB_BufPointer = (unsigned char*)buf; - srb.SRB_SenseLen = SENSE_LEN; - srb.SRB_CDBLen = sizeof(*pCDB); - - pCDB = (CDB10*) srb.CDBByte; - pCDB->operationCode = kScsiOpRead; - pCDB->logicalBlockAddr0 = (unsigned char) (startBlock >> 24); // MSB - pCDB->logicalBlockAddr1 = (unsigned char) (startBlock >> 16); - pCDB->logicalBlockAddr2 = (unsigned char) (startBlock >> 8); - pCDB->logicalBlockAddr3 = (unsigned char) startBlock; // LSB - pCDB->transferLength0 = (unsigned char) (numBlocks >> 8); // MSB - pCDB->transferLength1 = (unsigned char) numBlocks; // LSB - - return ExecSCSICommand(&srb); -} - -/* - * Write one or more blocks to the device. - */ -DIError ASPI::WriteBlocks(unsigned char adapter, unsigned char target, - unsigned char lun, long startBlock, short numBlocks, long blockSize, - const void* buf) -{ - SRB_ExecSCSICmd srb; - CDB10* pCDB; - - LOGI(" ASPI WriteBlocks start=%ld num=%d (size=%d)", - startBlock, numBlocks, blockSize); - - assert(sizeof(CDB10) == 10); - assert(startBlock >= 0); - assert(numBlocks > 0); - assert(buf != NULL); - - memset(&srb, 0, sizeof(srb)); - srb.SRB_Cmd = SC_EXEC_SCSI_CMD; - srb.SRB_HaId = adapter; - srb.SRB_Target = target; - srb.SRB_Lun = lun; - srb.SRB_Flags = SRB_DIR_IN; - srb.SRB_BufLen = numBlocks * blockSize; - srb.SRB_BufPointer = (unsigned char*)buf; - srb.SRB_SenseLen = SENSE_LEN; - srb.SRB_CDBLen = sizeof(*pCDB); - - pCDB = (CDB10*) srb.CDBByte; - pCDB->operationCode = kScsiOpWrite; - pCDB->logicalBlockAddr0 = (unsigned char) (startBlock >> 24); // MSB - pCDB->logicalBlockAddr1 = (unsigned char) (startBlock >> 16); - pCDB->logicalBlockAddr2 = (unsigned char) (startBlock >> 8); - pCDB->logicalBlockAddr3 = (unsigned char) startBlock; // LSB - pCDB->transferLength0 = (unsigned char) (numBlocks >> 8); // MSB - pCDB->transferLength1 = (unsigned char) numBlocks; // LSB - - return ExecSCSICommand(&srb); -} - - -/* - * Execute a SCSI command. - * - * Returns an error if ASPI reports an error or the SCSI status isn't - * kScsiStatGood. - * - * The Nero ASPI layer typically returns immediately, and hands back an - * SS_ERR when something fails. Win98 ASPI does the SS_PENDING thang. - */ -DIError ASPI::ExecSCSICommand(SRB_ExecSCSICmd* pSRB) -{ - HANDLE completionEvent = NULL; - DWORD eventStatus; - DWORD aspiStatus; - - assert(pSRB->SRB_Cmd == SC_EXEC_SCSI_CMD); - assert(pSRB->SRB_Flags == SRB_DIR_IN || - pSRB->SRB_Flags == SRB_DIR_OUT || - pSRB->SRB_Flags == 0); - - /* - * Set up event-waiting stuff, as described in the Adaptec ASPI docs. - */ - pSRB->SRB_Flags |= SRB_EVENT_NOTIFY; - - completionEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL); - if (completionEvent == NULL) { - LOGI("Failed creating a completion event?"); - return kDIErrGeneric; - } - - pSRB->SRB_PostProc = completionEvent; - - /* - * Send the request. - */ - (void)SendASPI32Command((LPSRB) pSRB); - aspiStatus = pSRB->SRB_Status; - if (aspiStatus == SS_PENDING) { - //LOGI(" (waiting for completion)"); - eventStatus = ::WaitForSingleObject(completionEvent, kTimeout * 1000); - - ::CloseHandle(completionEvent); - - if (eventStatus == WAIT_TIMEOUT) { - LOGI(" ASPI exec timed out!"); - return kDIErrSCSIFailure; - } else if (eventStatus != WAIT_OBJECT_0) { - LOGI(" ASPI exec returned weird wait state %ld", eventStatus); - return kDIErrGeneric; - } - } - - /* - * Check the final status. - */ - aspiStatus = pSRB->SRB_Status; - - if (aspiStatus == SS_COMP) { - /* success! */ - } else if (aspiStatus == SS_ERR) { - const CDB_SenseData* pSense = (const CDB_SenseData*) pSRB->SenseArea; - - LOGI(" ASPI SCSI command 0x%02x failed: scsiStatus=0x%02x" - " senseKey=0x%02x ASC=0x%02x\n", - pSRB->CDBByte[0], pSRB->SRB_TargStat, - pSense->senseKey, pSense->additionalSenseCode); - return kDIErrSCSIFailure; - } else { - // SS_ABORTED, SS_ABORT_FAIL, SS_NO_DEVICE, ... - LOGI(" ASPI failed on command 0x%02x: aspiStatus=%d scsiStatus=%d", - pSRB->CDBByte[0], aspiStatus, pSRB->SRB_TargStat); - return kDIErrASPIFailure; - } - - return kDIErrNone; -} - - -/* - * Return an array of accessible devices we found. - * - * Only return the devices matching device types in "deviceMask". - */ -DIError ASPI::GetAccessibleDevices(int deviceMask, ASPIDevice** ppDeviceArray, - int* pNumDevices) -{ - DIError dierr; - ASPIDevice* deviceArray = NULL; - int idx = 0; - - assert(deviceMask != 0); - assert((deviceMask & ~(kDevMaskCDROM | kDevMaskHardDrive)) == 0); - assert(ppDeviceArray != NULL); - assert(pNumDevices != NULL); - - deviceArray = new ASPIDevice[kMaxAccessibleDrives]; - if (deviceArray == NULL) - return kDIErrMalloc; - - LOGI("ASPI scanning %d host adapters", fHostAdapterCount); - - for (int ha = 0; ha < fHostAdapterCount; ha++) { - AdapterInfo adi; - - dierr = HostAdapterInquiry(ha, &adi); - if (dierr != kDIErrNone) { - LOGI(" ASPI inquiry on %d failed", ha); - continue; - } - - LOGI(" ASPI host adapter %d (SCSI ID=%d)", ha, adi.adapterScsiID); - LOGI(" identifier='%s' managerID='%s'", - adi.identifier, adi.managerID); - LOGI(" maxTargets=%d bufferAlignment=%d", - adi.maxTargets, adi.bufferAlignment); - - int maxTargets = adi.maxTargets; - if (!maxTargets) { - /* Win98 ASPI reports zero here for ATAPI */ - maxTargets = 8; - } - if (maxTargets > kMaxTargets) - maxTargets = kMaxTargets; - for (int targ = 0; targ < maxTargets; targ++) { - for (int lun = 0; lun < kMaxLuns; lun++) { - Inquiry inq; - unsigned char deviceType; - char addrString[48]; - bool deviceReady; - - dierr = GetDeviceType(ha, targ, lun, &deviceType); - if (dierr != kDIErrNone) - continue; - - sprintf(addrString, "%d:%d:%d", ha, targ, lun); - - dierr = DeviceInquiry(ha, targ, lun, &inq); - if (dierr != kDIErrNone) { - LOGI(" ASPI DeviceInquiry for '%s' (type=%d) failed", - addrString, deviceType); - continue; - } - - LOGI(" Device %s is %s '%s' '%s'", - addrString, DeviceTypeToString(deviceType), - inq.vendorID, inq.productID); - - if ((deviceMask & kDevMaskCDROM) != 0 && - deviceType == kScsiDevTypeCDROM) - { - /* found CD-ROM */ - } else if ((deviceMask & kDevMaskHardDrive) != 0 && - deviceType == kScsiDevTypeDASD) - { - /* found hard drive */ - } else - continue; - - if (idx >= kMaxAccessibleDrives) { - LOGI("GLITCH: ran out of places to stuff CD-ROM drives"); - assert(false); - goto done; - } - - dierr = TestUnitReady(ha, targ, lun, &deviceReady); - if (dierr != kDIErrNone) { - LOGI(" ASPI TestUnitReady for '%s' failed", addrString); - continue; - } - - deviceArray[idx].Init(ha, targ, lun, inq.vendorID, - inq.productID, deviceType, deviceReady); - idx++; - } - } - } - -done: - *ppDeviceArray = deviceArray; - *pNumDevices = idx; - return kDIErrNone; -} - -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/ASPI.h b/ciderpress/diskimg/ASPI.h deleted file mode 100644 index a6223c5..0000000 --- a/ciderpress/diskimg/ASPI.h +++ /dev/null @@ -1,202 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * ASPI (Advanced SCSI Programming Interface) definitions. - * - * This may be included directly by an application. It must not be necessary - * to include the lower-level headers, e.g. wnaspi32.h. - * - * TODO: this was only necessary for older versions of Windows, e.g. Win98, - * as a way to access SCSI drives. It's no longer needed. - */ -#ifndef __ASPI__ -#define __ASPI__ - -#if !defined(_WIN32) || !defined(WANT_ASPI) -/* - * Placeholder definition to keep Linux build happy. - */ -namespace DiskImgLib { - class DISKIMG_API ASPI { - public: - ASPI(void) {} - virtual ~ASPI(void) {} - }; -}; - -#else - - - -#ifndef __WNASPI32_H__ -struct SRB_ExecSCSICmd; // fwd -#endif - -namespace DiskImgLib { - -/* - * Descriptor for one SCSI device. - */ -class DISKIMG_API ASPIDevice { -public: - ASPIDevice(void) : fVendorID(NULL), fProductID(NULL), - fAdapter(0xff), fTarget(0xff), fLun(0xff), fDeviceReady(false) - {} - virtual ~ASPIDevice(void) { - delete[] fVendorID; - delete[] fProductID; - } - - void Init(unsigned char adapter, unsigned char target, unsigned char lun, - const unsigned char* vendor, unsigned const char* product, - int deviceType, bool ready) - { - fAdapter = adapter; - fTarget = target; - fLun = lun; - assert(fVendorID == NULL); - fVendorID = new char[strlen((const char*)vendor)+1]; - strcpy(fVendorID, (const char*)vendor); - assert(fProductID == NULL); - fProductID = new char[strlen((const char*)product)+1]; - strcpy(fProductID, (const char*)product); - fDeviceReady = ready; - fDeviceType = deviceType; - } - - enum { - kTypeDASD = 0, // kScsiDevTypeDASD - kTypeCDROM = 5, // kScsiDevTypeCDROM - }; - - unsigned char GetAdapter(void) const { return fAdapter; } - unsigned char GetTarget(void) const { return fTarget; } - unsigned char GetLun(void) const { return fLun; } - const char* GetVendorID(void) const { return fVendorID; } - const char* GetProductID(void) const { return fProductID; } - bool GetDeviceReady(void) const { return fDeviceReady; } - int GetDeviceType(void) const { return fDeviceType; } - -private: - // strings from SCSI inquiry, padded with spaces at end - char* fVendorID; - char* fProductID; - - unsigned char fAdapter; // physical or logical host adapter (0-15) - unsigned char fTarget; // SCSI ID on adapter (0-15) - unsigned char fLun; // logical unit (0-7) - - int fDeviceType; // e.g. kScsiDevTypeCDROM - bool fDeviceReady; -}; - -/* - * There should be only one instance of this in the library (part of the - * DiskImgLib Globals). It wouldn't actually break anything to have more than - * one, but there's no need for it. - */ -class DISKIMG_API ASPI { -public: - ASPI(void) : - fhASPI(NULL), - GetASPI32SupportInfo(NULL), - SendASPI32Command(NULL), - GetASPI32DLLVersion(NULL), - fASPIVersion(0), - fHostAdapterCount(-1) - {} - virtual ~ASPI(void); - - // load ASPI DLL if it exists - DIError Init(void); - - // return the version returned by the loaded DLL - DWORD GetVersion(void) const { - assert(fhASPI != NULL); - return fASPIVersion; - } - - // Return an *array* of ASPIDevice structures for drives in system; - // the caller is expected to delete[] it when done. - enum { kDevMaskCDROM = 0x01, kDevMaskHardDrive = 0x02 }; - DIError GetAccessibleDevices(int deviceMask, ASPIDevice** ppDeviceArray, - int* pNumDevices); - - // Get the type of the device (DTYPE_*, 0x00-0x1f) using ASPI query - DIError GetDeviceType(unsigned char adapter, unsigned char target, - unsigned char lun, unsigned char* pType); - // convert DTYPE_* to a string - const char* DeviceTypeToString(unsigned char deviceType); - - // Get the capacity, expressed as the highest-available LBA and the device - // block size. - DIError GetDeviceCapacity(unsigned char adapter, unsigned char target, - unsigned char lun, unsigned long* pLastBlock, unsigned long* pBlockSize); - - // Read blocks from the device. - DIError ReadBlocks(unsigned char adapter, unsigned char target, - unsigned char lun, long startBlock, short numBlocks, long blockSize, - void* buf); - - // Write blocks to the device. - DIError WriteBlocks(unsigned char adapter, unsigned char target, - unsigned char lun, long startBlock, short numBlocks, long blockSize, - const void* buf); - -private: - /* - * The interesting bits that come out of an SC_HA_INQUIRY request. - */ - typedef struct AdapterInfo { - unsigned char adapterScsiID; // SCSI ID of the adapter itself - unsigned char managerID[16+1]; // string describing manager - unsigned char identifier[16+1]; // string describing host adapter - unsigned char maxTargets; // max #of targets on this adapter - unsigned short bufferAlignment; // buffer alignment requirement - } AdapterInfo; - /* - * The interesting bits from a SCSI device INQUIRY command. - */ - typedef struct Inquiry { - unsigned char vendorID[8+1]; // vendor ID string - unsigned char productID[16+1]; // product ID string - unsigned char productRevision[4]; // product revision bytes - } Inquiry; - - // Issue an ASPI adapter inquiry request. - DIError HostAdapterInquiry(unsigned char adapter, - AdapterInfo* pAdapterInfo); - // Issue a SCSI device inquiry request. - DIError DeviceInquiry(unsigned char adapter, unsigned char target, - unsigned char lun, Inquiry* pInquiry); - // Issue a SCSI test unit ready request. - DIError TestUnitReady(unsigned char adapter, unsigned char target, - unsigned char lun, bool* pReady); - // execute a SCSI command - DIError ExecSCSICommand(SRB_ExecSCSICmd* pSRB); - - - enum { - kMaxAdapters = 16, - kMaxTargets = 16, - kMaxLuns = 8, - kTimeout = 30, // timeout, in seconds - kMaxAccessibleDrives = 16, - }; - - HMODULE fhASPI; - DWORD (*GetASPI32SupportInfo)(void); - DWORD (*SendASPI32Command)(void* lpsrb); - DWORD (*GetASPI32DLLVersion)(void); - DWORD fASPIVersion; - int fHostAdapterCount; -}; - -}; // namespace DiskImgLib - -#endif /*__ASPI__*/ - -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/CFFA.cpp b/ciderpress/diskimg/CFFA.cpp deleted file mode 100644 index a25b45f..0000000 --- a/ciderpress/diskimg/CFFA.cpp +++ /dev/null @@ -1,591 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * The "CFFA" DiskFS is a container class for multiple ProDOS and HFS volumes. - * - * The CFFA card doesn't have any RAM, so the author used a fixed partitioning - * scheme. You get 4 or 8 volumes -- depending on which firmware you jumper - * in -- at 32MB each. CF cards usually hold less than you would expect, so - * a 64MB card would have one 32MB volume and one less-than-32MB volume. - * - * With Dave's GS/OS driver, you get an extra drive or two at the end, at up - * to 1GB each. The driver only works in 4-volume mode. - * - * There is no magic CFFA block at the front, so it looks like a plain - * ProDOS or HFS volume. If the size is less than 32MB -- meaning there's - * only one volume -- we don't need to take an interest in the file, - * because the regular filesystem goodies will handle it just fine. If it's - * more than 32MB, we need to create a structure in which multiple volumes - * reside. - * - * The trick is finding all the volumes. The first four are easy. The - * fifth one is either another 32MB volume (if you're in 8-volume mode) - * or a volume whose size is somewhere between the amount of space left - * and 1GB. Not an issue until we get to CF cards > 128MB. We have to - * rely on the CFFA card making volumes as large as it can. - * - * I think it's reasonable to require that the first volume be either ProDOS - * or HFS. That way we don't go digging through large non-CFFA files when - * auto-probing. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -/* - * Figure out if this is a CFFA volume, and if so, whether it was formatted - * in 4-partition or 8-partition mode. - * - * The "imageOrder" parameter has no use here, because (in the current - * version) embedded parent volumes are implicitly ProDOS-ordered. - * - * "*pFormatFound" should be either a CFFA format or "unknown" on entry. - * If it's not "unknown", we will look for the specified format first. - * Otherwise, we look for 4-partition then 8-partition. The first one - * we find successfully is returned. - * - * Ideally we'd have some way to express ambiguity here, so that we could - * force the "disk format verification" dialog to come up. No such - * mechanism exists, and for now it doesn't seem worthwhile to add one. - */ -/*static*/ DIError DiskFSCFFA::TestImage(DiskImg* pImg, - DiskImg::SectorOrder imageOrder, DiskImg::FSFormat* pFormatFound) -{ - DIError dierr; - long totalBlocks = pImg->GetNumBlocks(); - long startBlock, maxBlocks, totalBlocksLeft; - long fsNumBlocks; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - //bool fiveIs32MB; - - assert(totalBlocks > kEarlyVolExpectedSize); - - // could be "generic" from an earlier override - if (*pFormatFound != DiskImg::kFormatCFFA4 && - *pFormatFound != DiskImg::kFormatCFFA8) - { - *pFormatFound = DiskImg::kFormatUnknown; - } - - LOGI("----- BEGIN CFFA SCAN (fmt=%d) -----", *pFormatFound); - - startBlock = 0; - totalBlocksLeft = totalBlocks; - - /* - * Look for a 32MB ProDOS or HFS volume in the first slot. If we - * don't find this, it's probably not CFFA. It's possible they just - * didn't format the first one, but that seems unlikely, and it's not - * unreasonable to insist that they format the first partition. - */ - maxBlocks = totalBlocksLeft; - if (maxBlocks > kEarlyVolExpectedSize) - maxBlocks = kEarlyVolExpectedSize; - - dierr = OpenSubVolume(pImg, startBlock, maxBlocks, true, - &pNewImg, &pNewFS); - if (dierr != kDIErrNone) { - LOGI(" CFFA failed opening sub-volume #1"); - goto bail; - } - fsNumBlocks = pNewFS->GetFSNumBlocks(); - delete pNewFS; - delete pNewImg; - if (fsNumBlocks != kEarlyVolExpectedSize && - fsNumBlocks != kEarlyVolExpectedSize-1) - { - LOGI(" CFFA found fsNumBlocks=%ld in slot #1", fsNumBlocks); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - LOGI(" CFFA found good volume in slot #1"); - - startBlock += maxBlocks; - totalBlocksLeft -= maxBlocks; - assert(totalBlocksLeft > 0); - - /* - * Look for a ProDOS or HFS volume <= 32MB in the second slot. If - * we don't find something here, and this is a 64MB card, then there's - * no advantage to using CFFA (in fact, the single-volume handling may - * be more convenient). If there's at least another 32MB, we continue - * looking. - */ - maxBlocks = totalBlocksLeft; - if (maxBlocks > kEarlyVolExpectedSize) - maxBlocks = kEarlyVolExpectedSize; - - dierr = OpenSubVolume(pImg, startBlock, maxBlocks, true, - &pNewImg, &pNewFS); - if (dierr != kDIErrNone) { - LOGI(" CFFA failed opening sub-volume #2"); - if (maxBlocks < kEarlyVolExpectedSize) - goto bail; - // otherwise, assume they just didn't format #2, and keep going - } else { - fsNumBlocks = pNewFS->GetFSNumBlocks(); - delete pNewFS; - delete pNewImg; -#if 0 - if (fsNumBlocks != kEarlyVolExpectedSize && - fsNumBlocks != kEarlyVolExpectedSize-1) - { - LOGI(" CFFA found fsNumBlocks=%ld in slot #2", fsNumBlocks); - dierr = kDIErrFilesystemNotFound; - goto bail; - } -#endif - LOGI(" CFFA found good volume in slot #2"); - } - - startBlock += maxBlocks; - totalBlocksLeft -= maxBlocks; - if (totalBlocksLeft == 0) { - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } - - /* - * Skip #3 and #4. - */ - LOGI(" CFFA skipping over slot #3"); - maxBlocks = kEarlyVolExpectedSize*2; - if (maxBlocks > totalBlocksLeft) - maxBlocks = totalBlocksLeft; - startBlock += maxBlocks; - totalBlocksLeft -= maxBlocks; - if (totalBlocksLeft == 0) { - // no more partitions to find; we're done - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } - LOGI(" CFFA skipping over slot #4"); - - /* - * Partition #5. We know where it starts, but not how large it is. - * Could be 32MB, could be 1GB, could be anything between. - * - * CF cards come in power-of-two sizes -- 128MB, 256MB, etc. -- but - * we don't want to make assumptions here. It's possible we're - * looking at an odd-sized image file that some clever person is - * expecting to access with CiderPress. - */ - maxBlocks = totalBlocksLeft; - if (maxBlocks > kOneGB) - maxBlocks = kOneGB; - if (maxBlocks <= kEarlyVolExpectedSize) { - /* - * Only enough room for one <= 32MB volume. Not expected for a - * real CFFA card, unless they come in 160MB sizes. - * - * Treat it like 4-partition; it'll look like somebody slapped a - * 32MB volume into the first 1GB area. - */ - LOGI(" CFFA assuming odd-sized slot #5"); - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } - - /* - * We could be looking at a 32MB ProDOS partition, 32MB HFS partition, - * or an up to 1GB HFS partition. We have to specify the size in - * the OpenSubVolume request, which means trying it both ways and - * finding a match from GetFSNumBlocks(). Complicating matters is - * truncation (ProDOS max 65535, not 65536) and round-off (not sure - * how HFS deals with left-over blocks). - * - * Start with <= 1GB. - */ - dierr = OpenSubVolume(pImg, startBlock, maxBlocks, true, - &pNewImg, &pNewFS); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - LOGI(" CFFA failed opening large sub-volume #5"); - // if we can't get #5, don't bother looking for #6 - // (we could try anyway, but it's easier to just skip it) - dierr = kDIErrNone; - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } - - fsNumBlocks = pNewFS->GetFSNumBlocks(); - delete pNewFS; - delete pNewImg; - if (fsNumBlocks < 2 || fsNumBlocks > maxBlocks) { - LOGI(" CFFA WARNING: FSNumBlocks #5 reported blocks=%ld", - fsNumBlocks); - } - if (fsNumBlocks == kEarlyVolExpectedSize-1 || - fsNumBlocks == kEarlyVolExpectedSize) - { - LOGI(" CFFA #5 is a 32MB volume"); - // tells us nothing -- could still be 4 or 8, so we keep going - maxBlocks = kEarlyVolExpectedSize; - } else if (fsNumBlocks > kEarlyVolExpectedSize) { - // must be a GS/OS 1GB area - LOGI(" CFFA #5 is larger than 32MB"); - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } else { - LOGI(" CFFA #5 was unexpectedly small (%ld blocks)", fsNumBlocks); - // just stop now - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } - - startBlock += maxBlocks; - totalBlocksLeft -= maxBlocks; - - if (!totalBlocksLeft) { - LOGI(" CFFA got 5 volumes"); - *pFormatFound = DiskImg::kFormatCFFA4; - goto bail; - } - - /* - * Various possibilities for slots 5 and up: - * A. Card in 4-partition mode. 5th partition isn't formatted. Don't - * bother looking for 6th. [already handled] - * B. Card in 4-partition mode. 5th partition is >32MB HFS. 6th - * partition will be at +1GB. [already handled] - * C. Card in 4-partition mode. 5th partition is 32MB ProDOS. 6th - * partition will be at +1GB. - * D. Card in 8-partition mode. 5th partition is 32MB HFS. 6th - * partition will be at +32MB. - * E. Card in 8-partition mode. 5th partition is 32MB ProDOS. 6th - * partition will be at +32MB. - * - * I'm ignoring D on the off chance somebody could create a 32MB HFS - * partition in the 1GB space. D and E are handled alike. - * - * The difference between C and D/E can *usually* be determined by - * opening up a 6th partition in the two expected locations. - */ - LOGI(" CFFA probing 6th slot for 4-vs-8"); - /* - * Look in two different places. If we find something at the - * +32MB mark, assume it's in "8 mode". If we find something at - * the +1GB mark, assume it's in "4 + GS/OS mode". If both exist - * we have a problem. - */ - bool foundSmall, foundGig; - - foundSmall = false; - maxBlocks = totalBlocksLeft; - if (maxBlocks > kEarlyVolExpectedSize) - maxBlocks = kEarlyVolExpectedSize; - dierr = OpenSubVolume(pImg, startBlock + kEarlyVolExpectedSize, - maxBlocks, true, &pNewImg, &pNewFS); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - LOGI(" CFFA no vol #6 found at +32MB"); - } else { - foundSmall = true; - delete pNewFS; - delete pNewImg; - } - - foundGig = false; - // no need to look if we don't have at least 1GB left! - if (totalBlocksLeft >= kOneGB) { - maxBlocks = totalBlocksLeft; - if (maxBlocks > kOneGB) - maxBlocks = kOneGB; - dierr = OpenSubVolume(pImg, startBlock + kOneGB, - maxBlocks, true, &pNewImg, &pNewFS); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - LOGI(" CFFA no vol #6 found at +1GB"); - } else { - foundGig = true; - delete pNewFS; - delete pNewImg; - } - } - - dierr = kDIErrNone; - - if (!foundSmall && !foundGig) { - LOGI(" CFFA no valid filesystem found in 6th position"); - *pFormatFound = DiskImg::kFormatCFFA4; - // don't bother looking for 7 and 8 - } else if (foundSmall && foundGig) { - LOGI(" CFFA WARNING: found valid volumes at +32MB *and* +1GB"); - // default to 4-partition mode - if (*pFormatFound == DiskImg::kFormatUnknown) - *pFormatFound = DiskImg::kFormatCFFA4; - } else if (foundGig) { - LOGI(" CFFA found 6th volume at +1GB, assuming 4-mode w/GSOS"); - if (fsNumBlocks < 2 || fsNumBlocks > kOneGB) { - LOGI(" CFFA WARNING: FSNumBlocks #6 reported as %ld", - fsNumBlocks); - } - *pFormatFound = DiskImg::kFormatCFFA4; - } else if (foundSmall) { - LOGI(" CFFA found 6th volume at +32MB, assuming 8-mode"); - if (fsNumBlocks < 2 || fsNumBlocks > kEarlyVolExpectedSize) { - LOGI(" CFFA WARNING: FSNumBlocks #6 reported as %ld", - fsNumBlocks); - } - *pFormatFound = DiskImg::kFormatCFFA8; - } else { - assert(false); // how'd we get here?? - } - - // done! - -bail: - LOGI("----- END CFFA SCAN (err=%d format=%d) -----", - dierr, *pFormatFound); - if (dierr == kDIErrNone) { - assert(*pFormatFound != DiskImg::kFormatUnknown); - } else { - *pFormatFound = DiskImg::kFormatUnknown; - } - return dierr; -} - - -/* - * Open up a sub-volume. - * - * If "scanOnly" is set, the full DiskFS initialization isn't performed. - * We just do enough to get the volume size info. - */ -/*static*/ DIError DiskFSCFFA::OpenSubVolume(DiskImg* pImg, long startBlock, - long numBlocks, bool scanOnly, DiskImg** ppNewImg, DiskFS** ppNewFS) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = pNewImg->OpenImage(pImg, startBlock, numBlocks); - if (dierr != kDIErrNone) { - LOGI(" CFFASub: OpenImage(%ld,%ld) failed (err=%d)", - startBlock, numBlocks, dierr); - goto bail; - } - //LOGI(" +++ CFFASub: new image has ro=%d (parent=%d)", - // pNewImg->GetReadOnly(), pImg->GetReadOnly()); - - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" CFFASub: analysis failed (err=%d)", dierr); - goto bail; - } - - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" CFFASub: unable to identify filesystem at %ld", - startBlock); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* open a DiskFS for the sub-image */ - LOGI(" CFFASub (%ld,%ld) analyze succeeded!", startBlock, numBlocks); - pNewFS = pNewImg->OpenAppropriateDiskFS(); - if (pNewFS == NULL) { - LOGI(" CFFASub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* we encapsulate arbitrary stuff, so encourage child to scan */ - pNewFS->SetScanForSubVolumes(kScanSubEnabled); - - /* - * Load the files from the sub-image. When doing our initial tests, - * or when loading data for the volume copier, we don't want to dig - * into our sub-volumes, just figure out what they are and where. - */ - InitMode initMode; - if (scanOnly) - initMode = kInitHeaderOnly; - else - initMode = kInitFull; - dierr = pNewFS->Initialize(pNewImg, initMode); - if (dierr != kDIErrNone) { - LOGI(" CFFASub: error %d reading list of files from disk", dierr); - goto bail; - } - -bail: - if (dierr != kDIErrNone) { - delete pNewFS; - delete pNewImg; - } else { - *ppNewImg = pNewImg; - *ppNewFS = pNewFS; - } - return dierr; -} - -/* - * Check to see if this is a CFFA volume. - */ -/*static*/ DIError DiskFSCFFA::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (pImg->GetNumBlocks() < kMinInterestingBlocks) - return kDIErrFilesystemNotFound; - if (pImg->GetIsEmbedded()) // don't look for CFFA inside CFFA! - return kDIErrFilesystemNotFound; - - /* assume ProDOS -- shouldn't matter, since it's embedded */ - if (TestImage(pImg, DiskImg::kSectorOrderProDOS, pFormat) == kDIErrNone) { - assert(*pFormat == DiskImg::kFormatCFFA4 || - *pFormat == DiskImg::kFormatCFFA8); - *pOrder = DiskImg::kSectorOrderProDOS; - return kDIErrNone; - } - - // make sure we didn't tamper with it - assert(*pFormat == DiskImg::kFormatUnknown); - - LOGI(" FS didn't find valid CFFA"); - return kDIErrFilesystemNotFound; -} - - -/* - * Prep the CFFA "container" for use. - */ -DIError DiskFSCFFA::Initialize(void) -{ - DIError dierr = kDIErrNone; - - LOGI("CFFA initializing (scanForSub=%d)", fScanForSubVolumes); - - /* seems pointless *not* to, but we just do what we're told */ - if (fScanForSubVolumes != kScanSubDisabled) { - dierr = FindSubVolumes(); - if (dierr != kDIErrNone) - return dierr; - } - - /* blank out the volume usage map */ - SetVolumeUsageMap(); - - return dierr; -} - - -/* - * Find the various sub-volumes and open them. - * - * We don't handle the volume specially unless it's at least 32MB, which - * means there are at least 2 partitions. - */ -DIError DiskFSCFFA::FindSubVolumes(void) -{ - DIError dierr; - long startBlock, blocksLeft; - - startBlock = 0; - blocksLeft = fpImg->GetNumBlocks(); - - if (fpImg->GetFSFormat() == DiskImg::kFormatCFFA4) { - LOGI(" CFFA opening 4+2 volumes"); - dierr = AddVolumeSeries(0, 4, kEarlyVolExpectedSize, /*ref*/startBlock, - /*ref*/blocksLeft); - if (dierr != kDIErrNone) - goto bail; - - LOGI(" CFFA after first 4, startBlock=%ld blocksLeft=%ld", - startBlock, blocksLeft); - if (blocksLeft > 0) { - dierr = AddVolumeSeries(4, 2, kOneGB, /*ref*/startBlock, - /*ref*/blocksLeft); - if (dierr != kDIErrNone) - goto bail; - } - } else if (fpImg->GetFSFormat() == DiskImg::kFormatCFFA8) { - LOGI(" CFFA opening 8 volumes"); - dierr = AddVolumeSeries(0, 8, kEarlyVolExpectedSize, /*ref*/startBlock, - /*ref*/blocksLeft); - if (dierr != kDIErrNone) - goto bail; - } else { - assert(false); - return kDIErrInternal; - } - - if (blocksLeft != 0) { - LOGI(" CFFA ignoring leftover %ld blocks", blocksLeft); - } - -bail: - return dierr; -} - -/* - * Add a series of equal-sized volumes. - * - * Updates "startBlock" and "totalBlocksLeft". - */ -DIError DiskFSCFFA::AddVolumeSeries(int start, int count, long blocksPerVolume, - long& startBlock, long& totalBlocksLeft) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - long maxBlocks, fsNumBlocks; - bool scanOnly = false; - - /* used by volume copier, to avoid deep scan */ - if (GetScanForSubVolumes() == kScanSubContainerOnly) - scanOnly = true; - - for (int i = start; i < start+count; i++) { - maxBlocks = blocksPerVolume; - if (maxBlocks > totalBlocksLeft) - maxBlocks = totalBlocksLeft; - - dierr = OpenSubVolume(fpImg, startBlock, maxBlocks, scanOnly, - &pNewImg, &pNewFS); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - LOGI(" CFFA failed opening sub-volume %d (not formatted?)", i); - /* create a fake one to represent the partition */ - dierr = CreatePlaceholder(startBlock, maxBlocks, NULL, NULL, - &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - AddSubVolumeToList(pNewImg, pNewFS); - } else { - LOGI(" CFFA unable to create placeholder (%ld, %ld) (err=%d)", - startBlock, maxBlocks, dierr); - goto bail; - } - } else { - fsNumBlocks = pNewFS->GetFSNumBlocks(); - if (fsNumBlocks < 2 || fsNumBlocks > blocksPerVolume) { - LOGI(" CFFA WARNING: FSNumBlocks #%d reported as %ld", - i, fsNumBlocks); - } - AddSubVolumeToList(pNewImg, pNewFS); - } - - startBlock += maxBlocks; - totalBlocksLeft -= maxBlocks; - if (!totalBlocksLeft) - break; // all done - } - -bail: - return dierr; -} diff --git a/ciderpress/diskimg/CMakeLists.txt b/ciderpress/diskimg/CMakeLists.txt deleted file mode 100644 index 7b4f0d5..0000000 --- a/ciderpress/diskimg/CMakeLists.txt +++ /dev/null @@ -1,75 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -set(CMAKE_BUILD_TYPE DEBUG) - -set(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -set(PROJECT_NAME diskimg) -set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) -project(${PROJECT_NAME}) - - -set(ALL_DEFINES " " ) -set(DEBUG_OPT "-D_DEBUG -DDEBUG -O0 -g3 " ) -set(RELEASE_OPT "-O3 " ) - -set(CMAKE_C_FLAGS "-Wall ${ALL_DEFINES} ") -set(CMAKE_CXX_FLAGS "-Wall ${ALL_DEFINES} ") - -set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_OPT} ") -set(CMAKE_CXX_FLAGS_RELEASE "${RELEASE_OPT}") -set(CMAKE_C_FLAGS_DEBUG "${DEBUG_OPT} ") -set(CMAKE_C_FLAGS_RELEASE "${RELEASE_OPT}") - -set(FIND_LIBRARY_USE_LIB64_PATHS TRUE) - -add_subdirectory( ${CMAKE_CURRENT_SOURCE_DIR}/libhfs) - -set (SOURCE -ASPI.cpp -DiskFS.cpp -FAT.cpp -Gutenberg.cpp -Nibble35.cpp -ProDOS.cpp -UNIDOS.cpp -CFFA.cpp -DiskImg.cpp -FDI.cpp -HFS.cpp -Nibble.cpp -RDOS.cpp -VolumeUsage.cpp -Container.cpp -DIUtil.cpp -FocusDrive.cpp -ImageWrapper.cpp -OuterWrapper.cpp -SPTI.cpp -Win32BlockIO.cpp -CPM.cpp -DOS33.cpp -GenericFD.cpp -MacPart.cpp -OzDOS.cpp -StdAfx.cpp -DDD.cpp -DOSImage.cpp -Global.cpp -MicroDrive.cpp -Pascal.cpp -TwoImg.cpp -) - -include_directories(BEFORE - ${PROJECT_ROOT} -) - -add_library( ${PROJECT_NAME} SHARED ${SOURCE}) -add_library( ${PROJECT_NAME}_static STATIC ${SOURCE}) - -target_link_libraries ( -${PROJECT_NAME} -) - - - diff --git a/ciderpress/diskimg/CPM.cpp b/ciderpress/diskimg/CPM.cpp deleted file mode 100644 index ea68b85..0000000 --- a/ciderpress/diskimg/CPM.cpp +++ /dev/null @@ -1,763 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Apple II CP/M disk format. - * - * Limitations: - * - Read-only. - * - Does not do much with user numbers. - * - Rumor has it that "sparse" files are possible. Not handled. - * - I'm currently treating the directory as fixed-length. This may - * not be correct. - * - Not handling special entries (volume label, date stamps, - * password control). - * - * As I have no practical experience with CP/M, this is the weakest of the - * filesystem implementations. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSCPM - * =========================================================================== - */ - -const int kBlkSize = 512; // really ought to be 1024 -const int kVolDirBlock = 24; // track 3 sector 0 -const int kVolDirCount = 4; // 4 prodos blocks -const int kNoDataByte = 0xe5; -const int kMaxUserNumber = 31; // 0-15 on some systems, 0-31 on others -const int kMaxSpecialUserNumber = 0x21; // 0x20 and 0x21 have special meanings -const int kMaxExtent = 31; // extent counter, 0-31 - -/* - * See if this looks like a CP/M volume. - * - * We test a few fields in the volume directory for validity. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t dirBuf[kBlkSize * kVolDirCount]; - uint8_t* dptr; - int i; - - assert(sizeof(dirBuf) == DiskFSCPM::kFullDirSize); - - for (i = 0; i < kVolDirCount; i++) { - dierr = pImg->ReadBlockSwapped(kVolDirBlock + i, dirBuf + kBlkSize*i, - imageOrder, DiskImg::kSectorOrderCPM); - if (dierr != kDIErrNone) - goto bail; - } - - dptr = dirBuf; - for (i = 0; i < DiskFSCPM::kFullDirSize/DiskFSCPM::kDirectoryEntryLen; i++) - { - if (*dptr != kNoDataByte) { - /* - * Usually userNumber is 0, but sometimes not. It's expected to - * be < 0x20 for a normal file, may be 0x21 or 0x22 for special - * entries (volume label, date stamps). - */ - if (*dptr > kMaxSpecialUserNumber) { - dierr = kDIErrFilesystemNotFound; - break; - } - - /* extent counter, 0-31 */ - if (dptr[12] > kMaxExtent) { - dierr = kDIErrFilesystemNotFound; - break; - } - - /* check for a valid filename here; high bit may be set on some bytes */ - uint8_t firstLet = *(dptr+1) & 0x7f; - if (firstLet < 0x20) { - dierr = kDIErrFilesystemNotFound; - break; - } - } - dptr += DiskFSCPM::kDirectoryEntryLen; - } - if (dierr == kDIErrNone) { - LOGI(" CPM found clean directory, imageOrder=%d", imageOrder); - } - -bail: - return dierr; -} - -/* - * Test to see if the image is a CP/M disk. - * - * On the Apple II, these were always on 5.25" disks. However, it's possible - * to create hard drive volumes up to 8MB. - */ -/*static*/ DIError DiskFSCPM::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - /* CP/M disks use 1K blocks, so ignore anything with odd count */ - if (pImg->GetNumBlocks() == 0 || - (pImg->GetNumBlocks() & 0x01) != 0) - { - LOGI(" CPM rejecting image with numBlocks=%ld", - pImg->GetNumBlocks()); - return kDIErrFilesystemNotFound; - } - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i]) == kDIErrNone) { - *pOrder = ordering[i]; - *pFormat = DiskImg::kFormatCPM; - return kDIErrNone; - } - } - - LOGI(" CPM didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSCPM::Initialize(void) -{ - DIError dierr = kDIErrNone; - - dierr = ReadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - fVolumeUsage.Create(fpImg->GetNumBlocks()); - dierr = ScanFileUsage(); - if (dierr != kDIErrNone) { - /* this might not be fatal; just means that *some* files are bad */ - dierr = kDIErrNone; - goto bail; - } - - fDiskIsGood = CheckDiskIsGood(); - - fVolumeUsage.Dump(); - - //A2File* pFile; - //pFile = GetNextFile(NULL); - //while (pFile != NULL) { - // pFile->Dump(); - // pFile = GetNextFile(pFile); - //} - -bail: - return dierr; -} - -/* - * Read the entire CP/M catalog (all 2K of it) into memory, and parse - * out the individual files. - * - * A single file can have more than one directory entry. We only want - * to create an A2File object for the first one. - */ -DIError DiskFSCPM::ReadCatalog(void) -{ - DIError dierr = kDIErrNone; - uint8_t dirBuf[kFullDirSize]; - uint8_t* dptr; - int i; - - for (i = 0; i < kVolDirCount; i++) { - dierr = fpImg->ReadBlock(kVolDirBlock + i, dirBuf + kBlkSize*i); - if (dierr != kDIErrNone) - goto bail; - } - - dptr = dirBuf; - for (i = 0; i < kNumDirEntries; i++) { - fDirEntry[i].userNumber = dptr[0x00]; - /* copy the filename, stripping the high bits off */ - for (int j = 0; j < kDirFileNameLen; j++) - fDirEntry[i].fileName[j] = dptr[0x01 + j] & 0x7f; - fDirEntry[i].fileName[kDirFileNameLen] = '\0'; - fDirEntry[i].extent = dptr[0x0c] + dptr[0x0e] * kExtentsInLowByte; - fDirEntry[i].S1 = dptr[0x0d]; - fDirEntry[i].records = dptr[0x0f]; - memcpy(fDirEntry[i].blocks, &dptr[0x10], kDirEntryBlockCount); - fDirEntry[i].readOnly = (dptr[0x09] & 0x80) != 0; - fDirEntry[i].system = (dptr[0x0a] & 0x80) != 0; - fDirEntry[i].badBlockList = false; // set if block list is bad - - dptr += kDirectoryEntryLen; - } - - /* create an entry for the first extent of each file */ - for (i = 0; i < kNumDirEntries; i++) { - A2FileCPM* pFile; - - if (fDirEntry[i].userNumber == kNoDataByte || fDirEntry[i].extent != 0) - continue; - if (fDirEntry[i].userNumber > kMaxUserNumber) { - /* skip over volume label, date stamps, etc */ - LOGI("Skipping entry with userNumber=0x%02x", - fDirEntry[i].userNumber); - } - - pFile = new A2FileCPM(this, fDirEntry); - FormatName(pFile->fFileName, (char*)fDirEntry[i].fileName); - pFile->fReadOnly = fDirEntry[i].readOnly; - pFile->fDirIdx = i; - - pFile->fLength = 0; - dierr = ComputeLength(pFile); - if (dierr != kDIErrNone) { - pFile->SetQuality(A2File::kQualityDamaged); - dierr = kDIErrNone; - } - AddFileToList(pFile); - } - - /* - * Validate the list of blocks. - */ - int maxCpmBlock; - maxCpmBlock = (fpImg->GetNumBlocks() - kVolDirBlock) / 2; - for (i = 0; i < kNumDirEntries; i++) { - if (fDirEntry[i].userNumber == kNoDataByte) - continue; - for (int j = 0; j < kDirEntryBlockCount; j++) { - if (fDirEntry[i].blocks[j] >= maxCpmBlock) { - LOGI(" CPM invalid block %d in file '%s'", - fDirEntry[i].blocks[j], fDirEntry[i].fileName); - //pFile->SetQuality(A2File::kQualityDamaged); - fDirEntry[i].badBlockList = true; - break; - } - } - } - -bail: - return dierr; -} - -/* - * Reformat from 11 chars with spaces into clean xxxxx.yyy format. - */ -void DiskFSCPM::FormatName(char* dstBuf, const char* srcBuf) -{ - char workBuf[kDirFileNameLen+1]; - char* cp; - - assert(strlen(srcBuf) < sizeof(workBuf)); - strcpy(workBuf, srcBuf); - - cp = workBuf; - while (*cp != '\0') { - //*cp &= 0x7f; // [no longer necessary] - if (*cp == ' ') - *cp = '\0'; - if (*cp == ':') // don't think this is allowed, but check - *cp = 'X'; // for it anyway - cp++; - } - - strcpy(dstBuf, workBuf); - dstBuf[8] = '\0'; // in case filename part is full 8 chars - strcat(dstBuf, "."); - strcat(dstBuf, workBuf+8); - - assert(strlen(dstBuf) <= A2FileCPM::kMaxFileName); -} - -/* - * Compute the length of a file. Sets "pFile->fLength". - * - * This requires walking through the list of extents and looking for the - * last one. We use the "records" field of the last extent to determine - * the file length. - * - * (Should probably just get the block list and then walk that, rather than - * having directory parse code in two places.) - */ -DIError DiskFSCPM::ComputeLength(A2FileCPM* pFile) -{ - int i; - int best, maxExtent; - - best = maxExtent = -1; - - for (i = 0; i < DiskFSCPM::kNumDirEntries; i++) { - if (fDirEntry[i].userNumber == kNoDataByte) - continue; - - if (strcmp((const char*)fDirEntry[i].fileName, - (const char*)fDirEntry[pFile->fDirIdx].fileName) == 0 && - fDirEntry[i].userNumber == fDirEntry[pFile->fDirIdx].userNumber) - { - /* this entry is part of the file */ - if (fDirEntry[i].extent > maxExtent) { - best = i; - maxExtent = fDirEntry[i].extent; - } - } - } - - if (maxExtent < 0 || best < 0) { - LOGI(" CPM couldn't find existing file '%s'!", pFile->fFileName); - assert(false); - return kDIErrInternal; - } - - pFile->fLength = kDirEntryBlockCount * 1024 * maxExtent + - fDirEntry[best].records * 128; - - return kDIErrNone; -} - - -/* - * Scan file usage into the volume usage map. - * - * Tracks 0, 1, and 2 are always used by the boot loader. The volume directory - * is on the first half of track 3 (blocks 0 and 1). - */ -DIError DiskFSCPM::ScanFileUsage(void) -{ - int cpmBlock; - int i, j; - - for (i = 0; i < kVolDirBlock; i++) - SetBlockUsage(i, VolumeUsage::kChunkPurposeSystem); - for (i = kVolDirBlock; i < kVolDirBlock + kVolDirCount; i++) - SetBlockUsage(i, VolumeUsage::kChunkPurposeVolumeDir); - - for (i = 0; i < kNumDirEntries; i++) { - if (fDirEntry[i].userNumber == kNoDataByte) - continue; - if (fDirEntry[i].badBlockList) - continue; - - for (j = 0; j < kDirEntryBlockCount; j++) { - cpmBlock = fDirEntry[i].blocks[j]; - if (cpmBlock == 0) - break; - SetBlockUsage(CPMToProDOSBlock(cpmBlock), - VolumeUsage::kChunkPurposeUserData); - SetBlockUsage(CPMToProDOSBlock(cpmBlock)+1, - VolumeUsage::kChunkPurposeUserData); - } - } - - return kDIErrNone; -} - -/* - * Update an entry in the usage map. - * - * "block" is a 512-byte block, so you will have to call here twice for every - * 1K CP/M block. - */ -void DiskFSCPM::SetBlockUsage(long block, VolumeUsage::ChunkPurpose purpose) -{ - VolumeUsage::ChunkState cstate; - - if (fVolumeUsage.GetChunkState(block, &cstate) != kDIErrNone) { - LOGI(" CPM ERROR: unable to set state on block %ld", block); - return; - } - - if (cstate.isUsed) { - cstate.purpose = VolumeUsage::kChunkPurposeConflict; - LOGI(" CPM conflicting uses for block=%ld", block); - } else { - cstate.isUsed = true; - cstate.isMarkedUsed = true; // no volume bitmap - cstate.purpose = purpose; - } - fVolumeUsage.SetChunkState(block, &cstate); -} - - -/* - * Scan for damaged files and conflicting file allocation entries. - * - * Appends some entries to the DiskImg notes, so this should only be run - * once per DiskFS. - * - * Returns "true" if disk appears to be perfect, "false" otherwise. - */ -bool DiskFSCPM::CheckDiskIsGood(void) -{ - //DIError dierr; - bool result = true; - - //if (fEarlyDamage) - // result = false; - - /* - * TO DO: look for multiple files occupying the same blocks. - */ - - /* - * Scan for "damaged" or "suspicious" files diagnosed earlier. - */ - bool damaged, suspicious; - ScanForDamagedFiles(&damaged, &suspicious); - - if (damaged) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files are damaged."); - result = false; - } else if (suspicious) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files look suspicious."); - result = false; - } - - return result; -} - - -/* - * =========================================================================== - * A2FileCPM - * =========================================================================== - */ - -/* - * Not a whole lot to do, since there's no fancy index blocks. - * - * Calling GetBlockList twice is probably not the best way to go through life. - * This needs an overhaul. - */ -DIError A2FileCPM::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - DIError dierr; - A2FDCPM* pOpenFile = NULL; - - if (fpOpenFile != NULL) - return kDIErrAlreadyOpen; - if (rsrcFork) - return kDIErrForkNotFound; - - assert(readOnly); - - pOpenFile = new A2FDCPM(this); - - dierr = GetBlockList(&pOpenFile->fBlockCount, NULL); - if (dierr != kDIErrNone) - goto bail; - - pOpenFile->fBlockList = new uint8_t[pOpenFile->fBlockCount+1]; - pOpenFile->fBlockList[pOpenFile->fBlockCount] = 0xff; - - dierr = GetBlockList(&pOpenFile->fBlockCount, pOpenFile->fBlockList); - if (dierr != kDIErrNone) - goto bail; - - assert(pOpenFile->fBlockList[pOpenFile->fBlockCount] == 0xff); - - pOpenFile->fOffset = 0; - //fOpen = true; - - fpOpenFile = pOpenFile; - *ppOpenFile = pOpenFile; - pOpenFile = NULL; - -bail: - delete pOpenFile; - return dierr; -} - - -/* - * Get the complete block list for a file. This will involve reading - * one or more directory entries. - * - * Call this once with "blockBuf" equal to "NULL" to get the block count, - * then call a second time after allocating blockBuf. - */ -DIError A2FileCPM::GetBlockList(long* pBlockCount, uint8_t* blockBuf) const -{ - di_off_t length = fLength; - int blockCount = 0; - int i, j; - - /* - * Run through the entries, pulling blocks out until we account for the - * entire length of the file. - * - * [Should probably pay more attention to extent numbers, making sure - * that they make sense. Not vital until we allow writes.] - */ - for (i = 0; i < DiskFSCPM::kNumDirEntries; i++) { - if (length <= 0) - break; - if (fpDirEntry[i].userNumber == kNoDataByte) - continue; - - if (strcmp((const char*)fpDirEntry[i].fileName, - (const char*)fpDirEntry[fDirIdx].fileName) == 0 && - fpDirEntry[i].userNumber == fpDirEntry[fDirIdx].userNumber) - { - /* this entry is part of the file */ - for (j = 0; j < DiskFSCPM::kDirEntryBlockCount; j++) { - if (fpDirEntry[i].blocks[j] == 0) { - LOGI(" CPM found sparse block %d/%d", i, j); - } - blockCount++; - - if (blockBuf != NULL) { - long listOffset = j + - fpDirEntry[i].extent * DiskFSCPM::kDirEntryBlockCount; - blockBuf[listOffset] = fpDirEntry[i].blocks[j]; - } - - length -= 1024; - if (length <= 0) - break; - } - } - } - - if (length > 0) { - LOGI(" CPM WARNING: can't account for %ld bytes!", (long) length); - //assert(false); - } - - //LOGI(" Returning blockCount=%d for '%s'", blockCount, - // fpDirEntry[fDirIdx].fileName); - if (pBlockCount != NULL) { - assert(blockBuf == NULL || *pBlockCount == blockCount); - *pBlockCount = blockCount; - } - - return kDIErrNone; -} - -/* - * Dump the contents of the A2File structure. - */ -void A2FileCPM::Dump(void) const -{ - LOGI("A2FileCPM '%s' length=%ld", fFileName, (long) fLength); -} - - -/* - * =========================================================================== - * A2FDCPM - * =========================================================================== - */ - -/* - * Read a chunk of data from the current offset. - */ -DIError A2FDCPM::Read(void* buf, size_t len, size_t* pActual) -{ - LOGI(" CP/M reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - - A2FileCPM* pFile = (A2FileCPM*) fpFile; - - /* don't allow them to read past the end of the file */ - if (fOffset + (long)len > pFile->fLength) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (size_t) (pFile->fLength - fOffset); - } - if (pActual != NULL) - *pActual = len; - long incrLen = len; - - DIError dierr = kDIErrNone; - const int kCPMBlockSize = kBlkSize*2; - assert(kCPMBlockSize == 1024); - uint8_t blkBuf[kCPMBlockSize]; - int blkIndex = (int) (fOffset / kCPMBlockSize); - int bufOffset = (int) (fOffset % kCPMBlockSize); // (& 0x3ff) - size_t thisCount; - long prodosBlock; - - if (len == 0) - return kDIErrNone; - assert(pFile->fLength != 0); - - while (len) { - if (blkIndex >= fBlockCount) { - /* ran out of data */ - return kDIErrDataUnderrun; - } - - if (fBlockList[blkIndex] == 0) { - /* - * Sparse block. - */ - memset(blkBuf, kNoDataByte, sizeof(blkBuf)); - } else { - /* - * Read one CP/M block (two ProDOS blocks) and pull out the - * set of data that the user wants. - * - * On some Microsoft Softcard disks, the first three tracks hold - * file data rather than the system image. - */ - prodosBlock = DiskFSCPM::CPMToProDOSBlock(fBlockList[blkIndex]); - if (prodosBlock >= 280) - prodosBlock -= 280; - - dierr = fpFile->GetDiskFS()->GetDiskImg()->ReadBlock(prodosBlock, - blkBuf); - if (dierr != kDIErrNone) { - LOGI(" CP/M error1 reading file '%s'", pFile->fFileName); - return dierr; - } - dierr = fpFile->GetDiskFS()->GetDiskImg()->ReadBlock(prodosBlock+1, - blkBuf + kBlkSize); - if (dierr != kDIErrNone) { - LOGI(" CP/M error2 reading file '%s'", pFile->fFileName); - return dierr; - } - } - - thisCount = kCPMBlockSize - bufOffset; - if (thisCount > len) - thisCount = len; - - memcpy(buf, blkBuf + bufOffset, thisCount); - len -= thisCount; - buf = (char*)buf + thisCount; - - bufOffset = 0; - blkIndex++; - } - - fOffset += incrLen; - - return dierr; -} - -/* - * Write data at the current offset. - */ -DIError A2FDCPM::Write(const void* buf, size_t len, size_t* pActual) -{ - return kDIErrNotSupported; -} - -/* - * Seek to a new offset. - */ -DIError A2FDCPM::Seek(di_off_t offset, DIWhence whence) -{ - di_off_t fileLength = ((A2FileCPM*) fpFile)->fLength; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fileLength) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fileLength) - return kDIErrInvalidArg; - fOffset = fileLength + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fileLength - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fileLength); - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDCPM::Tell(void) -{ - return fOffset; -} - -/* - * Release file state, such as it is. - */ -DIError A2FDCPM::Close(void) -{ - fpFile->CloseDescr(this); - return kDIErrNone; -} - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDCPM::GetSectorCount(void) const -{ - return fBlockCount * 4; -} -long A2FDCPM::GetBlockCount(void) const -{ - return fBlockCount * 2; -} - -/* - * Return the Nth track/sector in this file. - */ -DIError A2FDCPM::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - long cpmIdx = sectorIdx / 4; // 4 256-byte sectors per 1K CP/M block - if (cpmIdx >= fBlockCount) - return kDIErrInvalidIndex; // CP/M files can have *no* storage - - long cpmBlock = fBlockList[cpmIdx]; - long prodosBlock = DiskFSCPM::CPMToProDOSBlock(cpmBlock); - if (sectorIdx & 0x02) - prodosBlock++; - - BlockToTrackSector(prodosBlock, (sectorIdx & 0x01) != 0, pTrack, pSector); - return kDIErrNone; -} -/* - * Return the Nth 512-byte block in this file. Since things aren't stored - * in 512-byte blocks, we grab the appropriate 1K block and pick half. - */ -DIError A2FDCPM::GetStorage(long blockIdx, long* pBlock) const -{ - long cpmIdx = blockIdx / 2; // 4 256-byte sectors per 1K CP/M block - if (cpmIdx >= fBlockCount) - return kDIErrInvalidIndex; - - long cpmBlock = fBlockList[cpmIdx]; - long prodosBlock = DiskFSCPM::CPMToProDOSBlock(cpmBlock); - if (blockIdx & 0x01) - prodosBlock++; - - *pBlock = prodosBlock; - assert(*pBlock < fpFile->GetDiskFS()->GetDiskImg()->GetNumBlocks()); - return kDIErrNone; -} diff --git a/ciderpress/diskimg/CP_WNASPI32.H b/ciderpress/diskimg/CP_WNASPI32.H deleted file mode 100644 index 5695f9b..0000000 --- a/ciderpress/diskimg/CP_WNASPI32.H +++ /dev/null @@ -1,325 +0,0 @@ -/****************************************************************************** -** -** Module Name: wnaspi32.h -** -** Description: Header file for ASPI for Win32. This header includes -** macro and type declarations, and can be included without -** modification when using Borland C++ or Microsoft Visual -** C++ with 32-bit compilation. If you are using a different -** compiler then you MUST ensure that structures are packed -** onto byte alignments, and that C++ name mangling is turned -** off. -** -** Notes: This file created using 4 spaces per tab. -** -******************************************************************************/ - -#ifndef DISKIMG_WNASPI32_H -#define DISKIMG_WNASPI32_H - -/* -** Make sure structures are packed and undecorated. -*/ - -#ifdef __BORLANDC__ -#pragma option -a1 -#endif //__BORLANDC__ - -#ifdef _MSC_VER -#pragma pack(1) -#endif //__MSC_VER - -#ifdef __cplusplus -extern "C" { -#endif //__cplusplus - -//***************************************************************************** -// %%% SCSI MISCELLANEOUS EQUATES %%% -//***************************************************************************** - -#define SENSE_LEN 18 // Default sense buffer length (ATM: was 14) -#define SRB_DIR_SCSI 0x00 // Direction determined by SCSI -#define SRB_POSTING 0x01 // Enable ASPI posting -#define SRB_ENABLE_RESIDUAL_COUNT 0x04 // Enable residual byte count reporting -#define SRB_DIR_IN 0x08 // Transfer from SCSI target to host -#define SRB_DIR_OUT 0x10 // Transfer from host to SCSI target -#define SRB_EVENT_NOTIFY 0x40 // Enable ASPI event notification - -#define RESIDUAL_COUNT_SUPPORTED 0x02 // Extended buffer flag -#define MAX_SRB_TIMEOUT 108000lu // 30 hour maximum timeout in s -#define DEFAULT_SRB_TIMEOUT 108000lu // Max timeout by default - - -//***************************************************************************** -// %%% ASPI Command Definitions %%% -//***************************************************************************** - -#define SC_HA_INQUIRY 0x00 // Host adapter inquiry -#define SC_GET_DEV_TYPE 0x01 // Get device type -#define SC_EXEC_SCSI_CMD 0x02 // Execute SCSI command -#define SC_ABORT_SRB 0x03 // Abort an SRB -#define SC_RESET_DEV 0x04 // SCSI bus device reset -#define SC_SET_HA_PARMS 0x05 // Set HA parameters -#define SC_GET_DISK_INFO 0x06 // Get Disk information -#define SC_RESCAN_SCSI_BUS 0x07 // ReBuild SCSI device map -#define SC_GETSET_TIMEOUTS 0x08 // Get/Set target timeouts - -//***************************************************************************** -// %%% SRB Status %%% -//***************************************************************************** - -#define SS_PENDING 0x00 // SRB being processed -#define SS_COMP 0x01 // SRB completed without error -#define SS_ABORTED 0x02 // SRB aborted -#define SS_ABORT_FAIL 0x03 // Unable to abort SRB -#define SS_ERR 0x04 // SRB completed with error - -#define SS_INVALID_CMD 0x80 // Invalid ASPI command -#define SS_INVALID_HA 0x81 // Invalid host adapter number -#define SS_NO_DEVICE 0x82 // SCSI device not installed - -#define SS_INVALID_SRB 0xE0 // Invalid parameter set in SRB -#define SS_OLD_MANAGER 0xE1 // ASPI manager doesn't support Windows -#define SS_BUFFER_ALIGN 0xE1 // Buffer not aligned (replaces OLD_MANAGER in Win32) -#define SS_ILLEGAL_MODE 0xE2 // Unsupported Windows mode -#define SS_NO_ASPI 0xE3 // No ASPI managers resident -#define SS_FAILED_INIT 0xE4 // ASPI for windows failed init -#define SS_ASPI_IS_BUSY 0xE5 // No resources available to execute cmd -#define SS_BUFFER_TO_BIG 0xE6 // Buffer size to big to handle! -#define SS_MISMATCHED_COMPONENTS 0xE7 // The DLLs/EXEs of ASPI don't version check -#define SS_NO_ADAPTERS 0xE8 // No host adapters to manage -#define SS_INSUFFICIENT_RESOURCES 0xE9 // Couldn't allocate resources needed to init -#define SS_ASPI_IS_SHUTDOWN 0xEA // Call came to ASPI after PROCESS_DETACH -#define SS_BAD_INSTALL 0xEB // The DLL or other components are installed wrong - -//***************************************************************************** -// %%% Host Adapter Status %%% -//***************************************************************************** - -#define HASTAT_OK 0x00 // Host adapter did not detect an error -#define HASTAT_SEL_TO 0x11 // Selection Timeout -#define HASTAT_DO_DU 0x12 // Data overrun data underrun -#define HASTAT_BUS_FREE 0x13 // Unexpected bus free -#define HASTAT_PHASE_ERR 0x14 // Target bus phase sequence failure -#define HASTAT_TIMEOUT 0x09 // Timed out while SRB was waiting to be processed. -#define HASTAT_COMMAND_TIMEOUT 0x0B // Adapter timed out processing SRB. -#define HASTAT_MESSAGE_REJECT 0x0D // While processing SRB, the adapter received a MESSAGE -#define HASTAT_BUS_RESET 0x0E // A bus reset was detected. -#define HASTAT_PARITY_ERROR 0x0F // A parity error was detected. -#define HASTAT_REQUEST_SENSE_FAILED 0x10 // The adapter failed in issuing - -//***************************************************************************** -// %%% SRB - HOST ADAPTER INQUIRY - SC_HA_INQUIRY (0) %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_HA_INQUIRY - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 ASPI request flags - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 - BYTE HA_Count; // 08/008 Number of host adapters present - BYTE HA_SCSI_ID; // 09/009 SCSI ID of host adapter - BYTE HA_ManagerId[16]; // 0A/010 String describing the manager - BYTE HA_Identifier[16]; // 1A/026 String describing the host adapter - BYTE HA_Unique[16]; // 2A/042 Host Adapter Unique parameters - WORD HA_Rsvd1; // 3A/058 Reserved, MUST = 0 -} -SRB_HAInquiry, *PSRB_HAInquiry, FAR *LPSRB_HAInquiry; - -//***************************************************************************** -// %%% SRB - GET DEVICE TYPE - SC_GET_DEV_TYPE (1) %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GET_DEV_TYPE - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 Reserved, MUST = 0 - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 - BYTE SRB_Target; // 08/008 Target's SCSI ID - BYTE SRB_Lun; // 09/009 Target's LUN number - BYTE SRB_DeviceType; // 0A/010 Target's peripheral device type - BYTE SRB_Rsvd1; // 0B/011 Reserved, MUST = 0 -} -SRB_GDEVBlock, *PSRB_GDEVBlock, FAR *LPSRB_GDEVBlock; - -//***************************************************************************** -// %%% SRB - EXECUTE SCSI COMMAND - SC_EXEC_SCSI_CMD (2) %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_EXEC_SCSI_CMD - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 ASPI request flags - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved - BYTE SRB_Target; // 08/008 Target's SCSI ID - BYTE SRB_Lun; // 09/009 Target's LUN number - WORD SRB_Rsvd1; // 0A/010 Reserved for Alignment - DWORD SRB_BufLen; // 0C/012 Data Allocation Length - BYTE FAR *SRB_BufPointer; // 10/016 Data Buffer Pointer - BYTE SRB_SenseLen; // 14/020 Sense Allocation Length - BYTE SRB_CDBLen; // 15/021 CDB Length - BYTE SRB_HaStat; // 16/022 Host Adapter Status - BYTE SRB_TargStat; // 17/023 Target Status - VOID FAR *SRB_PostProc; // 18/024 Post routine - BYTE SRB_Rsvd2[20]; // 1C/028 Reserved, MUST = 0 - BYTE CDBByte[16]; // 30/048 SCSI CDB - BYTE SenseArea[SENSE_LEN+2]; // 50/064 Request Sense buffer -} -SRB_ExecSCSICmd, *PSRB_ExecSCSICmd, FAR *LPSRB_ExecSCSICmd; - -//***************************************************************************** -// %%% SRB - ABORT AN SRB - SC_ABORT_SRB (3) %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_ABORT_SRB - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 Reserved - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved - VOID FAR *SRB_ToAbort; // 08/008 Pointer to SRB to abort -} -SRB_Abort, *PSRB_Abort, FAR *LPSRB_Abort; - -//***************************************************************************** -// %%% SRB - BUS DEVICE RESET - SC_RESET_DEV (4) %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_RESET_DEV - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 ASPI request flags - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved - BYTE SRB_Target; // 08/008 Target's SCSI ID - BYTE SRB_Lun; // 09/009 Target's LUN number - BYTE SRB_Rsvd1[12]; // 0A/010 Reserved for Alignment - BYTE SRB_HaStat; // 16/022 Host Adapter Status - BYTE SRB_TargStat; // 17/023 Target Status - VOID FAR *SRB_PostProc; // 18/024 Post routine - BYTE SRB_Rsvd2[36]; // 1C/028 Reserved, MUST = 0 -} -SRB_BusDeviceReset, *PSRB_BusDeviceReset, FAR *LPSRB_BusDeviceReset; - -//***************************************************************************** -// %%% SRB - GET DISK INFORMATION - SC_GET_DISK_INFO %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GET_DISK_INFO - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 Reserved, MUST = 0 - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 - BYTE SRB_Target; // 08/008 Target's SCSI ID - BYTE SRB_Lun; // 09/009 Target's LUN number - BYTE SRB_DriveFlags; // 0A/010 Driver flags - BYTE SRB_Int13HDriveInfo; // 0B/011 Host Adapter Status - BYTE SRB_Heads; // 0C/012 Preferred number of heads translation - BYTE SRB_Sectors; // 0D/013 Preferred number of sectors translation - BYTE SRB_Rsvd1[10]; // 0E/014 Reserved, MUST = 0 -} -SRB_GetDiskInfo, *PSRB_GetDiskInfo, FAR *LPSRB_GetDiskInfo; - -//***************************************************************************** -// %%% SRB - RESCAN SCSI BUS(ES) ON SCSIPORT %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_RESCAN_SCSI_BUS - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 Reserved, MUST = 0 - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 -} -SRB_RescanPort, *PSRB_RescanPort, FAR *LPSRB_RescanPort; - -//***************************************************************************** -// %%% SRB - GET/SET TARGET TIMEOUTS %%% -//***************************************************************************** - -typedef struct // Offset -{ // HX/DEC - BYTE SRB_Cmd; // 00/000 ASPI command code = SC_GETSET_TIMEOUTS - BYTE SRB_Status; // 01/001 ASPI command status byte - BYTE SRB_HaId; // 02/002 ASPI host adapter number - BYTE SRB_Flags; // 03/003 ASPI request flags - DWORD SRB_Hdr_Rsvd; // 04/004 Reserved, MUST = 0 - BYTE SRB_Target; // 08/008 Target's SCSI ID - BYTE SRB_Lun; // 09/009 Target's LUN number - DWORD SRB_Timeout; // 0A/010 Timeout in half seconds -} -SRB_GetSetTimeouts, *PSRB_GetSetTimeouts, FAR *LPSRB_GetSetTimeouts; - -//***************************************************************************** -// %%% ASPIBUFF - Structure For Controllng I/O Buffers %%% -//***************************************************************************** - -typedef struct tag_ASPI32BUFF // Offset -{ // HX/DEC - PBYTE AB_BufPointer; // 00/000 Pointer to the ASPI allocated buffer - DWORD AB_BufLen; // 04/004 Length in bytes of the buffer - DWORD AB_ZeroFill; // 08/008 Flag set to 1 if buffer should be zeroed - DWORD AB_Reserved; // 0C/012 Reserved -} -ASPI32BUFF, *PASPI32BUFF, FAR *LPASPI32BUFF; - -//***************************************************************************** -// %%% PROTOTYPES - User Callable ASPI for Win32 Functions %%% -//***************************************************************************** - -typedef void *LPSRB; - -#if defined(__BORLANDC__) - -DWORD _import GetASPI32SupportInfo( void ); -DWORD _import SendASPI32Command( LPSRB ); -BOOL _import GetASPI32Buffer( PASPI32BUFF ); -BOOL _import FreeASPI32Buffer( PASPI32BUFF ); -BOOL _import TranslateASPI32Address( PDWORD, PDWORD ); - -#elif defined(_MSC_VER) - -__declspec(dllimport) DWORD GetASPI32SupportInfo( void ); -__declspec(dllimport) DWORD SendASPI32Command( LPSRB ); -__declspec(dllimport) BOOL GetASPI32Buffer( PASPI32BUFF ); -__declspec(dllimport) BOOL FreeASPI32Buffer( PASPI32BUFF ); -__declspec(dllimport) BOOL TranslateASPI32Address( PDWORD, PDWORD ); - -#else - -extern DWORD GetASPI32SupportInfo( void ); -extern DWORD SendASPI32Command( LPSRB ); -extern BOOL GetASPI32Buffer( PASPI32BUFF ); -extern BOOL FreeASPI32Buffer( PASPI32BUFF ); -extern BOOL TranslateASPI32Address( PDWORD, PDWORD ); - -#endif - -/* -** Restore compiler default packing and close off the C declarations. -*/ - -#ifdef __BORLANDC__ -#pragma option -a. -#endif //__BORLANDC__ - -#ifdef _MSC_VER -#pragma pack() -#endif //_MSC_VER - -#ifdef __cplusplus -} -#endif //__cplusplus - -#endif //DISKIMG_WNASPI32_H diff --git a/ciderpress/diskimg/CP_ntddscsi.h b/ciderpress/diskimg/CP_ntddscsi.h deleted file mode 100644 index 5ac6a45..0000000 --- a/ciderpress/diskimg/CP_ntddscsi.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * ntddscsi.h - * - * SCSI port IOCTL interface. - * - * This file is part of the w32api package. - * - * Contributors: - * Created by Casper S. Hornstrup - * - * THIS SOFTWARE IS NOT COPYRIGHTED - * - * This source code is offered for use in the public domain. You may - * use, modify or distribute it freely. - * - * This code is distributed in the hope that it will be useful but - * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY - * DISCLAIMED. This includes but is not limited to warranties of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * - */ - -#ifndef DISKIMG_NTDDSCSI_H -#define DISKIMG_NTDDSCSI_H - -#if __GNUC__ >=3 -#pragma GCC system_header -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#pragma pack(push,4) - -//#include "ntddk.h" - -#ifndef ULONG_PTR -# define ULONG_PTR DWORD -#endif - - -#define DD_SCSI_DEVICE_NAME "\\Device\\ScsiPort" -#define DD_SCSI_DEVICE_NAME_U L"\\Device\\ScsiPort" - -#define IOCTL_SCSI_BASE FILE_DEVICE_CONTROLLER - -#define IOCTL_SCSI_GET_INQUIRY_DATA \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0403, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_GET_CAPABILITIES \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0404, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_GET_ADDRESS \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0406, METHOD_BUFFERED, FILE_ANY_ACCESS) - -#define IOCTL_SCSI_MINIPORT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0402, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_SCSI_PASS_THROUGH \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0401, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_SCSI_PASS_THROUGH_DIRECT \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0405, METHOD_BUFFERED, FILE_READ_ACCESS | FILE_WRITE_ACCESS) - -#define IOCTL_SCSI_RESCAN_BUS \ - CTL_CODE(IOCTL_SCSI_BASE, 0x0407, METHOD_BUFFERED, FILE_ANY_ACCESS) - - -//DEFINE_GUID(ScsiRawInterfaceGuid, \ -// 0x53f56309L, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -//DEFINE_GUID(WmiScsiAddressGuid, \ -// 0x53f5630fL, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); - -typedef struct _SCSI_PASS_THROUGH { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - ULONG_PTR DataBufferOffset; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH, *PSCSI_PASS_THROUGH; - -typedef struct _SCSI_PASS_THROUGH_DIRECT { - USHORT Length; - UCHAR ScsiStatus; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - UCHAR CdbLength; - UCHAR SenseInfoLength; - UCHAR DataIn; - ULONG DataTransferLength; - ULONG TimeOutValue; - PVOID DataBuffer; - ULONG SenseInfoOffset; - UCHAR Cdb[16]; -} SCSI_PASS_THROUGH_DIRECT, *PSCSI_PASS_THROUGH_DIRECT; - -typedef struct _SRB_IO_CONTROL { - ULONG HeaderLength; - UCHAR Signature[8]; - ULONG Timeout; - ULONG ControlCode; - ULONG ReturnCode; - ULONG Length; -} SRB_IO_CONTROL, *PSRB_IO_CONTROL; - -typedef struct _SCSI_ADDRESS { - ULONG Length; - UCHAR PortNumber; - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; -} SCSI_ADDRESS, *PSCSI_ADDRESS; - -typedef struct _SCSI_BUS_DATA { - UCHAR NumberOfLogicalUnits; - UCHAR InitiatorBusId; - ULONG InquiryDataOffset; -}SCSI_BUS_DATA, *PSCSI_BUS_DATA; - -typedef struct _SCSI_ADAPTER_BUS_INFO { - UCHAR NumberOfBuses; - SCSI_BUS_DATA BusData[1]; -} SCSI_ADAPTER_BUS_INFO, *PSCSI_ADAPTER_BUS_INFO; - -typedef struct _IO_SCSI_CAPABILITIES { - ULONG Length; - ULONG MaximumTransferLength; - ULONG MaximumPhysicalPages; - ULONG SupportedAsynchronousEvents; - ULONG AlignmentMask; - BOOLEAN TaggedQueuing; - BOOLEAN AdapterScansDown; - BOOLEAN AdapterUsesPio; -} IO_SCSI_CAPABILITIES, *PIO_SCSI_CAPABILITIES; - -typedef struct _SCSI_INQUIRY_DATA { - UCHAR PathId; - UCHAR TargetId; - UCHAR Lun; - BOOLEAN DeviceClaimed; - ULONG InquiryDataLength; - ULONG NextInquiryDataOffset; - UCHAR InquiryData[1]; -} SCSI_INQUIRY_DATA, *PSCSI_INQUIRY_DATA; - -#define SCSI_IOCTL_DATA_OUT 0 -#define SCSI_IOCTL_DATA_IN 1 -#define SCSI_IOCTL_DATA_UNSPECIFIED 2 - -struct ADAPTER_OBJECT; -typedef struct ADAPTER_OBJECT* PADAPTER_OBJECT; - -typedef struct _DUMP_POINTERS { - PADAPTER_OBJECT AdapterObject; - PVOID MappedRegisterBase; - PVOID DumpData; - PVOID CommonBufferVa; - LARGE_INTEGER CommonBufferPa; - ULONG CommonBufferSize; - BOOLEAN AllocateCommonBuffers; - BOOLEAN UseDiskDump; - UCHAR Spare1[2]; - PVOID DeviceObject; -} DUMP_POINTERS, *PDUMP_POINTERS; - - -#pragma pack(pop) - -#ifdef __cplusplus -} -#endif - -#endif /*DISKIMG_NTDDSCSI_H*/ diff --git a/ciderpress/diskimg/Container.cpp b/ciderpress/diskimg/Container.cpp deleted file mode 100644 index 93a5fc2..0000000 --- a/ciderpress/diskimg/Container.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Base "container FS" support. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * Blank out the volume usage map, setting all entries to "embedded". - */ -void DiskFSContainer::SetVolumeUsageMap(void) -{ - VolumeUsage::ChunkState cstate; - long block; - - fVolumeUsage.Create(fpImg->GetNumBlocks()); - - cstate.isUsed = true; - cstate.isMarkedUsed = true; - cstate.purpose = VolumeUsage::kChunkPurposeEmbedded; - - for (block = fpImg->GetNumBlocks()-1; block >= 0; block--) - fVolumeUsage.SetChunkState(block, &cstate); -} - - -/* - * Create a "placeholder" sub-volume. Useful for some of the tools when - * dealing with unformatted (or unknown-formatted) partitions. - */ -DIError DiskFSContainer::CreatePlaceholder(long startBlock, long numBlocks, - const char* partName, const char* partType, - DiskImg** ppNewImg, DiskFS** ppNewFS) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - LOGI(" %s/CrPl creating placeholder for %ld +%ld", GetDebugName(), - startBlock, numBlocks); - - if (startBlock > fpImg->GetNumBlocks()) { - LOGI(" %s/CrPl start block out of range (%ld vs %ld)", - GetDebugName(), startBlock, fpImg->GetNumBlocks()); - return kDIErrBadPartition; - } - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - if (partName != NULL) { - if (partType != NULL) - pNewImg->AddNote(DiskImg::kNoteInfo, - "Partition name='%s' type='%s'.", partName, partType); - else - pNewImg->AddNote(DiskImg::kNoteInfo, - "Partition name='%s'.", partName); - } - - dierr = pNewImg->OpenImage(fpImg, startBlock, numBlocks); - if (dierr != kDIErrNone) { - LOGI(" %s/CrPl: OpenImage(%ld,%ld) failed (err=%d)", - GetDebugName(), startBlock, numBlocks, dierr); - goto bail; - } - - /* - * If this slot isn't formatted at all, the call will return with - * "unknown FS". If it's formatted enough to pass the initial test, - * but fails during "Initialize", we'll have a non-unknown value - * for the FS format. We need to stomp that. - */ - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" %s/CrPl: analysis failed (err=%d)", GetDebugName(), dierr); - goto bail; - } - if (pNewImg->GetFSFormat() != DiskImg::kFormatUnknown) { - dierr = pNewImg->OverrideFormat(pNewImg->GetPhysicalFormat(), - DiskImg::kFormatUnknown, pNewImg->GetSectorOrder()); - if (dierr != kDIErrNone) { - LOGI(" %s/CrPl: unable to override to unknown", - GetDebugName()); - goto bail; - } - } - - /* open a DiskFS for the sub-image, allowing "unknown" */ - pNewFS = pNewImg->OpenAppropriateDiskFS(true); - if (pNewFS == NULL) { - LOGI(" %s/CrPl: OpenAppropriateDiskFS failed", GetDebugName()); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* sets the DiskImg ptr (and very little else) */ - dierr = pNewFS->Initialize(pNewImg, kInitFull); - if (dierr != kDIErrNone) { - LOGI(" %s/CrPl: init failed (err=%d)", GetDebugName(), dierr); - goto bail; - } - -bail: - if (dierr != kDIErrNone) { - delete pNewFS; - delete pNewImg; - } else { - *ppNewImg = pNewImg; - *ppNewFS = pNewFS; - } - return dierr; -} diff --git a/ciderpress/diskimg/DDD.cpp b/ciderpress/diskimg/DDD.cpp deleted file mode 100644 index c600211..0000000 --- a/ciderpress/diskimg/DDD.cpp +++ /dev/null @@ -1,629 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Pack and unpack DDD format. - */ -/* -The trouble with unpacking DOS DDD 2.x files: - -[ Most of this is no longer relevant, but the discussion is enlightening. ] - -DDD writes its files as DOS 3.3 binary (type 'B') files, which have -starting address and length embedded as the first 4 bytes. Unfortunately, it -cannot write the length, because the largest possible 16-bit length value is -only 64K. Instead, DDD sets it to zero. DDD v2.0 does store a copy of the -length *in sectors* in the filename (e.g. "<397>"), but this doesn't really -help much. When CiderPress goes to extract or view the file, it just sees a -zero-length binary file. - -CiderPress could make an exception and assume that any binary file with -zero length and more than one sector allocated has a length equal to the -number of sectors times 256. This could cause problems for other things, -but it's probably pretty safe. However, we still don't have an accurate -idea of where the end of the file is. - -Knowing where the file ends is important because there is no identifying -information or checksum in a DDD file. The only way to know that it's a -DDD compressed disk is to try to unpack it and see if you end up at exactly -140K at the same time that you run out of input. Without knowing where the -file really ends this test is much less certain. (DDD 2.5 appears to have -added some sort of checksum, which was appended to the DOS filename, but -without knowing how it was calculated there's no way to verify it.) - -The only safe way to make this work would be to skip the automatic format -detection and tell CiderPress that the file is definitely DDD format. -There's currently no easy way to do that without complicating the user -interface. Filename extensions might be useful in making the decision, -but they're rare under DOS 3.3, and I don't know if the "<397>" convention -is common to all versions of DDD. - -Complicating the matter is that, if a DOS DDD file (type 'B') is converted -to ProDOS, the first 4 bytes will be stripped off. Without unpacking -the file and knowing to within a byte where it ends, there's no way to -automatically tell whether to start at byte 0 or byte 4. (DDD Pro files -have four bytes of garbage at the very start, probably in an attempt to -retain compatibility with the DOS version. Because it uses REL files the -4 bytes of extra DOS stuff aren't added when the files are copied around, -so this was a reasonably smart thing to do, but it complicates matters -for CiderPress because a file extracted from DOS and a file extracted -from ProDOS will come out differently because only the DOS version has the -leading 4 bytes stripped. This could be avoided if the DOS file uses the -'R' or 'S' file type, but we still lack an accurate file length.) - -To unpack a file created by DOS DDD v2.x with CiderPress: - - In an emulator, copy the file to a ProDOS disk, using something that - guesses at the actual length when one isn't provided (Copy ][+ 9.0 - may work). - - Reduce the length to within a byte or two of the actual end of file. - This is nearly impossible, because DDD doesn't zero out the remaining - data in the last sector. - - Insert 4 bytes of garbage at the front of the file. My copy of DDD - Pro 1.1 seems to like 03 c9 bf d0. -Probably not worth the effort. Just unpack it with an emulator. - -In general DDD is a rather poor choice, because the compression isn't -very good and there's no checksum. ShrinkIt is a much better way to go. - -NOTE: DOS DDD v2.0 seems to have a bug where it doesn't always write the last -run correctly. On the DOS system master this caused the last half of the -last sector (FID's T/S list) to have garbage written instead of zero bytes, -which caused CP to label FID as damaged. DDD v2.1 and later doesn't -appear to have this issue. Unfortunate that DDD 2.0 is what shipped on the -SST disk. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -const int kNumSymbols = 256; -const int kNumFavorites = 20; -const int kRLEDelim = 0x97; // value MUST have high bit set -//const int kMaxExcessByteCount = WrapperDDD::kMaxDDDZeroCount + 1; -//const int kTrackLen = 4096; -//const int kNumTracks = 35; - -/* I suspect this is random garbage, but it's appearing consistently */ -const unsigned long kDDDProSignature = 0xd0bfc903; - - -/* - * =========================================================================== - * BitBuffer - * =========================================================================== - */ - -/* - * Class for getting and putting bits to and from a file. - */ -class WrapperDDD::BitBuffer { -public: - BitBuffer(void) : fpGFD(NULL), fBits(0), fBitCount(0), fIOFailure(false) {} - ~BitBuffer(void) {} - - void SetFile(GenericFD* pGFD) { fpGFD = pGFD; } - void PutBits(uint8_t bits, int numBits); - uint8_t GetBits(int numBits); - - bool IOFailure(void) const { return fIOFailure; } - - static uint8_t Reverse(uint8_t val); - -private: - GenericFD* fpGFD; - uint8_t fBits; - int fBitCount; - bool fIOFailure; -}; - -/* - * Add bits to the buffer. - * - * We roll the low bits out of "bits" and shift them to the left (in the - * reverse order in which they were passed in). As soon as we get 8 bits - * we flush. - */ -void WrapperDDD::BitBuffer::PutBits(uint8_t bits, int numBits) -{ - assert(fBitCount >= 0 && fBitCount < 8); - assert(numBits > 0 && numBits <= 8); - assert(fpGFD != NULL); - - DIError dierr; - - while (numBits--) { - fBits = (fBits << 1) | (bits & 0x01); - fBitCount++; - - if (fBitCount == 8) { - dierr = fpGFD->Write(&fBits, 1); - fIOFailure = (dierr != kDIErrNone); - fBitCount = 0; - } - - bits >>= 1; - } -} - -/* - * Get bits from the buffer. - * - * These come out in the order in which they appear in the file, which - * means that in some cases they will have to be reversed. - */ -uint8_t WrapperDDD::BitBuffer::GetBits(int numBits) -{ - assert(fBitCount >= 0 && fBitCount < 8); - assert(numBits > 0 && numBits <= 8); - assert(fpGFD != NULL); - - DIError dierr; - uint8_t retVal; - - if (fBitCount == 0) { - /* have no bits */ - dierr = fpGFD->Read(&fBits, 1); - fIOFailure = (dierr != kDIErrNone); - fBitCount = 8; - } - - if (numBits <= fBitCount) { - /* just serve up what we've already got */ - retVal = fBits >> (8 - numBits); - fBits <<= numBits; - fBitCount -= numBits; - } else { - /* some old, some new; load what we have right-aligned */ - retVal = fBits >> (8 - fBitCount); - numBits -= fBitCount; - - dierr = fpGFD->Read(&fBits, 1); - fIOFailure = (dierr != kDIErrNone); - fBitCount = 8; - - /* make room for the rest (also zeroes out the low bits) */ - retVal <<= numBits; - - /* add the high bits from the new byte */ - retVal |= fBits >> (8 - numBits); - fBits <<= numBits; - fBitCount -= numBits; - } - - return retVal; -} - -/* - * Utility function to reverse the order of bits in a byte. - */ -/*static*/ uint8_t WrapperDDD::BitBuffer::Reverse(uint8_t val) -{ - int i; - uint8_t result = 0; // init is to make valgrind happy - - for (i = 0; i < 8; i++) { - result = (result << 1) + (val & 0x01); - val >>= 1; - } - - return result; -} - - -/* - * =========================================================================== - * DDD compression functions - * =========================================================================== - */ - -/* - * These are all odd, which when they're written in reverse order means - * they all have their hi bits set. - */ -static const uint8_t kFavoriteBitEnc[kNumFavorites] = { - 0x03, 0x09, 0x1f, 0x0f, 0x07, 0x1b, 0x0b, 0x0d, 0x15, 0x37, - 0x3d, 0x25, 0x05, 0xb1, 0x11, 0x21, 0x01, 0x57, 0x5d, 0x1d -}; -static const int kFavoriteBitEncLen[kNumFavorites] = { - 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, - 6, 6, 6, 6, 6, 6, 6, 7, 7, 7 -}; - - -/* - * Pack a disk image with DDD. - * - * Assumes pSrcGFD points to DOS-ordered sectors. (This is enforced when the - * disk image is first being created.) - */ -/*static*/ DIError WrapperDDD::PackDisk(GenericFD* pSrcGFD, GenericFD* pWrapperGFD, - short diskVolNum) -{ - DIError dierr = kDIErrNone; - BitBuffer bitBuffer; - - assert(diskVolNum >= 0 && diskVolNum < 256); - - /* write four zeroes to replace the DOS addr/len bytes */ - /* (actually, let's write the apparent DDD Pro v1.1 signature instead) */ - WriteLongLE(pWrapperGFD, kDDDProSignature); - - bitBuffer.SetFile(pWrapperGFD); - - bitBuffer.PutBits(0x00, 3); - bitBuffer.PutBits((uint8_t)diskVolNum, 8); - - /* - * Process all tracks. - */ - for (int track = 0; track < kNumTracks; track++) { - uint8_t trackBuf[kTrackLen]; - - dierr = pSrcGFD->Read(trackBuf, kTrackLen); - if (dierr != kDIErrNone) { - LOGI(" DDD error during read (err=%d)", dierr); - goto bail; - } - - PackTrack(trackBuf, &bitBuffer); - } - - /* write 8 bits of zeroes to flush remaining data out of buffer */ - bitBuffer.PutBits(0x00, 8); - - /* write another zero byte because that's what DDD Pro v1.1 does */ - long zero; - zero = 0; - dierr = pWrapperGFD->Write(&zero, 1); - if (dierr != kDIErrNone) - goto bail; - - assert(dierr == kDIErrNone); -bail: - return dierr; -} - -/* - * Compress a track full of data. - */ -/*static*/ void WrapperDDD::PackTrack(const uint8_t* trackBuf, BitBuffer* pBitBuf) -{ - uint16_t freqCounts[kNumSymbols]; - uint8_t favorites[kNumFavorites]; - int i, fav; - - ComputeFreqCounts(trackBuf, freqCounts); - ComputeFavorites(freqCounts, favorites); - - /* write favorites */ - for (fav = 0; fav < kNumFavorites; fav++) - pBitBuf->PutBits(favorites[fav], 8); - - /* - * Compress track data. Store runs as { 0x97 char count }, where - * a count of zero means 256. - */ - const uint8_t* ucp = trackBuf; - for (i = 0; i < kTrackLen; i++, ucp++) { - if (i < (kTrackLen-3) && - *ucp == *(ucp+1) && - *ucp == *(ucp+2) && - *ucp == *(ucp+3)) - { - int runLen = 4; - i += 3; - ucp += 3; - - while (i < kTrackLen-1 && *ucp == *(ucp+1)) { - runLen++; - ucp++; - i++; - - if (runLen == 256) { - runLen = 0; - break; - } - } - - pBitBuf->PutBits(kRLEDelim, 8); // note kRLEDelim has hi bit set - pBitBuf->PutBits(*ucp, 8); - pBitBuf->PutBits(runLen, 8); - - } else { - /* - * Not a run, see if it's one of our favorites. - */ - for (fav = 0; fav < kNumFavorites; fav++) { - if (*ucp == favorites[fav]) - break; - } - if (fav == kNumFavorites) { - /* just a plain byte */ - pBitBuf->PutBits(0x00, 1); - pBitBuf->PutBits(*ucp, 8); - } else { - /* found a favorite; leading hi bit is implied */ - pBitBuf->PutBits(kFavoriteBitEnc[fav], kFavoriteBitEncLen[fav]); - } - } - } -} - - -/* - * Compute the #of times each byte appears in trackBuf. Runs of four - * bytes or longer are completely ignored. - * - * "trackBuf" holds kTrackLen bytes of data, and "freqCounts" holds - * kNumSymbols (256) 16-bit values. - */ -/*static*/ void WrapperDDD::ComputeFreqCounts(const uint8_t* trackBuf, - uint16_t* freqCounts) -{ - const uint8_t* ucp; - int i; - - memset(freqCounts, 0, 256 * sizeof(uint16_t)); - - ucp = trackBuf; - for (i = 0; i < kTrackLen; i++, ucp++) { - if (i < (kTrackLen-3) && - *ucp == *(ucp+1) && - *ucp == *(ucp+2) && - *ucp == *(ucp+3)) - { - int runLen = 4; // DEBUG only - i += 3; - ucp += 3; - - while (i < kTrackLen-1 && *ucp == *(ucp+1)) { - runLen++; - ucp++; - i++; - - if (runLen == 256) { - runLen = 0; - break; - } - } - - //LOGI("Found run of %d of 0x%02x", runLen, *ucp); - } else { - /* not a run, just update stats */ - freqCounts[*ucp]++; - } - } -} - -/* - * Find the 20 most frequently occurring symbols, in order. - * - * Modifies "freqCounts". - */ -/*static*/ void WrapperDDD::ComputeFavorites(uint16_t* freqCounts, - uint8_t* favorites) -{ - int i, fav; - - for (fav = 0; fav < kNumFavorites; fav++) { - uint16_t bestCount = 0; - uint8_t bestSym = 0; - - for (i = 0; i < kNumSymbols; i++) { - if (freqCounts[i] >= bestCount) { - bestSym = (uint8_t) i; - bestCount = freqCounts[i]; - } - } - - favorites[fav] = bestSym; - freqCounts[bestSym] = 0; - } - - //LOGI("FAVORITES: "); - //for (fav = 0; fav < kNumFavorites; fav++) - // LOGI("%02x", favorites[fav]); - //LOGI(""); -} - - -/* - * =========================================================================== - * DDD expansion functions - * =========================================================================== - */ - -/* - * This is the reverse of the kFavoriteBitEnc table. The bits are - * reversed and lack the high bit. - */ -static const uint8_t kFavoriteBitDec[kNumFavorites] = { - 0x04, 0x01, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a, 0x06, 0x05, 0x1b, - 0x0f, 0x09, 0x08, 0x03, 0x02, 0x01, 0x00, 0x35, 0x1d, 0x1c -}; - -/* - * Entry point for unpacking a disk image compressed with DDD. - * - * The result is an unadorned DOS-ordered image. - */ -/*static*/ DIError WrapperDDD::UnpackDisk(GenericFD* pGFD, GenericFD* pNewGFD, - short* pDiskVolNum) -{ - DIError dierr = kDIErrNone; - BitBuffer bitBuffer; - uint8_t val; - long lbuf; - - assert(pGFD != NULL); - assert(pNewGFD != NULL); - - /* read four zeroes to skip the DOS addr/len bytes */ - assert(sizeof(lbuf) >= 4); - dierr = pGFD->Read(&lbuf, 4); - if (dierr != kDIErrNone) - goto bail; - - bitBuffer.SetFile(pGFD); - - val = bitBuffer.GetBits(3); - if (val != 0) { - LOGI(" DDD bits not zero, this isn't a DDD II file (0x%02x)", val); - dierr = kDIErrGeneric; - goto bail; - } - val = bitBuffer.GetBits(8); - *pDiskVolNum = bitBuffer.Reverse(val); - LOGI(" DDD found disk volume num = %d", *pDiskVolNum); - - int track; - for (track = 0; track < kNumTracks; track++) { - uint8_t trackBuf[kTrackLen]; - - if (!UnpackTrack(&bitBuffer, trackBuf)) { - LOGI(" DDD failed unpacking track %d", track); - dierr = kDIErrBadCompressedData; - goto bail; - } - if (bitBuffer.IOFailure()) { - LOGI(" DDD failure or EOF on input file"); - dierr = kDIErrBadCompressedData; - goto bail; - } - dierr = pNewGFD->Write(trackBuf, kTrackLen); - if (dierr != kDIErrNone) - goto bail; - } - - /* - * We should be within a byte or two of the end of the file. Try - * to read more and expect it to fail. - * - * Unfortunately, if this was a DOS DDD file, we could be up to 256 - * bytes off (the 1 additional byte it adds plus the remaining 255 - * bytes in the sector). We have to choose between a tight auto-detect - * and the ability to process DOS DDD files. - * - * Fortunately the need to hit track boundaries exactly and the quick test - * for long runs of bytes provides some opportunity for correct - * detection. - */ - size_t actual; - char sctBuf[256 + 16]; - dierr = pGFD->Read(&sctBuf, sizeof(sctBuf), &actual); - if (dierr == kDIErrNone) { - if (actual > /*kMaxExcessByteCount*/ 256) { - LOGW(" DDD looks like too much data in input file (%lu extra)", - (unsigned long) actual); - dierr = kDIErrBadCompressedData; - goto bail; - } else { - LOGI(" DDD excess bytes (%lu) within normal parameters", - (unsigned long) actual); - } - } - - LOGI(" DDD looks like a DDD archive!"); - dierr = kDIErrNone; - -bail: - return dierr; -} - -/* - * Unpack a single track. - * - * Returns "true" if all went well, "false" if something failed. - */ -/*static*/ bool WrapperDDD::UnpackTrack(BitBuffer* pBitBuffer, uint8_t* trackBuf) -{ - uint8_t favorites[kNumFavorites]; - uint8_t val; - uint8_t* trackPtr; - int fav; - - /* - * Start by pulling our favorites out, in reverse order. - */ - for (fav = 0; fav < kNumFavorites; fav++) { - val = pBitBuffer->GetBits(8); - val = pBitBuffer->Reverse(val); - favorites[fav] = val; - } - - trackPtr = trackBuf; - - /* - * Keep pulling data out until the track is full. - */ - while (trackPtr < trackBuf + kTrackLen) { - val = pBitBuffer->GetBits(1); - if (!val) { - /* simple byte */ - val = pBitBuffer->GetBits(8); - val = pBitBuffer->Reverse(val); - *trackPtr++ = val; - } else { - /* try for a prefix match */ - int extraBits; - - val = pBitBuffer->GetBits(2); - - for (extraBits = 0; extraBits < 4; extraBits++) { - val = (val << 1) | pBitBuffer->GetBits(1); - int start, end; - - if (extraBits == 0) { - start = 0; - end = 2; - } else if (extraBits == 1) { - start = 2; - end = 9; - } else if (extraBits == 2) { - start = 9; - end = 17; - } else { - start = 17; - end = 20; - } - - while (start < end) { - if (val == kFavoriteBitDec[start]) { - /* winner! */ - *trackPtr++ = favorites[start]; - break; - } - start++; - } - if (start != end) - break; // we got it, break out of for loop - } - if (extraBits == 4) { - /* we didn't get it, this must be RLE */ - uint8_t rleChar; - int rleCount; - - (void) pBitBuffer->GetBits(1); // get last bit of 0x97 - val = pBitBuffer->GetBits(8); - rleChar = pBitBuffer->Reverse(val); - val = pBitBuffer->GetBits(8); - rleCount = pBitBuffer->Reverse(val); - //LOGI(" DDD found run of %d of 0x%02x", rleCount, rleChar); - - if (rleCount == 0) - rleCount = 256; - - /* make sure we won't overrun */ - if (trackPtr + rleCount > trackBuf + kTrackLen) { - LOGI(" DDD overrun in RLE"); - return false; - } - while (rleCount--) - *trackPtr++ = rleChar; - } - } - } - - return true; -} diff --git a/ciderpress/diskimg/DIUtil.cpp b/ciderpress/diskimg/DIUtil.cpp deleted file mode 100644 index eae7d3e..0000000 --- a/ciderpress/diskimg/DIUtil.cpp +++ /dev/null @@ -1,350 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * DiskImgLib global utility functions. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -#define kFilenameExtDelim '.' /* separates extension from filename */ - -/* - * Get values from a memory buffer. - */ -uint16_t DiskImgLib::GetShortLE(const uint8_t* ptr) -{ - return *ptr | (uint16_t) *(ptr+1) << 8; -} - -uint32_t DiskImgLib::GetLongLE(const uint8_t* ptr) -{ - return *ptr | - (uint32_t) *(ptr+1) << 8 | - (uint32_t) *(ptr+2) << 16 | - (uint32_t) *(ptr+3) << 24; -} - -uint16_t DiskImgLib::GetShortBE(const uint8_t* ptr) -{ - return *(ptr+1) | (uint16_t) *ptr << 8; -} - -uint32_t DiskImgLib::GetLongBE(const uint8_t* ptr) -{ - return *(ptr+3) | - (uint32_t) *(ptr+2) << 8 | - (uint32_t) *(ptr+1) << 16 | - (uint32_t) *ptr << 24; -} - -uint32_t DiskImgLib::Get24BE(const uint8_t* ptr) -{ - return *(ptr+2) | - (uint32_t) *(ptr+1) << 8 | - (uint32_t) *ptr << 16; -} - -void DiskImgLib::PutShortLE(uint8_t* ptr, uint16_t val) -{ - *ptr++ = (uint8_t) val; - *ptr = val >> 8; -} - -void DiskImgLib::PutLongLE(uint8_t* ptr, uint32_t val) -{ - *ptr++ = (uint8_t) val; - *ptr++ = (uint8_t) (val >> 8); - *ptr++ = (uint8_t) (val >> 16); - *ptr = (uint8_t) (val >> 24); -} - -void DiskImgLib::PutShortBE(uint8_t* ptr, uint16_t val) -{ - *ptr++ = val >> 8; - *ptr = (uint8_t) val; -} - -void DiskImgLib::PutLongBE(uint8_t* ptr, uint32_t val) -{ - *ptr++ = (uint8_t) (val >> 24); - *ptr++ = (uint8_t) (val >> 16); - *ptr++ = (uint8_t) (val >> 8); - *ptr = (uint8_t) val; -} - - -/* - * Read a two-byte little-endian value. - */ -DIError DiskImgLib::ReadShortLE(GenericFD* pGFD, uint16_t* pBuf) -{ - DIError dierr; - uint8_t val[2]; - - dierr = pGFD->Read(&val[0], 1); - if (dierr == kDIErrNone) - dierr = pGFD->Read(&val[1], 1); - - *pBuf = val[0] | (short) val[1] << 8; - return dierr; -} - -/* - * Read a four-byte little-endian value. - */ -DIError DiskImgLib::ReadLongLE(GenericFD* pGFD, uint32_t* pBuf) -{ - DIError dierr; - uint8_t val[4]; - - dierr = pGFD->Read(&val[0], 1); - if (dierr == kDIErrNone) - dierr = pGFD->Read(&val[1], 1); - if (dierr == kDIErrNone) - dierr = pGFD->Read(&val[2], 1); - if (dierr == kDIErrNone) - dierr = pGFD->Read(&val[3], 1); - - *pBuf = val[0] | (uint32_t)val[1] << 8 | - (uint32_t)val[2] << 16 | (uint32_t)val[3] << 24; - return dierr; -} - -/* - * Write a two-byte little-endian value. - */ -DIError DiskImgLib::WriteShortLE(FILE* fp, uint16_t val) -{ - putc(val, fp); - putc(val >> 8, fp); - return kDIErrNone; -} - -/* - * Write a four-byte little-endian value. - */ -DIError DiskImgLib::WriteLongLE(FILE* fp, uint32_t val) -{ - putc(val, fp); - putc(val >> 8, fp); - putc(val >> 16, fp); - putc(val >> 24, fp); - return kDIErrNone; -} - -/* - * Write a two-byte little-endian value. - */ -DIError DiskImgLib::WriteShortLE(GenericFD* pGFD, uint16_t val) -{ - uint8_t buf; - - buf = (uint8_t) val; - pGFD->Write(&buf, 1); - buf = val >> 8; - return pGFD->Write(&buf, 1); -} - -/* - * Write a four-byte little-endian value. - */ -DIError DiskImgLib::WriteLongLE(GenericFD* pGFD, uint32_t val) -{ - uint8_t buf; - - buf = (uint8_t) val; - pGFD->Write(&buf, 1); - buf = (uint8_t) (val >> 8); - pGFD->Write(&buf, 1); - buf = (uint8_t) (val >> 16); - pGFD->Write(&buf, 1); - buf = (uint8_t) (val >> 24); - return pGFD->Write(&buf, 1); -} - -/* - * Write a two-byte big-endian value. - */ -DIError DiskImgLib::WriteShortBE(GenericFD* pGFD, uint16_t val) -{ - uint8_t buf; - - buf = val >> 8; - pGFD->Write(&buf, 1); - buf = (uint8_t) val; - return pGFD->Write(&buf, 1); -} - -/* - * Write a four-byte big-endian value. - */ -DIError DiskImgLib::WriteLongBE(GenericFD* pGFD, uint32_t val) -{ - uint8_t buf; - - buf = (uint8_t) (val >> 24); - pGFD->Write(&buf, 1); - buf = (uint8_t) (val >> 16); - pGFD->Write(&buf, 1); - buf = (uint8_t) (val >> 8); - pGFD->Write(&buf, 1); - buf = (uint8_t) val; - return pGFD->Write(&buf, 1); -} - - -/* - * Find the filename component of a local pathname. Uses the fssep passed - * in. If the fssep is '\0' (as is the case for DOS 3.3), then the entire - * pathname is returned. - * - * Always returns a pointer to a string; never returns NULL. - */ -const char* DiskImgLib::FilenameOnly(const char* pathname, char fssep) -{ - const char* retstr; - const char* pSlash; - char* tmpStr = NULL; - - assert(pathname != NULL); - if (fssep == '\0') { - retstr = pathname; - goto bail; - } - - pSlash = strrchr(pathname, fssep); - if (pSlash == NULL) { - retstr = pathname; /* whole thing is the filename */ - goto bail; - } - - pSlash++; - if (*pSlash == '\0') { - if (strlen(pathname) < 2) { - retstr = pathname; /* the pathname is just "/"? Whatever */ - goto bail; - } - - /* some bonehead put an fssep on the very end; back up before it */ - /* (not efficient, but this should be rare, and I'm feeling lazy) */ - tmpStr = strdup(pathname); - tmpStr[strlen(pathname)-1] = '\0'; - pSlash = strrchr(tmpStr, fssep); - - if (pSlash == NULL) { - retstr = pathname; /* just a filename with a '/' after it */ - goto bail; - } - - pSlash++; - if (*pSlash == '\0') { - retstr = pathname; /* I give up! */ - goto bail; - } - - retstr = pathname + (pSlash - tmpStr); - - } else { - retstr = pSlash; - } - -bail: - free(tmpStr); - return retstr; -} - -/* - * Return the filename extension found in a full pathname. - * - * An extension is the stuff following the last '.' in the filename. If - * there is nothing following the last '.', then there is no extension. - * - * Returns a pointer to the '.' preceding the extension, or NULL if no - * extension was found. - * - * We guarantee that there is at least one character after the '.'. - */ -const char* DiskImgLib::FindExtension(const char* pathname, char fssep) -{ - const char* pFilename; - const char* pExt; - - /* - * We have to isolate the filename so that we don't get excited - * about "/foo.bar/file". - */ - pFilename = FilenameOnly(pathname, fssep); - assert(pFilename != NULL); - pExt = strrchr(pFilename, kFilenameExtDelim); - - /* also check for "/blah/foo.", which doesn't count */ - if (pExt != NULL && *(pExt+1) != '\0') - return pExt; - - return NULL; -} - -/* - * Like strcpy(), but allocate with new[] instead. - * - * If "str" is NULL, or "new" fails, this returns NULL. - * - * TODO: should be "StrdupNew()" - */ -char* DiskImgLib::StrcpyNew(const char* str) -{ - char* newStr; - - if (str == NULL) - return NULL; - newStr = new char[strlen(str)+1]; - if (newStr != NULL) - strcpy(newStr, str); - return newStr; -} - - -#ifdef _WIN32 -/* - * Convert the value from GetLastError() to its DIError counterpart. - */ -DIError DiskImgLib::LastErrorToDIError(void) -{ - DWORD lastErr = ::GetLastError(); - - switch (lastErr) { - case ERROR_FILE_NOT_FOUND: return kDIErrFileNotFound; // 2 - case ERROR_ACCESS_DENIED: return kDIErrAccessDenied; // 5 - case ERROR_WRITE_PROTECT: return kDIErrWriteProtected; // 19 - case ERROR_SECTOR_NOT_FOUND: return kDIErrGeneric; // 27 - case ERROR_SHARING_VIOLATION: return kDIErrSharingViolation; // 32 - case ERROR_HANDLE_EOF: return kDIErrEOF; // 38 - case ERROR_INVALID_PARAMETER: return kDIErrInvalidArg; // 87 - case ERROR_SEM_TIMEOUT: return kDIErrGenericIO; // 121 - // ERROR_SEM_TIMEOUT seen read bad blocks from floptical under Win2K - - case ERROR_INVALID_HANDLE: // 6 - LOGI("HEY: got ERROR_INVALID_HANDLE!"); - return kDIErrInternal; - case ERROR_NEGATIVE_SEEK: // 131 - LOGI("HEY: got ERROR_NEGATIVE_SEEK!"); - return kDIErrInternal; - default: - LOGI("LastErrorToDIError: not converting 0x%08lx (%ld)", - lastErr, lastErr); - return kDIErrGeneric; - } -} - -/* - * Returns "true" if we're running on Win9x (Win95, Win98, WinME), "false" - * if not (could be WinNT/2K/XP or even Win31 with Win32s). - */ -bool DiskImgLib::IsWin9x(void) -{ - return false; -} -#endif diff --git a/ciderpress/diskimg/DOS33.cpp b/ciderpress/diskimg/DOS33.cpp deleted file mode 100644 index 0b61083..0000000 --- a/ciderpress/diskimg/DOS33.cpp +++ /dev/null @@ -1,3385 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskFSDOS33 and A2FileDOS classes. - * - * Works for DOS 3.2 and "wide DOS" as well. - * - * BUG: does not keep VolumeUsage up to date. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSDOS33 - * =========================================================================== - */ - -//const int kMaxSectors = 32; - -const int kSctSize = 256; -/* do we need a way to override these? */ -const int kVTOCTrack = 17; -const int kVTOCSector = 0; -const int kCatalogEntryOffset = 0x0b; // first entry in cat sect starts here -const int kCatalogEntrySize = 0x23; // length in bytes of catalog entries -const int kCatalogEntriesPerSect = 7; // #of entries per catalog sector -const int kEntryDeleted = 0xff; // this is used for track# of deleted files -const int kEntryUnused = 0x00; // this is track# in never-used entries -const int kMaxTSPairs = 0x7a; // 122 entries for 256-byte sectors -const int kTSOffset = 0x0c; // first T/S entry in a T/S list - -const int kMaxTSIterations = 32; - -/* - * Get a pointer to the Nth entry in a catalog sector. - */ -static inline uint8_t* GetCatalogEntryPtr(uint8_t* basePtr, int entryNum) -{ - assert(entryNum >= 0 && entryNum < kCatalogEntriesPerSect); - return basePtr + kCatalogEntryOffset + entryNum * kCatalogEntrySize; -} - - -/* - * Test this image for DOS3.3-ness. - * - * Some notes on tricky disks... - * - * DISK019B (Ultima II player master) has a copy of the VTOC in track 11 - * sector 1, which causes a loop back to track 11 sector f. We may want - * to be clever here and allow it, but we have to be careful because - * we must be similarly clever in the VTOC read routines. (Need a more - * sophisticated loop detector, since a loop will crank our "foundGood" up.) - * - * DISK038B (Congo Bongo) has some "crack" titles and a valid VTOC, but not - * much else. Could allow it if the user explicitly told us to use DOS33, - * but it's a little thin. - * - * DISK112B.X (Ultima I player master) has a catalog that jumps around a lot. - * It's perfectly valid, but we don't really detect it properly. Forcing - * DOS interpretation should be acceptable. - * - * DISK175A (Standing Stones) has an extremely short but valid catalog track. - * - * DISK198B (Aliens+docs) gets 3 and bails with a self-reference. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, - int* pGoodCount) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - int numTracks, numSectors; - int catTrack, catSect; - int foundGood = 0; - int iterations = 0; - - *pGoodCount = 0; - - dierr = pImg->ReadTrackSectorSwapped(kVTOCTrack, kVTOCSector, - sctBuf, imageOrder, DiskImg::kSectorOrderDOS); - if (dierr != kDIErrNone) - goto bail; - - catTrack = sctBuf[0x01]; - catSect = sctBuf[0x02]; - numTracks = sctBuf[0x34]; - numSectors = sctBuf[0x35]; - - if (!(sctBuf[0x27] == kMaxTSPairs) || - /*!(sctBuf[0x36] == 0 && sctBuf[0x37] == 1) ||*/ // bytes per sect - !(numTracks <= DiskFSDOS33::kMaxTracks) || - !(numSectors == 13 || numSectors == 16 || numSectors == 32) || - !(catTrack < numTracks && catSect < numSectors) || - 0) - { - LOGI(" DOS header test failed (order=%d)", imageOrder); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - foundGood++; // score one for a valid-looking VTOC - - /* - * Walk through the catalog track to try to figure out ordering. - */ - while (catTrack != 0 && catSect != 0 && - iterations < DiskFSDOS33::kMaxCatalogSectors) - { - dierr = pImg->ReadTrackSectorSwapped(catTrack, catSect, sctBuf, - imageOrder, DiskImg::kSectorOrderDOS); - if (dierr != kDIErrNone) { - dierr = kDIErrNone; - break; /* allow it if earlier stuff was okay */ - } - - if (catTrack == sctBuf[1] && catSect == sctBuf[2] +1) - foundGood++; - else if (catTrack == sctBuf[1] && catSect == sctBuf[2]) { - LOGI(" DOS detected self-reference on cat (%d,%d)", - catTrack, catSect); - break; - } - catTrack = sctBuf[1]; - catSect = sctBuf[2]; - iterations++; // watch for infinite loops - } - if (iterations >= DiskFSDOS33::kMaxCatalogSectors) { - /* possible cause: LF->CR conversion screws up link to sector $0a */ - dierr = kDIErrDirectoryLoop; - LOGI(" DOS directory links cause a loop (order=%d)", imageOrder); - goto bail; - } - - LOGI(" DOS foundGood=%d order=%d", foundGood, imageOrder); - *pGoodCount = foundGood; - -bail: - return dierr; -} - -/* - * Test to see if the image is a DOS 3.2 or DOS 3.3 disk. - */ -/*static*/ DIError DiskFSDOS33::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (pImg->GetNumTracks() > kMaxInterestingTracks) - return kDIErrFilesystemNotFound; - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown; - int bestCount = 0; - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - int goodCount = 0; - - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i], &goodCount) == kDIErrNone) { - if (goodCount > bestCount) { - bestCount = goodCount; - bestOrder = ordering[i]; - } - } - } - - if (bestCount >= 4 || - (leniency == kLeniencyVery && bestCount >= 2)) - { - LOGI(" DOS test: bestCount=%d for order=%d", bestCount, bestOrder); - assert(bestOrder != DiskImg::kSectorOrderUnknown); - *pOrder = bestOrder; - *pFormat = DiskImg::kFormatDOS33; - if (pImg->GetNumSectPerTrack() == 13) - *pFormat = DiskImg::kFormatDOS32; - return kDIErrNone; - } - - LOGI(" DOS33 didn't find valid DOS3.2 or DOS3.3"); - return kDIErrFilesystemNotFound; -} - - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSDOS33::Initialize(InitMode initMode) -{ - DIError dierr = kDIErrNone; - - fVolumeUsage.Create(fpImg->GetNumTracks(), fpImg->GetNumSectPerTrack()); - - dierr = ReadVTOC(); - if (dierr != kDIErrNone) - goto bail; - //DumpVTOC(); - - dierr = ScanVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - if (initMode == kInitHeaderOnly) { - LOGI(" DOS - headerOnly set, skipping file load"); - goto bail; - } - - /* read the contents of the catalog, creating our A2File list */ - dierr = ReadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - /* run through and get file lengths and data offsets */ - dierr = GetFileLengths(); - if (dierr != kDIErrNone) - goto bail; - - /* mark DOS tracks appropriately */ - FixVolumeUsageMap(); - - fDiskIsGood = CheckDiskIsGood(); - - fVolumeUsage.Dump(); - -// A2File* pFile; -// pFile = GetNextFile(NULL); -// while (pFile != NULL) { -// pFile->Dump(); -// pFile = GetNextFile(pFile); -// } - -bail: - return dierr; -} - -/* - * Read some fields from the disk Volume Table of Contents. - */ -DIError DiskFSDOS33::ReadVTOC(void) -{ - DIError dierr; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - fFirstCatTrack = fVTOC[0x01]; - fFirstCatSector = fVTOC[0x02]; - fVTOCVolumeNumber = fVTOC[0x06]; - fVTOCNumTracks = fVTOC[0x34]; - fVTOCNumSectors = fVTOC[0x35]; - - if (fFirstCatTrack >= fpImg->GetNumTracks()) - return kDIErrBadDiskImage; - if (fFirstCatSector >= fpImg->GetNumSectPerTrack()) - return kDIErrBadDiskImage; - - if (fVTOCNumTracks != fpImg->GetNumTracks()) { - LOGI(" DOS33 warning: VTOC numtracks %d vs %ld", - fVTOCNumTracks, fpImg->GetNumTracks()); - } - if (fVTOCNumSectors != fpImg->GetNumSectPerTrack()) { - LOGI(" DOS33 warning: VTOC numsect %d vs %d", - fVTOCNumSectors, fpImg->GetNumSectPerTrack()); - } - - // call SetDiskVolumeNum with the appropriate thing - UpdateVolumeNum(); - -bail: - FreeVolBitmap(); - return dierr; -} - -/* - * Call this if fpImg's volume num (derived from nibble formats) or - * the VTOC's volume number changes. - */ -void DiskFSDOS33::UpdateVolumeNum(void) -{ - /* use the sector-embedded volume number, if available */ - if (fpImg->GetDOSVolumeNum() == DiskImg::kVolumeNumNotSet) - SetDiskVolumeNum(fVTOCVolumeNumber); - else - SetDiskVolumeNum(fpImg->GetDOSVolumeNum()); - if (fDiskVolumeNum != fVTOCVolumeNumber) { - LOGI(" NOTE: ignoring VTOC vol (%d) in favor of embedded (%d)", - fVTOCVolumeNumber, fDiskVolumeNum); - } -} - -/* - * Set the disk volume number (fDiskVolumeNum) and derived fields. - */ -void DiskFSDOS33::SetDiskVolumeNum(int val) -{ - if (val < 0 || val > 255) { - // Actual valid range should be 1-254, but it's possible for a - // sector edit to put invalid stuff here. It's just one byte - // though, so 0-255 should be guaranteed. - assert(false); - return; - } - fDiskVolumeNum = val; - sprintf(fDiskVolumeName, "DOS%03d", fDiskVolumeNum); - if (fpImg->GetFSFormat() == DiskImg::kFormatDOS32) - sprintf(fDiskVolumeID, "DOS 3.2 Volume %03d", fDiskVolumeNum); - else - sprintf(fDiskVolumeID, "DOS 3.3 Volume %03d", fDiskVolumeNum); -} - - -/* - * Dump some VTOC fields. - */ -void DiskFSDOS33::DumpVTOC(void) -{ - - LOGI("VTOC catalog: track=%d sector=%d", - fFirstCatTrack, fFirstCatSector); - LOGI(" volnum=%d numTracks=%d numSects=%d", - fVTOCVolumeNumber, fVTOCNumTracks, fVTOCNumSectors); -} - -/* - * Update an entry in the VolumeUsage map, watching for conflicts. - */ -void DiskFSDOS33::SetSectorUsage(long track, long sector, - VolumeUsage::ChunkPurpose purpose) -{ - VolumeUsage::ChunkState cstate; - - //LOGI(" DOS setting usage %d,%d to %d", track, sector, purpose); - - fVolumeUsage.GetChunkState(track, sector, &cstate); - if (cstate.isUsed) { - cstate.purpose = VolumeUsage::kChunkPurposeConflict; -// LOGI(" DOS conflicting uses for t=%d s=%d", track, sector); - } else { - cstate.isUsed = true; - cstate.purpose = purpose; - } - fVolumeUsage.SetChunkState(track, sector, &cstate); -} - -/* - * Examine the volume bitmap, setting fields in the VolumeUsage map - * as appropriate. We mark "isMarkedUsed", but leave "isUsed" clear. The - * "isUsed" flag gets set by the DOS catalog track processor and the file - * scanners. - * - * We can't mark the DOS tracks, because there's no reliable way to tell by - * looking at a DOS disk whether it has a bootable DOS image. It's possible - * the tracks are marked in-use because files are stored there. Some - * tweaked versions of DOS freed up a few sectors on track 2, so partial - * allocation isn't a good indicator. - * - * What we have to do is wait until we have all the information for the - * various files, and mark the tracks as owned by DOS if nobody else - * claims them. - */ -DIError DiskFSDOS33::ScanVolBitmap(void) -{ - DIError dierr; - VolumeUsage::ChunkState cstate; - char freemap[32+1] = "--------------------------------"; - - cstate.isUsed = false; - cstate.isMarkedUsed = true; - cstate.purpose = (VolumeUsage::ChunkPurpose) 0; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - LOGI(" map 0123456789abcdef"); - - for (int i = 0; i < kMaxTracks; i++) { - uint32_t val, origVal; - int bit; - - val = (uint32_t) fVTOC[0x38 + i*4] << 24; - val |= (uint32_t) fVTOC[0x39 + i*4] << 16; - val |= (uint32_t) fVTOC[0x3a + i*4] << 8; - val |= (uint32_t) fVTOC[0x3b + i*4]; - origVal = val; - - /* init the VolumeUsage stuff */ - for (bit = fpImg->GetNumSectPerTrack()-1; bit >= 0; bit--) { - freemap[bit] = val & 0x80000000 ? '.' : 'X'; - - if (i < fpImg->GetNumTracks() && !(val & 0x80000000)) { - /* mark the sector as in-use */ - if (fVolumeUsage.SetChunkState(i, bit, &cstate) != kDIErrNone) { - assert(false); - } - } - val <<= 1; - } - LOGI(" %2d: %s (0x%08x)", i, freemap, origVal); - } - - /* we know the VTOC is used, so mark it now */ - SetSectorUsage(kVTOCTrack, kVTOCSector, VolumeUsage::kChunkPurposeVolumeDir); - -bail: - FreeVolBitmap(); - return dierr; -} - - -/* - * Load the VTOC into the buffer. - */ -DIError DiskFSDOS33::LoadVolBitmap(void) -{ - DIError dierr; - - assert(!fVTOCLoaded); - - dierr = fpImg->ReadTrackSector(kVTOCTrack, kVTOCSector, fVTOC); - if (dierr != kDIErrNone) - return dierr; - - fVTOCLoaded = true; - return kDIErrNone; -} - -/* - * Save our copy of the volume bitmap. - */ -DIError DiskFSDOS33::SaveVolBitmap(void) -{ - if (!fVTOCLoaded) { - assert(false); - return kDIErrNotReady; - } - - return fpImg->WriteTrackSector(kVTOCTrack, kVTOCSector, fVTOC); -} - -/* - * Throw away the volume bitmap, discarding any unsaved changes. - * - * It's okay to call this if the bitmap isn't loaded. - */ -void DiskFSDOS33::FreeVolBitmap(void) -{ - fVTOCLoaded = false; - -#ifdef _DEBUG - memset(fVTOC, 0x99, sizeof(fVTOC)); -#endif -} - -/* - * Return entry N from the VTOC. - */ -inline uint32_t DiskFSDOS33::GetVTOCEntry(const uint8_t* pVTOC, long track) const -{ - uint32_t val; - val = (uint32_t) pVTOC[0x38 + track*4] << 24; - val |= (uint32_t) pVTOC[0x39 + track*4] << 16; - val |= (uint32_t) pVTOC[0x3a + track*4] << 8; - val |= (uint32_t) pVTOC[0x3b + track*4]; - - return val; -} - -/* - * Allocate a new sector from the unused pool. - * - * Only touches the in-memory copy. - */ -DIError DiskFSDOS33::AllocSector(TrackSector* pTS) -{ - uint32_t val; - uint32_t mask; - long track, numSectPerTrack; - - /* we could compute "mask", but it's faster and easier to do this */ - numSectPerTrack = GetDiskImg()->GetNumSectPerTrack(); - if (numSectPerTrack == 13) - mask = 0xfff80000; - else if (numSectPerTrack == 16) - mask = 0xffff0000; - else if (numSectPerTrack == 32) - mask = 0xffffffff; - else { - assert(false); - return kDIErrInternal; - } - - /* - * Start by finding a track with a free sector. We know it's free - * because the bits aren't all zero. - * - * In theory we don't need "mask", because the DOS format routine is - * good about leaving the unused bits clear, and nobody else disturbs - * them. However, it's best not to rely on it. - */ - for (track = kVTOCTrack; track > 0; track--) { - val = GetVTOCEntry(fVTOC, track); - if ((val & mask) != 0) - break; - } - if (track == 0) { - long numTracks = GetDiskImg()->GetNumTracks(); - for (track = kVTOCTrack; track < numTracks; track++) - { - val = GetVTOCEntry(fVTOC, track); - if ((val & mask) != 0) - break; - } - if (track == numTracks) { - LOGI("DOS33 AllocSector unable to find empty sector"); - return kDIErrDiskFull; - } - } - - /* - * We've got the track. Now find the first free sector. - */ - int sector; - sector = numSectPerTrack-1; - while (sector >= 0) { - if (val & 0x80000000) { - //LOGI("+++ allocating T=%d S=%d", track, sector); - SetSectorUseEntry(track, sector, true); - break; - } - - val <<= 1; - sector--; - } - if (sector < 0) { - assert(false); - return kDIErrInternal; // should not have failed - } - - /* - * Mostly for fun, update the VTOC allocation thingy. - */ - fVTOC[0x30] = (uint8_t) track; // last track where alloc happened - if (track < kVTOCTrack) - fVTOC[0x31] = 0xff; // descending - else - fVTOC[0x31] = 0x01; // ascending - - pTS->track = (char) track; - pTS->sector = (char) sector; - - return kDIErrNone; -} - -/* - * Create an in-use map for an empty disk. Sets up the VTOC map only. - * - * If "withDOS" is set, mark the first 3 tracks as in-use. - */ -DIError DiskFSDOS33::CreateEmptyBlockMap(bool withDOS) -{ - DIError dierr; - long track, sector, maxTrack; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - if (withDOS) - maxTrack = 3; - else - maxTrack = 1; - - /* - * Set each bit individually. Slower, but exercises standard functions. - * - * Clear all "in use" flags, except for track 0, track 17, and (if - * withDOS is set) tracks 1 and 2. - */ - for (track = fpImg->GetNumTracks()-1; track >= 0; track--) { - for (sector = fpImg->GetNumSectPerTrack()-1; sector >= 0; sector--) { - if (track < maxTrack || track == kVTOCTrack) - SetSectorUseEntry(track, sector, true); - else - SetSectorUseEntry(track, sector, false); - } - } - - dierr = SaveVolBitmap(); - FreeVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - return kDIErrNone; -} - -/* - * Get the state of an entry in the VTOC sector use map. - * - * Returns "true" if it's in use, "false" otherwise. - */ -bool DiskFSDOS33::GetSectorUseEntry(long track, int sector) const -{ - assert(fVTOCLoaded); - assert(track >= 0 && track < fpImg->GetNumTracks()); - assert(sector >= 0 && sector < fpImg->GetNumSectPerTrack()); - - uint32_t val, mask; - - val = GetVTOCEntry(fVTOC, track); - //val = (uint32_t) fVTOC[0x38 + track*4] << 24; - //val |= (uint32_t) fVTOC[0x39 + track*4] << 16; - //val |= (uint32_t) fVTOC[0x3a + track*4] << 8; - //val |= (uint32_t) fVTOC[0x3b + track*4]; - - /* - * The highest-numbered sector is now in the high bit. If this is a - * 16-sector disk, the high bit holds the state of sector 15. - * - * A '1' indicates the sector is free, '0' indicates it's in use. - */ - mask = 1L << (32 - fpImg->GetNumSectPerTrack() + sector); - return (val & mask) == 0; -} - -/* - * Change the state of an entry in the VTOC sector use map. - */ -void DiskFSDOS33::SetSectorUseEntry(long track, int sector, bool inUse) -{ - assert(fVTOCLoaded); - assert(track >= 0 && track < fpImg->GetNumTracks()); - assert(sector >= 0 && sector < fpImg->GetNumSectPerTrack()); - - uint32_t val, mask; - - val = GetVTOCEntry(fVTOC, track); - - /* highest sector is always in the high bit */ - mask = 1L << (32 - fpImg->GetNumSectPerTrack() + sector); - if (inUse) - val &= ~mask; - else - val |= mask; - - fVTOC[0x38 + track*4] = (uint8_t) (val >> 24); - fVTOC[0x39 + track*4] = (uint8_t) (val >> 16); - fVTOC[0x3a + track*4] = (uint8_t) (val >> 8); - fVTOC[0x3b + track*4] = (uint8_t) val; -} - - -/* - * Get the amount of free space remaining. - */ -DIError DiskFSDOS33::GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const -{ - DIError dierr; - long track, sector, freeSectors; - - dierr = const_cast(this)->LoadVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - freeSectors = 0; - for (track = GetDiskImg()->GetNumTracks()-1; track >= 0; track--) { - for (sector = GetDiskImg()->GetNumSectPerTrack()-1; sector >= 0; sector--) - { - if (!GetSectorUseEntry(track, sector)) - freeSectors++; - } - } - - *pTotalUnits = fpImg->GetNumTracks() * fpImg->GetNumSectPerTrack(); - *pFreeUnits = freeSectors; - *pUnitSize = kSectorSize; - - const_cast(this)->FreeVolBitmap(); - return kDIErrNone; -} - - -/* - * Fix up the DOS tracks. - * - * Any sectors marked used but not actually in use by a file are marked as - * in use by the system. We have to be somewhat careful here because some - * disks had DOS removed to add space, un-set the last few sectors of track 2 - * that weren't actually used by DOS, or did some other funky thing. - */ -void DiskFSDOS33::FixVolumeUsageMap(void) -{ - VolumeUsage::ChunkState cstate; - int track, sector; - - for (track = 0; track < 3; track++) { - for (sector = 0; sector < fpImg->GetNumSectPerTrack(); sector++) { - fVolumeUsage.GetChunkState(track, sector, &cstate); - if (cstate.isMarkedUsed && !cstate.isUsed) { - cstate.isUsed = true; - cstate.purpose = VolumeUsage::kChunkPurposeSystem; - fVolumeUsage.SetChunkState(track, sector, &cstate); - } - } - } -} - - -/* - * Read the disk's catalog. - * - * NOTE: supposedly DOS stops reading the catalog track when it finds the - * first entry with a 00 byte, which is why deleted files use ff. If so, - * it *might* make sense to mimic this behavior, though on a health disk - * we shouldn't be finding garbage anyway. - * - * Fills out "fCatalogSectors" as it works. - */ -DIError DiskFSDOS33::ReadCatalog(void) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - int catTrack, catSect; - int iterations; - - catTrack = fFirstCatTrack; - catSect = fFirstCatSector; - iterations = 0; - - memset(fCatalogSectors, 0, sizeof(fCatalogSectors)); - - while (catTrack != 0 && catSect != 0 && iterations < kMaxCatalogSectors) - { - SetSectorUsage(catTrack, catSect, VolumeUsage::kChunkPurposeVolumeDir); - - LOGI(" DOS33 reading catalog sector T=%d S=%d", catTrack, catSect); - dierr = fpImg->ReadTrackSector(catTrack, catSect, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* - * Watch for flaws that the DOS detector allows. - */ - if (catTrack == sctBuf[0x01] && catSect == sctBuf[0x02]) { - LOGI(" DOS detected self-reference on cat (%d,%d)", - catTrack, catSect); - break; - } - - /* - * Check the next track/sector in the chain. If the pointer is - * broken, there's a very good chance that this isn't really a - * catalog sector, so we want to bail out now. - */ - if (sctBuf[0x01] >= fpImg->GetNumTracks() || - sctBuf[0x02] >= fpImg->GetNumSectPerTrack()) - { - LOGI(" DOS bailing out early on catalog read due to funky T/S"); - break; - } - - dierr = ProcessCatalogSector(catTrack, catSect, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - fCatalogSectors[iterations].track = catTrack; - fCatalogSectors[iterations].sector = catSect; - - catTrack = sctBuf[0x01]; - catSect = sctBuf[0x02]; - - iterations++; // watch for infinite loops - - } - if (iterations >= kMaxCatalogSectors) { - dierr = kDIErrDirectoryLoop; - goto bail; - } - -bail: - return dierr; -} - -/* - * Process the list of files in one sector of the catalog. - * - * Pass in the track, sector, and the contents of that track and sector. - * (We only use "catTrack" and "catSect" to fill out some fields.) - */ -DIError DiskFSDOS33::ProcessCatalogSector(int catTrack, int catSect, - const uint8_t* sctBuf) -{ - A2FileDOS* pFile; - const uint8_t* pEntry; - int i; - - pEntry = &sctBuf[kCatalogEntryOffset]; - - for (i = 0; i < kCatalogEntriesPerSect; i++) { - if (pEntry[0x00] != kEntryUnused && pEntry[0x00] != kEntryDeleted) { - pFile = new A2FileDOS(this); - - pFile->SetQuality(A2File::kQualityGood); - - pFile->fTSListTrack = pEntry[0x00]; - pFile->fTSListSector = pEntry[0x01]; - pFile->fLocked = (pEntry[0x02] & 0x80) != 0; - switch (pEntry[0x02] & 0x7f) { - case 0x00: pFile->fFileType = A2FileDOS::kTypeText; break; - case 0x01: pFile->fFileType = A2FileDOS::kTypeInteger; break; - case 0x02: pFile->fFileType = A2FileDOS::kTypeApplesoft; break; - case 0x04: pFile->fFileType = A2FileDOS::kTypeBinary; break; - case 0x08: pFile->fFileType = A2FileDOS::kTypeS; break; - case 0x10: pFile->fFileType = A2FileDOS::kTypeReloc; break; - case 0x20: pFile->fFileType = A2FileDOS::kTypeA; break; - case 0x40: pFile->fFileType = A2FileDOS::kTypeB; break; - default: - /* some odd arrangement of bit flags? */ - LOGI(" DOS33 peculiar filetype byte 0x%02x", pEntry[0x02]); - pFile->fFileType = A2FileDOS::kTypeUnknown; - pFile->SetQuality(A2File::kQualitySuspicious); - break; - } - - memcpy(pFile->fFileName, &pEntry[0x03], A2FileDOS::kMaxFileName); - pFile->fFileName[A2FileDOS::kMaxFileName] = '\0'; - pFile->FixFilename(); - - pFile->fLengthInSectors = pEntry[0x21]; - pFile->fLengthInSectors |= (uint16_t) pEntry[0x22] << 8; - - pFile->fCatTS.track = catTrack; - pFile->fCatTS.sector = catSect; - pFile->fCatEntryNum = i; - - /* can't do these yet, so just set to defaults */ - pFile->fLength = 0; - pFile->fSparseLength = 0; - pFile->fDataOffset = 0; - - AddFileToList(pFile); - } - - pEntry += kCatalogEntrySize; - } - - return kDIErrNone; -} - - -/* - * Perform consistency checks on the filesystem. - * - * Returns "true" if disk appears to be perfect, "false" otherwise. - */ -bool DiskFSDOS33::CheckDiskIsGood(void) -{ - DIError dierr; - const DiskImg* pDiskImg = GetDiskImg(); - bool result = true; - int i; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Make sure the VTOC is marked in use, or things could go badly. - * Ditto for the catalog tracks. - */ - if (!GetSectorUseEntry(kVTOCTrack, kVTOCSector)) { - fpImg->AddNote(DiskImg::kNoteWarning, "VTOC sector marked as free."); - result = false; - } - for (i = 0; i < kMaxCatalogSectors; i++) { - if (!GetSectorUseEntry(fCatalogSectors[i].track, - fCatalogSectors[i].sector)) - { - fpImg->AddNote(DiskImg::kNoteWarning, - "Catalog sector %d,%d is marked as free.", - fCatalogSectors[i].track, fCatalogSectors[i].sector); - result = false; - } - } - - /* - * Check for used blocks that aren't marked in-use. - * - * This requires that VolumeUsage be accurate. Since this function is - * only run during initial startup, any later deviation between VU and - * the block use map is irrelevant. - */ - VolumeUsage::ChunkState cstate; - long track, sector; - long notMarked, extraUsed, conflicts; - notMarked = extraUsed = conflicts = 0; - for (track = 0; track < pDiskImg->GetNumTracks(); track++) { - for (sector = 0; sector < pDiskImg->GetNumSectPerTrack(); sector++) { - dierr = fVolumeUsage.GetChunkState(track, sector, &cstate); - if (dierr != kDIErrNone) { - fpImg->AddNote(DiskImg::kNoteWarning, - "Internal volume usage error on t=%ld s=%ld.", - track, sector); - result = false; - goto bail; - } - - if (cstate.isUsed && !cstate.isMarkedUsed) - notMarked++; - if (!cstate.isUsed && cstate.isMarkedUsed) - extraUsed++; - if (cstate.purpose == VolumeUsage::kChunkPurposeConflict) - conflicts++; - } - } - if (extraUsed > 0) { - fpImg->AddNote(DiskImg::kNoteInfo, - "%ld sector%s marked used but not part of any file.", - extraUsed, extraUsed == 1 ? " is" : "s are"); - // not a problem, really - } - if (notMarked > 0) { - fpImg->AddNote(DiskImg::kNoteWarning, - "%ld sector%s used by files but not marked used.", - notMarked, notMarked == 1 ? " is" : "s are"); - result = false; - } - if (conflicts > 0) { - fpImg->AddNote(DiskImg::kNoteWarning, - "%ld sector%s used by more than one file.", - conflicts, conflicts == 1 ? " is" : "s are"); - result = false; - } - - /* - * Scan for "damaged" files or "suspicious" files diagnosed earlier. - */ - bool damaged, suspicious; - ScanForDamagedFiles(&damaged, &suspicious); - - if (damaged) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files are damaged."); - result = false; - } else if (suspicious) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files look suspicious."); - result = false; - } - -bail: - FreeVolBitmap(); - return result; -} - - -/* - * Run through our list of files, computing the lengths and marking file - * usage in the VolumeUsage object. - */ -DIError DiskFSDOS33::GetFileLengths(void) -{ - A2FileDOS* pFile; - TrackSector* tsList = NULL; - TrackSector* indexList = NULL; - int tsCount; - int indexCount; - - pFile = (A2FileDOS*) GetNextFile(NULL); - while (pFile != NULL) { - DIError dierr; - dierr = pFile->LoadTSList(&tsList, &tsCount, &indexList, &indexCount); - if (dierr != kDIErrNone) { - LOGI("DOS failed loading TS list for '%s'", - pFile->GetPathName()); - pFile->SetQuality(A2File::kQualityDamaged); - } else { - MarkFileUsage(pFile, tsList, tsCount, indexList, indexCount); - dierr = ComputeLength(pFile, tsList, tsCount); - if (dierr != kDIErrNone) { - LOGI("DOS unable to get length for '%s'", - pFile->GetPathName()); - pFile->SetQuality(A2File::kQualityDamaged); - } - } - - if (pFile->fLengthInSectors != indexCount + tsCount) { - LOGI("DOS NOTE: file '%s' has len-in-sect=%d but actual=%d", - pFile->GetPathName(), pFile->fLengthInSectors, - indexCount + tsCount); - // expected on sparse random-access text files - } - - delete[] tsList; - delete[] indexList; - tsList = indexList = NULL; - - pFile = (A2FileDOS*) GetNextFile(pFile); - } - - return kDIErrNone; -} - -/* - * Compute the length and starting data offset of the file. - * - * For Text, there are two situations: sequential and random. For - * sequential text files, we just need to find the first 00 byte. For - * random, there can be 00s everywhere, and in fact there can be holes - * in the T/S list. The plan: since DOS doesn't let you "truncate" a - * text file, just scan the last sector for 00. The length is the - * number of previous T/S entries * 256 plus the sector offset. - * --> This does the wrong thing for random-access text files, which - * need to retain their full length, and doesn't work right for sequential - * text files that (somehow) had their last block over-allocated. It does - * the right thing most of the time, but we either need to be more clever - * here or provide a way to override the default (bool fTrimTextFiles?). - * - * For Applesoft and Integer, the file length is stored as the first two - * bytes of the file. - * - * For Binary, the file length is stored in the second two bytes (after - * the two-byte address). Some files (with low-memory loaders) used a - * fake length, and DDD 2.x sets both address and length to zero. - * - * For Reloc, S, A2, B2, and "unknown", we just multiply the sector count. - * We get an accurate sector count from the T/S list (the value in the - * directory entry might have been tampered with). - * - * To handle DDD 2.x files correctly, we need to identify them as such by - * looking for 'B' with address=0 and length=0, a T/S count of at least 8 - * (the smallest possible compression of a 35-track disk is 2385 bytes), - * and a '<' in the filename. If found, we start from offset=0 - * (because DDD Pro 1.x includes the 4 leading bytes) and include all - * sectors, we'll get the actual file plus at most 256 garbage bytes. - * - * On success, we set the following: - * pFile->fLength - * pFile->fSparseLength - * pFile->fDataOffset - */ -DIError DiskFSDOS33::ComputeLength(A2FileDOS* pFile, const TrackSector* tsList, - int tsCount) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - - assert(pFile != NULL); - assert(tsList != NULL); - assert(tsCount >= 0); - - pFile->fDataOffset = 0; - - pFile->fAuxType = 0; - if (pFile->fFileType == A2FileDOS::kTypeApplesoft) - pFile->fAuxType = 0x0801; - /* for text files it's default record length; assume zero */ - - if (tsCount == 0) { - /* no data at all */ - pFile->fLength = 0; - } else if (pFile->fFileType == A2FileDOS::kTypeApplesoft || - pFile->fFileType == A2FileDOS::kTypeInteger || - pFile->fFileType == A2FileDOS::kTypeBinary) - { - /* read first sector and analyze it */ - //LOGI(" DOS reading first file sector"); - dierr = fpImg->ReadTrackSector(tsList[0].track, tsList[0].sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - if (pFile->fFileType == A2FileDOS::kTypeBinary) { - pFile->fAuxType = - sctBuf[0x00] | (uint16_t) sctBuf[0x01] << 8; - pFile->fLength = - sctBuf[0x02] | (uint16_t) sctBuf[0x03] << 8; - pFile->fDataOffset = 4; // take the above into account - } else { - pFile->fLength = - sctBuf[0x00] | (uint16_t) sctBuf[0x01] << 8; - pFile->fDataOffset = 2; // take the above into account - } - - if (pFile->fFileType == A2FileDOS::kTypeBinary && - pFile->fLength == 0 && pFile->fAuxType == 0 && - tsCount >= 8 && - strchr(pFile->fFileName, '<') != NULL && - strchr(pFile->fFileName, '>') != NULL) - { - LOGI(" DOS found probable DDD archive, tweaking '%s' (lis=%u)", - pFile->GetPathName(), pFile->fLengthInSectors); - //dierr = TrimLastSectorDown(pFile, tsBuf, WrapperDDD::kMaxDDDZeroCount); - //if (dierr != kDIErrNone) - // goto bail; - //LOGI(" DOS scanned DDD file '%s' to length %ld (tsCount=%d)", - // pFile->fFileName, pFile->fLength, pFile->fTSListCount); - pFile->fLength = tsCount * kSctSize; - pFile->fDataOffset = 0; - } - - /* catch bogus lengths in damaged A/I/B files */ - if (pFile->fLength > tsCount * kSctSize) { - LOGI(" DOS33 capping max len from %ld to %d in '%s'", - (long) pFile->fLength, tsCount * kSctSize, - pFile->fFileName); - pFile->fLength = tsCount * kSctSize - pFile->fDataOffset; - if (pFile->fLength < 0) // can't happen here? - pFile->fLength = 0; - - /* - * This could cause a problem, because if the user changes a 'T' - * file to 'B', the bogus file length will mark the file as - * "suspicious" and we won't allow writing to the disk (which - * makes it hard to switch the file type back). We really don't - * want to weaken this test though. - */ - pFile->SetQuality(A2File::kQualitySuspicious); - } - - } else if (pFile->fFileType == A2FileDOS::kTypeText) { - /* scan text file */ - pFile->fLength = tsCount * kSctSize; - dierr = TrimLastSectorUp(pFile, tsList[tsCount-1]); - if (dierr != kDIErrNone) - goto bail; - - LOGI(" DOS scanned text file '%s' down to %d+%ld = %ld", - pFile->fFileName, - (tsCount-1) * kSctSize, - (long)pFile->fLength - (tsCount-1) * kSctSize, - (long)pFile->fLength); - - /* TO DO: something clever to discern random access record length? */ - } else { - /* S/R/A/B: just use the TS count */ - pFile->fLength = tsCount * kSctSize; - } - - /* - * Compute the sparse length for random-access text files. - */ - int i, sparseCount; - sparseCount = 0; - for (i = 0; i < tsCount; i++) { - if (tsList[i].track == 0 && tsList[i].sector == 0) - sparseCount++; - } - pFile->fSparseLength = pFile->fLength - sparseCount * kSctSize; - if (pFile->fSparseLength == -pFile->fDataOffset) { - /* - * This can happen for a completely sparse file. Looks sort of - * stupid to have a length of "-4", so force it to zero. - */ - pFile->fSparseLength = 0; - } - -bail: - return dierr; -} - -/* - * Trim the zeroes off the end of the last sector. We begin at the start - * of the sector and stop at the first zero found. - * - * Modifies pFile->fLength, which should be set to a roughly accurate - * value on entry. - * - * The caller should endeavor to strip out T=0 S=0 entries that come after - * the body of the file. They're valid in the middle for random-access - * text files. - */ -DIError DiskFSDOS33::TrimLastSectorUp(A2FileDOS* pFile, TrackSector lastTS) -{ - DIError dierr; - uint8_t sctBuf[kSctSize]; - int i; - - if (lastTS.track == 0) { - /* happens on files with lots of "sparse" space at the end */ - return kDIErrNone; - } - - //LOGI(" DOS reading LAST file sector"); - dierr = fpImg->ReadTrackSector(lastTS.track, lastTS.sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* start with EOF equal to previous sectors */ - pFile->fLength -= kSctSize; - for (i = 0; i < kSctSize; i++) { - if (sctBuf[i] == 0x00) - break; - else - pFile->fLength++; - } - -bail: - return dierr; -} - -/* - * Given lists of tracks and sector for data and TS index sectors, set the - * entries in the volume usage map. - */ -void DiskFSDOS33::MarkFileUsage(A2FileDOS* pFile, TrackSector* tsList, int tsCount, - TrackSector* indexList, int indexCount) -{ - int i; - - for (i = 0; i < tsCount; i++) { - /* mark all sectors as in-use by file */ - if (tsList[i].track == 0 && tsList[i].sector == 0) { - /* sparse sector in random-access text file */ - } else { - SetSectorUsage(tsList[i].track, tsList[i].sector, - VolumeUsage::kChunkPurposeUserData); - } - } - - for (i = 0; i < indexCount; i++) { - /* mark the T/S sectors as in-use by file structures */ - SetSectorUsage(indexList[i].track, indexList[i].sector, - VolumeUsage::kChunkPurposeFileStruct); - } -} - - - -#if 0 -/* - * Trim the zeroes off the end of the last sector. We begin at the end - * of the sector and back up. - * - * It is possible (one out of between 128 and 256 times) that we have just - * the trailing zero in this sector, and we need to back up to the previous - * sector to find the actual end. We know a file can end with three zeroes - * and we suspect it might be possible to end with four, which means we could - * have between 0 and 3 zeroes in the previous sector, and between 1 and 4 - * in this sector. If we just tack on three more zeroes, we weaken our - * length test slightly, because we must allow a "slop" of up to seven bytes. - * It's a little more work, but scanning the next-to-last sector is probably - * worthwhile given the otherwise flaky nature of DDD storage. - */ -DIError -DiskFSDOS33::TrimLastSectorDown(A2FileDOS* pFile, uint16_t* tsBuf, - int maxZeroCount) -{ - DIError dierr; - uint8_t sctBuf[kSctSize]; - int i; - - //LOGI(" DOS reading LAST file sector"); - dierr = fpImg->ReadTrackSector( - pFile->TSTrack(tsBuf[pFile->fTSListCount-1]), - pFile->TSSector(tsBuf[pFile->fTSListCount-1]), - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* find the first trailing zero by finding the last non-zero */ - for (i = kSctSize-1; i >= 0; i--) { - if (sctBuf[i] != 0x00) - break; - } - if (i < 0) { - /* sector was nothing but zeroes */ - DebugBreak(); - } else { - /* peg it at 256; if it went over that, DDD would've added a sector */ - i += maxZeroCount; - if (i > kSctSize) - i = kSctSize; - pFile->fLength = (pFile->fTSListCount-1) * kSctSize + i; - } - -bail: - return dierr; -} -#endif - - -/* - * Convert high ASCII to low ASCII. - * - * Some people put inverse and flashing text into filenames, not to mention - * control characters, so we have to cope with those too. - * - * We modify the first "len" bytes of "buf" in place. - */ -/*static*/ void DiskFSDOS33::LowerASCII(uint8_t* buf, long len) -{ - while (len--) { - if (*buf & 0x80) { - if (*buf >= 0xa0) - *buf &= 0x7f; - else - *buf = (*buf & 0x7f) + 0x20; - } else - *buf = ((*buf & 0x3f) ^ 0x20) + 0x20; - - buf++; - } -} - - -/* - * Determine whether or not "name" is a valid DOS 3.3 filename. - * - * Names can be up to 30 characters and can contain absolutely anything. - * To make life easier on DOS users, we ban the use of the comma, block - * control characters and high ASCII, and don't allow completely blank - * names. Later on we will later convert to upper case, so we allow lower - * case letters here. - * - * Filenames simply pad out to 30 characters with spaces, so the only - * "invalid" character is a trailing space. Because we're using C-style - * strings, we implicitly ban the use of '\0' in the name. - */ -/*static*/ bool DiskFSDOS33::IsValidFileName(const char* name) -{ - bool nonSpace = false; - int len = 0; - - /* count letters, skipping control chars */ - while (*name != '\0') { - char ch = *name++; - - if (ch < 0x20 || ch >= 0x7f || ch == ',') - return false; - if (ch != 0x20) - nonSpace = true; - len++; - } - if (len == 0 || len > A2FileDOS::kMaxFileName) - return false; // can't be empty, can't be huge - if (!nonSpace) - return false; // must have one non-ctrl non-space char - if (*(name-1) == ' ') - return false; // no trailing spaces - - return true; -} - -/* - * Determine whether "name" is a valid volume number. - */ -/*static*/ bool DiskFSDOS33::IsValidVolumeName(const char* name) -{ - long val; - char* endp; - - val = strtol(name, &endp, 10); - if (*endp != '\0' || val < 1 || val > 254) - return false; - - return true; -} - - -/* - * Put a DOS 3.2/3.3 filesystem image on the specified DiskImg. - * - * If "volName" is "DOS", a basic DOS image will be written to the first three - * tracks of the disk, and the in-use map will be updated appropriately. - * - * It would seem at first glance that putting the volume number into the - * volume name string would make the interface more consistent with the - * rest of the filesystems. The first glance is substantially correct, but - * the DOS stuff has a separate "set volume number" interface already, used - * to deal with the various locations where volume numbers can be stored - * (2MG header, VTOC, sector address headers) in the various formats. - * - * So, instead of stuffing the volume number into "volName" and creating - * some other path for specifying "add DOS image", I continue to use the - * defined ways of setting the volume number and abuse "volName" slightly. - */ -DIError DiskFSDOS33::Format(DiskImg* pDiskImg, const char* volName) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[256]; - bool addDOS = false; - - if (pDiskImg->GetNumTracks() < kMinTracks || - pDiskImg->GetNumTracks() > kMaxTracks) - { - LOGI(" DOS33 can't format numTracks=%ld", pDiskImg->GetNumTracks()); - return kDIErrInvalidArg; - } - if (pDiskImg->GetNumSectPerTrack() != 13 && - pDiskImg->GetNumSectPerTrack() != 16 && - pDiskImg->GetNumSectPerTrack() != 32) - { - LOGI(" DOS33 can't format sectors=%d", - pDiskImg->GetNumSectPerTrack()); - return kDIErrInvalidArg; - } - - if (volName != NULL && strcmp(volName, "DOS") == 0) { - if (pDiskImg->GetNumSectPerTrack() != 16 && - pDiskImg->GetNumSectPerTrack() != 13) - { - LOGI("NOTE: numSectPerTrack = %d, can't write DOS tracks", - pDiskImg->GetNumSectPerTrack()); - return kDIErrInvalidArg; - } - addDOS = true; - } - - /* set fpImg so calls that rely on it will work; we un-set it later */ - assert(fpImg == NULL); - SetDiskImg(pDiskImg); - - LOGI(" DOS33 formatting disk image (sectorOrder=%d)", - fpImg->GetSectorOrder()); - - /* write DOS sectors */ - dierr = fpImg->OverrideFormat(fpImg->GetPhysicalFormat(), - DiskImg::kFormatGenericDOSOrd, fpImg->GetSectorOrder()); - if (dierr != kDIErrNone) - goto bail; - - /* - * We should now zero out the disk blocks, but on a 32MB volume that can - * take a little while. The blocks are zeroed for us when a disk is - * created, so this is really only needed if we're re-formatting an - * existing disk. CiderPress currently doesn't do that, so we're going - * to skip it here. - */ -// dierr = fpImg->ZeroImage(); - LOGI(" DOS33 (not zeroing blocks)"); - - if (addDOS) { - dierr = WriteDOSTracks(pDiskImg->GetNumSectPerTrack()); - if (dierr != kDIErrNone) - goto bail; - } - - /* - * Set up the static fields in the VTOC. - */ - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - fVTOC[0x00] = 0x04; // (no reason) - fVTOC[0x01] = kVTOCTrack; // first cat track - fVTOC[0x02] = fpImg->GetNumSectPerTrack()-1; // first cat sector - fVTOC[0x03] = 3; // version - if (fpImg->GetDOSVolumeNum() == DiskImg::kVolumeNumNotSet) - fVTOC[0x06] = kDefaultVolumeNum; // VTOC volume number - else - fVTOC[0x06] = (uint8_t) fpImg->GetDOSVolumeNum(); - fVTOC[0x27] = 122; // max T/S pairs - fVTOC[0x30] = kVTOCTrack+1; // last alloc - fVTOC[0x31] = 1; // ascending - fVTOC[0x34] = (uint8_t)fpImg->GetNumTracks(); // #of tracks - fVTOC[0x35] = fpImg->GetNumSectPerTrack(); // #of sectors - fVTOC[0x36] = 0x00; // bytes/sector (lo) - fVTOC[0x37] = 0x01; // bytes/sector (hi) - if (pDiskImg->GetNumSectPerTrack() == 13) { - // minor changes for DOS 3.2 - fVTOC[0x00] = 0x02; - fVTOC[0x03] = 2; - } - - dierr = SaveVolBitmap(); - FreeVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Fill the sectors in the catalog track. - */ - int sect; - memset(sctBuf, 0, sizeof(sctBuf)); - sctBuf[0x01] = kVTOCTrack; - for (sect = fpImg->GetNumSectPerTrack()-1; sect > 1; sect--) { - sctBuf[0x02] = sect-1; - - dierr = fpImg->WriteTrackSector(kVTOCTrack, sect, sctBuf); - if (dierr != kDIErrNone) - goto bail; - } - - /* - * Generate the initial block usage map. The only entries in use are - * right at the start of the disk. - */ - CreateEmptyBlockMap(addDOS); - - /* check our work, and set some object fields, by reading what we wrote */ - dierr = ReadVTOC(); - if (dierr != kDIErrNone) { - LOGI(" GLITCH: couldn't read header we just wrote (err=%d)", dierr); - goto bail; - } - - /* don't do this -- assume they're going to call Initialize() later */ - //ScanVolBitmap(); - -bail: - SetDiskImg(NULL); // shouldn't really be set by us - return dierr; -} - -/* - * Write a DOS image into tracks 0-2. - * - * This takes the number of sectors per track as an argument so we can figure - * out which version of DOS to write. This probably ought to be an enum so - * we can specify various versions of DOS. - */ -DIError DiskFSDOS33::WriteDOSTracks(int sectPerTrack) -{ - DIError dierr = kDIErrNone; - long track, sector; - const uint8_t* buf = gDOS33Tracks; - - if (sectPerTrack == 13) { - LOGI(" DOS33 writing DOS 3.3 tracks"); - buf = gDOS32Tracks; - - for (track = 0; track < 3; track++) { - for (sector = 0; sector < 13; sector++) { - dierr = fpImg->WriteTrackSector(track, sector, buf); - if (dierr != kDIErrNone) - goto bail; - buf += kSctSize; - } - } - } else if (sectPerTrack == 16) { - LOGI(" DOS33 writing DOS 3.3 tracks"); - buf = gDOS33Tracks; - - // this should be used for 32-sector disks - - for (track = 0; track < 3; track++) { - for (sector = 0; sector < 16; sector++) { - dierr = fpImg->WriteTrackSector(track, sector, buf); - if (dierr != kDIErrNone) - goto bail; - buf += kSctSize; - } - } - } else { - LOGI(" DOS33 *not* writing DOS tracks to %d-sector disk", - sectPerTrack); - assert(false); - } - -bail: - return dierr; -} - -/* - * Normalize a DOS 3.3 path. Used when adding files from DiskArchive. - * The path may contain subdirectory components, which we need to strip away. - * - * "*pNormalizedBufLen" is used to pass in the length of the buffer and - * pass out the length of the string (should the buffer prove inadequate). - */ -DIError DiskFSDOS33::NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) -{ - DIError dierr = kDIErrNone; - char tmpBuf[A2FileDOS::kMaxFileName+1]; - int len; - - DoNormalizePath(path, fssep, tmpBuf); - len = strlen(tmpBuf)+1; - - if (*pNormalizedBufLen < len) - dierr = kDIErrDataOverrun; - else - strcpy(normalizedBuf, tmpBuf); - *pNormalizedBufLen = len; - - return dierr; -} - -/* - * Normalize a DOS 3.3 pathname. Lower case becomes upper case, control - * characters and high ASCII get stripped, and ',' becomes '_'. - * - * "outBuf" must be able to hold kMaxFileName+1 characters. - */ -void DiskFSDOS33::DoNormalizePath(const char* name, char fssep, char* outBuf) -{ - char* outp = outBuf; - const char* cp; - - /* throw out leading pathname, if any */ - if (fssep != '\0') { - cp = strrchr(name, fssep); - if (cp != NULL) - name = cp+1; - } - - while (*name != '\0' && (outp - outBuf) <= A2FileDOS::kMaxFileName) { - if (*name >= 0x20 && *name < 0x7f) { - if (*name == ',') - *outp = '_'; - else - *outp = toupper(*name); - - outp++; - } - name++; - } - *outp = '\0'; - - if (*outBuf == '\0') { - /* nothing left */ - strcpy(outBuf, "BLANK"); - } -} - -/* - * Create a file on a DOS 3.2/3.3 disk. - * - * The file will be created with an empty T/S list. - * - * It is not possible to set the aux type here. Aux types only apply to 'B' - * files, and since they're stored in the first data sector (which we don't - * create), there's nowhere to put it. We stuff it into the aux type value - * in the linear file list, on the assumption that somebody will come along - * and politely Write to the file, even if it's zero bytes long. - * - * (Technically speaking, setting the file type here is bogus, because a - * 'B' file with no data sectors is invalid. However, we don't want to - * handle arbitrary changes later -- switching from 'T' to 'B' requires - * either rewriting the entire file, or confusing the user by changing the - * type without adjusting the first 4 bytes -- so we set it now. It's also - * helpful to set it now because the Write routine needs to know how many - * bytes offset from the start of the file it needs to be. We could avoid - * most of this weirdness by just going ahead and allocating the first - * sector of the file now, and modifying the Write() function to understand - * that the first block is already there. Need to do that someday.) - */ -DIError DiskFSDOS33::CreateFile(const CreateParms* pParms, A2File** ppNewFile) -{ - DIError dierr = kDIErrNone; - const bool createUnique = (GetParameter(kParm_CreateUnique) != 0); - char normalName[A2FileDOS::kMaxFileName+1]; -// char storageName[A2FileDOS::kMaxFileName+1]; - A2FileDOS::FileType fileType; - A2FileDOS* pNewFile = NULL; - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - assert(pParms != NULL); - assert(pParms->pathName != NULL); - assert(pParms->storageType == A2FileProDOS::kStorageSeedling); - LOGI(" DOS33 ---v--- CreateFile '%s'", pParms->pathName); - - *ppNewFile = NULL; - - DoNormalizePath(pParms->pathName, pParms->fssep, normalName); - - /* - * See if the file already exists. - * - * If "create unique" is set, we append digits until the name doesn't - * match any others. The name will be modified in place. - */ - if (createUnique) { - MakeFileNameUnique(normalName); - } else { - if (GetFileByName(normalName) != NULL) { - LOGI(" DOS33 create: normalized name '%s' already exists", - normalName); - dierr = kDIErrFileExists; - goto bail; - } - } - - fileType = A2FileDOS::ConvertFileType(pParms->fileType, 0); - - /* - * Allocate a directory entry and T/S list. - */ - uint8_t sctBuf[kSctSize]; - TrackSector catSect; - TrackSector tsSect; - int catEntry; - A2FileDOS* pPrevEntry; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* allocate a sector for the T/S list, and zero it out */ - dierr = AllocSector(&tsSect); - if (dierr != kDIErrNone) - goto bail; - - memset(sctBuf, 0, kSctSize); - dierr = fpImg->WriteTrackSector(tsSect.track, tsSect.sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* - * Find the first free catalog entry. Also returns a pointer to the - * previous entry. - */ - dierr = GetFreeCatalogEntry(&catSect, &catEntry, sctBuf, &pPrevEntry); - if (dierr != kDIErrNone) { - LOGI("DOS unable to find an empty entry in the catalog"); - goto bail; - } - LOGI(" DOS found free catalog entry T=%d S=%d ent=%d prev=0x%08lx", - catSect.track, catSect.sector, catEntry, (long) pPrevEntry); - - /* create the new dir entry at the specified location */ - CreateDirEntry(sctBuf, catEntry, normalName, &tsSect, - (uint8_t) fileType, pParms->access); - - /* - * Flush everything to disk. - */ - dierr = fpImg->WriteTrackSector(catSect.track, catSect.sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - dierr = SaveVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Create a new entry for our file list. - */ - pNewFile = new A2FileDOS(this); - if (pNewFile == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - pNewFile->fTSListTrack = tsSect.track; - pNewFile->fTSListSector = tsSect.sector; - pNewFile->fLengthInSectors = 1; - pNewFile->fLocked = false; - strcpy(pNewFile->fFileName, normalName); - pNewFile->fFileType = fileType; - - pNewFile->fCatTS.track = catSect.track; - pNewFile->fCatTS.sector = catSect.sector; - pNewFile->fCatEntryNum = catEntry; - - pNewFile->fAuxType = (uint16_t) pParms->auxType; - pNewFile->fDataOffset = 0; - switch (pNewFile->fFileType) { - case A2FileDOS::kTypeInteger: - pNewFile->fDataOffset = 2; - break; - case A2FileDOS::kTypeApplesoft: - pNewFile->fDataOffset = 2; - pNewFile->fAuxType = 0x0801; - break; - case A2FileDOS::kTypeBinary: - pNewFile->fDataOffset = 4; - break; - default: - break; - } - pNewFile->fLength = 0; - pNewFile->fSparseLength = 0; - - /* - * Insert it in the proper place, so that the order of the files matches - * the order of entries in the catalog. - */ - InsertFileInList(pNewFile, pPrevEntry); - - *ppNewFile = pNewFile; - pNewFile = NULL; - -bail: - delete pNewFile; - FreeVolBitmap(); - return dierr; -} - -/* - * Make the name pointed to by "fileName" unique. The name should already - * be FS-normalized, and be in a buffer that can hold at least kMaxFileName+1 - * bytes. - * - * (This is nearly identical to the code in the ProDOS implementation. I'd - * like to make it a general DiskFS function, but making the loop condition - * work requires setting up callbacks, which isn't hard here but is a little - * annoying in ProDOS because of the subdir buffer. So it's cut & paste - * for now.) - * - * Returns an error on failure, which should be impossible. - */ -DIError DiskFSDOS33::MakeFileNameUnique(char* fileName) -{ - assert(fileName != NULL); - assert(strlen(fileName) <= A2FileDOS::kMaxFileName); - - if (GetFileByName(fileName) == NULL) - return kDIErrNone; - - LOGI(" DOS found duplicate of '%s', making unique", fileName); - - int nameLen = strlen(fileName); - int dotOffset=0, dotLen=0; - char dotBuf[kMaxExtensionLen+1]; - - /* ensure the result will be null-terminated */ - memset(fileName + nameLen, 0, (A2FileDOS::kMaxFileName - nameLen) +1); - - /* - * If this has what looks like a filename extension, grab it. We want - * to preserve ".gif", ".c", etc. - */ - const char* cp = strrchr(fileName, '.'); - if (cp != NULL) { - int tmpOffset = cp - fileName; - if (tmpOffset > 0 && nameLen - tmpOffset <= kMaxExtensionLen) { - LOGI(" DOS (keeping extension '%s')", cp); - assert(strlen(cp) <= kMaxExtensionLen); - strcpy(dotBuf, cp); - dotOffset = tmpOffset; - dotLen = nameLen - dotOffset; - } - } - - const int kMaxDigits = 999; - int digits = 0; - int digitLen; - int copyOffset; - char digitBuf[4]; - do { - if (digits == kMaxDigits) - return kDIErrFileExists; - digits++; - - /* not the most efficient way to do this, but it'll do */ - sprintf(digitBuf, "%d", digits); - digitLen = strlen(digitBuf); - if (nameLen + digitLen > A2FileDOS::kMaxFileName) - copyOffset = A2FileDOS::kMaxFileName - dotLen - digitLen; - else - copyOffset = nameLen - dotLen; - memcpy(fileName + copyOffset, digitBuf, digitLen); - if (dotLen != 0) - memcpy(fileName + copyOffset + digitLen, dotBuf, dotLen); - } while (GetFileByName(fileName) != NULL); - - LOGI(" DOS converted to unique name: %s", fileName); - - return kDIErrNone; -} - -/* - * Find the first free entry in the catalog. - * - * Also returns an A2File pointer for the previous entry in the catalog. - * - * The contents of the catalog sector will be in "sctBuf". - */ -DIError DiskFSDOS33::GetFreeCatalogEntry(TrackSector* pCatSect, int* pCatEntry, - uint8_t* sctBuf, A2FileDOS** ppPrevEntry) -{ - DIError dierr = kDIErrNone; - uint8_t* pEntry; - int sct, ent; - bool found = false; - - for (sct = 0; sct < kMaxCatalogSectors; sct++) { - if (fCatalogSectors[sct].track == 0 && - fCatalogSectors[sct].sector == 0) - { - /* end of list reached */ - LOGI("DOS catalog is full"); - dierr = kDIErrVolumeDirFull; - goto bail; - } - dierr = fpImg->ReadTrackSector(fCatalogSectors[sct].track, - fCatalogSectors[sct].sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - pEntry = &sctBuf[kCatalogEntryOffset]; - for (ent = 0; ent < kCatalogEntriesPerSect; ent++) { - if (pEntry[0x00] == 0x00 || pEntry[0x00] == kEntryDeleted) { - /* winner! */ - *pCatSect = fCatalogSectors[sct]; - *pCatEntry = ent; - found = true; - break; - } - - pEntry += kCatalogEntrySize; - } - - if (found) - break; - } - - if (sct == kMaxCatalogSectors) { - /* didn't find anything, assume the disk is full */ - dierr = kDIErrVolumeDirFull; - // fall through to "bail" - } else { - /* figure out what the previous entry is */ - TrackSector prevTS; - int prevEntry; - - if (*pCatEntry != 0) { - prevTS = *pCatSect; - prevEntry = *pCatEntry -1; - } else if (sct != 0) { - prevTS = fCatalogSectors[sct-1]; - prevEntry = kCatalogEntriesPerSect-1; - } else { - /* disk was empty; there's no previous entry */ - prevTS.track = 0; - prevTS.sector = 0; - prevEntry = -1; - } - - /* now find it in the linear file list */ - *ppPrevEntry = NULL; - if (prevEntry >= 0) { - A2FileDOS* pFile = (A2FileDOS*) GetNextFile(NULL); - while (pFile != NULL) { - if (pFile->fCatTS.track == prevTS.track && - pFile->fCatTS.sector == prevTS.sector && - pFile->fCatEntryNum == prevEntry) - { - *ppPrevEntry = pFile; - break; - } - pFile = (A2FileDOS*) GetNextFile(pFile); - } - assert(*ppPrevEntry != NULL); - } - } - -bail: - return dierr; -} - -/* - * Fill out the catalog entry in the location specified. - */ -void DiskFSDOS33::CreateDirEntry(uint8_t* sctBuf, int catEntry, - const char* fileName, TrackSector* pTSSect, uint8_t fileType, - int access) -{ - char highName[A2FileDOS::kMaxFileName+1]; - uint8_t* pEntry; - - pEntry = GetCatalogEntryPtr(sctBuf, catEntry); - if (pEntry[0x00] != 0x00 && pEntry[0x00] != kEntryDeleted) { - /* somebody screwed up */ - assert(false); - return; - } - - A2FileDOS::MakeDOSName(highName, fileName); - - pEntry[0x00] = pTSSect->track; - pEntry[0x01] = pTSSect->sector; - pEntry[0x02] = fileType; - if ((access & A2FileProDOS::kAccessWrite) == 0) - pEntry[0x02] |= (uint8_t) A2FileDOS::kTypeLocked; - memcpy(&pEntry[0x03], highName, A2FileDOS::kMaxFileName); - PutShortLE(&pEntry[0x21], 1); // assume file is 1 sector long -} - -/* - * Delete a file. - * - * This entails freeing up the allocated sectors and changing a byte in - * the directory entry. We then remove it from the DiskFS file list. - */ -DIError DiskFSDOS33::DeleteFile(A2File* pGenericFile) -{ - DIError dierr = kDIErrNone; - A2FileDOS* pFile = (A2FileDOS*) pGenericFile; - TrackSector* tsList = NULL; - TrackSector* indexList = NULL; - int tsCount, indexCount; - uint8_t sctBuf[kSctSize]; - uint8_t* pEntry; - - if (pGenericFile == NULL) { - assert(false); - return kDIErrInvalidArg; - } - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - if (pGenericFile->IsFileOpen()) - return kDIErrFileOpen; - - LOGI(" Deleting '%s'", pFile->GetPathName()); - - /* - * Update the block usage map. Nothing is permanent until we flush - * the data to disk. - */ - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - dierr = pFile->LoadTSList(&tsList, &tsCount, &indexList, &indexCount); - if (dierr != kDIErrNone) { - LOGI("Failed loading TS lists while deleting '%s'", - pFile->GetPathName()); - goto bail; - } - - FreeTrackSectors(tsList, tsCount); - FreeTrackSectors(indexList, indexCount); - - /* - * Mark the entry as deleted. - */ - dierr = fpImg->ReadTrackSector(pFile->fCatTS.track, pFile->fCatTS.sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - pEntry = GetCatalogEntryPtr(sctBuf, pFile->fCatEntryNum); - assert(pEntry[0x00] != 0x00 && pEntry[0x00] != kEntryDeleted); - pEntry[0x00] = kEntryDeleted; - dierr = fpImg->WriteTrackSector(pFile->fCatTS.track, pFile->fCatTS.sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* - * Save our updated copy of the volume bitmap to disk. - */ - dierr = SaveVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Remove the A2File* from the list. - */ - DeleteFileFromList(pFile); - -bail: - FreeVolBitmap(); - delete[] tsList; - delete[] indexList; - return dierr; -} - -/* - * Mark all of the track/sector entries in "pList" as free. - */ -void DiskFSDOS33::FreeTrackSectors(TrackSector* pList, int count) -{ - VolumeUsage::ChunkState cstate; - int i; - - cstate.isUsed = false; - cstate.isMarkedUsed = false; - cstate.purpose = VolumeUsage::kChunkPurposeUnknown; - - for (i = 0; i < count; i++) { - if (pList[i].track == 0 && pList[i].sector == 0) - continue; // sparse file - - if (!GetSectorUseEntry(pList[i].track, pList[i].sector)) { - LOGI("WARNING: freeing unallocated sector T=%d S=%d", - pList[i].track, pList[i].sector); - assert(false); // impossible unless disk is "damaged" - } - SetSectorUseEntry(pList[i].track, pList[i].sector, false); - - fVolumeUsage.SetChunkState(pList[i].track, pList[i].sector, &cstate); - } -} - -/* - * Rename a file. - * - * "newName" must already be normalized. - */ -DIError DiskFSDOS33::RenameFile(A2File* pGenericFile, const char* newName) -{ - DIError dierr = kDIErrNone; - A2FileDOS* pFile = (A2FileDOS*) pGenericFile; - char normalName[A2FileDOS::kMaxFileName+1]; - char dosName[A2FileDOS::kMaxFileName+1]; - uint8_t sctBuf[kSctSize]; - uint8_t* pEntry; - - if (pFile == NULL || newName == NULL) - return kDIErrInvalidArg; - if (!IsValidFileName(newName)) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - LOGI(" DOS renaming '%s' to '%s'", pFile->GetPathName(), newName); - - /* - * Update the disk catalog entry. - */ - dierr = fpImg->ReadTrackSector(pFile->fCatTS.track, pFile->fCatTS.sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - pEntry = GetCatalogEntryPtr(sctBuf, pFile->fCatEntryNum); - - DoNormalizePath(newName, '\0', normalName); - A2FileDOS::MakeDOSName(dosName, normalName); - memcpy(&pEntry[0x03], dosName, A2FileDOS::kMaxFileName); - - dierr = fpImg->WriteTrackSector(pFile->fCatTS.track, pFile->fCatTS.sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* - * Update our internal copy. - */ - char storedName[A2FileDOS::kMaxFileName+1]; - strcpy(storedName, dosName); - LowerASCII((uint8_t*)storedName, A2FileDOS::kMaxFileName); - A2FileDOS::TrimTrailingSpaces(storedName); - - strcpy(pFile->fFileName, storedName); - -bail: - return dierr; -} - -/* - * Set the file's attributes. - * - * We allow the file to be locked or unlocked, and we allow the file type - * to be changed. We don't try to rewrite the file if they're changing to or - * from a format with embedded data (e.g. BAS or BIN); instead, we just - * change the type letter. We do need to re-evaluate the end-of-file - * value afterward. - * - * Changing the aux type is only allowed for BIN files. - */ -DIError DiskFSDOS33::SetFileInfo(A2File* pGenericFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) -{ - DIError dierr = kDIErrNone; - A2FileDOS* pFile = (A2FileDOS*) pGenericFile; - TrackSector* tsList = NULL; - int tsCount; - bool nowLocked; - bool typeChanged; - - if (pFile == NULL) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - - LOGI("DOS SetFileInfo '%s' type=0x%02x aux=0x%04x access=0x%02x", - pFile->GetPathName(), fileType, auxType, accessFlags); - - /* - * We can ignore the file/aux type, or we can verify that they're not - * trying to change it. The latter is a little more work but makes - * the API a little more communicative. - */ - if (!A2FileDOS::IsValidType(fileType)) { - LOGI("DOS SetFileInfo invalid file type"); - dierr = kDIErrInvalidArg; - goto bail; - } - if (auxType != pFile->GetAuxType() && fileType != 0x06) { - /* this only makes sense for BIN files */ - LOGI("DOS SetFileInfo aux type mismatch; ignoring"); - //dierr = kDIErrNotSupported; - //goto bail; - } - - nowLocked = (accessFlags & A2FileProDOS::kAccessWrite) == 0; - typeChanged = (fileType != pFile->GetFileType()); - - /* - * Update the file type and locked status, if necessary. - */ - if (nowLocked != pFile->fLocked || typeChanged) { - A2FileDOS::FileType newFileType; - uint8_t sctBuf[kSctSize]; - uint8_t* pEntry; - - LOGI("Updating file '%s'", pFile->GetPathName()); - - dierr = fpImg->ReadTrackSector(pFile->fCatTS.track, pFile->fCatTS.sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - pEntry = GetCatalogEntryPtr(sctBuf, pFile->fCatEntryNum); - - newFileType = A2FileDOS::ConvertFileType(fileType, 0); - pEntry[0x02] = (uint8_t) newFileType; - if (nowLocked) - pEntry[0x02] |= 0x80; - - dierr = fpImg->WriteTrackSector(pFile->fCatTS.track, pFile->fCatTS.sector, - sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* update our local copy */ - pFile->fLocked = nowLocked; - } - - if (!typeChanged && auxType == pFile->GetAuxType()) { - /* only the locked status has changed; skip the rest */ - goto bail; - } - - /* - * If the file has type BIN (either because it was before and we left it - * alone, or we changed it to BIN), we need to figure out what the aux - * type should be. There are two situations: - * - * (1) User specified an aux type. If the aux type passed in doesn't match - * what's in the A2FileDOS structure, we assume they meant to change it. - * (2) User didn't specify an aux type change. If the file was BIN before, - * we don't need to do anything, but if it was just changed to BIN then - * we need to extract the aux type from the first sector of the file. - * - * There's also a 3rd situation: they changed the aux type for a non-BIN - * file. This should have been blocked earlier. - * - * On top of all this, if we changed the file type at all then we need to - * re-scan the file length and "data offset" value. - */ - uint16_t newAuxType; - newAuxType = (uint16_t) auxType; - - dierr = pFile->LoadTSList(&tsList, &tsCount); - if (dierr != kDIErrNone) { - LOGI(" DOS SFI: unable to load TS list (err=%d)", dierr); - goto bail; - } - - if (fileType == 0x06 && tsCount > 0) { - uint8_t sctBuf[kSctSize]; - - dierr = fpImg->ReadTrackSector(tsList[0].track, - tsList[0].sector, sctBuf); - if (dierr != kDIErrNone) { - LOGI("DOS SFI: unable to get first sector of file"); - goto bail; - } - - if (auxType == pFile->GetAuxType()) { - newAuxType = GetShortLE(&sctBuf[0x00]); - LOGI(" Aux type not changed, extracting from file (0x%04x)", - newAuxType); - } else { - LOGI(" Aux type changed (to 0x%04x), changing file", - newAuxType); - - PutShortLE(&sctBuf[0x00], newAuxType); - dierr = fpImg->WriteTrackSector(tsList[0].track, - tsList[0].sector, sctBuf); - if (dierr != kDIErrNone) { - LOGI("DOS SFI: unable to write first sector of file"); - goto bail; - } - } - } else { - /* not BIN or file has no sectors */ - if (pFile->fFileType == A2FileDOS::kTypeApplesoft) - newAuxType = 0x0801; - else - newAuxType = 0x0000; - } - - /* update our local copy */ - pFile->fFileType = A2FileDOS::ConvertFileType(fileType, 0); - pFile->fAuxType = newAuxType; - - /* - * Recalculate the file's length and "data offset". This may also mark - * the file as "suspicious". We wouldn't be here if the file was - * suspicious when we opened the disk image -- the image would have - * been marked read-only -- so if it's suspicious now, it's probably - * from a previous file type change attempt in the current session. - * Clear the flag so it doesn't "stick". - */ - pFile->ResetQuality(); - (void) ComputeLength(pFile, tsList, tsCount); - -bail: - delete[] tsList; - return dierr; -} - -/* - * Change the disk volume name (number). - * - * We can't change the 2MG header, and we can't change the values embedded - * in the sector headers, so all we do is change the VTOC entry. - */ -DIError DiskFSDOS33::RenameVolume(const char* newName) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - long newNumber; - char* endp; - - if (!IsValidVolumeName(newName)) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - - // convert the number; we already ascertained that it's valid - newNumber = strtol(newName, &endp, 10); - - dierr = fpImg->ReadTrackSector(kVTOCTrack, kVTOCSector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - sctBuf[0x06] = (uint8_t) newNumber; - - dierr = fpImg->WriteTrackSector(kVTOCTrack, kVTOCSector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - fVTOCVolumeNumber = newNumber; - UpdateVolumeNum(); - -bail: - return dierr; -} - - -/* - * =========================================================================== - * A2FileDOS - * =========================================================================== - */ - -/* - * Constructor. - */ -A2FileDOS::A2FileDOS(DiskFS* pDiskFS) : A2File(pDiskFS) -{ - fTSListTrack = -1; - fTSListSector = -1; - fLengthInSectors = 0; - fLocked = false; - fFileName[0] = '\0'; - fFileType = kTypeUnknown; - - fCatTS.track = fCatTS.sector = 0; - fCatEntryNum = -1; - - fAuxType = 0; - fDataOffset = 0; - fLength = -1; - fSparseLength = -1; - - fpOpenFile = NULL; -} - -/* - * Destructor. Make sure an "open" file gets "closed". - */ -A2FileDOS::~A2FileDOS(void) -{ - delete fpOpenFile; -} - - -/* - * Convert the filetype enum to a ProDOS type. - * - * Remember that the DOS filetype field is actually a bit field, so we need - * to handle situations where more than one bit is set. - * - * Ideally this is a reversible transformation, so files copied to ProDOS - * volumes can be copied back to DOS with no loss of information. The reverse - * is *not* true, because of file type reduction and the potential loss of - * accurate file length info. - * - * I'm not entirely certain about the conversion of 'R' to REL, largely - * because I can't find any information on the REL format. However, Copy ][+ - * does convert to REL, and the Binary ][ standard says I should as well. - */ -uint32_t A2FileDOS::GetFileType(void) const -{ - long retval; - - switch (fFileType) { - case kTypeText: retval = 0x04; break; // TXT - case kTypeInteger: retval = 0xfa; break; // INT - case kTypeApplesoft: retval = 0xfc; break; // BAS - case kTypeBinary: retval = 0x06; break; // BIN - case kTypeS: retval = 0xf2; break; // $f2 - case kTypeReloc: retval = 0xfe; break; // REL - case kTypeA: retval = 0xf3; break; // $f3 - case kTypeB: retval = 0xf4; break; // $f4 - case kTypeUnknown: - default: - retval = 0x00; // NON - break; - } - - return retval; -} - -/* - * Convert a ProDOS 8 file type to its DOS equivalent. - * - * We need to know the file length because files over 64K can't fit into - * DOS A/I/B files. Text files can be as long as they want, and the - * other types don't have a length word defined, so they're fine. - * - * We can't just convert them later, because by that point they've already - * got a 2-byte or 4-byte header reserved. - * - * Because we don't generally know the eventual length of the file at - * the time we're creating it, this doesn't work nearly as well as could - * be hoped. We can make life a little less confusing for the caller by - * using type 'S' for any unknown type. - */ -/*static*/ A2FileDOS::FileType A2FileDOS::ConvertFileType(long prodosType, - di_off_t fileLen) -{ - const long kMaxBinary = 65535; - FileType newType; - - switch (prodosType) { - case 0xb0: newType = kTypeText; break; // SRC - case 0x04: newType = kTypeText; break; // TXT - case 0xfa: newType = kTypeInteger; break; // INT - case 0xfc: newType = kTypeApplesoft; break; // BAS - case 0x06: newType = kTypeBinary; break; // BIN - case 0xf2: newType = kTypeS; break; // $f2 - case 0xfe: newType = kTypeReloc; break; // REL - case 0xf3: newType = kTypeA; break; // $f3 - case 0xf4: newType = kTypeB; break; // $f4 - default: newType = kTypeS; break; - } - - if (fileLen > kMaxBinary && - (newType == kTypeInteger || newType == kTypeApplesoft || - newType == kTypeBinary)) - { - LOGI(" DOS setting type for large A/I/B file to S"); - newType = kTypeS; - } - - return newType; -} - -/* - * Determine whether the specified type has a valid DOS mapping. - */ -/*static*/ bool A2FileDOS::IsValidType(long prodosType) -{ - switch (prodosType) { - case 0xb0: // SRC - case 0x04: // TXT - case 0xfa: // INT - case 0xfc: // BAS - case 0x06: // BIN - case 0xf2: // $f2 - case 0xfe: // REL - case 0xf3: // $f3 - case 0xf4: // $f4 - return true; - default: - return false; - } -} - -/* - * Match the ProDOS equivalents of "locked" and "unlocked". - */ -uint32_t A2FileDOS::GetAccess(void) const -{ - if (fLocked) - return DiskFS::kFileAccessLocked; // 0x01 read - else - return DiskFS::kFileAccessUnlocked; // 0xc3 read/write/rename/destroy -} - -/* - * "Fix" a DOS3.3 filename. Convert DOS-ASCII to normal ASCII, and strip - * trailing spaces. - */ -void A2FileDOS::FixFilename(void) -{ - DiskFSDOS33::LowerASCII((uint8_t*)fFileName, kMaxFileName); - TrimTrailingSpaces(fFileName); -} - -/* - * Trim the spaces off the end of a filename. - * - * Assumes the filename has already been converted to low ASCII. - */ -/*static*/ void A2FileDOS::TrimTrailingSpaces(char* filename) -{ - char* lastspc = filename + strlen(filename); - - assert(*lastspc == '\0'); - - while (--lastspc) { - if (*lastspc != ' ') - break; - } - - *(lastspc+1) = '\0'; -} - -/* - * Encode a filename into high ASCII, padded out with spaces to - * kMaxFileName chars. Lower case is converted to upper case. This - * does not filter out control characters or other chunk. - * - * "buf" must be able to hold kMaxFileName+1 chars. - */ -/*static*/ void A2FileDOS::MakeDOSName(char* buf, const char* name) -{ - for (int i = 0; i < kMaxFileName; i++) { - if (*name == '\0') - *buf++ = (char) 0xa0; - else - *buf++ = toupper(*name++) | 0x80; - } - *buf = '\0'; -} - - -/* - * Set up state for this file. - */ -DIError A2FileDOS::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - DIError dierr = kDIErrNone; - A2FDDOS* pOpenFile = NULL; - - if (!readOnly) { - if (fpDiskFS->GetDiskImg()->GetReadOnly()) - return kDIErrAccessDenied; - if (fpDiskFS->GetFSDamaged()) - return kDIErrBadDiskImage; - } - - if (fpOpenFile != NULL) { - dierr = kDIErrAlreadyOpen; - goto bail; - } - - if (rsrcFork) - return kDIErrForkNotFound; - - pOpenFile = new A2FDDOS(this); - - dierr = LoadTSList(&pOpenFile->fTSList, &pOpenFile->fTSCount, - &pOpenFile->fIndexList, &pOpenFile->fIndexCount); - if (dierr != kDIErrNone) { - LOGI("DOS33 unable to load TS for '%s' open", GetPathName()); - goto bail; - } - - pOpenFile->fOffset = 0; - pOpenFile->fOpenEOF = fLength; - pOpenFile->fOpenSectorsUsed = fLengthInSectors; - - fpOpenFile = pOpenFile; // add it to our single-member "open file set" - *ppOpenFile = pOpenFile; - pOpenFile = NULL; - -bail: - delete pOpenFile; - return dierr; -} - -/* - * Dump the contents of an A2FileDOS. - */ -void A2FileDOS::Dump(void) const -{ - LOGI("A2FileDOS '%s'", fFileName); - LOGI(" TS T=%-2d S=%-2d", fTSListTrack, fTSListSector); - LOGI(" Cat T=%-2d S=%-2d", fCatTS.track, fCatTS.sector); - LOGI(" type=%d lck=%d slen=%d", fFileType, fLocked, fLengthInSectors); - LOGI(" auxtype=0x%04x length=%ld", - fAuxType, (long) fLength); -} - - -/* - * Load the T/S list for this file. - * - * A single T/S sector holds 122 entries, enough to store a 30.5K file. - * It's very unlikely that a file will need more than two, although it's - * possible for a random-access text file to have a very large number of - * entries. - * - * If "pIndexList" and "pIndexCount" are non-NULL, the list of index blocks is - * also loaded. - * - * It's entirely possible to get a large T/S list back that is filled - * entirely with zeroes. This can happen if we have a large set of T/S - * index sectors that are all zero. We have to leave space for them so - * that the Write function can use the existing allocated index blocks. - * - * THOUGHT: we may want to use the file type to tighten this up a bit. - * For example, we're currently very careful around random-access text - * files, but if the file doesn't have type 'T' then random access is - * impossible. Currently this isn't a problem, but for e.g. T/S lists - * with garbage at the end would could deal with the problem more generally. - */ -DIError A2FileDOS::LoadTSList(TrackSector** pTSList, int* pTSCount, - TrackSector** pIndexList, int* pIndexCount) -{ - DIError dierr = kDIErrNone; - DiskImg* pDiskImg; - const int kDefaultTSAlloc = 2; - const int kDefaultIndexAlloc = 8; - TrackSector* tsList = NULL; - TrackSector* indexList = NULL; - int tsCount, tsAlloc; - int indexCount, indexAlloc; - uint8_t sctBuf[kSctSize]; - int track, sector, iterations; - - LOGI("--- DOS loading T/S list for '%s'", GetPathName()); - - /* over-alloc for small files to reduce reallocs */ - tsAlloc = kMaxTSPairs * kDefaultTSAlloc; - tsList = new TrackSector[tsAlloc]; - tsCount = 0; - - indexAlloc = kDefaultIndexAlloc; - indexList = new TrackSector[indexAlloc]; - indexCount = 0; - - if (tsList == NULL || indexList == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - assert(fpDiskFS != NULL); - pDiskImg = fpDiskFS->GetDiskImg(); - assert(pDiskImg != NULL); - - /* get the first T/S sector for this file */ - track = fTSListTrack; - sector = fTSListSector; - if (track >= pDiskImg->GetNumTracks() || - sector >= pDiskImg->GetNumSectPerTrack()) - { - LOGI(" DOS33 invalid initial T/S %d,%d in '%s'", track, sector, - fFileName); - dierr = kDIErrBadFile; - goto bail; - } - - /* - * Run through the set of t/s pairs. - */ - iterations = 0; - do { - uint16_t sectorOffset; - int lastNonZero; - - /* - * Add the current T/S sector to the index list. - */ - if (indexCount == indexAlloc) { - LOGI("+++ expanding index list"); - TrackSector* newList; - indexAlloc += kDefaultIndexAlloc; - newList = new TrackSector[indexAlloc]; - if (newList == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - memcpy(newList, indexList, indexCount * sizeof(TrackSector)); - delete[] indexList; - indexList = newList; - } - indexList[indexCount].track = track; - indexList[indexCount].sector = sector; - indexCount++; - - - //LOGI("+++ scanning T/S at T=%d S=%d", track, sector); - dierr = pDiskImg->ReadTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - /* grab next track/sector */ - track = sctBuf[0x01]; - sector = sctBuf[0x02]; - sectorOffset = GetShortLE(&sctBuf[0x05]); - - /* if T/S link is bogus, whole sector is probably bad */ - if (track >= pDiskImg->GetNumTracks() || - sector >= pDiskImg->GetNumSectPerTrack()) - { - // bogus T/S, mark file as damaged and stop - LOGI(" DOS33 invalid T/S link %d,%d in '%s'", track, sector, - GetPathName()); - dierr = kDIErrBadFile; - goto bail; - } - if ((sectorOffset % kMaxTSPairs) != 0) { - LOGI(" DOS33 invalid T/S header sector offset %u in '%s'", - sectorOffset, GetPathName()); - // not fatal, just weird - } - - /* - * Make sure we have enough room to hold an entire sector full of - * T/S pairs in the list. - */ - if (tsCount + kMaxTSPairs > tsAlloc) { - LOGI("+++ expanding ts list"); - TrackSector* newList; - tsAlloc += kMaxTSPairs * kDefaultTSAlloc; - newList = new TrackSector[tsAlloc]; - if (newList == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - memcpy(newList, tsList, tsCount * sizeof(TrackSector)); - delete[] tsList; - tsList = newList; - } - - /* - * Add the entries. If there's another T/S list linked, we just - * grab the entire sector. If not, we grab every entry until the - * last 0,0. (Can't stop at the first (0,0), or we'll drop a - * piece of a random access text file.) - */ - dierr = ExtractTSPairs(sctBuf, &tsList[tsCount], &lastNonZero); - if (dierr != kDIErrNone) - goto bail; - - if (track != 0 && sector != 0) { - /* more T/S lists to come, so we keep all entries */ - tsCount += kMaxTSPairs; - } else { - /* this was the last one */ - if (lastNonZero == -1) { - /* this is ALWAYS the case for a newly-created file */ - //LOGI(" DOS33 odd -- last T/S sector of '%s' was empty", - // GetPathName()); - } - tsCount += lastNonZero +1; - } - - iterations++; // watch for infinite loops - } while (!(track == 0 && sector == 0) && iterations < kMaxTSIterations); - - if (iterations == kMaxTSIterations) { - dierr = kDIErrFileLoop; - goto bail; - } - - *pTSList = tsList; - *pTSCount = tsCount; - tsList = NULL; - - if (pIndexList != NULL) { - *pIndexList = indexList; - *pIndexCount = indexCount; - indexList = NULL; - } - -bail: - delete[] tsList; - delete[] indexList; - return dierr; -} - -/* - * Extract the track/sector pairs from the TS list in "sctBuf". The entries - * are copied to "tsList", which is assumed to have enough space to hold - * at least kMaxTSPairs entries. - * - * The last non-zero entry will be identified and stored in "*pLastNonZero". - * If all entries are zero, it will be set to -1. - * - * Sometimes files will have junk at the tail end of an otherwise valid - * T/S list. We can't just stop when we hit the first (0,0) entry because - * that'll screw up random-access text file handling. What we can do is - * try to detect the situation, and mark the file as "suspicious" without - * returning an error if we see it. - * - * If a TS entry appears to be invalid, this returns an error after all - * entries have been copied. If it looks to be partially valid, only the - * valid parts are copied out, with the rest zeroed. - */ -DIError A2FileDOS::ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList, - int* pLastNonZero) -{ - DIError dierr = kDIErrNone; - const DiskImg* pDiskImg = fpDiskFS->GetDiskImg(); - const uint8_t* ptr; - int i, track, sector; - - *pLastNonZero = -1; - memset(tsList, 0, sizeof(TrackSector) * kMaxTSPairs); - - ptr = &sctBuf[kTSOffset]; // offset of first T/S entry (0x0c) - - for (i = 0; i < kMaxTSPairs; i++) { - track = *ptr++; - sector = *ptr++; - - if (dierr == kDIErrNone && - (track >= pDiskImg->GetNumTracks() || - sector >= pDiskImg->GetNumSectPerTrack() || - (track == 0 && sector != 0))) - { - LOGI(" DOS33 invalid T/S %d,%d in '%s'", track, sector, - fFileName); - - if (i > 0 && tsList[i-1].track == 0 && tsList[i-1].sector == 0) { - LOGI(" T/S list looks partially valid"); - SetQuality(kQualitySuspicious); - goto bail; // quit immediately - } else { - dierr = kDIErrBadFile; - // keep going, just so caller has the full set to stare at - } - } - - if (track != 0 || sector != 0) - *pLastNonZero = i; - - tsList[i].track = track; - tsList[i].sector = sector; - } - -bail: - return dierr; -} - - -/* - * =========================================================================== - * A2FDDOS - * =========================================================================== - */ - -/* - * Read data from the current offset. - * - * Files read back as they would from ProDOS, i.e. if you read a binary - * file you won't see the 4 bytes of length and address. - */ -DIError A2FDDOS::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" DOS reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - - A2FileDOS* pFile = (A2FileDOS*) fpFile; - - /* - * Don't allow them to read past the end of the file. The length value - * stored in pFile->fLength already has pFile->fDataOffset subtracted - * from the actual data length, so don't factor it in again. - */ - if (fOffset + (long)len > fOpenEOF) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (size_t) (fOpenEOF - fOffset); - } - if (pActual != NULL) - *pActual = len; - long incrLen = len; - - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - di_off_t actualOffset = fOffset + pFile->fDataOffset; // adjust for embedded len - int tsIndex = (int) (actualOffset / kSctSize); - int bufOffset = (int) (actualOffset % kSctSize); // (& 0xff) - size_t thisCount; - - if (len == 0) - return kDIErrNone; - assert(fOpenEOF != 0); - - assert(tsIndex >= 0 && tsIndex < fTSCount); - - /* could be more clever in here and avoid double-buffering */ - while (len) { - if (tsIndex >= fTSCount) { - /* should've caught this earlier */ - assert(false); - LOGI(" DOS ran off the end (fTSCount=%d)", fTSCount); - return kDIErrDataUnderrun; - } - - if (fTSList[tsIndex].track == 0 && fTSList[tsIndex].sector == 0) { - //LOGI(" DOS sparse sector T=%d S=%d", - // TSTrack(fTSList[tsIndex]), TSSector(fTSList[tsIndex])); - memset(sctBuf, 0, sizeof(sctBuf)); - } else { - dierr = pFile->GetDiskFS()->GetDiskImg()->ReadTrackSector( - fTSList[tsIndex].track, - fTSList[tsIndex].sector, - sctBuf); - if (dierr != kDIErrNone) { - LOGI(" DOS error reading file '%s'", pFile->GetPathName()); - return dierr; - } - } - thisCount = kSctSize - bufOffset; - if (thisCount > len) - thisCount = len; - memcpy(buf, sctBuf + bufOffset, thisCount); - len -= thisCount; - buf = (char*)buf + thisCount; - - bufOffset = 0; - tsIndex++; - } - - fOffset += incrLen; - - return dierr; -} - -/* - * Write data at the current offset. - * - * For simplicity, we assume that we're writing a brand-new file in one - * shot. As it happens, that's all we're currently required to do, so even - * if we wrote a more sophisticated function it wouldn't get exercised. - * Because of the way we write, there's no way to mimic the behavior of - * random-access text file allocation, so that isn't supported. - * - * The data in "buf" should *not* include the 2-4 bytes of header present - * on A/I/B files. That's already factored in. - * - * Modifies fOpenEOF, fOpenSectorsUsed, and sets fModified. - */ -DIError A2FDDOS::Write(const void* buf, size_t len, size_t* pActual) -{ - DIError dierr = kDIErrNone; - A2FileDOS* pFile = (A2FileDOS*) fpFile; - DiskFSDOS33* pDiskFS = (DiskFSDOS33*) fpFile->GetDiskFS(); - uint8_t sctBuf[kSctSize]; - - LOGD(" DOS Write len=%lu %s", (unsigned long) len, pFile->GetPathName()); - - if (len >= 0x01000000) { // 16MB - assert(false); - return kDIErrInvalidArg; - } - assert(fOffset == 0); // big simplifying assumption - assert(fOpenEOF == 0); // another one - assert(fTSCount == 0); // must hold for our newly-created files - assert(fIndexCount == 1); // must hold for our newly-created files - assert(fOpenSectorsUsed == fTSCount + fIndexCount); - assert(buf != NULL); - - long actualLen = (long) len + pFile->fDataOffset; - long numSectors = (actualLen + kSctSize -1) / kSctSize; - TrackSector firstIndex; - int i; - - /* - * Nothing to do for zero-length write; don't even set fModified. Note, - * however, that a zero-length 'B' file is actually 4 bytes long, and - * must have a data block allocated. - */ - if (actualLen == 0) - goto bail; - assert(numSectors > 0); - - dierr = pDiskFS->LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Start by allocating a full T/S list. The existing T/S list is - * empty, but we do have one T/S index sector to fill before we - * allocate any others. - * - * Since we determined above that there was nothing interesting in - * our T/S list, we just grab the one allocated block, throw out - * the lists, and reallocate them. - */ - firstIndex = fIndexList[0]; - delete[] fTSList; - delete[] fIndexList; - fTSList = fIndexList = NULL; - - fTSCount = numSectors; - fTSList = new TrackSector[fTSCount]; - fIndexCount = (numSectors + kMaxTSPairs -1) / kMaxTSPairs; - assert(fIndexCount > 0); - fIndexList = new TrackSector[fIndexCount]; - if (fTSList == NULL || fIndexList == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - /* - * Allocate all of the index sectors. In theory we should to this along - * with the file sectors, so that the index and file sectors are - * interspersed with the data, but in practice 99% of the file have - * only one or two index blocks. By grouping them together we improve - * the performance for emulators and CiderPress. - */ - fIndexList[0] = firstIndex; - for (i = 1; i < fIndexCount; i++) { - TrackSector allocTS; - - dierr = pDiskFS->AllocSector(&allocTS); - if (dierr != kDIErrNone) - goto bail; - fIndexList[i] = allocTS; - } - /* - * Allocate the data sectors. - */ - for (i = 0; i < fTSCount; i++) { - TrackSector allocTS; - - dierr = pDiskFS->AllocSector(&allocTS); - if (dierr != kDIErrNone) - goto bail; - fTSList[i] = allocTS; - } - - /* - * Write the sectors into the T/S list. - */ - const uint8_t* curPtr; - int sectorIdx; - - curPtr = (const uint8_t*) buf; - sectorIdx = 0; - - if (pFile->fDataOffset > 0) { - /* handle first sector specially */ - assert(pFile->fDataOffset < kSctSize); - int dataInFirstSct = kSctSize - pFile->fDataOffset; - if (dataInFirstSct > actualLen - pFile->fDataOffset) - dataInFirstSct = actualLen - pFile->fDataOffset; - - // dataInFirstSct could be zero (== len) - memset(sctBuf, 0, sizeof(sctBuf)); - memcpy(sctBuf + pFile->fDataOffset, curPtr, - dataInFirstSct); - dierr = pDiskFS->GetDiskImg()->WriteTrackSector(fTSList[sectorIdx].track, - fTSList[sectorIdx].sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - sectorIdx++; - actualLen -= dataInFirstSct + pFile->fDataOffset; - curPtr += dataInFirstSct; - } - while (actualLen > 0) { - if (actualLen >= kSctSize) { - /* write directly from input */ - dierr = pDiskFS->GetDiskImg()->WriteTrackSector(fTSList[sectorIdx].track, - fTSList[sectorIdx].sector, curPtr); - if (dierr != kDIErrNone) - goto bail; - } else { - /* make a copy of the partial buffer */ - memset(sctBuf, 0, sizeof(sctBuf)); - memcpy(sctBuf, curPtr, actualLen); - dierr = pDiskFS->GetDiskImg()->WriteTrackSector(fTSList[sectorIdx].track, - fTSList[sectorIdx].sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - } - - sectorIdx++; - actualLen -= kSctSize; // goes negative; that's fine - curPtr += kSctSize; - } - assert(sectorIdx == fTSCount); - - /* - * Fill out the T/S list sectors. Failure here presents a potential - * problem because, once we've written the first T/S entry, the file - * appears to have storage that it actually doesn't. The easiest way - * to handle this safely is to start by writing the last index block - * first. - */ - for (i = fIndexCount-1; i >= 0; i--) { - int tsOffset = i * kMaxTSPairs; - assert(tsOffset < fTSCount); - - memset(sctBuf, 0, kSctSize); - if (i != fIndexCount-1) { - sctBuf[0x01] = fIndexList[i+1].track; - sctBuf[0x02] = fIndexList[i+1].sector; - } - PutShortLE(&sctBuf[0x05], kMaxTSPairs * i); - - int ent = i * kMaxTSPairs; // start here - for (int j = 0; j < kMaxTSPairs; j++) { - if (ent == fTSCount) - break; - sctBuf[kTSOffset + j*2] = fTSList[ent].track; - sctBuf[kTSOffset + j*2 +1] = fTSList[ent].sector; - ent++; - } - - dierr = pDiskFS->GetDiskImg()->WriteTrackSector(fIndexList[i].track, - fIndexList[i].sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - } - - dierr = pDiskFS->SaveVolBitmap(); - if (dierr != kDIErrNone) { - /* - * This is awkward -- we wrote the first T/S list, so the file - * now appears to have content, but the blocks aren't marked used. - * We read the VTOC successfully though, so it's VERY unlikely - * that this will fail. If it does, it's likely that any attempt - * to mitigate the problem will also fail. (Maybe we could force - * the object into read-only mode?) - */ - goto bail; - } - - /* finish up */ - fOpenSectorsUsed = fIndexCount + fTSCount; - fOpenEOF = len; - fOffset += len; - fModified = true; - - if (!UpdateProgress(fOffset)) - dierr = kDIErrCancelled; - -bail: - pDiskFS->FreeVolBitmap(); - return dierr; -} - -/* - * Seek to the specified offset. - */ -DIError A2FDDOS::Seek(di_off_t offset, DIWhence whence) -{ - //di_off_t fileLength = fpFile->GetDataLength(); - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fOpenEOF) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fOpenEOF) - return kDIErrInvalidArg; - fOffset = fOpenEOF + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fOpenEOF - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fOpenEOF); - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDDOS::Tell(void) -{ - return fOffset; -} - -/* - * Release file state. - * - * If the file was modified, we need to update the sector usage count in - * the catalog track, and possibly a length word in the first sector of - * the file (for A/I/B). - * - * Given the current "write all at once" implementation of Write, we could - * have handled the length word back when initially writing the data, but - * someday we may fix that and I don't want to have to rewrite this part. - * - * Most applications don't check the value of "Close", or call it from a - * destructor, so we call CloseDescr whether we succeed or not. - */ -DIError A2FDDOS::Close(void) -{ - DIError dierr = kDIErrNone; - - if (fModified) { - DiskFSDOS33* pDiskFS = (DiskFSDOS33*) fpFile->GetDiskFS(); - A2FileDOS* pFile = (A2FileDOS*) fpFile; - uint8_t sctBuf[kSctSize]; - uint8_t* pEntry; - - /* - * Fill in the length and address, if needed for this type of file. - */ - if (pFile->fFileType == A2FileDOS::kTypeInteger || - pFile->fFileType == A2FileDOS::kTypeApplesoft || - pFile->fFileType == A2FileDOS::kTypeBinary) - { - assert(fTSCount > 0); - assert(pFile->fDataOffset > 0); - //assert(fOpenEOF < 65536); - if (fOpenEOF > 65535) { - LOGW("WARNING: DOS Close trimming A/I/B file from %ld to 65535", - (long) fOpenEOF); - fOpenEOF = 65535; - } - dierr = pDiskFS->GetDiskImg()->ReadTrackSector(fTSList[0].track, - fTSList[0].sector, sctBuf); - if (dierr != kDIErrNone) { - LOGW("DOS Close: unable to get first sector of file"); - goto bail; - } - - if (pFile->fFileType == A2FileDOS::kTypeInteger || - pFile->fFileType == A2FileDOS::kTypeApplesoft) - { - PutShortLE(&sctBuf[0x00], (uint16_t) fOpenEOF); - } else { - PutShortLE(&sctBuf[0x00], pFile->fAuxType); - PutShortLE(&sctBuf[0x02], (uint16_t) fOpenEOF); - } - - dierr = pDiskFS->GetDiskImg()->WriteTrackSector(fTSList[0].track, - fTSList[0].sector, sctBuf); - if (dierr != kDIErrNone) { - LOGW("DOS Close: unable to write first sector of file"); - goto bail; - } - } else if (pFile->fFileType == A2FileDOS::kTypeText) { - /* - * The length of text files can be determined by looking for the - * first $00. A file of exactly 256 bytes occupies only one - * sector though, so running out of sectors also works -- the - * last $00 is not mandatory. - * - * Bottom line is that the value we just wrote for fOpenEOF is - * *probably* recoverable, so we can stuff it into "fLength" - * with some assurance that it will be there when we reopen the - * file. - */ - } else { - /* - * The remaining file types have a length based solely on - * sector count. We need to round off our length value. - */ - fOpenEOF = ((fOpenEOF + kSctSize-1) / kSctSize) * kSctSize; - } - - /* - * Update our internal copies of stuff. - */ - pFile->fLength = fOpenEOF; - pFile->fSparseLength = pFile->fLength; - pFile->fLengthInSectors = (uint16_t) fOpenSectorsUsed; - - /* - * Update the sector count in the directory entry. - */ - dierr = pDiskFS->GetDiskImg()->ReadTrackSector(pFile->fCatTS.track, - pFile->fCatTS.sector, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - pEntry = GetCatalogEntryPtr(sctBuf, pFile->fCatEntryNum); - assert(GetShortLE(&pEntry[0x21]) == 1); // holds for new file - PutShortLE(&pEntry[0x21], pFile->fLengthInSectors); - dierr = pDiskFS->GetDiskImg()->WriteTrackSector(pFile->fCatTS.track, - pFile->fCatTS.sector, sctBuf); - } - -bail: - fpFile->CloseDescr(this); - return dierr; -} - - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDDOS::GetSectorCount(void) const -{ - return fTSCount; -} - -long A2FDDOS::GetBlockCount(void) const -{ - return (fTSCount+1)/2; -} - -/* - * Return the Nth track/sector in this file. - * - * Returns (0,0) for a sparse sector. - */ -DIError A2FDDOS::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - if (sectorIdx < 0 || sectorIdx >= fTSCount) - return kDIErrInvalidIndex; - - *pTrack = fTSList[sectorIdx].track; - *pSector = fTSList[sectorIdx].sector; - return kDIErrNone; -} -/* - * Return the Nth 512-byte block in this file. Since things aren't stored - * in 512-byte blocks, we're reduced to finding storage at (tsIndex*2) and - * converting it to a block number. - */ -DIError A2FDDOS::GetStorage(long blockIdx, long* pBlock) const -{ - long sectorIdx = blockIdx * 2; - if (sectorIdx < 0 || sectorIdx >= fTSCount) - return kDIErrInvalidIndex; - - bool dummy; - TrackSectorToBlock(fTSList[sectorIdx].track, - fTSList[sectorIdx].sector, pBlock, &dummy); - assert(*pBlock < fpFile->GetDiskFS()->GetDiskImg()->GetNumBlocks()); - return kDIErrNone; -} - - -/* - * Dump the T/S list for an open file. - */ -void A2FDDOS::DumpTSList(void) const -{ - //A2FileDOS* pFile = (A2FileDOS*) fpFile; - LOGI(" DOS T/S list for '%s' (count=%d)", - ((A2FileDOS*)fpFile)->fFileName, fTSCount); - - int i; - for (i = 0; i <= fTSCount; i++) { - LOGI(" %3d: T=%-2d S=%d", i, fTSList[i].track, fTSList[i].sector); - } -} diff --git a/ciderpress/diskimg/DOSImage.cpp b/ciderpress/diskimg/DOSImage.cpp deleted file mode 100644 index 73c2962..0000000 --- a/ciderpress/diskimg/DOSImage.cpp +++ /dev/null @@ -1,2904 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * DOS images. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -namespace DiskImgLib { - -/* - * Three 16-sector tracks, in DOS order (i.e. track 0 sector 0 followed - * by track 0 sector 1). - * - * Obtained from tracks 0-2 of a newly-formatted disk created - * by "INIT HELLO" after booting the DOS 3.3 system master. - */ -/*static*/ const uint8_t DiskFSDOS33::gDOS33Tracks[16 * 3 * 256] = { - 0x01, 0xa5, 0x27, 0xc9, 0x09, 0xd0, 0x18, 0xa5, - 0x2b, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, - 0x3f, 0xa9, 0x5c, 0x85, 0x3e, 0x18, 0xad, 0xfe, - 0x08, 0x6d, 0xff, 0x08, 0x8d, 0xfe, 0x08, 0xae, - 0xff, 0x08, 0x30, 0x15, 0xbd, 0x4d, 0x08, 0x85, - 0x3d, 0xce, 0xff, 0x08, 0xad, 0xfe, 0x08, 0x85, - 0x27, 0xce, 0xfe, 0x08, 0xa6, 0x2b, 0x6c, 0x3e, - 0x00, 0xee, 0xfe, 0x08, 0xee, 0xfe, 0x08, 0x20, - 0x89, 0xfe, 0x20, 0x93, 0xfe, 0x20, 0x2f, 0xfb, - 0xa6, 0x2b, 0x6c, 0xfd, 0x08, 0x00, 0x0d, 0x0b, - 0x09, 0x07, 0x05, 0x03, 0x01, 0x0e, 0x0c, 0x0a, - 0x08, 0x06, 0x04, 0x02, 0x0f, 0x00, 0x20, 0x64, - 0xa7, 0xb0, 0x08, 0xa9, 0x00, 0xa8, 0x8d, 0x5d, - 0xb6, 0x91, 0x40, 0xad, 0xc5, 0xb5, 0x4c, 0xd2, - 0xa6, 0xad, 0x5d, 0xb6, 0xf0, 0x08, 0xee, 0xbd, - 0xb5, 0xd0, 0x03, 0xee, 0xbe, 0xb5, 0xa9, 0x00, - 0x8d, 0x5d, 0xb6, 0x4c, 0x46, 0xa5, 0x8d, 0xbc, - 0xb5, 0x20, 0xa8, 0xa6, 0x20, 0xea, 0xa2, 0x4c, - 0x7d, 0xa2, 0xa0, 0x13, 0xb1, 0x42, 0xd0, 0x14, - 0xc8, 0xc0, 0x17, 0xd0, 0xf7, 0xa0, 0x19, 0xb1, - 0x42, 0x99, 0xa4, 0xb5, 0xc8, 0xc0, 0x1d, 0xd0, - 0xf6, 0x4c, 0xbc, 0xa6, 0xa2, 0xff, 0x8e, 0x5d, - 0xb6, 0xd0, 0xf6, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x20, 0x58, 0xfc, 0xa9, 0xc2, 0x20, 0xed, 0xfd, - 0xa9, 0x01, 0x20, 0xda, 0xfd, 0xa9, 0xad, 0x20, - 0xed, 0xfd, 0xa9, 0x00, 0x20, 0xda, 0xfd, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb6, 0x09, - - 0x8e, 0xe9, 0xb7, 0x8e, 0xf7, 0xb7, 0xa9, 0x01, - 0x8d, 0xf8, 0xb7, 0x8d, 0xea, 0xb7, 0xad, 0xe0, - 0xb7, 0x8d, 0xe1, 0xb7, 0xa9, 0x02, 0x8d, 0xec, - 0xb7, 0xa9, 0x04, 0x8d, 0xed, 0xb7, 0xac, 0xe7, - 0xb7, 0x88, 0x8c, 0xf1, 0xb7, 0xa9, 0x01, 0x8d, - 0xf4, 0xb7, 0x8a, 0x4a, 0x4a, 0x4a, 0x4a, 0xaa, - 0xa9, 0x00, 0x9d, 0xf8, 0x04, 0x9d, 0x78, 0x04, - 0x20, 0x93, 0xb7, 0xa2, 0xff, 0x9a, 0x8e, 0xeb, - 0xb7, 0x4c, 0xc8, 0xbf, 0x20, 0x89, 0xfe, 0x4c, - 0x84, 0x9d, 0xad, 0xe7, 0xb7, 0x38, 0xed, 0xf1, - 0xb7, 0x8d, 0xe1, 0xb7, 0xad, 0xe7, 0xb7, 0x8d, - 0xf1, 0xb7, 0xce, 0xf1, 0xb7, 0xa9, 0x02, 0x8d, - 0xec, 0xb7, 0xa9, 0x04, 0x8d, 0xed, 0xb7, 0xa9, - 0x02, 0x8d, 0xf4, 0xb7, 0x20, 0x93, 0xb7, 0xad, - 0xe7, 0xb7, 0x8d, 0xfe, 0xb6, 0x18, 0x69, 0x09, - 0x8d, 0xf1, 0xb7, 0xa9, 0x0a, 0x8d, 0xe1, 0xb7, - 0x38, 0xe9, 0x01, 0x8d, 0xff, 0xb6, 0x8d, 0xed, - 0xb7, 0x20, 0x93, 0xb7, 0x60, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xad, 0xe5, 0xb7, 0xac, 0xe4, - 0xb7, 0x20, 0xb5, 0xb7, 0xac, 0xed, 0xb7, 0x88, - 0x10, 0x07, 0xa0, 0x0f, 0xea, 0xea, 0xce, 0xec, - 0xb7, 0x8c, 0xed, 0xb7, 0xce, 0xf1, 0xb7, 0xce, - 0xe1, 0xb7, 0xd0, 0xdf, 0x60, 0x08, 0x78, 0x20, - 0x00, 0xbd, 0xb0, 0x03, 0x28, 0x18, 0x60, 0x28, - 0x38, 0x60, 0xad, 0xbc, 0xb5, 0x8d, 0xf1, 0xb7, - 0xa9, 0x00, 0x8d, 0xf0, 0xb7, 0xad, 0xf9, 0xb5, - 0x49, 0xff, 0x8d, 0xeb, 0xb7, 0x60, 0xa9, 0x00, - 0xa8, 0x91, 0x42, 0xc8, 0xd0, 0xfb, 0x60, 0x00, - 0x1b, 0x02, 0x0a, 0x1b, 0xe8, 0xb7, 0x00, 0xb6, - 0x01, 0x60, 0x02, 0xfe, 0x00, 0x01, 0xfb, 0xb7, - 0x00, 0xb7, 0x00, 0x00, 0x02, 0xeb, 0xfe, 0x60, - 0x02, 0x00, 0x00, 0x00, 0x01, 0xef, 0xd8, 0x00, - - 0xa2, 0x00, 0xa0, 0x02, 0x88, 0xb1, 0x3e, 0x4a, - 0x3e, 0x00, 0xbc, 0x4a, 0x3e, 0x00, 0xbc, 0x99, - 0x00, 0xbb, 0xe8, 0xe0, 0x56, 0x90, 0xed, 0xa2, - 0x00, 0x98, 0xd0, 0xe8, 0xa2, 0x55, 0xbd, 0x00, - 0xbc, 0x29, 0x3f, 0x9d, 0x00, 0xbc, 0xca, 0x10, - 0xf5, 0x60, 0x38, 0x86, 0x27, 0x8e, 0x78, 0x06, - 0xbd, 0x8d, 0xc0, 0xbd, 0x8e, 0xc0, 0x30, 0x7c, - 0xad, 0x00, 0xbc, 0x85, 0x26, 0xa9, 0xff, 0x9d, - 0x8f, 0xc0, 0x1d, 0x8c, 0xc0, 0x48, 0x68, 0xea, - 0xa0, 0x04, 0x48, 0x68, 0x20, 0xb9, 0xb8, 0x88, - 0xd0, 0xf8, 0xa9, 0xd5, 0x20, 0xb8, 0xb8, 0xa9, - 0xaa, 0x20, 0xb8, 0xb8, 0xa9, 0xad, 0x20, 0xb8, - 0xb8, 0x98, 0xa0, 0x56, 0xd0, 0x03, 0xb9, 0x00, - 0xbc, 0x59, 0xff, 0xbb, 0xaa, 0xbd, 0x29, 0xba, - 0xa6, 0x27, 0x9d, 0x8d, 0xc0, 0xbd, 0x8c, 0xc0, - 0x88, 0xd0, 0xeb, 0xa5, 0x26, 0xea, 0x59, 0x00, - 0xbb, 0xaa, 0xbd, 0x29, 0xba, 0xae, 0x78, 0x06, - 0x9d, 0x8d, 0xc0, 0xbd, 0x8c, 0xc0, 0xb9, 0x00, - 0xbb, 0xc8, 0xd0, 0xea, 0xaa, 0xbd, 0x29, 0xba, - 0xa6, 0x27, 0x20, 0xbb, 0xb8, 0xa9, 0xde, 0x20, - 0xb8, 0xb8, 0xa9, 0xaa, 0x20, 0xb8, 0xb8, 0xa9, - 0xeb, 0x20, 0xb8, 0xb8, 0xa9, 0xff, 0x20, 0xb8, - 0xb8, 0xbd, 0x8e, 0xc0, 0xbd, 0x8c, 0xc0, 0x60, - 0x18, 0x48, 0x68, 0x9d, 0x8d, 0xc0, 0x1d, 0x8c, - 0xc0, 0x60, 0xa0, 0x00, 0xa2, 0x56, 0xca, 0x30, - 0xfb, 0xb9, 0x00, 0xbb, 0x5e, 0x00, 0xbc, 0x2a, - 0x5e, 0x00, 0xbc, 0x2a, 0x91, 0x3e, 0xc8, 0xc4, - 0x26, 0xd0, 0xeb, 0x60, 0xa0, 0x20, 0x88, 0xf0, - 0x61, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x49, 0xd5, - 0xd0, 0xf4, 0xea, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, - 0xc9, 0xaa, 0xd0, 0xf2, 0xa0, 0x56, 0xbd, 0x8c, - 0xc0, 0x10, 0xfb, 0xc9, 0xad, 0xd0, 0xe7, 0xa9, - - 0x00, 0x88, 0x84, 0x26, 0xbc, 0x8c, 0xc0, 0x10, - 0xfb, 0x59, 0x00, 0xba, 0xa4, 0x26, 0x99, 0x00, - 0xbc, 0xd0, 0xee, 0x84, 0x26, 0xbc, 0x8c, 0xc0, - 0x10, 0xfb, 0x59, 0x00, 0xba, 0xa4, 0x26, 0x99, - 0x00, 0xbb, 0xc8, 0xd0, 0xee, 0xbc, 0x8c, 0xc0, - 0x10, 0xfb, 0xd9, 0x00, 0xba, 0xd0, 0x13, 0xbd, - 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xde, 0xd0, 0x0a, - 0xea, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xaa, - 0xf0, 0x5c, 0x38, 0x60, 0xa0, 0xfc, 0x84, 0x26, - 0xc8, 0xd0, 0x04, 0xe6, 0x26, 0xf0, 0xf3, 0xbd, - 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xd5, 0xd0, 0xf0, - 0xea, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xaa, - 0xd0, 0xf2, 0xa0, 0x03, 0xbd, 0x8c, 0xc0, 0x10, - 0xfb, 0xc9, 0x96, 0xd0, 0xe7, 0xa9, 0x00, 0x85, - 0x27, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x2a, 0x85, - 0x26, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x25, 0x26, - 0x99, 0x2c, 0x00, 0x45, 0x27, 0x88, 0x10, 0xe7, - 0xa8, 0xd0, 0xb7, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, - 0xc9, 0xde, 0xd0, 0xae, 0xea, 0xbd, 0x8c, 0xc0, - 0x10, 0xfb, 0xc9, 0xaa, 0xd0, 0xa4, 0x18, 0x60, - 0x86, 0x2b, 0x85, 0x2a, 0xcd, 0x78, 0x04, 0xf0, - 0x53, 0xa9, 0x00, 0x85, 0x26, 0xad, 0x78, 0x04, - 0x85, 0x27, 0x38, 0xe5, 0x2a, 0xf0, 0x33, 0xb0, - 0x07, 0x49, 0xff, 0xee, 0x78, 0x04, 0x90, 0x05, - 0x69, 0xfe, 0xce, 0x78, 0x04, 0xc5, 0x26, 0x90, - 0x02, 0xa5, 0x26, 0xc9, 0x0c, 0xb0, 0x01, 0xa8, - 0x38, 0x20, 0xee, 0xb9, 0xb9, 0x11, 0xba, 0x20, - 0x00, 0xba, 0xa5, 0x27, 0x18, 0x20, 0xf1, 0xb9, - 0xb9, 0x1d, 0xba, 0x20, 0x00, 0xba, 0xe6, 0x26, - 0xd0, 0xc3, 0x20, 0x00, 0xba, 0x18, 0xad, 0x78, - 0x04, 0x29, 0x03, 0x2a, 0x05, 0x2b, 0xaa, 0xbd, - 0x80, 0xc0, 0xa6, 0x2b, 0x60, 0xaa, 0xa0, 0xa0, - - 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe6, 0x46, 0xd0, - 0x02, 0xe6, 0x47, 0x38, 0xe9, 0x01, 0xd0, 0xf0, - 0x60, 0x01, 0x30, 0x28, 0x24, 0x20, 0x1e, 0x1d, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x70, 0x2c, 0x26, - 0x22, 0x1f, 0x1e, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, - 0xa6, 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, - 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, - 0xbc, 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, - 0xd3, 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, - 0xde, 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, - 0xec, 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, - 0xf6, 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, - 0xff, 0xb3, 0xb3, 0xa0, 0xe0, 0xb3, 0xc3, 0xc5, - 0xb3, 0xa0, 0xe0, 0xb3, 0xc3, 0xc5, 0xb3, 0xa0, - 0xe0, 0xb3, 0xb3, 0xc5, 0xaa, 0xa0, 0x82, 0xb3, - 0xb3, 0xc5, 0xaa, 0xa0, 0x82, 0xc5, 0xb3, 0xb3, - 0xaa, 0x88, 0x82, 0xc5, 0xb3, 0xb3, 0xaa, 0x88, - 0x82, 0xc5, 0xc4, 0xb3, 0xb0, 0x88, 0x00, 0x01, - 0x98, 0x99, 0x02, 0x03, 0x9c, 0x04, 0x05, 0x06, - 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0x07, 0x08, - 0xa8, 0xa9, 0xaa, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, - 0xb0, 0xb1, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, - 0xb8, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, - 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, - 0xc8, 0xc9, 0xca, 0x1b, 0xcc, 0x1c, 0x1d, 0x1e, - 0xd0, 0xd1, 0xd2, 0x1f, 0xd4, 0xd5, 0x20, 0x21, - 0xd8, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0x29, 0x2a, 0x2b, - 0xe8, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, - 0xf0, 0xf1, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, - 0xf8, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, - - 0x01, 0x0a, 0x11, 0x0a, 0x08, 0x20, 0x20, 0x0e, - 0x18, 0x06, 0x02, 0x31, 0x02, 0x09, 0x08, 0x27, - 0x22, 0x00, 0x12, 0x0a, 0x0a, 0x04, 0x00, 0x00, - 0x03, 0x2a, 0x00, 0x04, 0x00, 0x00, 0x22, 0x08, - 0x10, 0x28, 0x12, 0x02, 0x00, 0x02, 0x08, 0x11, - 0x0a, 0x08, 0x02, 0x28, 0x11, 0x01, 0x39, 0x22, - 0x31, 0x01, 0x05, 0x18, 0x20, 0x28, 0x02, 0x10, - 0x06, 0x02, 0x09, 0x02, 0x05, 0x2c, 0x10, 0x00, - 0x08, 0x2e, 0x00, 0x05, 0x02, 0x28, 0x18, 0x02, - 0x30, 0x23, 0x02, 0x20, 0x32, 0x04, 0x11, 0x02, - 0x14, 0x02, 0x08, 0x09, 0x12, 0x20, 0x0e, 0x2f, - 0x23, 0x30, 0x2f, 0x23, 0x30, 0x0c, 0x17, 0x2a, - 0x3f, 0x27, 0x23, 0x30, 0x37, 0x23, 0x30, 0x12, - 0x1a, 0x08, 0x30, 0x2f, 0x08, 0x30, 0x2f, 0x27, - 0x23, 0x30, 0x37, 0x23, 0x30, 0x3a, 0x22, 0x34, - 0x3c, 0x2a, 0x35, 0x08, 0x35, 0x2f, 0x2a, 0x2a, - 0x08, 0x35, 0x2f, 0x2a, 0x25, 0x08, 0x35, 0x2f, - 0x29, 0x10, 0x08, 0x31, 0x2f, 0x29, 0x11, 0x08, - 0x31, 0x2f, 0x29, 0x0f, 0x08, 0x31, 0x2f, 0x29, - 0x10, 0x11, 0x11, 0x11, 0x0f, 0x12, 0x12, 0x01, - 0x0f, 0x27, 0x23, 0x30, 0x2f, 0x23, 0x30, 0x1a, - 0x02, 0x2a, 0x08, 0x35, 0x2f, 0x2a, 0x37, 0x08, - 0x35, 0x2f, 0x2a, 0x2a, 0x08, 0x35, 0x2f, 0x2a, - 0x3a, 0x08, 0x35, 0x2f, 0x06, 0x2f, 0x23, 0x30, - 0x2f, 0x23, 0x30, 0x18, 0x12, 0x12, 0x01, 0x0f, - 0x27, 0x23, 0x30, 0x37, 0x23, 0x30, 0x1a, 0x3a, - 0x3a, 0x3a, 0x02, 0x2a, 0x3a, 0x3a, 0x12, 0x1a, - 0x27, 0x23, 0x30, 0x37, 0x23, 0x30, 0x18, 0x22, - 0x29, 0x3a, 0x24, 0x28, 0x25, 0x22, 0x25, 0x3a, - 0x24, 0x28, 0x25, 0x22, 0x25, 0x24, 0x24, 0x32, - 0x25, 0x34, 0x25, 0x24, 0x24, 0x32, 0x25, 0x34, - 0x25, 0x24, 0x28, 0x32, 0x28, 0x29, 0x21, 0x29, - - 0x10, 0xa1, 0x45, 0x28, 0x21, 0x82, 0x80, 0x38, - 0x62, 0x19, 0x0b, 0xc5, 0x0b, 0x24, 0x21, 0x9c, - 0x88, 0x00, 0x48, 0x28, 0x2b, 0x10, 0x00, 0x03, - 0x0c, 0xa9, 0x01, 0x10, 0x01, 0x00, 0x88, 0x22, - 0x40, 0xa0, 0x48, 0x09, 0x01, 0x08, 0x21, 0x44, - 0x29, 0x22, 0x08, 0xa0, 0x45, 0x06, 0xe4, 0x8a, - 0xc4, 0x06, 0x16, 0x60, 0x80, 0xa0, 0x09, 0x40, - 0x18, 0x0a, 0x24, 0x0a, 0x16, 0xb0, 0x43, 0x00, - 0x20, 0xbb, 0x00, 0x14, 0x08, 0xa0, 0x60, 0x0a, - 0xc0, 0x8f, 0x0a, 0x83, 0xca, 0x11, 0x44, 0x08, - 0x51, 0x0a, 0x20, 0x26, 0x4a, 0x80, 0x38, 0xbd, - 0x8d, 0xc0, 0xbd, 0x8e, 0xc0, 0x30, 0x5e, 0xa9, - 0xff, 0x9d, 0x8f, 0xc0, 0xdd, 0x8c, 0xc0, 0x48, - 0x68, 0x20, 0xc3, 0xbc, 0x20, 0xc3, 0xbc, 0x9d, - 0x8d, 0xc0, 0xdd, 0x8c, 0xc0, 0xea, 0x88, 0xd0, - 0xf0, 0xa9, 0xd5, 0x20, 0xd5, 0xbc, 0xa9, 0xaa, - 0x20, 0xd5, 0xbc, 0xa9, 0x96, 0x20, 0xd5, 0xbc, - 0xa5, 0x41, 0x20, 0xc4, 0xbc, 0xa5, 0x44, 0x20, - 0xc4, 0xbc, 0xa5, 0x3f, 0x20, 0xc4, 0xbc, 0xa5, - 0x41, 0x45, 0x44, 0x45, 0x3f, 0x48, 0x4a, 0x05, - 0x3e, 0x9d, 0x8d, 0xc0, 0xbd, 0x8c, 0xc0, 0x68, - 0x09, 0xaa, 0x20, 0xd4, 0xbc, 0xa9, 0xde, 0x20, - 0xd5, 0xbc, 0xa9, 0xaa, 0x20, 0xd5, 0xbc, 0xa9, - 0xeb, 0x20, 0xd5, 0xbc, 0x18, 0xbd, 0x8e, 0xc0, - 0xbd, 0x8c, 0xc0, 0x60, 0x48, 0x4a, 0x05, 0x3e, - 0x9d, 0x8d, 0xc0, 0xdd, 0x8c, 0xc0, 0x68, 0xea, - 0xea, 0xea, 0x09, 0xaa, 0xea, 0xea, 0x48, 0x68, - 0x9d, 0x8d, 0xc0, 0xdd, 0x8c, 0xc0, 0x60, 0x88, - 0xa5, 0xe8, 0x91, 0xa0, 0x94, 0x88, 0x96, 0xe8, - 0x91, 0xa0, 0x94, 0x88, 0x96, 0x91, 0x91, 0xc8, - 0x94, 0xd0, 0x96, 0x91, 0x91, 0xc8, 0x94, 0xd0, - 0x96, 0x91, 0xa3, 0xc8, 0xa0, 0xa5, 0x85, 0xa4, - - 0x84, 0x48, 0x85, 0x49, 0xa0, 0x02, 0x8c, 0xf8, - 0x06, 0xa0, 0x04, 0x8c, 0xf8, 0x04, 0xa0, 0x01, - 0xb1, 0x48, 0xaa, 0xa0, 0x0f, 0xd1, 0x48, 0xf0, - 0x1b, 0x8a, 0x48, 0xb1, 0x48, 0xaa, 0x68, 0x48, - 0x91, 0x48, 0xbd, 0x8e, 0xc0, 0xa0, 0x08, 0xbd, - 0x8c, 0xc0, 0xdd, 0x8c, 0xc0, 0xd0, 0xf6, 0x88, - 0xd0, 0xf8, 0x68, 0xaa, 0xbd, 0x8e, 0xc0, 0xbd, - 0x8c, 0xc0, 0xa0, 0x08, 0xbd, 0x8c, 0xc0, 0x48, - 0x68, 0x48, 0x68, 0x8e, 0xf8, 0x05, 0xdd, 0x8c, - 0xc0, 0xd0, 0x03, 0x88, 0xd0, 0xee, 0x08, 0xbd, - 0x89, 0xc0, 0xa0, 0x06, 0xb1, 0x48, 0x99, 0x36, - 0x00, 0xc8, 0xc0, 0x0a, 0xd0, 0xf6, 0xa0, 0x03, - 0xb1, 0x3c, 0x85, 0x47, 0xa0, 0x02, 0xb1, 0x48, - 0xa0, 0x10, 0xd1, 0x48, 0xf0, 0x06, 0x91, 0x48, - 0x28, 0xa0, 0x00, 0x08, 0x6a, 0x90, 0x05, 0xbd, - 0x8a, 0xc0, 0xb0, 0x03, 0xbd, 0x8b, 0xc0, 0x66, - 0x35, 0x28, 0x08, 0xd0, 0x0b, 0xa0, 0x07, 0x20, - 0x00, 0xba, 0x88, 0xd0, 0xfa, 0xae, 0xf8, 0x05, - 0xa0, 0x04, 0xb1, 0x48, 0x20, 0x5a, 0xbe, 0x28, - 0xd0, 0x11, 0xa4, 0x47, 0x10, 0x0d, 0xa0, 0x12, - 0x88, 0xd0, 0xfd, 0xe6, 0x46, 0xd0, 0xf7, 0xe6, - 0x47, 0xd0, 0xf3, 0xa0, 0x0c, 0xb1, 0x48, 0xf0, - 0x5a, 0xc9, 0x04, 0xf0, 0x58, 0x6a, 0x08, 0xb0, - 0x03, 0x20, 0x00, 0xb8, 0xa0, 0x30, 0x8c, 0x78, - 0x05, 0xae, 0xf8, 0x05, 0x20, 0x44, 0xb9, 0x90, - 0x24, 0xce, 0x78, 0x05, 0x10, 0xf3, 0xad, 0x78, - 0x04, 0x48, 0xa9, 0x60, 0x20, 0x95, 0xbe, 0xce, - 0xf8, 0x06, 0xf0, 0x28, 0xa9, 0x04, 0x8d, 0xf8, - 0x04, 0xa9, 0x00, 0x20, 0x5a, 0xbe, 0x68, 0x20, - 0x5a, 0xbe, 0x4c, 0xbc, 0xbd, 0xa4, 0x2e, 0xcc, - 0x78, 0x04, 0xf0, 0x1c, 0xad, 0x78, 0x04, 0x48, - 0x98, 0x20, 0x95, 0xbe, 0x68, 0xce, 0xf8, 0x04, - - 0xd0, 0xe5, 0xf0, 0xca, 0x68, 0xa9, 0x40, 0x28, - 0x4c, 0x48, 0xbe, 0xf0, 0x39, 0x4c, 0xaf, 0xbe, - 0xa0, 0x03, 0xb1, 0x48, 0x48, 0xa5, 0x2f, 0xa0, - 0x0e, 0x91, 0x48, 0x68, 0xf0, 0x08, 0xc5, 0x2f, - 0xf0, 0x04, 0xa9, 0x20, 0xd0, 0xe1, 0xa0, 0x05, - 0xb1, 0x48, 0xa8, 0xb9, 0xb8, 0xbf, 0xc5, 0x2d, - 0xd0, 0x97, 0x28, 0x90, 0x1c, 0x20, 0xdc, 0xb8, - 0x08, 0xb0, 0x8e, 0x28, 0xa2, 0x00, 0x86, 0x26, - 0x20, 0xc2, 0xb8, 0xae, 0xf8, 0x05, 0x18, 0x24, - 0x38, 0xa0, 0x0d, 0x91, 0x48, 0xbd, 0x88, 0xc0, - 0x60, 0x20, 0x2a, 0xb8, 0x90, 0xf0, 0xa9, 0x10, - 0xb0, 0xee, 0x48, 0xa0, 0x01, 0xb1, 0x3c, 0x6a, - 0x68, 0x90, 0x08, 0x0a, 0x20, 0x6b, 0xbe, 0x4e, - 0x78, 0x04, 0x60, 0x85, 0x2a, 0x20, 0x8e, 0xbe, - 0xb9, 0x78, 0x04, 0x24, 0x35, 0x30, 0x03, 0xb9, - 0xf8, 0x04, 0x8d, 0x78, 0x04, 0xa5, 0x2a, 0x24, - 0x35, 0x30, 0x05, 0x99, 0xf8, 0x04, 0x10, 0x03, - 0x99, 0x78, 0x04, 0x4c, 0xa0, 0xb9, 0x8a, 0x4a, - 0x4a, 0x4a, 0x4a, 0xa8, 0x60, 0x48, 0xa0, 0x02, - 0xb1, 0x48, 0x6a, 0x66, 0x35, 0x20, 0x8e, 0xbe, - 0x68, 0x0a, 0x24, 0x35, 0x30, 0x05, 0x99, 0xf8, - 0x04, 0x10, 0x03, 0x99, 0x78, 0x04, 0x60, 0xa0, - 0x03, 0xb1, 0x48, 0x85, 0x41, 0xa9, 0xaa, 0x85, - 0x3e, 0xa0, 0x56, 0xa9, 0x00, 0x85, 0x44, 0x99, - 0xff, 0xbb, 0x88, 0xd0, 0xfa, 0x99, 0x00, 0xbb, - 0x88, 0xd0, 0xfa, 0xa9, 0x50, 0x20, 0x95, 0xbe, - 0xa9, 0x28, 0x85, 0x45, 0xa5, 0x44, 0x20, 0x5a, - 0xbe, 0x20, 0x0d, 0xbf, 0xa9, 0x08, 0xb0, 0x24, - 0xa9, 0x30, 0x8d, 0x78, 0x05, 0x38, 0xce, 0x78, - 0x05, 0xf0, 0x19, 0x20, 0x44, 0xb9, 0xb0, 0xf5, - 0xa5, 0x2d, 0xd0, 0xf1, 0x20, 0xdc, 0xb8, 0xb0, - 0xec, 0xe6, 0x44, 0xa5, 0x44, 0xc9, 0x23, 0x90, - - 0xd3, 0x18, 0x90, 0x05, 0xa0, 0x0d, 0x91, 0x48, - 0x38, 0xbd, 0x88, 0xc0, 0x60, 0xa9, 0x00, 0x85, - 0x3f, 0xa0, 0x80, 0xd0, 0x02, 0xa4, 0x45, 0x20, - 0x56, 0xbc, 0xb0, 0x6b, 0x20, 0x2a, 0xb8, 0xb0, - 0x66, 0xe6, 0x3f, 0xa5, 0x3f, 0xc9, 0x10, 0x90, - 0xec, 0xa0, 0x0f, 0x84, 0x3f, 0xa9, 0x30, 0x8d, - 0x78, 0x05, 0x99, 0xa8, 0xbf, 0x88, 0x10, 0xfa, - 0xa4, 0x45, 0x20, 0x87, 0xbf, 0x20, 0x87, 0xbf, - 0x20, 0x87, 0xbf, 0x48, 0x68, 0xea, 0x88, 0xd0, - 0xf1, 0x20, 0x44, 0xb9, 0xb0, 0x23, 0xa5, 0x2d, - 0xf0, 0x15, 0xa9, 0x10, 0xc5, 0x45, 0xa5, 0x45, - 0xe9, 0x01, 0x85, 0x45, 0xc9, 0x05, 0xb0, 0x11, - 0x38, 0x60, 0x20, 0x44, 0xb9, 0xb0, 0x05, 0x20, - 0xdc, 0xb8, 0x90, 0x1c, 0xce, 0x78, 0x05, 0xd0, - 0xf1, 0x20, 0x44, 0xb9, 0xb0, 0x0b, 0xa5, 0x2d, - 0xc9, 0x0f, 0xd0, 0x05, 0x20, 0xdc, 0xb8, 0x90, - 0x8c, 0xce, 0x78, 0x05, 0xd0, 0xeb, 0x38, 0x60, - 0xa4, 0x2d, 0xb9, 0xa8, 0xbf, 0x30, 0xdd, 0xa9, - 0xff, 0x99, 0xa8, 0xbf, 0xc6, 0x3f, 0x10, 0xca, - 0xa5, 0x44, 0xd0, 0x0a, 0xa5, 0x45, 0xc9, 0x10, - 0x90, 0xe5, 0xc6, 0x45, 0xc6, 0x45, 0x18, 0x60, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0x00, 0x0d, 0x0b, 0x09, 0x07, 0x05, 0x03, 0x01, - 0x0e, 0x0c, 0x0a, 0x08, 0x06, 0x04, 0x02, 0x0f, - 0x20, 0x93, 0xfe, 0xad, 0x81, 0xc0, 0xad, 0x81, - 0xc0, 0xa9, 0x00, 0x8d, 0x00, 0xe0, 0x4c, 0x44, - 0xb7, 0x00, 0x00, 0x00, 0x8d, 0x63, 0xaa, 0x8d, - 0x70, 0xaa, 0x8d, 0x71, 0xaa, 0x60, 0x20, 0x5b, - 0xa7, 0x8c, 0xb7, 0xaa, 0x60, 0x20, 0x7e, 0xae, - 0xae, 0x9b, 0xb3, 0x9a, 0x20, 0x16, 0xa3, 0xba, - 0x8e, 0x9b, 0xb3, 0xa9, 0x09, 0x4c, 0x85, 0xb3, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0xd3, 0x9c, 0x81, 0x9e, 0xbd, 0x9e, 0x75, 0xaa, - 0x93, 0xaa, 0x60, 0xaa, 0x00, 0x9d, 0xbb, 0xb5, - 0xea, 0x9e, 0x11, 0x9f, 0x22, 0x9f, 0x2e, 0x9f, - 0x51, 0x9f, 0x60, 0x9f, 0x70, 0x9f, 0x4e, 0xa5, - 0x12, 0xa4, 0x96, 0xa3, 0xd0, 0xa4, 0xef, 0xa4, - 0x62, 0xa2, 0x70, 0xa2, 0x74, 0xa2, 0xe9, 0xa2, - 0x1a, 0xa5, 0xc5, 0xa5, 0x0f, 0xa5, 0xdc, 0xa5, - 0xa2, 0xa2, 0x97, 0xa2, 0x80, 0xa2, 0x6d, 0xa5, - 0x32, 0xa2, 0x3c, 0xa2, 0x28, 0xa2, 0x2d, 0xa2, - 0x50, 0xa2, 0x79, 0xa5, 0x9d, 0xa5, 0x30, 0xa3, - 0x5c, 0xa3, 0x8d, 0xa3, 0x7c, 0xa2, 0xfc, 0xa4, - 0xfc, 0xa4, 0x65, 0xd8, 0x00, 0xe0, 0x3c, 0xd4, - 0xf2, 0xd4, 0x36, 0xe8, 0xe5, 0xa4, 0xe3, 0xe3, - 0x00, 0xe0, 0x03, 0xe0, 0xfc, 0xa4, 0xfc, 0xa4, - 0x65, 0xd8, 0x00, 0xe0, 0x3c, 0xd4, 0xf2, 0xd4, - 0x06, 0xa5, 0x06, 0xa5, 0x67, 0x10, 0x84, 0x9d, - 0x3c, 0x0c, 0xf2, 0x0c, 0xad, 0xe9, 0xb7, 0x4a, - 0x4a, 0x4a, 0x4a, 0x8d, 0x6a, 0xaa, 0xad, 0xea, - 0xb7, 0x8d, 0x68, 0xaa, 0xad, 0x00, 0xe0, 0x49, - 0x20, 0xd0, 0x11, 0x8d, 0xb6, 0xaa, 0xa2, 0x0a, - 0xbd, 0x61, 0x9d, 0x9d, 0x55, 0x9d, 0xca, 0xd0, - 0xf7, 0x4c, 0xbc, 0x9d, 0xa9, 0x40, 0x8d, 0xb6, - 0xaa, 0xa2, 0x0c, 0xbd, 0x6b, 0x9d, 0x9d, 0x55, - 0x9d, 0xca, 0xd0, 0xf7, 0x38, 0xb0, 0x12, 0xad, - 0xb6, 0xaa, 0xd0, 0x04, 0xa9, 0x20, 0xd0, 0x05, - 0x0a, 0x10, 0x05, 0xa9, 0x4c, 0x20, 0xb2, 0xa5, - 0x18, 0x08, 0x20, 0x51, 0xa8, 0xa9, 0x00, 0x8d, - 0x5e, 0xaa, 0x8d, 0x52, 0xaa, 0x28, 0x6a, 0x8d, - 0x51, 0xaa, 0x30, 0x03, 0x6c, 0x5e, 0x9d, 0x6c, - 0x5c, 0x9d, 0x0a, 0x10, 0x19, 0x8d, 0xb6, 0xaa, - 0xa2, 0x0c, 0xbd, 0x77, 0x9d, 0x9d, 0x55, 0x9d, - 0xca, 0xd0, 0xf7, 0xa2, 0x1d, 0xbd, 0x93, 0xaa, - - 0x9d, 0x75, 0xaa, 0xca, 0x10, 0xf7, 0xad, 0xb1, - 0xaa, 0x8d, 0x57, 0xaa, 0x20, 0xd4, 0xa7, 0xad, - 0xb3, 0xaa, 0xf0, 0x09, 0x48, 0x20, 0x9d, 0xa6, - 0x68, 0xa0, 0x00, 0x91, 0x40, 0x20, 0x5b, 0xa7, - 0xad, 0x5f, 0xaa, 0xd0, 0x20, 0xa2, 0x2f, 0xbd, - 0x51, 0x9e, 0x9d, 0xd0, 0x03, 0xca, 0x10, 0xf7, - 0xad, 0x53, 0x9e, 0x8d, 0xf3, 0x03, 0x49, 0xa5, - 0x8d, 0xf4, 0x03, 0xad, 0x52, 0x9e, 0x8d, 0xf2, - 0x03, 0xa9, 0x06, 0xd0, 0x05, 0xad, 0x62, 0xaa, - 0xf0, 0x06, 0x8d, 0x5f, 0xaa, 0x4c, 0x80, 0xa1, - 0x60, 0x4c, 0xbf, 0x9d, 0x4c, 0x84, 0x9d, 0x4c, - 0xfd, 0xaa, 0x4c, 0xb5, 0xb7, 0xad, 0x0f, 0x9d, - 0xac, 0x0e, 0x9d, 0x60, 0xad, 0xc2, 0xaa, 0xac, - 0xc1, 0xaa, 0x60, 0x4c, 0x51, 0xa8, 0xea, 0xea, - 0x4c, 0x59, 0xfa, 0x4c, 0x65, 0xff, 0x4c, 0x58, - 0xff, 0x4c, 0x65, 0xff, 0x4c, 0x65, 0xff, 0x65, - 0xff, 0x20, 0xd1, 0x9e, 0xad, 0x51, 0xaa, 0xf0, - 0x15, 0x48, 0xad, 0x5c, 0xaa, 0x91, 0x28, 0x68, - 0x30, 0x03, 0x4c, 0x26, 0xa6, 0x20, 0xea, 0x9d, - 0xa4, 0x24, 0xa9, 0x60, 0x91, 0x28, 0xad, 0xb3, - 0xaa, 0xf0, 0x03, 0x20, 0x82, 0xa6, 0xa9, 0x03, - 0x8d, 0x52, 0xaa, 0x20, 0xba, 0x9f, 0x20, 0xba, - 0x9e, 0x8d, 0x5c, 0xaa, 0x8e, 0x5a, 0xaa, 0x4c, - 0xb3, 0x9f, 0x6c, 0x38, 0x00, 0x20, 0xd1, 0x9e, - 0xad, 0x52, 0xaa, 0x0a, 0xaa, 0xbd, 0x11, 0x9d, - 0x48, 0xbd, 0x10, 0x9d, 0x48, 0xad, 0x5c, 0xaa, - 0x60, 0x8d, 0x5c, 0xaa, 0x8e, 0x5a, 0xaa, 0x8c, - 0x5b, 0xaa, 0xba, 0xe8, 0xe8, 0x8e, 0x59, 0xaa, - 0xa2, 0x03, 0xbd, 0x53, 0xaa, 0x95, 0x36, 0xca, - 0x10, 0xf8, 0x60, 0xae, 0xb7, 0xaa, 0xf0, 0x03, - 0x4c, 0x78, 0x9f, 0xae, 0x51, 0xaa, 0xf0, 0x08, - 0xc9, 0xbf, 0xf0, 0x75, 0xc5, 0x33, 0xf0, 0x27, - - 0xa2, 0x02, 0x8e, 0x52, 0xaa, 0xcd, 0xb2, 0xaa, - 0xd0, 0x19, 0xca, 0x8e, 0x52, 0xaa, 0xca, 0x8e, - 0x5d, 0xaa, 0xae, 0x5d, 0xaa, 0x9d, 0x00, 0x02, - 0xe8, 0x8e, 0x5d, 0xaa, 0xc9, 0x8d, 0xd0, 0x75, - 0x4c, 0xcd, 0x9f, 0xc9, 0x8d, 0xd0, 0x7d, 0xa2, - 0x00, 0x8e, 0x52, 0xaa, 0x4c, 0xa4, 0x9f, 0xa2, - 0x00, 0x8e, 0x52, 0xaa, 0xc9, 0x8d, 0xf0, 0x07, - 0xad, 0xb3, 0xaa, 0xf0, 0x67, 0xd0, 0x5e, 0x48, - 0x38, 0xad, 0xb3, 0xaa, 0xd0, 0x03, 0x20, 0x5e, - 0xa6, 0x68, 0x90, 0xec, 0xae, 0x5a, 0xaa, 0x4c, - 0x15, 0x9f, 0xc9, 0x8d, 0xd0, 0x05, 0xa9, 0x05, - 0x8d, 0x52, 0xaa, 0x20, 0x0e, 0xa6, 0x4c, 0x99, - 0x9f, 0xcd, 0xb2, 0xaa, 0xf0, 0x85, 0xc9, 0x8a, - 0xf0, 0xf1, 0xa2, 0x04, 0x8e, 0x52, 0xaa, 0xd0, - 0xe1, 0xa9, 0x00, 0x8d, 0x52, 0xaa, 0xf0, 0x25, - 0xa9, 0x00, 0x8d, 0xb7, 0xaa, 0x20, 0x51, 0xa8, - 0x4c, 0xdc, 0xa4, 0xad, 0x00, 0x02, 0xcd, 0xb2, - 0xaa, 0xf0, 0x0a, 0xa9, 0x8d, 0x8d, 0x00, 0x02, - 0xa2, 0x00, 0x8e, 0x5a, 0xaa, 0xa9, 0x40, 0xd0, - 0x06, 0xa9, 0x10, 0xd0, 0x02, 0xa9, 0x20, 0x2d, - 0x5e, 0xaa, 0xf0, 0x0f, 0x20, 0xba, 0x9f, 0x20, - 0xc5, 0x9f, 0x8d, 0x5c, 0xaa, 0x8c, 0x5b, 0xaa, - 0x8e, 0x5a, 0xaa, 0x20, 0x51, 0xa8, 0xae, 0x59, - 0xaa, 0x9a, 0xad, 0x5c, 0xaa, 0xac, 0x5b, 0xaa, - 0xae, 0x5a, 0xaa, 0x38, 0x60, 0x6c, 0x36, 0x00, - 0xa9, 0x8d, 0x4c, 0xc5, 0x9f, 0xa0, 0xff, 0x8c, - 0x5f, 0xaa, 0xc8, 0x8c, 0x62, 0xaa, 0xee, 0x5f, - 0xaa, 0xa2, 0x00, 0x08, 0xbd, 0x00, 0x02, 0xcd, - 0xb2, 0xaa, 0xd0, 0x01, 0xe8, 0x8e, 0x5d, 0xaa, - 0x20, 0xa4, 0xa1, 0x29, 0x7f, 0x59, 0x84, 0xa8, - 0xc8, 0x0a, 0xf0, 0x02, 0x68, 0x08, 0x90, 0xf0, - 0x28, 0xf0, 0x20, 0xb9, 0x84, 0xa8, 0xd0, 0xd6, - - 0xad, 0x00, 0x02, 0xcd, 0xb2, 0xaa, 0xf0, 0x03, - 0x4c, 0xa4, 0x9f, 0xad, 0x01, 0x02, 0xc9, 0x8d, - 0xd0, 0x06, 0x20, 0x5b, 0xa7, 0x4c, 0x95, 0x9f, - 0x4c, 0xc4, 0xa6, 0x0e, 0x5f, 0xaa, 0xac, 0x5f, - 0xaa, 0x20, 0x5e, 0xa6, 0x90, 0x0c, 0xa9, 0x02, - 0x39, 0x09, 0xa9, 0xf0, 0x05, 0xa9, 0x0f, 0x4c, - 0xd2, 0xa6, 0xc0, 0x06, 0xd0, 0x02, 0x84, 0x33, - 0xa9, 0x20, 0x39, 0x09, 0xa9, 0xf0, 0x61, 0x20, - 0x95, 0xa0, 0x08, 0x20, 0xa4, 0xa1, 0xf0, 0x1e, - 0x0a, 0x90, 0x05, 0x30, 0x03, 0x4c, 0x00, 0xa0, - 0x6a, 0x4c, 0x59, 0xa0, 0x20, 0x93, 0xa1, 0xf0, - 0x0d, 0x99, 0x75, 0xaa, 0xc8, 0xc0, 0x3c, 0x90, - 0xf3, 0x20, 0x93, 0xa1, 0xd0, 0xfb, 0x28, 0xd0, - 0x0f, 0xac, 0x5f, 0xaa, 0xa9, 0x10, 0x39, 0x09, - 0xa9, 0xf0, 0x0c, 0xa0, 0x1e, 0x08, 0xd0, 0xcb, - 0xad, 0x93, 0xaa, 0xc9, 0xa0, 0xf0, 0x13, 0xad, - 0x75, 0xaa, 0xc9, 0xa0, 0xd0, 0x4b, 0xac, 0x5f, - 0xaa, 0xa9, 0xc0, 0x39, 0x09, 0xa9, 0xf0, 0x02, - 0x10, 0x3f, 0x4c, 0x00, 0xa0, 0xa0, 0x3c, 0xa9, - 0xa0, 0x99, 0x74, 0xaa, 0x88, 0xd0, 0xfa, 0x60, - 0x8d, 0x75, 0xaa, 0xa9, 0x0c, 0x39, 0x09, 0xa9, - 0xf0, 0x27, 0x20, 0xb9, 0xa1, 0xb0, 0x1f, 0xa8, - 0xd0, 0x17, 0xe0, 0x11, 0xb0, 0x13, 0xac, 0x5f, - 0xaa, 0xa9, 0x08, 0x39, 0x09, 0xa9, 0xf0, 0x06, - 0xe0, 0x08, 0xb0, 0xce, 0x90, 0x0b, 0x8a, 0xd0, - 0x08, 0xa9, 0x02, 0x4c, 0xd2, 0xa6, 0x4c, 0xc4, - 0xa6, 0xa9, 0x00, 0x8d, 0x65, 0xaa, 0x8d, 0x74, - 0xaa, 0x8d, 0x66, 0xaa, 0x8d, 0x6c, 0xaa, 0x8d, - 0x6d, 0xaa, 0x20, 0xdc, 0xbf, 0xad, 0x5d, 0xaa, - 0x20, 0xa4, 0xa1, 0xd0, 0x1f, 0xc9, 0x8d, 0xd0, - 0xf7, 0xae, 0x5f, 0xaa, 0xad, 0x65, 0xaa, 0x1d, - 0x0a, 0xa9, 0x5d, 0x0a, 0xa9, 0xd0, 0x93, 0xae, - - 0x63, 0xaa, 0xf0, 0x76, 0x8d, 0x63, 0xaa, 0x8e, - 0x5d, 0xaa, 0xd0, 0xdc, 0xa2, 0x0a, 0xdd, 0x40, - 0xa9, 0xf0, 0x05, 0xca, 0xd0, 0xf8, 0xf0, 0xb6, - 0xbd, 0x4a, 0xa9, 0x30, 0x47, 0x0d, 0x65, 0xaa, - 0x8d, 0x65, 0xaa, 0xca, 0x8e, 0x64, 0xaa, 0x20, - 0xb9, 0xa1, 0xb0, 0xa2, 0xad, 0x64, 0xaa, 0x0a, - 0x0a, 0xa8, 0xa5, 0x45, 0xd0, 0x09, 0xa5, 0x44, - 0xd9, 0x55, 0xa9, 0x90, 0x8c, 0xa5, 0x45, 0xd9, - 0x58, 0xa9, 0x90, 0x0b, 0xd0, 0x83, 0xa5, 0x44, - 0xd9, 0x57, 0xa9, 0x90, 0x02, 0xd0, 0xf5, 0xad, - 0x63, 0xaa, 0xd0, 0x94, 0x98, 0x4a, 0xa8, 0xa5, - 0x45, 0x99, 0x67, 0xaa, 0xa5, 0x44, 0x99, 0x66, - 0xaa, 0x4c, 0xe8, 0xa0, 0x48, 0xa9, 0x80, 0x0d, - 0x65, 0xaa, 0x8d, 0x65, 0xaa, 0x68, 0x29, 0x7f, - 0x0d, 0x74, 0xaa, 0x8d, 0x74, 0xaa, 0xd0, 0xe9, - 0xf0, 0x9c, 0x20, 0x80, 0xa1, 0x4c, 0x83, 0x9f, - 0x20, 0x5b, 0xa7, 0x20, 0xae, 0xa1, 0xad, 0x5f, - 0xaa, 0xaa, 0xbd, 0x1f, 0x9d, 0x48, 0xbd, 0x1e, - 0x9d, 0x48, 0x60, 0xae, 0x5d, 0xaa, 0xbd, 0x00, - 0x02, 0xc9, 0x8d, 0xf0, 0x06, 0xe8, 0x8e, 0x5d, - 0xaa, 0xc9, 0xac, 0x60, 0x20, 0x93, 0xa1, 0xf0, - 0xfa, 0xc9, 0xa0, 0xf0, 0xf7, 0x60, 0xa9, 0x00, - 0xa0, 0x16, 0x99, 0xba, 0xb5, 0x88, 0xd0, 0xfa, - 0x60, 0xa9, 0x00, 0x85, 0x44, 0x85, 0x45, 0x20, - 0xa4, 0xa1, 0x08, 0xc9, 0xa4, 0xf0, 0x3c, 0x28, - 0x4c, 0xce, 0xa1, 0x20, 0xa4, 0xa1, 0xd0, 0x06, - 0xa6, 0x44, 0xa5, 0x45, 0x18, 0x60, 0x38, 0xe9, - 0xb0, 0x30, 0x21, 0xc9, 0x0a, 0xb0, 0x1d, 0x20, - 0xfe, 0xa1, 0x65, 0x44, 0xaa, 0xa9, 0x00, 0x65, - 0x45, 0xa8, 0x20, 0xfe, 0xa1, 0x20, 0xfe, 0xa1, - 0x8a, 0x65, 0x44, 0x85, 0x44, 0x98, 0x65, 0x45, - 0x85, 0x45, 0x90, 0xcf, 0x38, 0x60, 0x06, 0x44, - - 0x26, 0x45, 0x60, 0x28, 0x20, 0xa4, 0xa1, 0xf0, - 0xc5, 0x38, 0xe9, 0xb0, 0x30, 0xee, 0xc9, 0x0a, - 0x90, 0x08, 0xe9, 0x07, 0x30, 0xe6, 0xc9, 0x10, - 0xb0, 0xe2, 0xa2, 0x04, 0x20, 0xfe, 0xa1, 0xca, - 0xd0, 0xfa, 0x05, 0x44, 0x85, 0x44, 0x4c, 0x04, - 0xa2, 0xa5, 0x44, 0x4c, 0x95, 0xfe, 0xa5, 0x44, - 0x4c, 0x8b, 0xfe, 0xad, 0x5e, 0xaa, 0x0d, 0x74, - 0xaa, 0x8d, 0x5e, 0xaa, 0x60, 0x2c, 0x74, 0xaa, - 0x50, 0x03, 0x20, 0xc8, 0x9f, 0xa9, 0x70, 0x4d, - 0x74, 0xaa, 0x2d, 0x5e, 0xaa, 0x8d, 0x5e, 0xaa, - 0x60, 0xa9, 0x00, 0x8d, 0xb3, 0xaa, 0xa5, 0x44, - 0x48, 0x20, 0x16, 0xa3, 0x68, 0x8d, 0x57, 0xaa, - 0x4c, 0xd4, 0xa7, 0xa9, 0x05, 0x20, 0xaa, 0xa2, - 0x20, 0x64, 0xa7, 0xa0, 0x00, 0x98, 0x91, 0x40, - 0x60, 0xa9, 0x07, 0xd0, 0x02, 0xa9, 0x08, 0x20, - 0xaa, 0xa2, 0x4c, 0xea, 0xa2, 0xa9, 0x0c, 0xd0, - 0xf6, 0xad, 0x08, 0x9d, 0x8d, 0xbd, 0xb5, 0xad, - 0x09, 0x9d, 0x8d, 0xbe, 0xb5, 0xa9, 0x09, 0x8d, - 0x63, 0xaa, 0x20, 0xc8, 0xa2, 0x4c, 0xea, 0xa2, - 0x20, 0xa3, 0xa2, 0x20, 0x8c, 0xa6, 0xd0, 0xfb, - 0x4c, 0x71, 0xb6, 0xa9, 0x00, 0x4c, 0xd5, 0xa3, - 0xa9, 0x01, 0x8d, 0x63, 0xaa, 0xad, 0x6c, 0xaa, - 0xd0, 0x0a, 0xad, 0x6d, 0xaa, 0xd0, 0x05, 0xa9, - 0x01, 0x8d, 0x6c, 0xaa, 0xad, 0x6c, 0xaa, 0x8d, - 0xbd, 0xb5, 0xad, 0x6d, 0xaa, 0x8d, 0xbe, 0xb5, - 0x20, 0xea, 0xa2, 0xa5, 0x45, 0xd0, 0x03, 0x4c, - 0xc8, 0xa6, 0x85, 0x41, 0xa5, 0x44, 0x85, 0x40, - 0x20, 0x43, 0xa7, 0x20, 0x4e, 0xa7, 0x20, 0x1a, - 0xa7, 0xad, 0x63, 0xaa, 0x8d, 0xbb, 0xb5, 0x4c, - 0xa8, 0xa6, 0xad, 0x75, 0xaa, 0xc9, 0xa0, 0xf0, - 0x25, 0x20, 0x64, 0xa7, 0xb0, 0x3a, 0x20, 0xfc, - 0xa2, 0x4c, 0xea, 0xa2, 0x20, 0xaf, 0xa7, 0xd0, - - 0x05, 0xa9, 0x00, 0x8d, 0xb3, 0xaa, 0xa0, 0x00, - 0x98, 0x91, 0x40, 0x20, 0x4e, 0xa7, 0xa9, 0x02, - 0x8d, 0xbb, 0xb5, 0x4c, 0xa8, 0xa6, 0x20, 0x92, - 0xa7, 0xd0, 0x05, 0x20, 0x9a, 0xa7, 0xf0, 0x10, - 0x20, 0xaf, 0xa7, 0xf0, 0xf6, 0x20, 0xaa, 0xa7, - 0xf0, 0xf1, 0x20, 0xfc, 0xa2, 0x4c, 0x16, 0xa3, - 0x60, 0xa9, 0x09, 0x2d, 0x65, 0xaa, 0xc9, 0x09, - 0xf0, 0x03, 0x4c, 0x00, 0xa0, 0xa9, 0x04, 0x20, - 0xd5, 0xa3, 0xad, 0x73, 0xaa, 0xac, 0x72, 0xaa, - 0x20, 0xe0, 0xa3, 0xad, 0x6d, 0xaa, 0xac, 0x6c, - 0xaa, 0x20, 0xe0, 0xa3, 0xad, 0x73, 0xaa, 0xac, - 0x72, 0xaa, 0x4c, 0xff, 0xa3, 0x20, 0xa8, 0xa2, - 0xa9, 0x7f, 0x2d, 0xc2, 0xb5, 0xc9, 0x04, 0xf0, - 0x03, 0x4c, 0xd0, 0xa6, 0xa9, 0x04, 0x20, 0xd5, - 0xa3, 0x20, 0x7a, 0xa4, 0xaa, 0xad, 0x65, 0xaa, - 0x29, 0x01, 0xd0, 0x06, 0x8e, 0x72, 0xaa, 0x8c, - 0x73, 0xaa, 0x20, 0x7a, 0xa4, 0xae, 0x72, 0xaa, - 0xac, 0x73, 0xaa, 0x4c, 0x71, 0xa4, 0x20, 0x5d, - 0xa3, 0x20, 0x51, 0xa8, 0x6c, 0x72, 0xaa, 0xad, - 0xb6, 0xaa, 0xf0, 0x20, 0xa5, 0xd6, 0x10, 0x03, - 0x4c, 0xcc, 0xa6, 0xa9, 0x02, 0x20, 0xd5, 0xa3, - 0x38, 0xa5, 0xaf, 0xe5, 0x67, 0xa8, 0xa5, 0xb0, - 0xe5, 0x68, 0x20, 0xe0, 0xa3, 0xa5, 0x68, 0xa4, - 0x67, 0x4c, 0xff, 0xa3, 0xa9, 0x01, 0x20, 0xd5, - 0xa3, 0x38, 0xa5, 0x4c, 0xe5, 0xca, 0xa8, 0xa5, - 0x4d, 0xe5, 0xcb, 0x20, 0xe0, 0xa3, 0xa5, 0xcb, - 0xa4, 0xca, 0x4c, 0xff, 0xa3, 0x8d, 0xc2, 0xb5, - 0x48, 0x20, 0xa8, 0xa2, 0x68, 0x4c, 0xc4, 0xa7, - 0x8c, 0xc1, 0xb5, 0x8c, 0xc3, 0xb5, 0x8d, 0xc2, - 0xb5, 0xa9, 0x04, 0x8d, 0xbb, 0xb5, 0xa9, 0x01, - 0x8d, 0xbc, 0xb5, 0x20, 0xa8, 0xa6, 0xad, 0xc2, - 0xb5, 0x8d, 0xc3, 0xb5, 0x4c, 0xa8, 0xa6, 0x8c, - - 0xc3, 0xb5, 0x8d, 0xc4, 0xb5, 0xa9, 0x02, 0x4c, - 0x86, 0xb6, 0x20, 0xa8, 0xa6, 0x4c, 0xea, 0xa2, - 0x4c, 0xd0, 0xa6, 0x20, 0x16, 0xa3, 0x20, 0xa8, - 0xa2, 0xa9, 0x23, 0x2d, 0xc2, 0xb5, 0xf0, 0xf0, - 0x8d, 0xc2, 0xb5, 0xad, 0xb6, 0xaa, 0xf0, 0x28, - 0xa9, 0x02, 0x20, 0xb1, 0xa4, 0x20, 0x7a, 0xa4, - 0x18, 0x65, 0x67, 0xaa, 0x98, 0x65, 0x68, 0xc5, - 0x74, 0xb0, 0x70, 0x85, 0xb0, 0x85, 0x6a, 0x86, - 0xaf, 0x86, 0x69, 0xa6, 0x67, 0xa4, 0x68, 0x20, - 0x71, 0xa4, 0x20, 0x51, 0xa8, 0x6c, 0x60, 0x9d, - 0xa9, 0x01, 0x20, 0xb1, 0xa4, 0x20, 0x7a, 0xa4, - 0x38, 0xa5, 0x4c, 0xed, 0x60, 0xaa, 0xaa, 0xa5, - 0x4d, 0xed, 0x61, 0xaa, 0x90, 0x45, 0xa8, 0xc4, - 0x4b, 0x90, 0x40, 0xf0, 0x3e, 0x84, 0xcb, 0x86, - 0xca, 0x8e, 0xc3, 0xb5, 0x8c, 0xc4, 0xb5, 0x4c, - 0x0a, 0xa4, 0xad, 0x0a, 0x9d, 0x8d, 0xc3, 0xb5, - 0xad, 0x0b, 0x9d, 0x8d, 0xc4, 0xb5, 0xa9, 0x00, - 0x8d, 0xc2, 0xb5, 0xa9, 0x02, 0x8d, 0xc1, 0xb5, - 0xa9, 0x03, 0x8d, 0xbb, 0xb5, 0xa9, 0x02, 0x8d, - 0xbc, 0xb5, 0x20, 0xa8, 0xa6, 0xad, 0x61, 0xaa, - 0x8d, 0xc2, 0xb5, 0xa8, 0xad, 0x60, 0xaa, 0x8d, - 0xc1, 0xb5, 0x60, 0x20, 0xea, 0xa2, 0x4c, 0xcc, - 0xa6, 0xcd, 0xc2, 0xb5, 0xf0, 0x1a, 0xae, 0x5f, - 0xaa, 0x8e, 0x62, 0xaa, 0x4a, 0xf0, 0x03, 0x4c, - 0x9e, 0xa5, 0xa2, 0x1d, 0xbd, 0x75, 0xaa, 0x9d, - 0x93, 0xaa, 0xca, 0x10, 0xf7, 0x4c, 0x7a, 0xa5, - 0x60, 0xad, 0xb6, 0xaa, 0xf0, 0x03, 0x8d, 0xb7, - 0xaa, 0x20, 0x13, 0xa4, 0x20, 0xc8, 0x9f, 0x20, - 0x51, 0xa8, 0x6c, 0x58, 0x9d, 0xa5, 0x4a, 0x85, - 0xcc, 0xa5, 0x4b, 0x85, 0xcd, 0x6c, 0x56, 0x9d, - 0x20, 0x16, 0xa4, 0x20, 0xc8, 0x9f, 0x20, 0x51, - 0xa8, 0x6c, 0x56, 0x9d, 0x20, 0x65, 0xd6, 0x85, - - 0x33, 0x85, 0xd8, 0x4c, 0xd2, 0xd7, 0x20, 0x65, - 0x0e, 0x85, 0x33, 0x85, 0xd8, 0x4c, 0xd4, 0x0f, - 0x20, 0x26, 0xa5, 0xa9, 0x05, 0x8d, 0x52, 0xaa, - 0x4c, 0x83, 0x9f, 0x20, 0x26, 0xa5, 0xa9, 0x01, - 0x8d, 0x51, 0xaa, 0x4c, 0x83, 0x9f, 0x20, 0x64, - 0xa7, 0x90, 0x06, 0x20, 0xa3, 0xa2, 0x4c, 0x34, - 0xa5, 0x20, 0x4e, 0xa7, 0xad, 0x65, 0xaa, 0x29, - 0x06, 0xf0, 0x13, 0xa2, 0x03, 0xbd, 0x6e, 0xaa, - 0x9d, 0xbd, 0xb5, 0xca, 0x10, 0xf7, 0xa9, 0x0a, - 0x8d, 0xbb, 0xb5, 0x20, 0xa8, 0xa6, 0x60, 0xa9, - 0x40, 0x2d, 0x65, 0xaa, 0xf0, 0x05, 0xad, 0x66, - 0xaa, 0xd0, 0x05, 0xa9, 0xfe, 0x8d, 0x66, 0xaa, - 0xad, 0x0d, 0x9d, 0x8d, 0xbc, 0xb5, 0xa9, 0x0b, - 0x20, 0xaa, 0xa2, 0x4c, 0x97, 0xa3, 0xa9, 0x06, - 0x20, 0xaa, 0xa2, 0xad, 0xbf, 0xb5, 0x8d, 0x66, - 0xaa, 0x60, 0xa9, 0x4c, 0x20, 0xb2, 0xa5, 0xf0, - 0x2e, 0xa9, 0x00, 0x8d, 0xb6, 0xaa, 0xa0, 0x1e, - 0x20, 0x97, 0xa0, 0xa2, 0x09, 0xbd, 0xb7, 0xaa, - 0x9d, 0x74, 0xaa, 0xca, 0xd0, 0xf7, 0xa9, 0xc0, - 0x8d, 0x51, 0xaa, 0x4c, 0xd1, 0xa4, 0xa9, 0x20, - 0x20, 0xb2, 0xa5, 0xf0, 0x05, 0xa9, 0x01, 0x4c, - 0xd2, 0xa6, 0xa9, 0x00, 0x8d, 0xb7, 0xaa, 0x4c, - 0x84, 0x9d, 0xcd, 0x00, 0xe0, 0xf0, 0x0e, 0x8d, - 0x80, 0xc0, 0xcd, 0x00, 0xe0, 0xf0, 0x06, 0x8d, - 0x81, 0xc0, 0xcd, 0x00, 0xe0, 0x60, 0x20, 0xa3, - 0xa2, 0xad, 0x4f, 0xaa, 0x8d, 0xb4, 0xaa, 0xad, - 0x50, 0xaa, 0x8d, 0xb5, 0xaa, 0xad, 0x75, 0xaa, - 0x8d, 0xb3, 0xaa, 0xd0, 0x0e, 0x20, 0x64, 0xa7, - 0x90, 0x06, 0x20, 0xa3, 0xa2, 0x4c, 0xeb, 0xa5, - 0x20, 0x4e, 0xa7, 0xad, 0x65, 0xaa, 0x29, 0x04, - 0xf0, 0x1b, 0xad, 0x6e, 0xaa, 0xd0, 0x08, 0xae, - 0x6f, 0xaa, 0xf0, 0x11, 0xce, 0x6f, 0xaa, 0xce, - - 0x6e, 0xaa, 0x20, 0x8c, 0xa6, 0xf0, 0x38, 0xc9, - 0x8d, 0xd0, 0xf7, 0xf0, 0xe5, 0x60, 0x20, 0x5e, - 0xa6, 0xb0, 0x66, 0xad, 0x5c, 0xaa, 0x8d, 0xc3, - 0xb5, 0xa9, 0x04, 0x8d, 0xbb, 0xb5, 0xa9, 0x01, - 0x8d, 0xbc, 0xb5, 0x4c, 0xa8, 0xa6, 0x20, 0x5e, - 0xa6, 0xb0, 0x4e, 0xa9, 0x06, 0x8d, 0x52, 0xaa, - 0x20, 0x8c, 0xa6, 0xd0, 0x0f, 0x20, 0xfc, 0xa2, - 0xa9, 0x03, 0xcd, 0x52, 0xaa, 0xf0, 0xce, 0xa9, - 0x05, 0x4c, 0xd2, 0xa6, 0xc9, 0xe0, 0x90, 0x02, - 0x29, 0x7f, 0x8d, 0x5c, 0xaa, 0xae, 0x5a, 0xaa, - 0xf0, 0x09, 0xca, 0xbd, 0x00, 0x02, 0x09, 0x80, - 0x9d, 0x00, 0x02, 0x4c, 0xb3, 0x9f, 0x48, 0xad, - 0xb6, 0xaa, 0xf0, 0x0e, 0xa6, 0x76, 0xe8, 0xf0, - 0x0d, 0xa6, 0x33, 0xe0, 0xdd, 0xf0, 0x07, 0x68, - 0x18, 0x60, 0xa5, 0xd9, 0x30, 0xf9, 0x68, 0x38, - 0x60, 0x20, 0xfc, 0xa2, 0x20, 0x5b, 0xa7, 0x4c, - 0xb3, 0x9f, 0x20, 0x9d, 0xa6, 0x20, 0x4e, 0xa7, - 0xa9, 0x03, 0xd0, 0xa1, 0xa9, 0x03, 0x8d, 0xbb, - 0xb5, 0xa9, 0x01, 0x8d, 0xbc, 0xb5, 0x20, 0xa8, - 0xa6, 0xad, 0xc3, 0xb5, 0x60, 0xad, 0xb5, 0xaa, - 0x85, 0x41, 0xad, 0xb4, 0xaa, 0x85, 0x40, 0x60, - 0x20, 0x06, 0xab, 0x90, 0x16, 0xad, 0xc5, 0xb5, - 0xc9, 0x05, 0xf0, 0x03, 0x4c, 0x5e, 0xb6, 0x4c, - 0x92, 0xb6, 0xea, 0xea, 0xea, 0xea, 0xa2, 0x00, - 0x8e, 0xc3, 0xb5, 0x60, 0xa9, 0x0b, 0xd0, 0x0a, - 0xa9, 0x0c, 0xd0, 0x06, 0xa9, 0x0e, 0xd0, 0x02, - 0xa9, 0x0d, 0x8d, 0x5c, 0xaa, 0x20, 0xe6, 0xbf, - 0xad, 0xb6, 0xaa, 0xf0, 0x04, 0xa5, 0xd8, 0x30, - 0x0e, 0xa2, 0x00, 0x20, 0x02, 0xa7, 0xae, 0x5c, - 0xaa, 0x20, 0x02, 0xa7, 0x20, 0xc8, 0x9f, 0x20, - 0x51, 0xa8, 0x20, 0x5e, 0xa6, 0xae, 0x5c, 0xaa, - 0xa9, 0x03, 0xb0, 0x03, 0x6c, 0x5a, 0x9d, 0x6c, - - 0x5e, 0x9d, 0xbd, 0x3f, 0xaa, 0xaa, 0x8e, 0x63, - 0xaa, 0xbd, 0x71, 0xa9, 0x48, 0x09, 0x80, 0x20, - 0xc5, 0x9f, 0xae, 0x63, 0xaa, 0xe8, 0x68, 0x10, - 0xed, 0x60, 0xad, 0x66, 0xaa, 0x8d, 0xbf, 0xb5, - 0xad, 0x68, 0xaa, 0x8d, 0xc0, 0xb5, 0xad, 0x6a, - 0xaa, 0x8d, 0xc1, 0xb5, 0xad, 0x06, 0x9d, 0x8d, - 0xc3, 0xb5, 0xad, 0x07, 0x9d, 0x8d, 0xc4, 0xb5, - 0xa5, 0x40, 0x8d, 0x4f, 0xaa, 0xa5, 0x41, 0x8d, - 0x50, 0xaa, 0x60, 0xa0, 0x1d, 0xb9, 0x75, 0xaa, - 0x91, 0x40, 0x88, 0x10, 0xf8, 0x60, 0xa0, 0x1e, - 0xb1, 0x40, 0x99, 0xa9, 0xb5, 0xc8, 0xc0, 0x26, - 0xd0, 0xf6, 0x60, 0xa0, 0x00, 0x8c, 0x51, 0xaa, - 0x8c, 0x52, 0xaa, 0x60, 0xa9, 0x00, 0x85, 0x45, - 0x20, 0x92, 0xa7, 0x4c, 0x73, 0xa7, 0x20, 0x9a, - 0xa7, 0xf0, 0x1d, 0x20, 0xaa, 0xa7, 0xd0, 0x0a, - 0xa5, 0x40, 0x85, 0x44, 0xa5, 0x41, 0x85, 0x45, - 0xd0, 0xec, 0xa0, 0x1d, 0xb1, 0x40, 0xd9, 0x75, - 0xaa, 0xd0, 0xe3, 0x88, 0x10, 0xf6, 0x18, 0x60, - 0x38, 0x60, 0xad, 0x00, 0x9d, 0xae, 0x01, 0x9d, - 0xd0, 0x0a, 0xa0, 0x25, 0xb1, 0x40, 0xf0, 0x09, - 0xaa, 0x88, 0xb1, 0x40, 0x86, 0x41, 0x85, 0x40, - 0x8a, 0x60, 0xa0, 0x00, 0xb1, 0x40, 0x60, 0xad, - 0xb3, 0xaa, 0xf0, 0x0e, 0xad, 0xb4, 0xaa, 0xc5, - 0x40, 0xd0, 0x08, 0xad, 0xb5, 0xaa, 0xc5, 0x41, - 0xf0, 0x01, 0xca, 0x60, 0x4d, 0xc2, 0xb5, 0xf0, - 0x0a, 0x29, 0x7f, 0xf0, 0x06, 0x20, 0xea, 0xa2, - 0x4c, 0xd0, 0xa6, 0x60, 0x38, 0xad, 0x00, 0x9d, - 0x85, 0x40, 0xad, 0x01, 0x9d, 0x85, 0x41, 0xad, - 0x57, 0xaa, 0x8d, 0x63, 0xaa, 0xa0, 0x00, 0x98, - 0x91, 0x40, 0xa0, 0x1e, 0x38, 0xa5, 0x40, 0xe9, - 0x2d, 0x91, 0x40, 0x48, 0xa5, 0x41, 0xe9, 0x00, - 0xc8, 0x91, 0x40, 0xaa, 0xca, 0x68, 0x48, 0xc8, - - 0x91, 0x40, 0x8a, 0xc8, 0x91, 0x40, 0xaa, 0xca, - 0x68, 0x48, 0xc8, 0x91, 0x40, 0xc8, 0x8a, 0x91, - 0x40, 0xce, 0x63, 0xaa, 0xf0, 0x17, 0xaa, 0x68, - 0x38, 0xe9, 0x26, 0xc8, 0x91, 0x40, 0x48, 0x8a, - 0xe9, 0x00, 0xc8, 0x91, 0x40, 0x85, 0x41, 0x68, - 0x85, 0x40, 0x4c, 0xe5, 0xa7, 0x48, 0xa9, 0x00, - 0xc8, 0x91, 0x40, 0xc8, 0x91, 0x40, 0xad, 0xb6, - 0xaa, 0xf0, 0x0b, 0x68, 0x85, 0x74, 0x85, 0x70, - 0x68, 0x85, 0x73, 0x85, 0x6f, 0x60, 0x68, 0x85, - 0x4d, 0x85, 0xcb, 0x68, 0x85, 0x4c, 0x85, 0xca, - 0x60, 0xa5, 0x39, 0xcd, 0x03, 0x9d, 0xf0, 0x12, - 0x8d, 0x56, 0xaa, 0xa5, 0x38, 0x8d, 0x55, 0xaa, - 0xad, 0x02, 0x9d, 0x85, 0x38, 0xad, 0x03, 0x9d, - 0x85, 0x39, 0xa5, 0x37, 0xcd, 0x05, 0x9d, 0xf0, - 0x12, 0x8d, 0x54, 0xaa, 0xa5, 0x36, 0x8d, 0x53, - 0xaa, 0xad, 0x04, 0x9d, 0x85, 0x36, 0xad, 0x05, - 0x9d, 0x85, 0x37, 0x60, 0x49, 0x4e, 0x49, 0xd4, - 0x4c, 0x4f, 0x41, 0xc4, 0x53, 0x41, 0x56, 0xc5, - 0x52, 0x55, 0xce, 0x43, 0x48, 0x41, 0x49, 0xce, - 0x44, 0x45, 0x4c, 0x45, 0x54, 0xc5, 0x4c, 0x4f, - 0x43, 0xcb, 0x55, 0x4e, 0x4c, 0x4f, 0x43, 0xcb, - 0x43, 0x4c, 0x4f, 0x53, 0xc5, 0x52, 0x45, 0x41, - 0xc4, 0x45, 0x58, 0x45, 0xc3, 0x57, 0x52, 0x49, - 0x54, 0xc5, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, - 0x4f, 0xce, 0x4f, 0x50, 0x45, 0xce, 0x41, 0x50, - 0x50, 0x45, 0x4e, 0xc4, 0x52, 0x45, 0x4e, 0x41, - 0x4d, 0xc5, 0x43, 0x41, 0x54, 0x41, 0x4c, 0x4f, - 0xc7, 0x4d, 0x4f, 0xce, 0x4e, 0x4f, 0x4d, 0x4f, - 0xce, 0x50, 0x52, 0xa3, 0x49, 0x4e, 0xa3, 0x4d, - 0x41, 0x58, 0x46, 0x49, 0x4c, 0x45, 0xd3, 0x46, - 0xd0, 0x49, 0x4e, 0xd4, 0x42, 0x53, 0x41, 0x56, - 0xc5, 0x42, 0x4c, 0x4f, 0x41, 0xc4, 0x42, 0x52, - - 0x55, 0xce, 0x56, 0x45, 0x52, 0x49, 0x46, 0xd9, - 0x00, 0x21, 0x70, 0xa0, 0x70, 0xa1, 0x70, 0xa0, - 0x70, 0x20, 0x70, 0x20, 0x70, 0x20, 0x70, 0x20, - 0x70, 0x60, 0x00, 0x22, 0x06, 0x20, 0x74, 0x22, - 0x06, 0x22, 0x04, 0x23, 0x78, 0x22, 0x70, 0x30, - 0x70, 0x40, 0x70, 0x40, 0x80, 0x40, 0x80, 0x08, - 0x00, 0x08, 0x00, 0x04, 0x00, 0x40, 0x70, 0x40, - 0x00, 0x21, 0x79, 0x20, 0x71, 0x20, 0x71, 0x20, - 0x70, 0xd6, 0xc4, 0xd3, 0xcc, 0xd2, 0xc2, 0xc1, - 0xc3, 0xc9, 0xcf, 0x40, 0x20, 0x10, 0x08, 0x04, - 0x02, 0x01, 0xc0, 0xa0, 0x90, 0x00, 0x00, 0xfe, - 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x07, - 0x00, 0x01, 0x00, 0xff, 0x7f, 0x00, 0x00, 0xff, - 0x7f, 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0xff, - 0xff, 0x0d, 0x07, 0x8d, 0x4c, 0x41, 0x4e, 0x47, - 0x55, 0x41, 0x47, 0x45, 0x20, 0x4e, 0x4f, 0x54, - 0x20, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, - 0x4c, 0xc5, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x20, - 0x45, 0x52, 0x52, 0x4f, 0xd2, 0x57, 0x52, 0x49, - 0x54, 0x45, 0x20, 0x50, 0x52, 0x4f, 0x54, 0x45, - 0x43, 0x54, 0x45, 0xc4, 0x45, 0x4e, 0x44, 0x20, - 0x4f, 0x46, 0x20, 0x44, 0x41, 0x54, 0xc1, 0x46, - 0x49, 0x4c, 0x45, 0x20, 0x4e, 0x4f, 0x54, 0x20, - 0x46, 0x4f, 0x55, 0x4e, 0xc4, 0x56, 0x4f, 0x4c, - 0x55, 0x4d, 0x45, 0x20, 0x4d, 0x49, 0x53, 0x4d, - 0x41, 0x54, 0x43, 0xc8, 0x49, 0x2f, 0x4f, 0x20, - 0x45, 0x52, 0x52, 0x4f, 0xd2, 0x44, 0x49, 0x53, - 0x4b, 0x20, 0x46, 0x55, 0x4c, 0xcc, 0x46, 0x49, - 0x4c, 0x45, 0x20, 0x4c, 0x4f, 0x43, 0x4b, 0x45, - 0xc4, 0x53, 0x59, 0x4e, 0x54, 0x41, 0x58, 0x20, - 0x45, 0x52, 0x52, 0x4f, 0xd2, 0x4e, 0x4f, 0x20, - 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x53, 0x20, - - 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, - 0xc5, 0x46, 0x49, 0x4c, 0x45, 0x20, 0x54, 0x59, - 0x50, 0x45, 0x20, 0x4d, 0x49, 0x53, 0x4d, 0x41, - 0x54, 0x43, 0xc8, 0x50, 0x52, 0x4f, 0x47, 0x52, - 0x41, 0x4d, 0x20, 0x54, 0x4f, 0x4f, 0x20, 0x4c, - 0x41, 0x52, 0x47, 0xc5, 0x4e, 0x4f, 0x54, 0x20, - 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x20, 0x43, - 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0xc4, 0x8d, 0x00, - 0x03, 0x19, 0x19, 0x24, 0x33, 0x3e, 0x4c, 0x5b, - 0x64, 0x6d, 0x78, 0x84, 0x98, 0xaa, 0xbb, 0x2d, - 0x98, 0x00, 0x00, 0xf0, 0xfd, 0x1b, 0xfd, 0x03, - 0x03, 0xf4, 0x0d, 0x28, 0x8d, 0x0d, 0x00, 0x00, - 0x00, 0x30, 0x00, 0x0b, 0x01, 0x20, 0xfe, 0x00, - 0x02, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xd0, 0x00, 0xc8, 0xc5, 0xcc, - 0xcc, 0xcf, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0x03, 0x84, 0x00, 0x00, 0x00, 0x40, 0x00, - 0xc1, 0xd0, 0xd0, 0xcc, 0xc5, 0xd3, 0xcf, 0xc6, - 0xd4, 0xe8, 0xb7, 0xbb, 0xb3, 0xbb, 0xb4, 0x00, - 0xc0, 0x7e, 0xb3, 0x21, 0xab, 0x05, 0xac, 0x57, - 0xac, 0x6f, 0xac, 0x2a, 0xad, 0x97, 0xad, 0xee, - 0xac, 0xf5, 0xac, 0x39, 0xac, 0x11, 0xad, 0x8d, - 0xae, 0x17, 0xad, 0x7e, 0xb3, 0x7e, 0xb3, 0x89, - 0xac, 0x95, 0xac, 0x86, 0xac, 0x92, 0xac, 0x7e, - 0xb3, 0x7e, 0xb3, 0xbd, 0xac, 0xc9, 0xac, 0xba, - 0xac, 0xc6, 0xac, 0x7e, 0xb3, 0xe0, 0x00, 0xf0, - - 0x02, 0xa2, 0x02, 0x8e, 0x5f, 0xaa, 0xba, 0x8e, - 0x9b, 0xb3, 0x20, 0x6a, 0xae, 0xad, 0xbb, 0xb5, - 0xc9, 0x0d, 0xb0, 0x0b, 0x0a, 0xaa, 0xbd, 0xca, - 0xaa, 0x48, 0xbd, 0xc9, 0xaa, 0x48, 0x60, 0x4c, - 0x63, 0xb3, 0x20, 0x28, 0xab, 0x4c, 0x7f, 0xb3, - 0x20, 0xdc, 0xab, 0xa9, 0x01, 0x8d, 0xe3, 0xb5, - 0xae, 0xbe, 0xb5, 0xad, 0xbd, 0xb5, 0xd0, 0x05, - 0xe0, 0x00, 0xd0, 0x01, 0xe8, 0x8d, 0xe8, 0xb5, - 0x8e, 0xe9, 0xb5, 0x20, 0xc9, 0xb1, 0x90, 0x5e, - 0x8e, 0x9c, 0xb3, 0xae, 0x5f, 0xaa, 0xbd, 0x09, - 0xa9, 0xae, 0x9c, 0xb3, 0x4a, 0xb0, 0x0d, 0xad, - 0x51, 0xaa, 0xc9, 0xc0, 0xd0, 0x03, 0x4c, 0x5f, - 0xb3, 0x4c, 0x73, 0xb3, 0xa9, 0x00, 0x9d, 0xe8, - 0xb4, 0xa9, 0x01, 0x9d, 0xe7, 0xb4, 0x8e, 0x9c, - 0xb3, 0x20, 0x44, 0xb2, 0xae, 0x9c, 0xb3, 0x9d, - 0xc7, 0xb4, 0x8d, 0xd2, 0xb5, 0x8d, 0xd4, 0xb5, - 0xad, 0xf1, 0xb5, 0x9d, 0xc6, 0xb4, 0x8d, 0xd1, - 0xb5, 0x8d, 0xd3, 0xb5, 0xad, 0xc2, 0xb5, 0x9d, - 0xc8, 0xb4, 0x20, 0x37, 0xb0, 0x20, 0x0c, 0xaf, - 0x20, 0xd6, 0xb7, 0x20, 0x3a, 0xaf, 0xae, 0x9c, - 0xb3, 0xa9, 0x06, 0x8d, 0xc5, 0xb5, 0xbd, 0xc6, - 0xb4, 0x8d, 0xd1, 0xb5, 0xbd, 0xc7, 0xb4, 0x8d, - 0xd2, 0xb5, 0xbd, 0xc8, 0xb4, 0x8d, 0xc2, 0xb5, - 0x8d, 0xf6, 0xb5, 0xbd, 0xe7, 0xb4, 0x8d, 0xee, - 0xb5, 0xbd, 0xe8, 0xb4, 0x8d, 0xef, 0xb5, 0x8e, - 0xd9, 0xb5, 0xa9, 0xff, 0x8d, 0xe0, 0xb5, 0x8d, - 0xe1, 0xb5, 0xad, 0xe2, 0xb3, 0x8d, 0xda, 0xb5, - 0x18, 0x4c, 0x5e, 0xaf, 0xa9, 0x00, 0xaa, 0x9d, - 0xd1, 0xb5, 0xe8, 0xe0, 0x2d, 0xd0, 0xf8, 0xad, - 0xbf, 0xb5, 0x49, 0xff, 0x8d, 0xf9, 0xb5, 0xad, - 0xc0, 0xb5, 0x8d, 0xf8, 0xb5, 0xad, 0xc1, 0xb5, - 0x0a, 0x0a, 0x0a, 0x0a, 0xaa, 0x8e, 0xf7, 0xb5, - - 0xa9, 0x11, 0x8d, 0xfa, 0xb5, 0x60, 0x20, 0x1d, - 0xaf, 0x20, 0x34, 0xaf, 0x20, 0xc3, 0xb2, 0xa9, - 0x02, 0x2d, 0xd5, 0xb5, 0xf0, 0x21, 0x20, 0xf7, - 0xaf, 0xa9, 0x00, 0x18, 0x20, 0x11, 0xb0, 0x38, - 0xce, 0xd8, 0xb5, 0xd0, 0xf7, 0xae, 0xd9, 0xb5, - 0xad, 0xee, 0xb5, 0x9d, 0xe7, 0xb4, 0xad, 0xef, - 0xb5, 0x9d, 0xe8, 0xb4, 0x20, 0x37, 0xb0, 0x4c, - 0x7f, 0xb3, 0x20, 0x28, 0xab, 0xad, 0xf6, 0xb5, - 0x30, 0x2b, 0xad, 0xbd, 0xb5, 0x85, 0x42, 0xad, - 0xbe, 0xb5, 0x85, 0x43, 0xae, 0x9c, 0xb3, 0x20, - 0x1c, 0xb2, 0x20, 0x37, 0xb0, 0x4c, 0x7f, 0xb3, - 0xad, 0xbc, 0xb5, 0xc9, 0x05, 0xb0, 0x0b, 0x0a, - 0xaa, 0xbd, 0xe6, 0xaa, 0x48, 0xbd, 0xe5, 0xaa, - 0x48, 0x60, 0x4c, 0x67, 0xb3, 0x4c, 0x7b, 0xb3, - 0xad, 0xf6, 0xb5, 0x30, 0xf8, 0xad, 0xbc, 0xb5, - 0xc9, 0x05, 0xb0, 0xee, 0x0a, 0xaa, 0xbd, 0xf2, - 0xaa, 0x48, 0xbd, 0xf1, 0xaa, 0x48, 0x60, 0x20, - 0x00, 0xb3, 0x20, 0xa8, 0xac, 0x8d, 0xc3, 0xb5, - 0x4c, 0x7f, 0xb3, 0x20, 0x00, 0xb3, 0x20, 0xb5, - 0xb1, 0x20, 0xa8, 0xac, 0x48, 0x20, 0xa2, 0xb1, - 0xa0, 0x00, 0x68, 0x91, 0x42, 0x4c, 0x96, 0xac, - 0x20, 0xb6, 0xb0, 0xb0, 0x0b, 0xb1, 0x42, 0x48, - 0x20, 0x5b, 0xb1, 0x20, 0x94, 0xb1, 0x68, 0x60, - 0x4c, 0x6f, 0xb3, 0x20, 0x00, 0xb3, 0xad, 0xc3, - 0xb5, 0x20, 0xda, 0xac, 0x4c, 0x7f, 0xb3, 0x20, - 0x00, 0xb3, 0x20, 0xa2, 0xb1, 0xa0, 0x00, 0xb1, - 0x42, 0x20, 0xda, 0xac, 0x20, 0xb5, 0xb1, 0x4c, - 0xca, 0xac, 0x48, 0x20, 0xb6, 0xb0, 0x68, 0x91, - 0x42, 0xa9, 0x40, 0x0d, 0xd5, 0xb5, 0x8d, 0xd5, - 0xb5, 0x20, 0x5b, 0xb1, 0x4c, 0x94, 0xb1, 0xa9, - 0x80, 0x8d, 0x9e, 0xb3, 0xd0, 0x05, 0xa9, 0x00, - 0x8d, 0x9e, 0xb3, 0x20, 0x28, 0xab, 0xae, 0x9c, - - 0xb3, 0xbd, 0xc8, 0xb4, 0x29, 0x7f, 0x0d, 0x9e, - 0xb3, 0x9d, 0xc8, 0xb4, 0x20, 0x37, 0xb0, 0x4c, - 0x7f, 0xb3, 0x20, 0x00, 0xb3, 0x4c, 0x7f, 0xb3, - 0x20, 0x28, 0xab, 0x20, 0xb6, 0xb0, 0xb0, 0xef, - 0xee, 0xe4, 0xb5, 0xd0, 0xf6, 0xee, 0xe5, 0xb5, - 0x4c, 0x1b, 0xad, 0x20, 0x28, 0xab, 0xae, 0x9c, - 0xb3, 0xbd, 0xc8, 0xb4, 0x10, 0x03, 0x4c, 0x7b, - 0xb3, 0xae, 0x9c, 0xb3, 0xbd, 0xc6, 0xb4, 0x8d, - 0xd1, 0xb5, 0x9d, 0xe6, 0xb4, 0xa9, 0xff, 0x9d, - 0xc6, 0xb4, 0xbc, 0xc7, 0xb4, 0x8c, 0xd2, 0xb5, - 0x20, 0x37, 0xb0, 0x18, 0x20, 0x5e, 0xaf, 0xb0, - 0x2a, 0x20, 0x0c, 0xaf, 0xa0, 0x0c, 0x8c, 0x9c, - 0xb3, 0xb1, 0x42, 0x30, 0x0b, 0xf0, 0x09, 0x48, - 0xc8, 0xb1, 0x42, 0xa8, 0x68, 0x20, 0x89, 0xad, - 0xac, 0x9c, 0xb3, 0xc8, 0xc8, 0xd0, 0xe7, 0xad, - 0xd3, 0xb5, 0xac, 0xd4, 0xb5, 0x20, 0x89, 0xad, - 0x38, 0xb0, 0xd1, 0x20, 0xfb, 0xaf, 0x4c, 0x7f, - 0xb3, 0x38, 0x20, 0xdd, 0xb2, 0xa9, 0x00, 0xa2, - 0x05, 0x9d, 0xf0, 0xb5, 0xca, 0x10, 0xfa, 0x60, - 0x20, 0xdc, 0xab, 0xa9, 0xff, 0x8d, 0xf9, 0xb5, - 0x20, 0xf7, 0xaf, 0xa9, 0x16, 0x8d, 0x9d, 0xb3, - 0x20, 0x2f, 0xae, 0x20, 0x2f, 0xae, 0xa2, 0x0b, - 0xbd, 0xaf, 0xb3, 0x20, 0xed, 0xfd, 0xca, 0x10, - 0xf7, 0x86, 0x45, 0xad, 0xf6, 0xb7, 0x85, 0x44, - 0x20, 0x42, 0xae, 0x20, 0x2f, 0xae, 0x20, 0x2f, - 0xae, 0x18, 0x20, 0x11, 0xb0, 0xb0, 0x5d, 0xa2, - 0x00, 0x8e, 0x9c, 0xb3, 0xbd, 0xc6, 0xb4, 0xf0, - 0x53, 0x30, 0x4a, 0xa0, 0xa0, 0xbd, 0xc8, 0xb4, - 0x10, 0x02, 0xa0, 0xaa, 0x98, 0x20, 0xed, 0xfd, - 0xbd, 0xc8, 0xb4, 0x29, 0x7f, 0xa0, 0x07, 0x0a, - 0x0a, 0xb0, 0x03, 0x88, 0xd0, 0xfa, 0xb9, 0xa7, - 0xb3, 0x20, 0xed, 0xfd, 0xa9, 0xa0, 0x20, 0xed, - - 0xfd, 0xbd, 0xe7, 0xb4, 0x85, 0x44, 0xbd, 0xe8, - 0xb4, 0x85, 0x45, 0x20, 0x42, 0xae, 0xa9, 0xa0, - 0x20, 0xed, 0xfd, 0xe8, 0xe8, 0xe8, 0xa0, 0x1d, - 0xbd, 0xc6, 0xb4, 0x20, 0xed, 0xfd, 0xe8, 0x88, - 0x10, 0xf6, 0x20, 0x2f, 0xae, 0x20, 0x30, 0xb2, - 0x90, 0xa7, 0xb0, 0x9e, 0x4c, 0x7f, 0xb3, 0xa9, - 0x8d, 0x20, 0xed, 0xfd, 0xce, 0x9d, 0xb3, 0xd0, - 0x08, 0x20, 0x0c, 0xfd, 0xa9, 0x15, 0x8d, 0x9d, - 0xb3, 0x60, 0xa0, 0x02, 0xa9, 0x00, 0x48, 0xa5, - 0x44, 0xd9, 0xa4, 0xb3, 0x90, 0x12, 0xf9, 0xa4, - 0xb3, 0x85, 0x44, 0xa5, 0x45, 0xe9, 0x00, 0x85, - 0x45, 0x68, 0x69, 0x00, 0x48, 0x4c, 0x47, 0xae, - 0x68, 0x09, 0xb0, 0x20, 0xed, 0xfd, 0x88, 0x10, - 0xdb, 0x60, 0x20, 0x08, 0xaf, 0xa0, 0x00, 0x8c, - 0xc5, 0xb5, 0xb1, 0x42, 0x99, 0xd1, 0xb5, 0xc8, - 0xc0, 0x2d, 0xd0, 0xf6, 0x18, 0x60, 0x20, 0x08, - 0xaf, 0xa0, 0x00, 0xb9, 0xd1, 0xb5, 0x91, 0x42, - 0xc8, 0xc0, 0x2d, 0xd0, 0xf6, 0x60, 0x20, 0xdc, - 0xab, 0xa9, 0x04, 0x20, 0x58, 0xb0, 0xad, 0xf9, - 0xb5, 0x49, 0xff, 0x8d, 0xc1, 0xb3, 0xa9, 0x11, - 0x8d, 0xeb, 0xb3, 0xa9, 0x01, 0x8d, 0xec, 0xb3, - 0xa2, 0x38, 0xa9, 0x00, 0x9d, 0xbb, 0xb3, 0xe8, - 0xd0, 0xfa, 0xa2, 0x0c, 0xe0, 0x8c, 0xf0, 0x14, - 0xa0, 0x03, 0xb9, 0xa0, 0xb3, 0x9d, 0xf3, 0xb3, - 0xe8, 0x88, 0x10, 0xf6, 0xe0, 0x44, 0xd0, 0xec, - 0xa2, 0x48, 0xd0, 0xe8, 0x20, 0xfb, 0xaf, 0xa2, - 0x00, 0x8a, 0x9d, 0xbb, 0xb4, 0xe8, 0xd0, 0xfa, - 0x20, 0x45, 0xb0, 0xa9, 0x11, 0xac, 0xf0, 0xb3, - 0x88, 0x88, 0x8d, 0xec, 0xb7, 0x8d, 0xbc, 0xb4, - 0x8c, 0xbd, 0xb4, 0xc8, 0x8c, 0xed, 0xb7, 0xa9, - 0x02, 0x20, 0x58, 0xb0, 0xac, 0xbd, 0xb4, 0x88, - 0x30, 0x05, 0xd0, 0xec, 0x98, 0xf0, 0xe6, 0x20, - - 0xc2, 0xb7, 0x20, 0x4a, 0xb7, 0x4c, 0x7f, 0xb3, - 0xa2, 0x00, 0xf0, 0x06, 0xa2, 0x02, 0xd0, 0x02, - 0xa2, 0x04, 0xbd, 0xc7, 0xb5, 0x85, 0x42, 0xbd, - 0xc8, 0xb5, 0x85, 0x43, 0x60, 0x2c, 0xd5, 0xb5, - 0x70, 0x01, 0x60, 0x20, 0xe4, 0xaf, 0xa9, 0x02, - 0x20, 0x52, 0xb0, 0xa9, 0xbf, 0x2d, 0xd5, 0xb5, - 0x8d, 0xd5, 0xb5, 0x60, 0xad, 0xd5, 0xb5, 0x30, - 0x01, 0x60, 0x20, 0x4b, 0xaf, 0xa9, 0x02, 0x20, - 0x52, 0xb0, 0xa9, 0x7f, 0x2d, 0xd5, 0xb5, 0x8d, - 0xd5, 0xb5, 0x60, 0xad, 0xc9, 0xb5, 0x8d, 0xf0, - 0xb7, 0xad, 0xca, 0xb5, 0x8d, 0xf1, 0xb7, 0xae, - 0xd3, 0xb5, 0xac, 0xd4, 0xb5, 0x60, 0x08, 0x20, - 0x34, 0xaf, 0x20, 0x4b, 0xaf, 0x20, 0x0c, 0xaf, - 0x28, 0xb0, 0x09, 0xae, 0xd1, 0xb5, 0xac, 0xd2, - 0xb5, 0x4c, 0xb5, 0xaf, 0xa0, 0x01, 0xb1, 0x42, - 0xf0, 0x08, 0xaa, 0xc8, 0xb1, 0x42, 0xa8, 0x4c, - 0xb5, 0xaf, 0xad, 0xbb, 0xb5, 0xc9, 0x04, 0xf0, - 0x02, 0x38, 0x60, 0x20, 0x44, 0xb2, 0xa0, 0x02, - 0x91, 0x42, 0x48, 0x88, 0xad, 0xf1, 0xb5, 0x91, - 0x42, 0x48, 0x20, 0x3a, 0xaf, 0x20, 0xd6, 0xb7, - 0xa0, 0x05, 0xad, 0xde, 0xb5, 0x91, 0x42, 0xc8, - 0xad, 0xdf, 0xb5, 0x91, 0x42, 0x68, 0xaa, 0x68, - 0xa8, 0xa9, 0x02, 0xd0, 0x02, 0xa9, 0x01, 0x8e, - 0xd3, 0xb5, 0x8c, 0xd4, 0xb5, 0x20, 0x52, 0xb0, - 0xa0, 0x05, 0xb1, 0x42, 0x8d, 0xdc, 0xb5, 0x18, - 0x6d, 0xda, 0xb5, 0x8d, 0xde, 0xb5, 0xc8, 0xb1, - 0x42, 0x8d, 0xdd, 0xb5, 0x6d, 0xdb, 0xb5, 0x8d, - 0xdf, 0xb5, 0x18, 0x60, 0x20, 0xe4, 0xaf, 0xa9, - 0x01, 0x4c, 0x52, 0xb0, 0xac, 0xcb, 0xb5, 0xad, - 0xcc, 0xb5, 0x8c, 0xf0, 0xb7, 0x8d, 0xf1, 0xb7, - 0xae, 0xd6, 0xb5, 0xac, 0xd7, 0xb5, 0x60, 0xa9, - 0x01, 0xd0, 0x02, 0xa9, 0x02, 0xac, 0xc3, 0xaa, - - 0x8c, 0xf0, 0xb7, 0xac, 0xc4, 0xaa, 0x8c, 0xf1, - 0xb7, 0xae, 0xfa, 0xb5, 0xa0, 0x00, 0x4c, 0x52, - 0xb0, 0x08, 0x20, 0x45, 0xb0, 0x28, 0xb0, 0x08, - 0xac, 0xbd, 0xb3, 0xae, 0xbc, 0xb3, 0xd0, 0x0a, - 0xae, 0xbc, 0xb4, 0xd0, 0x02, 0x38, 0x60, 0xac, - 0xbd, 0xb4, 0x8e, 0x97, 0xb3, 0x8c, 0x98, 0xb3, - 0xa9, 0x01, 0x20, 0x52, 0xb0, 0x18, 0x60, 0x20, - 0x45, 0xb0, 0xae, 0x97, 0xb3, 0xac, 0x98, 0xb3, - 0xa9, 0x02, 0x4c, 0x52, 0xb0, 0xad, 0xc5, 0xaa, - 0x8d, 0xf0, 0xb7, 0xad, 0xc6, 0xaa, 0x8d, 0xf1, - 0xb7, 0x60, 0x8e, 0xec, 0xb7, 0x8c, 0xed, 0xb7, - 0x8d, 0xf4, 0xb7, 0xc9, 0x02, 0xd0, 0x06, 0x0d, - 0xd5, 0xb5, 0x8d, 0xd5, 0xb5, 0xad, 0xf9, 0xb5, - 0x49, 0xff, 0x8d, 0xeb, 0xb7, 0xad, 0xf7, 0xb5, - 0x8d, 0xe9, 0xb7, 0xad, 0xf8, 0xb5, 0x8d, 0xea, - 0xb7, 0xad, 0xe2, 0xb5, 0x8d, 0xf2, 0xb7, 0xad, - 0xe3, 0xb5, 0x8d, 0xf3, 0xb7, 0xa9, 0x01, 0x8d, - 0xe8, 0xb7, 0xac, 0xc1, 0xaa, 0xad, 0xc2, 0xaa, - 0x20, 0xb5, 0xb7, 0xad, 0xf6, 0xb7, 0x8d, 0xbf, - 0xb5, 0xa9, 0xff, 0x8d, 0xeb, 0xb7, 0xb0, 0x01, - 0x60, 0xad, 0xf5, 0xb7, 0xa0, 0x07, 0xc9, 0x20, - 0xf0, 0x08, 0xa0, 0x04, 0xc9, 0x10, 0xf0, 0x02, - 0xa0, 0x08, 0x98, 0x4c, 0x85, 0xb3, 0xad, 0xe4, - 0xb5, 0xcd, 0xe0, 0xb5, 0xd0, 0x08, 0xad, 0xe5, - 0xb5, 0xcd, 0xe1, 0xb5, 0xf0, 0x66, 0x20, 0x1d, - 0xaf, 0xad, 0xe5, 0xb5, 0xcd, 0xdd, 0xb5, 0x90, - 0x1c, 0xd0, 0x08, 0xad, 0xe4, 0xb5, 0xcd, 0xdc, - 0xb5, 0x90, 0x12, 0xad, 0xe5, 0xb5, 0xcd, 0xdf, - 0xb5, 0x90, 0x10, 0xd0, 0x08, 0xad, 0xe4, 0xb5, - 0xcd, 0xde, 0xb5, 0x90, 0x06, 0x20, 0x5e, 0xaf, - 0x90, 0xd7, 0x60, 0x38, 0xad, 0xe4, 0xb5, 0xed, - 0xdc, 0xb5, 0x0a, 0x69, 0x0c, 0xa8, 0x20, 0x0c, - - 0xaf, 0xb1, 0x42, 0xd0, 0x0f, 0xad, 0xbb, 0xb5, - 0xc9, 0x04, 0xf0, 0x02, 0x38, 0x60, 0x20, 0x34, - 0xb1, 0x4c, 0x20, 0xb1, 0x8d, 0xd6, 0xb5, 0xc8, - 0xb1, 0x42, 0x8d, 0xd7, 0xb5, 0x20, 0xdc, 0xaf, - 0xad, 0xe4, 0xb5, 0x8d, 0xe0, 0xb5, 0xad, 0xe5, - 0xb5, 0x8d, 0xe1, 0xb5, 0x20, 0x10, 0xaf, 0xac, - 0xe6, 0xb5, 0x18, 0x60, 0x8c, 0x9d, 0xb3, 0x20, - 0x44, 0xb2, 0xac, 0x9d, 0xb3, 0xc8, 0x91, 0x42, - 0x8d, 0xd7, 0xb5, 0x88, 0xad, 0xf1, 0xb5, 0x91, - 0x42, 0x8d, 0xd6, 0xb5, 0x20, 0x10, 0xaf, 0x20, - 0xd6, 0xb7, 0xa9, 0xc0, 0x0d, 0xd5, 0xb5, 0x8d, - 0xd5, 0xb5, 0x60, 0xae, 0xea, 0xb5, 0x8e, 0xbd, - 0xb5, 0xae, 0xeb, 0xb5, 0x8e, 0xbe, 0xb5, 0xae, - 0xec, 0xb5, 0xac, 0xed, 0xb5, 0x8e, 0xbf, 0xb5, - 0x8c, 0xc0, 0xb5, 0xe8, 0xd0, 0x01, 0xc8, 0xcc, - 0xe9, 0xb5, 0xd0, 0x11, 0xec, 0xe8, 0xb5, 0xd0, - 0x0c, 0xa2, 0x00, 0xa0, 0x00, 0xee, 0xea, 0xb5, - 0xd0, 0x03, 0xee, 0xeb, 0xb5, 0x8e, 0xec, 0xb5, - 0x8c, 0xed, 0xb5, 0x60, 0xee, 0xe6, 0xb5, 0xd0, - 0x08, 0xee, 0xe4, 0xb5, 0xd0, 0x03, 0xee, 0xe5, - 0xb5, 0x60, 0xac, 0xc3, 0xb5, 0xae, 0xc4, 0xb5, - 0x84, 0x42, 0x86, 0x43, 0xee, 0xc3, 0xb5, 0xd0, - 0x03, 0xee, 0xc4, 0xb5, 0x60, 0xac, 0xc1, 0xb5, - 0xd0, 0x08, 0xae, 0xc2, 0xb5, 0xf0, 0x07, 0xce, - 0xc2, 0xb5, 0xce, 0xc1, 0xb5, 0x60, 0x4c, 0x7f, - 0xb3, 0x20, 0xf7, 0xaf, 0xad, 0xc3, 0xb5, 0x85, - 0x42, 0xad, 0xc4, 0xb5, 0x85, 0x43, 0xa9, 0x01, - 0x8d, 0x9d, 0xb3, 0xa9, 0x00, 0x8d, 0xd8, 0xb5, - 0x18, 0xee, 0xd8, 0xb5, 0x20, 0x11, 0xb0, 0xb0, - 0x51, 0xa2, 0x00, 0x8e, 0x9c, 0xb3, 0xbd, 0xc6, - 0xb4, 0xf0, 0x1f, 0x30, 0x22, 0xa0, 0x00, 0xe8, - 0xe8, 0xe8, 0xb1, 0x42, 0xdd, 0xc6, 0xb4, 0xd0, - - 0x0a, 0xc8, 0xc0, 0x1e, 0xd0, 0xf3, 0xae, 0x9c, - 0xb3, 0x18, 0x60, 0x20, 0x30, 0xb2, 0x90, 0xdb, - 0xb0, 0xcf, 0xac, 0x9d, 0xb3, 0xd0, 0xc1, 0xac, - 0x9d, 0xb3, 0xd0, 0xef, 0xa0, 0x00, 0xe8, 0xe8, - 0xe8, 0xb1, 0x42, 0x9d, 0xc6, 0xb4, 0xc8, 0xc0, - 0x1e, 0xd0, 0xf5, 0xae, 0x9c, 0xb3, 0x38, 0x60, - 0x18, 0xad, 0x9c, 0xb3, 0x69, 0x23, 0xaa, 0xe0, - 0xf5, 0x60, 0xa9, 0x00, 0xac, 0x9d, 0xb3, 0xd0, - 0x97, 0x4c, 0x77, 0xb3, 0xad, 0xf1, 0xb5, 0xf0, - 0x21, 0xce, 0xf0, 0xb5, 0x30, 0x17, 0x18, 0xa2, - 0x04, 0x3e, 0xf1, 0xb5, 0xca, 0xd0, 0xfa, 0x90, - 0xf0, 0xee, 0xee, 0xb5, 0xd0, 0x03, 0xee, 0xef, - 0xb5, 0xad, 0xf0, 0xb5, 0x60, 0xa9, 0x00, 0x8d, - 0xf1, 0xb5, 0xa9, 0x00, 0x8d, 0x9e, 0xb3, 0x20, - 0xf7, 0xaf, 0x18, 0xad, 0xeb, 0xb3, 0x6d, 0xec, - 0xb3, 0xf0, 0x09, 0xcd, 0xef, 0xb3, 0x90, 0x14, - 0xa9, 0xff, 0xd0, 0x0a, 0xad, 0x9e, 0xb3, 0xd0, - 0x37, 0xa9, 0x01, 0x8d, 0x9e, 0xb3, 0x8d, 0xec, - 0xb3, 0x18, 0x69, 0x11, 0x8d, 0xeb, 0xb3, 0x8d, - 0xf1, 0xb5, 0xa8, 0x0a, 0x0a, 0xa8, 0xa2, 0x04, - 0x18, 0xb9, 0xf6, 0xb3, 0x9d, 0xf1, 0xb5, 0xf0, - 0x06, 0x38, 0xa9, 0x00, 0x99, 0xf6, 0xb3, 0x88, - 0xca, 0xd0, 0xee, 0x90, 0xbd, 0x20, 0xfb, 0xaf, - 0xad, 0xf0, 0xb3, 0x8d, 0xf0, 0xb5, 0xd0, 0x89, - 0x4c, 0x77, 0xb3, 0xad, 0xf1, 0xb5, 0xd0, 0x01, - 0x60, 0x48, 0x20, 0xf7, 0xaf, 0xac, 0xf0, 0xb5, - 0x68, 0x18, 0x20, 0xdd, 0xb2, 0xa9, 0x00, 0x8d, - 0xf1, 0xb5, 0x4c, 0xfb, 0xaf, 0xa2, 0xfc, 0x7e, - 0xf6, 0xb4, 0xe8, 0xd0, 0xfa, 0xc8, 0xcc, 0xf0, - 0xb3, 0xd0, 0xf2, 0x0a, 0x0a, 0xa8, 0xf0, 0x0f, - 0xa2, 0x04, 0xbd, 0xf1, 0xb5, 0x19, 0xf6, 0xb3, - 0x99, 0xf6, 0xb3, 0x88, 0xca, 0xd0, 0xf3, 0x60, - - 0xad, 0xbd, 0xb5, 0x8d, 0xe6, 0xb5, 0x8d, 0xea, - 0xb5, 0xad, 0xbe, 0xb5, 0x8d, 0xe4, 0xb5, 0x8d, - 0xeb, 0xb5, 0xa9, 0x00, 0x8d, 0xe5, 0xb5, 0xa0, - 0x10, 0xaa, 0xad, 0xe6, 0xb5, 0x4a, 0xb0, 0x03, - 0x8a, 0x90, 0x0e, 0x18, 0xad, 0xe5, 0xb5, 0x6d, - 0xe8, 0xb5, 0x8d, 0xe5, 0xb5, 0x8a, 0x6d, 0xe9, - 0xb5, 0x6a, 0x6e, 0xe5, 0xb5, 0x6e, 0xe4, 0xb5, - 0x6e, 0xe6, 0xb5, 0x88, 0xd0, 0xdb, 0xad, 0xbf, - 0xb5, 0x8d, 0xec, 0xb5, 0x6d, 0xe6, 0xb5, 0x8d, - 0xe6, 0xb5, 0xad, 0xc0, 0xb5, 0x8d, 0xed, 0xb5, - 0x6d, 0xe4, 0xb5, 0x8d, 0xe4, 0xb5, 0xa9, 0x00, - 0x6d, 0xe5, 0xb5, 0x8d, 0xe5, 0xb5, 0x60, 0xa9, - 0x01, 0xd0, 0x22, 0xa9, 0x02, 0xd0, 0x1e, 0xa9, - 0x03, 0xd0, 0x1a, 0xa9, 0x04, 0xd0, 0x16, 0xa9, - 0x05, 0xd0, 0x12, 0xa9, 0x06, 0xd0, 0x0e, 0x4c, - 0xed, 0xbf, 0xea, 0xa9, 0x0a, 0xd0, 0x06, 0xad, - 0xc5, 0xb5, 0x18, 0x90, 0x01, 0x38, 0x08, 0x8d, - 0xc5, 0xb5, 0xa9, 0x00, 0x85, 0x48, 0x20, 0x7e, - 0xae, 0x28, 0xae, 0x9b, 0xb3, 0x9a, 0x60, 0x11, - 0x0d, 0x00, 0x00, 0xee, 0x69, 0x01, 0x00, 0x00, - 0x00, 0x00, 0xff, 0xff, 0x01, 0x0a, 0x64, 0xd4, - 0xc9, 0xc1, 0xc2, 0xd3, 0xd2, 0xc1, 0xc2, 0xa0, - 0xc5, 0xcd, 0xd5, 0xcc, 0xcf, 0xd6, 0xa0, 0xcb, - 0xd3, 0xc9, 0xc4, 0x04, 0x11, 0x0f, 0x03, 0x00, - 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x23, - 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, - 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x9d, 0x01, 0x00, 0xfe, - 0x02, 0x06, 0x00, 0x75, 0xaa, 0x00, 0x00, 0x00, - 0x98, 0x00, 0x97, 0x00, 0x96, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, - 0x02, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - - -/* - * Three 13-sector tracks, in DOS order (i.e. track 0 sector 0 followed - * by track 0 sector 1). - * - * Obtained from the DOS 3.2.1 system master. The last two sectors of - * track 2 were unreadable, and have been zeroed out here. - */ -/*static*/ const uint8_t DiskFSDOS33::gDOS32Tracks[13 * 3 * 256] = { - 0xf0, 0x4a, 0x99, 0xff, 0xff, 0x03, 0x3c, 0xad, - 0xff, 0xff, 0xff, 0x26, 0xb3, 0xff, 0xff, 0x4d, - 0x4a, 0x10, 0xff, 0xff, 0x3d, 0x4a, 0xca, 0xff, - 0xff, 0xa5, 0x4a, 0xc8, 0xff, 0xff, 0x03, 0x4a, - 0x40, 0xff, 0xff, 0x46, 0x08, 0x91, 0xff, 0xff, - 0x20, 0x33, 0x09, 0xff, 0xff, 0x03, 0xbd, 0xcc, - 0xff, 0xff, 0x43, 0xc8, 0x1d, 0xff, 0xff, 0x20, - 0x40, 0x07, 0xff, 0xff, 0x3e, 0x91, 0x29, 0xff, - 0xff, 0x85, 0x09, 0x3c, 0xff, 0xff, 0x5d, 0x00, - 0xa5, 0xff, 0xff, 0xa9, 0x1d, 0xc8, 0xff, 0xff, - 0x3f, 0x4a, 0x40, 0xff, 0xff, 0x85, 0x2a, 0x91, - 0xff, 0xff, 0xc0, 0x85, 0x09, 0xff, 0xff, 0x09, - 0x4a, 0x99, 0xff, 0xff, 0x4a, 0x3c, 0x1d, 0xff, - 0xff, 0x4a, 0x85, 0x07, 0xff, 0xff, 0x4a, 0x4a, - 0x29, 0xff, 0xff, 0x4a, 0x4a, 0x2a, 0xff, 0xff, - 0x8a, 0x4a, 0xa5, 0xff, 0xff, 0x40, 0x08, 0xc8, - 0xff, 0xff, 0x84, 0x00, 0x40, 0xff, 0xff, 0x41, - 0xbd, 0x91, 0xff, 0xff, 0x85, 0x00, 0x09, 0xff, - 0xff, 0x03, 0xa0, 0x66, 0xff, 0xff, 0xcc, 0x32, - 0x1d, 0xff, 0xff, 0xad, 0xa2, 0x2a, 0xff, 0xff, - 0x27, 0x00, 0x26, 0xff, 0xff, 0x85, 0x3e, 0x4a, - 0xff, 0xff, 0x09, 0x6c, 0x3c, 0xff, 0xff, 0xa9, - 0x3f, 0x26, 0xff, 0xff, 0x2b, 0xe6, 0x4a, 0xff, - 0xff, 0xa6, 0x3f, 0x4a, 0xff, 0xff, 0xf4, 0x85, - 0x4a, 0xff, 0xff, 0xd0, 0x03, 0x4a, 0x60, 0xff, - 0xc8, 0xcc, 0x08, 0x2b, 0xff, 0x08, 0xad, 0x66, - 0xa6, 0xff, 0x00, 0x3e, 0xbd, 0x40, 0xff, 0x99, - 0x85, 0xc8, 0x91, 0xff, 0x0a, 0xed, 0x40, 0x09, - 0xff, 0x0a, 0xd0, 0x91, 0xff, 0xff, 0x0a, 0x3d, - 0x09, 0x0d, 0xff, 0x08, 0xe6, 0x33, 0x4a, 0xff, - 0x00, 0x41, 0x1d, 0x4a, 0xff, 0xb9, 0xe6, 0x2a, - 0x4a, 0xff, 0x99, 0x06, 0x26, 0x08, 0x36, 0x48, - - 0x8e, 0xe9, 0x37, 0x8e, 0xf7, 0x37, 0xa9, 0x01, - 0x8d, 0xf8, 0x37, 0x8d, 0xea, 0x37, 0xad, 0xe0, - 0x37, 0x8d, 0xe1, 0x37, 0xa9, 0x00, 0x8d, 0xec, - 0x37, 0xad, 0xe2, 0x37, 0x8d, 0xed, 0x37, 0xad, - 0xe3, 0x37, 0x8d, 0xf1, 0x37, 0xa9, 0x01, 0x8d, - 0xf4, 0x37, 0x8a, 0x4a, 0x4a, 0x4a, 0x4a, 0xaa, - 0xa9, 0x00, 0x9d, 0xf8, 0x04, 0x9d, 0x78, 0x04, - 0x20, 0x93, 0x37, 0xa2, 0xff, 0x9a, 0x8e, 0xeb, - 0x37, 0x20, 0x93, 0xfe, 0x20, 0x89, 0xfe, 0x4c, - 0x03, 0x1b, 0xad, 0xf1, 0x37, 0x8d, 0xe3, 0x37, - 0x38, 0xad, 0xe7, 0x37, 0xed, 0xe3, 0x37, 0x8d, - 0xe0, 0x37, 0xa9, 0x00, 0x8d, 0xec, 0x37, 0x8d, - 0xed, 0x37, 0x8d, 0xf0, 0x37, 0xad, 0xe7, 0x37, - 0x8d, 0xf1, 0x37, 0x8d, 0xfe, 0x36, 0xa9, 0x0a, - 0x8d, 0xe1, 0x37, 0x8d, 0xe2, 0x37, 0xa9, 0x48, - 0x8d, 0xff, 0x36, 0xa9, 0x02, 0x8d, 0xf4, 0x37, - 0x20, 0x93, 0x37, 0xad, 0xe3, 0x37, 0x8d, 0xf1, - 0x37, 0xad, 0xe0, 0x37, 0x8d, 0xe1, 0x37, 0x20, - 0x93, 0x37, 0x60, 0xad, 0xe5, 0x37, 0xac, 0xe4, - 0x37, 0x20, 0xb5, 0x37, 0xac, 0xed, 0x37, 0xc8, - 0xc0, 0x0d, 0xd0, 0x05, 0xa0, 0x00, 0xee, 0xec, - 0x37, 0x8c, 0xed, 0x37, 0xee, 0xf1, 0x37, 0xce, - 0xe1, 0x37, 0xd0, 0xdf, 0x60, 0x08, 0x78, 0x20, - 0x00, 0x3d, 0xb0, 0x03, 0x28, 0x18, 0x60, 0x28, - 0x38, 0x60, 0xad, 0xbc, 0x35, 0x8d, 0xf1, 0x37, - 0xa9, 0x00, 0x8d, 0xf0, 0x37, 0xad, 0xf9, 0x35, - 0x49, 0xff, 0x8d, 0xeb, 0x37, 0x60, 0xa9, 0x00, - 0xa8, 0x91, 0x42, 0xc8, 0xd0, 0xfb, 0x60, 0x00, - 0x1b, 0x09, 0x0a, 0x1b, 0xe8, 0x37, 0x00, 0x36, - 0x01, 0x60, 0x01, 0x00, 0x00, 0x01, 0xfb, 0x37, - 0x00, 0x37, 0x00, 0x00, 0x02, 0x07, 0xfe, 0x60, - 0x01, 0x00, 0x00, 0x00, 0x01, 0xef, 0xd8, 0x00, - - 0xa2, 0x32, 0xa0, 0x00, 0xb1, 0x3e, 0x85, 0x26, - 0x4a, 0x4a, 0x4a, 0x9d, 0x00, 0x3b, 0xc8, 0xb1, - 0x3e, 0x85, 0x27, 0x4a, 0x4a, 0x4a, 0x9d, 0x33, - 0x3b, 0xc8, 0xb1, 0x3e, 0x85, 0x2a, 0x4a, 0x4a, - 0x4a, 0x9d, 0x66, 0x3b, 0xc8, 0xb1, 0x3e, 0x4a, - 0x26, 0x2a, 0x4a, 0x26, 0x27, 0x4a, 0x26, 0x26, - 0x9d, 0x99, 0x3b, 0xc8, 0xb1, 0x3e, 0x4a, 0x26, - 0x2a, 0x4a, 0x26, 0x27, 0x4a, 0x9d, 0xcc, 0x3b, - 0xa5, 0x26, 0x2a, 0x29, 0x1f, 0x9d, 0x00, 0x3c, - 0xa5, 0x27, 0x29, 0x1f, 0x9d, 0x33, 0x3c, 0xa5, - 0x2a, 0x29, 0x1f, 0x9d, 0x66, 0x3c, 0xc8, 0xca, - 0x10, 0xaa, 0xb1, 0x3e, 0xaa, 0x29, 0x07, 0x8d, - 0x99, 0x3c, 0x8a, 0x4a, 0x4a, 0x4a, 0x8d, 0xff, - 0x3b, 0x60, 0x38, 0xbd, 0x8d, 0xc0, 0xbd, 0x8e, - 0xc0, 0x30, 0x7c, 0x86, 0x27, 0x8e, 0x78, 0x06, - 0xad, 0x00, 0x3c, 0x85, 0x26, 0xa9, 0xff, 0x9d, - 0x8f, 0xc0, 0x1d, 0x8c, 0xc0, 0x48, 0x68, 0xea, - 0xa0, 0x0a, 0x05, 0x26, 0x20, 0xf4, 0x38, 0x88, - 0xd0, 0xf8, 0xa9, 0xd5, 0x20, 0xf3, 0x38, 0xa9, - 0xaa, 0x20, 0xf3, 0x38, 0xa9, 0xad, 0x20, 0xf3, - 0x38, 0x98, 0xa0, 0x9a, 0xd0, 0x03, 0xb9, 0x00, - 0x3c, 0x59, 0xff, 0x3b, 0xaa, 0xbd, 0x9a, 0x3c, - 0xa6, 0x27, 0x9d, 0x8d, 0xc0, 0xbd, 0x8c, 0xc0, - 0x88, 0xd0, 0xeb, 0xa5, 0x26, 0xea, 0x59, 0x00, - 0x3b, 0xaa, 0xbd, 0x9a, 0x3c, 0xae, 0x78, 0x06, - 0x9d, 0x8d, 0xc0, 0xbd, 0x8c, 0xc0, 0xb9, 0x00, - 0x3b, 0xc8, 0xd0, 0xea, 0xaa, 0xbd, 0x9a, 0x3c, - 0xa6, 0x27, 0x20, 0xf6, 0x38, 0xa9, 0xde, 0x20, - 0xf3, 0x38, 0xa9, 0xaa, 0x20, 0xf3, 0x38, 0xa9, - 0xeb, 0x20, 0xf3, 0x38, 0xbd, 0x8e, 0xc0, 0xbd, - 0x8c, 0xc0, 0x60, 0x18, 0x48, 0x68, 0x9d, 0x8d, - 0xc0, 0x1d, 0x8c, 0xc0, 0x60, 0xa0, 0x20, 0x88, - - 0xf0, 0x61, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x49, - 0xd5, 0xd0, 0xf4, 0xea, 0xbd, 0x8c, 0xc0, 0x10, - 0xfb, 0xc9, 0xaa, 0xd0, 0xf2, 0xa0, 0x9a, 0xbd, - 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xad, 0xd0, 0xe7, - 0xa9, 0x00, 0x88, 0x84, 0x26, 0xbc, 0x8c, 0xc0, - 0x10, 0xfb, 0x59, 0x00, 0x3a, 0xa4, 0x26, 0x99, - 0x00, 0x3c, 0xd0, 0xee, 0x84, 0x26, 0xbc, 0x8c, - 0xc0, 0x10, 0xfb, 0x59, 0x00, 0x3a, 0xa4, 0x26, - 0x99, 0x00, 0x3b, 0xc8, 0xd0, 0xee, 0xbc, 0x8c, - 0xc0, 0x10, 0xfb, 0xd9, 0x00, 0x3a, 0xd0, 0x13, - 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xde, 0xd0, - 0x0a, 0xea, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, - 0xaa, 0xf0, 0x5c, 0x38, 0x60, 0xa0, 0xf8, 0x84, - 0x26, 0xc8, 0xd0, 0x04, 0xe6, 0x26, 0xf0, 0xf3, - 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xd5, 0xd0, - 0xf0, 0xea, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, - 0xaa, 0xd0, 0xf2, 0xa0, 0x03, 0xbd, 0x8c, 0xc0, - 0x10, 0xfb, 0xc9, 0xb5, 0xd0, 0xe7, 0xa9, 0x00, - 0x85, 0x27, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x2a, - 0x85, 0x26, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x25, - 0x26, 0x99, 0x2c, 0x00, 0x45, 0x27, 0x88, 0x10, - 0xe7, 0xa8, 0xd0, 0xb7, 0xbd, 0x8c, 0xc0, 0x10, - 0xfb, 0xc9, 0xde, 0xd0, 0xae, 0xea, 0xbd, 0x8c, - 0xc0, 0x10, 0xfb, 0xc9, 0xaa, 0xd0, 0xa4, 0x18, - 0x60, 0xa2, 0x32, 0xa0, 0x00, 0xbd, 0x00, 0x3c, - 0x4a, 0x4a, 0x4a, 0x85, 0x27, 0x4a, 0x85, 0x26, - 0x4a, 0x1d, 0x00, 0x3b, 0x91, 0x3e, 0xc8, 0xbd, - 0x33, 0x3c, 0x4a, 0x4a, 0x4a, 0x4a, 0x26, 0x27, - 0x4a, 0x26, 0x26, 0x1d, 0x33, 0x3b, 0x91, 0x3e, - 0xc8, 0xbd, 0x66, 0x3c, 0x4a, 0x4a, 0x4a, 0x4a, - 0x26, 0x27, 0x4a, 0x26, 0x26, 0x1d, 0x66, 0x3b, - 0x91, 0x3e, 0xc8, 0xa5, 0x26, 0x29, 0x07, 0x1d, - - 0x99, 0x3b, 0x91, 0x3e, 0xc8, 0xa5, 0x27, 0x29, - 0x07, 0x1d, 0xcc, 0x3b, 0x91, 0x3e, 0xc8, 0xca, - 0x10, 0xb3, 0xad, 0x99, 0x3c, 0x4a, 0x4a, 0x4a, - 0x0d, 0xff, 0x3b, 0x91, 0x3e, 0x60, 0x86, 0x2b, - 0x85, 0x2a, 0xcd, 0x78, 0x04, 0xf0, 0x53, 0xa9, - 0x00, 0x85, 0x26, 0xad, 0x78, 0x04, 0x85, 0x27, - 0x38, 0xe5, 0x2a, 0xf0, 0x33, 0xb0, 0x07, 0x49, - 0xff, 0xee, 0x78, 0x04, 0x90, 0x05, 0x69, 0xfe, - 0xce, 0x78, 0x04, 0xc5, 0x26, 0x90, 0x02, 0xa5, - 0x26, 0xc9, 0x0c, 0xb0, 0x01, 0xa8, 0x38, 0x20, - 0x6c, 0x3a, 0xb9, 0x8c, 0x3a, 0x20, 0x7b, 0x3a, - 0xa5, 0x27, 0x18, 0x20, 0x6f, 0x3a, 0xb9, 0x98, - 0x3a, 0x20, 0x7b, 0x3a, 0xe6, 0x26, 0xd0, 0xc3, - 0x20, 0x7b, 0x3a, 0x18, 0xad, 0x78, 0x04, 0x29, - 0x03, 0x2a, 0x05, 0x2b, 0xaa, 0xbd, 0x80, 0xc0, - 0xa6, 0x2b, 0x60, 0xa2, 0x11, 0xca, 0xd0, 0xfd, - 0xe6, 0x46, 0xd0, 0x02, 0xe6, 0x47, 0x38, 0xe9, - 0x01, 0xd0, 0xf0, 0x60, 0x01, 0x30, 0x28, 0x24, - 0x20, 0x1e, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x70, 0x2c, 0x26, 0x22, 0x1f, 0x1e, 0x1d, 0x1c, - 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x08, 0x10, 0x18, - 0x02, 0x03, 0x04, 0x05, 0x06, 0x20, 0x28, 0x30, - 0x07, 0x09, 0x38, 0x40, 0x0a, 0x48, 0x50, 0x58, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x11, 0x12, 0x13, - 0x14, 0x15, 0x16, 0x17, 0x19, 0x1a, 0x1b, 0x1c, - 0x1d, 0x1e, 0x21, 0x22, 0x23, 0x24, 0x60, 0x68, - 0x25, 0x26, 0x70, 0x78, 0x27, 0x80, 0x88, 0x90, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x31, - 0x32, 0x33, 0x98, 0xa0, 0x34, 0xa8, 0xb0, 0xb8, - 0x35, 0x36, 0x37, 0x39, 0x3a, 0xc0, 0xc8, 0xd0, - 0x3b, 0x3c, 0xd8, 0xe0, 0x3e, 0xe8, 0xf0, 0xf8, - - 0x1b, 0x18, 0x06, 0x14, 0x05, 0x05, 0x04, 0x0d, - 0x04, 0x03, 0x02, 0x01, 0x0a, 0x01, 0x00, 0x03, - 0x00, 0x03, 0x03, 0x03, 0x03, 0x06, 0x00, 0x00, - 0x1a, 0x05, 0x1f, 0x1c, 0x13, 0x15, 0x05, 0x04, - 0x10, 0x01, 0x15, 0x12, 0x00, 0x0f, 0x12, 0x09, - 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x03, 0x1c, 0x19, 0x06, 0x06, 0x06, - 0x05, 0x10, 0x04, 0x04, 0x03, 0x02, 0x01, 0x0b, - 0x07, 0x04, 0x00, 0x00, 0x03, 0x03, 0x0e, 0x03, - 0x05, 0x1a, 0x1c, 0x1f, 0x0c, 0x04, 0x04, 0x07, - 0x17, 0x00, 0x0f, 0x18, 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, 0x07, 0x1a, - 0x06, 0x15, 0x06, 0x05, 0x11, 0x04, 0x04, 0x03, - 0x02, 0x02, 0x01, 0x08, 0x05, 0x00, 0x00, 0x03, - 0x03, 0x05, 0x03, 0x04, 0x1e, 0x08, 0x1c, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x00, 0x01, 0x01, - 0x00, 0x1d, 0x07, 0x07, 0x16, 0x06, 0x05, 0x12, - 0x0e, 0x04, 0x03, 0x02, 0x02, 0x01, 0x01, 0x06, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x1e, 0x07, 0x07, 0x17, - 0x13, 0x05, 0x05, 0x0f, 0x0c, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x1f, - - 0x0c, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x14, 0x1d, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x18, 0x1e, 0x07, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x10, 0x15, 0x01, 0x00, - 0x00, 0x00, 0x1c, 0x1f, 0x19, 0x10, 0x0d, 0x1b, - 0x0f, 0x00, 0x14, 0x1e, 0x05, 0x01, 0x1c, 0x01, - 0x04, 0x03, 0x11, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x1d, 0x1e, 0x03, 0x0a, 0x04, - 0x01, 0x02, 0x00, 0x02, 0x04, 0x13, 0x19, 0x05, - 0x08, 0x08, 0x04, 0x00, 0x10, 0x15, 0x15, 0x0e, - 0x18, 0x0f, 0x0c, 0x0c, 0x04, 0x1c, 0x01, 0x14, - 0x01, 0x0c, 0x00, 0x00, 0x11, 0x02, 0x05, 0x00, - 0x1c, 0x10, 0x01, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x1f, - 0x00, 0x00, 0x00, 0x0d, 0x0a, 0x00, 0x02, 0x19, - 0x0c, 0x1a, 0x03, 0x0c, 0x0c, 0x00, 0x00, 0x10, - 0x0c, 0x01, 0x00, 0x00, 0x00, 0x02, 0x05, 0x12, - 0x10, 0x16, 0x00, 0x09, 0x09, 0x09, 0x0c, 0x05, - 0x11, 0x03, 0x00, 0x01, 0x00, 0x10, 0x00, 0x01, - 0x10, 0x07, 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, - 0xb7, 0xba, 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, - 0xda, 0xdb, 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, - 0xee, 0xef, 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, - 0xfe, 0xff, 0x1c, 0x1c, 0x1c, 0x00, 0x00, 0x00, - 0xa4, 0x2d, 0xb9, 0xd0, 0x3c, 0xa0, 0x05, 0x4c, - 0x0a, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x0a, 0x02, 0x07, 0x0c, 0x04, 0x09, - 0x01, 0x06, 0x0b, 0x03, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x84, 0x48, 0x85, 0x49, 0xa0, 0x02, 0x8c, 0xf8, - 0x06, 0xa0, 0x04, 0x8c, 0xf8, 0x04, 0xa0, 0x01, - 0xb1, 0x48, 0xaa, 0xa0, 0x0f, 0xd1, 0x48, 0xf0, - 0x1b, 0x8a, 0x48, 0xb1, 0x48, 0xaa, 0x68, 0x48, - 0x91, 0x48, 0xbd, 0x8e, 0xc0, 0xa0, 0x08, 0xbd, - 0x8c, 0xc0, 0xdd, 0x8c, 0xc0, 0xd0, 0xf6, 0x88, - 0xd0, 0xf8, 0x68, 0xaa, 0xbd, 0x8e, 0xc0, 0xbd, - 0x8c, 0xc0, 0xbd, 0x8c, 0xc0, 0x48, 0x68, 0x8e, - 0xf8, 0x05, 0xdd, 0x8c, 0xc0, 0x08, 0xbd, 0x89, - 0xc0, 0xa0, 0x06, 0xb1, 0x48, 0x99, 0x36, 0x00, - 0xc8, 0xc0, 0x0a, 0xd0, 0xf6, 0xa0, 0x03, 0xb1, - 0x3c, 0x85, 0x47, 0xa0, 0x02, 0xb1, 0x48, 0xa0, - 0x10, 0xd1, 0x48, 0xf0, 0x06, 0x91, 0x48, 0x28, - 0xa0, 0x00, 0x08, 0x6a, 0x90, 0x05, 0xbd, 0x8a, - 0xc0, 0xb0, 0x03, 0xbd, 0x8b, 0xc0, 0x66, 0x35, - 0x28, 0x08, 0xd0, 0x0b, 0xa0, 0x07, 0x20, 0x7f, - 0x3a, 0x88, 0xd0, 0xfa, 0xae, 0xf8, 0x05, 0xa0, - 0x04, 0xb1, 0x48, 0x20, 0x4b, 0x3e, 0x28, 0xd0, - 0x0d, 0xa0, 0x12, 0x88, 0xd0, 0xfd, 0xe6, 0x46, - 0xd0, 0xf7, 0xe6, 0x47, 0xd0, 0xf3, 0xa0, 0x0c, - 0xb1, 0x48, 0xf0, 0x5a, 0xc9, 0x04, 0xf0, 0x58, - 0x6a, 0x08, 0xb0, 0x03, 0x20, 0x00, 0x38, 0xa0, - 0x30, 0x8c, 0x78, 0x05, 0xae, 0xf8, 0x05, 0x20, - 0x65, 0x39, 0x90, 0x24, 0xce, 0x78, 0x05, 0x10, - 0xf3, 0xad, 0x78, 0x04, 0x48, 0xa9, 0x60, 0x20, - 0x86, 0x3e, 0xce, 0xf8, 0x06, 0xf0, 0x28, 0xa9, - 0x04, 0x8d, 0xf8, 0x04, 0xa9, 0x00, 0x20, 0x4b, - 0x3e, 0x68, 0x20, 0x4b, 0x3e, 0x4c, 0xaf, 0x3d, - 0xa4, 0x2e, 0xcc, 0x78, 0x04, 0xf0, 0x22, 0xad, - 0x78, 0x04, 0x48, 0x98, 0x20, 0x86, 0x3e, 0x68, - 0xce, 0xf8, 0x04, 0xd0, 0xe5, 0xf0, 0xca, 0x68, - 0xa9, 0x40, 0x28, 0x4c, 0x39, 0x3e, 0xf0, 0x37, - - 0xa0, 0x03, 0xb1, 0x48, 0x85, 0x2f, 0x4c, 0xa0, - 0x3e, 0xa0, 0x03, 0xb1, 0x48, 0x48, 0xa5, 0x2f, - 0xa0, 0x0e, 0x91, 0x48, 0x68, 0xf0, 0x08, 0xc5, - 0x2f, 0xf0, 0x04, 0xa9, 0x20, 0xd0, 0xdb, 0xa0, - 0x05, 0xa5, 0x2d, 0xd1, 0x48, 0xd0, 0x95, 0x28, - 0x90, 0x18, 0x20, 0xfd, 0x38, 0x08, 0xb0, 0x8c, - 0x28, 0x20, 0xc1, 0x39, 0xae, 0xf8, 0x05, 0x18, - 0x24, 0x38, 0xa0, 0x0d, 0x91, 0x48, 0xbd, 0x88, - 0xc0, 0x60, 0x20, 0x6a, 0x38, 0x90, 0xf0, 0xa9, - 0x10, 0xb0, 0xee, 0x48, 0xa0, 0x01, 0xb1, 0x3c, - 0x6a, 0x68, 0x90, 0x08, 0x0a, 0x20, 0x5c, 0x3e, - 0x4e, 0x78, 0x04, 0x60, 0x85, 0x2e, 0x20, 0x7f, - 0x3e, 0xb9, 0x78, 0x04, 0x24, 0x35, 0x30, 0x03, - 0xb9, 0xf8, 0x04, 0x8d, 0x78, 0x04, 0xa5, 0x2e, - 0x24, 0x35, 0x30, 0x05, 0x99, 0xf8, 0x04, 0x10, - 0x03, 0x99, 0x78, 0x04, 0x4c, 0x1e, 0x3a, 0x8a, - 0x4a, 0x4a, 0x4a, 0x4a, 0xa8, 0x60, 0x48, 0xa0, - 0x02, 0xb1, 0x48, 0x6a, 0x66, 0x35, 0x20, 0x7f, - 0x3e, 0x68, 0x0a, 0x24, 0x35, 0x30, 0x05, 0x99, - 0xf8, 0x04, 0x10, 0x03, 0x99, 0x78, 0x04, 0x60, - 0xa9, 0x80, 0x8d, 0x78, 0x04, 0xa9, 0x00, 0x85, - 0x41, 0x20, 0x1e, 0x3a, 0xa9, 0xaa, 0x85, 0x4a, - 0xa0, 0x50, 0x84, 0x47, 0xa9, 0x27, 0x85, 0x4b, - 0xbd, 0x8d, 0xc0, 0xbd, 0x8e, 0xc0, 0xa9, 0xff, - 0x9d, 0x8f, 0xc0, 0xdd, 0x8c, 0xc0, 0x24, 0x00, - 0x88, 0xf0, 0x0f, 0x48, 0x68, 0xea, 0x48, 0x68, - 0xea, 0xea, 0x9d, 0x8d, 0xc0, 0xdd, 0x8c, 0xc0, - 0xb0, 0xee, 0xc6, 0x4b, 0xd0, 0xf0, 0xa4, 0x47, - 0xea, 0xea, 0xd0, 0x06, 0x48, 0x68, 0x48, 0x68, - 0xc1, 0x00, 0xea, 0x9d, 0x8d, 0xc0, 0xdd, 0x8c, - 0xc0, 0x88, 0xd0, 0xf0, 0xa9, 0xd5, 0x20, 0xcc, - 0x3f, 0xa9, 0xaa, 0x20, 0xcd, 0x3f, 0xa9, 0xb5, - - 0x20, 0xcd, 0x3f, 0xa5, 0x2f, 0x20, 0xbd, 0x3f, - 0xa5, 0x41, 0x20, 0xbd, 0x3f, 0xa5, 0x4b, 0x20, - 0xbd, 0x3f, 0xa5, 0x2f, 0x45, 0x41, 0x45, 0x4b, - 0x48, 0x4a, 0x05, 0x4a, 0x9d, 0x8d, 0xc0, 0xdd, - 0x8c, 0xc0, 0x68, 0x09, 0xaa, 0x20, 0xcc, 0x3f, - 0xa9, 0xde, 0x20, 0xcd, 0x3f, 0xa9, 0xaa, 0x20, - 0xcd, 0x3f, 0xa9, 0xeb, 0x20, 0xcd, 0x3f, 0xa9, - 0xff, 0x20, 0xcd, 0x3f, 0xa0, 0x02, 0x84, 0x46, - 0xa0, 0xad, 0xd0, 0x06, 0x88, 0xf0, 0x0d, 0x48, - 0x68, 0xea, 0x48, 0x68, 0x9d, 0x8d, 0xc0, 0xdd, - 0x8c, 0xc0, 0xb0, 0xf0, 0xc6, 0x46, 0xd0, 0xf2, - 0xa4, 0x47, 0x18, 0x24, 0x00, 0x9d, 0x8d, 0xc0, - 0xbd, 0x8c, 0xc0, 0xa5, 0x4b, 0x69, 0x0a, 0x85, - 0x4b, 0xe9, 0x0c, 0xf0, 0x0a, 0xb0, 0x01, 0x2c, - 0x85, 0x4b, 0xa9, 0xff, 0x4c, 0xeb, 0x3e, 0x48, - 0x68, 0xa4, 0x47, 0xbd, 0x8d, 0xc0, 0xbd, 0x8e, - 0xc0, 0x30, 0x32, 0x88, 0x48, 0x68, 0x48, 0x68, - 0x48, 0x68, 0x88, 0xd0, 0xf7, 0x20, 0x65, 0x39, - 0xb0, 0x04, 0xa5, 0x2d, 0xf0, 0x0a, 0xa4, 0x47, - 0x88, 0xc0, 0x10, 0x90, 0x18, 0x4c, 0xb2, 0x3e, - 0xe6, 0x41, 0xa5, 0x41, 0xc9, 0x23, 0xb0, 0x12, - 0x0a, 0x20, 0x1e, 0x3a, 0xa4, 0x47, 0xc8, 0xc8, - 0x84, 0x47, 0x4c, 0xb2, 0x3e, 0xa9, 0x40, 0x4c, - 0x39, 0x3e, 0x4c, 0x37, 0x3e, 0x48, 0x4a, 0x05, - 0x4a, 0x9d, 0x8d, 0xc0, 0xdd, 0x8c, 0xc0, 0x68, - 0xc1, 0x00, 0x09, 0xaa, 0xea, 0x48, 0x68, 0xea, - 0x9d, 0x8d, 0xc0, 0xdd, 0x8c, 0xc0, 0x60, 0x01, - 0x60, 0x4c, 0xdd, 0x25, 0x8d, 0x63, 0x2a, 0x8d, - 0x70, 0x2a, 0x8d, 0x71, 0x2a, 0x60, 0x20, 0x5b, - 0x27, 0x8c, 0xb7, 0x2a, 0x60, 0x20, 0x7e, 0x2e, - 0xae, 0x9b, 0x33, 0x9a, 0x20, 0x16, 0x23, 0xba, - 0x8e, 0x9b, 0x33, 0xa9, 0x09, 0x4c, 0x85, 0x33, - - 0x4c, 0x84, 0x1d, 0xa9, 0xbf, 0x85, 0x41, 0xa2, - 0x00, 0x86, 0x40, 0xa0, 0x00, 0xa1, 0x40, 0x85, - 0x26, 0x98, 0x45, 0x26, 0x85, 0x26, 0x98, 0x41, - 0x40, 0x81, 0x40, 0xc5, 0x26, 0xd0, 0x05, 0xc8, - 0xd0, 0xef, 0xf0, 0x04, 0xc6, 0x41, 0xd0, 0xe3, - 0xa5, 0x41, 0x29, 0xdf, 0x85, 0x43, 0x86, 0x42, - 0xa1, 0x42, 0x48, 0x85, 0x26, 0x98, 0x45, 0x26, - 0x85, 0x26, 0x98, 0x41, 0x40, 0x81, 0x42, 0xc5, - 0x26, 0xd0, 0x09, 0xc8, 0xd0, 0xef, 0xa4, 0x43, - 0x68, 0x4c, 0x51, 0x1b, 0x68, 0x81, 0x42, 0xa4, - 0x41, 0xc8, 0x8c, 0x79, 0x1c, 0x38, 0x98, 0xed, - 0x7a, 0x1c, 0x8d, 0x78, 0x1c, 0x38, 0xed, 0x76, - 0x1c, 0xf0, 0x9d, 0x8d, 0x7b, 0x1c, 0xad, 0x76, - 0x1c, 0x8d, 0x0d, 0x1d, 0xa9, 0x1d, 0x8d, 0x49, - 0x37, 0xa9, 0x84, 0x8d, 0x48, 0x37, 0xa2, 0x00, - 0x86, 0x40, 0xbd, 0x29, 0x1c, 0xa8, 0xbd, 0x2a, - 0x1c, 0x85, 0x41, 0x4c, 0x93, 0x1b, 0x18, 0xb1, - 0x40, 0x6d, 0x7b, 0x1c, 0x91, 0x40, 0xc8, 0xd0, - 0x02, 0xe6, 0x41, 0xc8, 0xd0, 0x02, 0xe6, 0x41, - 0xa5, 0x41, 0xdd, 0x2c, 0x1c, 0x90, 0xe7, 0x98, - 0xdd, 0x2b, 0x1c, 0x90, 0xe1, 0x8a, 0x18, 0x69, - 0x04, 0xaa, 0xec, 0x28, 0x1c, 0x90, 0xcb, 0xa2, - 0x00, 0x8e, 0x9c, 0x33, 0xbd, 0x5a, 0x1c, 0x85, - 0x40, 0xbd, 0x5b, 0x1c, 0x85, 0x41, 0xa2, 0x00, - 0xa1, 0x40, 0x20, 0x8e, 0xf8, 0xa4, 0x2f, 0xc0, - 0x02, 0xd0, 0x11, 0xb1, 0x40, 0xcd, 0x76, 0x1c, - 0x90, 0x0a, 0xcd, 0x77, 0x1c, 0xb0, 0x05, 0x6d, - 0x7b, 0x1c, 0x91, 0x40, 0x38, 0xa5, 0x2f, 0x65, - 0x40, 0x85, 0x40, 0xa9, 0x00, 0x65, 0x41, 0x85, - 0x41, 0xae, 0x9c, 0x33, 0xdd, 0x5d, 0x1c, 0x90, - 0xcd, 0xa5, 0x40, 0xdd, 0x5c, 0x1c, 0x90, 0xc6, - 0x8a, 0x18, 0x69, 0x04, 0xaa, 0xec, 0x59, 0x1c, - - 0x90, 0xaf, 0xa9, 0x3f, 0x85, 0x41, 0xac, 0x79, - 0x1c, 0x88, 0x84, 0x43, 0xa9, 0x00, 0x85, 0x40, - 0x85, 0x42, 0xa8, 0xb1, 0x40, 0x91, 0x42, 0xc8, - 0xd0, 0xf9, 0xce, 0x7c, 0x1c, 0xf0, 0x06, 0xc6, - 0x41, 0xc6, 0x43, 0xd0, 0xee, 0x4c, 0x54, 0x1e, - 0x24, 0x00, 0x1d, 0x56, 0x1d, 0x58, 0x1d, 0x5a, - 0x1d, 0x64, 0x1d, 0x66, 0x1d, 0x6c, 0x1d, 0x70, - 0x1d, 0x78, 0x1d, 0x7c, 0x1d, 0x7e, 0x1d, 0x80, - 0x1d, 0xc1, 0x2a, 0xfd, 0x2a, 0xe4, 0x37, 0xe8, - 0x37, 0xee, 0x37, 0xf0, 0x37, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x84, 0x1d, 0x84, 0x28, 0xfd, 0x2a, - 0x97, 0x33, 0x00, 0x37, 0xe0, 0x37, 0xfe, 0x35, - 0xfe, 0x35, 0x00, 0x38, 0x8f, 0x3a, 0x00, 0x3d, - 0xff, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x40, - 0x00, 0x00, 0x23, 0x00, 0x23, 0xa5, 0x74, 0x86, - 0x6f, 0x85, 0x70, 0xa0, 0x00, 0x84, 0x8b, 0xa5, - 0x6d, 0xa6, 0x6e, 0x85, 0x9b, 0x86, 0x9c, 0xa9, - 0x55, 0xa2, 0x00, 0x85, 0x5e, 0x86, 0x5f, 0xc5, - 0x52, 0xf0, 0x05, 0x20, 0x1a, 0x1d, 0xf0, 0xf7, - 0xa9, 0x07, 0x85, 0x8f, 0xa5, 0x69, 0xa6, 0x6a, - 0x85, 0x5e, 0x86, 0x5f, 0xe4, 0x6c, 0xd0, 0x04, - 0xc5, 0x6b, 0xf0, 0x05, 0x20, 0x10, 0x1d, 0xf0, - 0xf3, 0x85, 0x94, 0x86, 0x95, 0xa9, 0x03, 0x85, - 0x8f, 0xa5, 0x94, 0xa6, 0x95, 0xe4, 0x6e, 0xd0, - 0x07, 0xc5, 0x6d, 0xd0, 0x03, 0x4c, 0x59, 0x1d, - 0x85, 0x5e, 0x86, 0x00, 0xa0, 0x00, 0xb1, 0x5e, - 0xaa, 0xc8, 0xb1, 0x5e, 0x08, 0xc8, 0xb1, 0x5e, - 0x65, 0x94, 0x85, 0x94, 0xc8, 0xb1, 0x5e, 0x65, - 0x95, 0x85, 0x95, 0x28, 0x10, 0xd3, 0x8a, 0x30, - 0xd0, 0xa6, 0x1c, 0xa6, 0x1b, 0xa6, 0x1a, 0x80, - 0x1a, 0x65, 0x5e, 0x85, 0x5e, 0x90, 0x02, 0xe6, - - 0xd3, 0x1c, 0x81, 0x1e, 0xbd, 0x1e, 0x75, 0x2a, - 0x93, 0x2a, 0x60, 0x2a, 0x00, 0x1b, 0xbb, 0x35, - 0xea, 0x1e, 0x11, 0x1f, 0x22, 0x1f, 0x2e, 0x1f, - 0x51, 0x1f, 0x60, 0x1f, 0x70, 0x1f, 0x4e, 0x25, - 0x12, 0x24, 0x96, 0x23, 0xd0, 0x24, 0xef, 0x24, - 0x62, 0x22, 0x70, 0x22, 0x74, 0x22, 0xe9, 0x22, - 0x1a, 0x25, 0xc5, 0x25, 0x0f, 0x25, 0xdc, 0x25, - 0xa2, 0x22, 0x97, 0x22, 0x80, 0x22, 0x6d, 0x25, - 0x32, 0x22, 0x3c, 0x22, 0x28, 0x22, 0x2d, 0x22, - 0x50, 0x22, 0x79, 0x25, 0x9d, 0x25, 0x30, 0x23, - 0x5c, 0x23, 0x8d, 0x23, 0x7c, 0x22, 0x36, 0xe8, - 0xe5, 0x24, 0xe3, 0xe3, 0x00, 0xe0, 0x03, 0xe0, - 0x00, 0x00, 0x36, 0xe8, 0xe5, 0x24, 0xe3, 0xe3, - 0x00, 0xe0, 0x03, 0xe0, 0xfc, 0x24, 0xfc, 0x24, - 0x65, 0xd8, 0x00, 0xe0, 0x3c, 0xd4, 0xf2, 0xd4, - 0x06, 0x25, 0x06, 0x25, 0x67, 0x10, 0x84, 0x1d, - 0x3c, 0x0c, 0xf2, 0x0c, 0xad, 0xe9, 0x37, 0x4a, - 0x4a, 0x4a, 0x4a, 0x8d, 0x6a, 0x2a, 0xad, 0xea, - 0x37, 0x8d, 0x68, 0x2a, 0xad, 0x00, 0xe0, 0x49, - 0x20, 0xd0, 0x11, 0x8d, 0xb6, 0x2a, 0xa2, 0x0a, - 0xbd, 0x61, 0x1d, 0x9d, 0x55, 0x1d, 0xca, 0xd0, - 0xf7, 0x4c, 0xbc, 0x1d, 0xa9, 0x40, 0x8d, 0xb6, - 0x2a, 0xa2, 0x0c, 0xbd, 0x6b, 0x1d, 0x9d, 0x55, - 0x1d, 0xca, 0xd0, 0xf7, 0x38, 0xb0, 0x12, 0xad, - 0xb6, 0x2a, 0xd0, 0x04, 0xa9, 0x20, 0xd0, 0x05, - 0x0a, 0x10, 0x05, 0xa9, 0x4c, 0x20, 0xb2, 0x25, - 0x18, 0x08, 0x20, 0x51, 0x28, 0xa9, 0x00, 0x8d, - 0x5e, 0x2a, 0x8d, 0x52, 0x2a, 0x28, 0x6a, 0x8d, - 0x51, 0x2a, 0x30, 0x03, 0x6c, 0x5e, 0x1d, 0x6c, - 0x5c, 0x1d, 0x0a, 0x10, 0x19, 0x8d, 0xb6, 0x2a, - 0xa2, 0x0c, 0xbd, 0x77, 0x1d, 0x9d, 0x55, 0x1d, - 0xca, 0xd0, 0xf7, 0xa2, 0x1d, 0xbd, 0x93, 0x2a, - - 0x9d, 0x75, 0x2a, 0xca, 0x10, 0xf7, 0xad, 0xb1, - 0x2a, 0x8d, 0x57, 0x2a, 0x20, 0xd4, 0x27, 0xad, - 0xb3, 0x2a, 0xf0, 0x09, 0x48, 0x20, 0x9d, 0x26, - 0x68, 0xa0, 0x00, 0x91, 0x40, 0x20, 0x5b, 0x27, - 0xad, 0x5f, 0x2a, 0xd0, 0x20, 0xa2, 0x2f, 0xbd, - 0x51, 0x1e, 0x9d, 0xd0, 0x03, 0xca, 0x10, 0xf7, - 0xad, 0x53, 0x1e, 0x8d, 0xf3, 0x03, 0x49, 0xa5, - 0x8d, 0xf4, 0x03, 0xad, 0x52, 0x1e, 0x8d, 0xf2, - 0x03, 0xa9, 0x06, 0xd0, 0x05, 0xad, 0x62, 0x2a, - 0xf0, 0x06, 0x8d, 0x5f, 0x2a, 0x4c, 0x80, 0x21, - 0x60, 0x4c, 0xbf, 0x1d, 0x4c, 0x84, 0x1d, 0x4c, - 0xfd, 0x2a, 0x4c, 0xb5, 0x37, 0xad, 0x0f, 0x1d, - 0xac, 0x0e, 0x1d, 0x60, 0xad, 0xc2, 0x2a, 0xac, - 0xc1, 0x2a, 0x60, 0x4c, 0x51, 0x28, 0xea, 0xea, - 0x4c, 0x59, 0xfa, 0x4c, 0x65, 0xff, 0x4c, 0x58, - 0xff, 0x4c, 0x65, 0xff, 0x4c, 0x65, 0xff, 0x65, - 0xff, 0x20, 0xd1, 0x1e, 0xad, 0x51, 0x2a, 0xf0, - 0x15, 0x48, 0xad, 0x5c, 0x2a, 0x91, 0x28, 0x68, - 0x30, 0x03, 0x4c, 0x26, 0x26, 0x20, 0xea, 0x1d, - 0xa4, 0x24, 0xa9, 0x60, 0x91, 0x28, 0xad, 0xb3, - 0x2a, 0xf0, 0x03, 0x20, 0x82, 0x26, 0xa9, 0x03, - 0x8d, 0x52, 0x2a, 0x20, 0xba, 0x1f, 0x20, 0xba, - 0x1e, 0x8d, 0x5c, 0x2a, 0x8e, 0x5a, 0x2a, 0x4c, - 0xb3, 0x1f, 0x6c, 0x38, 0x00, 0x20, 0xd1, 0x1e, - 0xad, 0x52, 0x2a, 0x0a, 0xaa, 0xbd, 0x11, 0x1d, - 0x48, 0xbd, 0x10, 0x1d, 0x48, 0xad, 0x5c, 0x2a, - 0x60, 0x8d, 0x5c, 0x2a, 0x8e, 0x5a, 0x2a, 0x8c, - 0x5b, 0x2a, 0xba, 0xe8, 0xe8, 0x8e, 0x59, 0x2a, - 0xa2, 0x03, 0xbd, 0x53, 0x2a, 0x95, 0x36, 0xca, - 0x10, 0xf8, 0x60, 0xae, 0xb7, 0x2a, 0xf0, 0x03, - 0x4c, 0x78, 0x1f, 0xae, 0x51, 0x2a, 0xf0, 0x08, - 0xc9, 0xbf, 0xf0, 0x75, 0xc5, 0x33, 0xf0, 0x27, - - 0xa2, 0x02, 0x8e, 0x52, 0x2a, 0xcd, 0xb2, 0x2a, - 0xd0, 0x19, 0xca, 0x8e, 0x52, 0x2a, 0xca, 0x8e, - 0x5d, 0x2a, 0xae, 0x5d, 0x2a, 0x9d, 0x00, 0x02, - 0xe8, 0x8e, 0x5d, 0x2a, 0xc9, 0x8d, 0xd0, 0x75, - 0x4c, 0xcd, 0x1f, 0xc9, 0x8d, 0xd0, 0x7d, 0xa2, - 0x00, 0x8e, 0x52, 0x2a, 0x4c, 0xa4, 0x1f, 0xa2, - 0x00, 0x8e, 0x52, 0x2a, 0xc9, 0x8d, 0xf0, 0x07, - 0xad, 0xb3, 0x2a, 0xf0, 0x67, 0xd0, 0x5e, 0x48, - 0x38, 0xad, 0xb3, 0x2a, 0xd0, 0x03, 0x20, 0x5e, - 0x26, 0x68, 0x90, 0xec, 0xae, 0x5a, 0x2a, 0x4c, - 0x15, 0x1f, 0xc9, 0x8d, 0xd0, 0x05, 0xa9, 0x05, - 0x8d, 0x52, 0x2a, 0x20, 0x0e, 0x26, 0x4c, 0x99, - 0x1f, 0xcd, 0xb2, 0x2a, 0xf0, 0x85, 0xc9, 0x8a, - 0xf0, 0xf1, 0xa2, 0x04, 0x8e, 0x52, 0x2a, 0xd0, - 0xe1, 0xa9, 0x00, 0x8d, 0x52, 0x2a, 0xf0, 0x25, - 0xa9, 0x00, 0x8d, 0xb7, 0x2a, 0x20, 0x51, 0x28, - 0x4c, 0xdc, 0x24, 0xad, 0x00, 0x02, 0xcd, 0xb2, - 0x2a, 0xf0, 0x0a, 0xa9, 0x8d, 0x8d, 0x00, 0x02, - 0xa2, 0x00, 0x8e, 0x5a, 0x2a, 0xa9, 0x40, 0xd0, - 0x06, 0xa9, 0x10, 0xd0, 0x02, 0xa9, 0x20, 0x2d, - 0x5e, 0x2a, 0xf0, 0x0f, 0x20, 0xba, 0x1f, 0x20, - 0xc5, 0x1f, 0x8d, 0x5c, 0x2a, 0x8c, 0x5b, 0x2a, - 0x8e, 0x5a, 0x2a, 0x20, 0x51, 0x28, 0xae, 0x59, - 0x2a, 0x9a, 0xad, 0x5c, 0x2a, 0xac, 0x5b, 0x2a, - 0xae, 0x5a, 0x2a, 0x38, 0x60, 0x6c, 0x36, 0x00, - 0xa9, 0x8d, 0x4c, 0xc5, 0x1f, 0xa0, 0xff, 0x8c, - 0x5f, 0x2a, 0xc8, 0x8c, 0x62, 0x2a, 0xee, 0x5f, - 0x2a, 0xa2, 0x00, 0x08, 0xbd, 0x00, 0x02, 0xcd, - 0xb2, 0x2a, 0xd0, 0x01, 0xe8, 0x8e, 0x5d, 0x2a, - 0x20, 0xa4, 0x21, 0x29, 0x7f, 0x59, 0x84, 0x28, - 0xc8, 0x0a, 0xf0, 0x02, 0x68, 0x08, 0x90, 0xf0, - 0x28, 0xf0, 0x20, 0xb9, 0x84, 0x28, 0xd0, 0xd6, - - 0xad, 0x00, 0x02, 0xcd, 0xb2, 0x2a, 0xf0, 0x03, - 0x4c, 0xa4, 0x1f, 0xad, 0x01, 0x02, 0xc9, 0x8d, - 0xd0, 0x06, 0x20, 0x5b, 0x27, 0x4c, 0x95, 0x1f, - 0x4c, 0xc4, 0x26, 0x0e, 0x5f, 0x2a, 0xac, 0x5f, - 0x2a, 0x20, 0x5e, 0x26, 0x90, 0x0c, 0xa9, 0x02, - 0x39, 0x09, 0x29, 0xf0, 0x05, 0xa9, 0x0f, 0x4c, - 0xd2, 0x26, 0xc0, 0x06, 0xd0, 0x02, 0x84, 0x33, - 0xa9, 0x20, 0x39, 0x09, 0x29, 0xf0, 0x61, 0x20, - 0x95, 0x20, 0x08, 0x20, 0xa4, 0x21, 0xf0, 0x1e, - 0x0a, 0x90, 0x05, 0x30, 0x03, 0x4c, 0x00, 0x20, - 0x6a, 0x4c, 0x59, 0x20, 0x20, 0x93, 0x21, 0xf0, - 0x0d, 0x99, 0x75, 0x2a, 0xc8, 0xc0, 0x3c, 0x90, - 0xf3, 0x20, 0x93, 0x21, 0xd0, 0xfb, 0x28, 0xd0, - 0x0f, 0xac, 0x5f, 0x2a, 0xa9, 0x10, 0x39, 0x09, - 0x29, 0xf0, 0x0c, 0xa0, 0x1e, 0x08, 0xd0, 0xcb, - 0xad, 0x93, 0x2a, 0xc9, 0xa0, 0xf0, 0x13, 0xad, - 0x75, 0x2a, 0xc9, 0xa0, 0xd0, 0x4b, 0xac, 0x5f, - 0x2a, 0xa9, 0xc0, 0x39, 0x09, 0x29, 0xf0, 0x02, - 0x10, 0x3f, 0x4c, 0x00, 0x20, 0xa0, 0x3c, 0xa9, - 0xa0, 0x99, 0x74, 0x2a, 0x88, 0xd0, 0xfa, 0x60, - 0x8d, 0x75, 0x2a, 0xa9, 0x0c, 0x39, 0x09, 0x29, - 0xf0, 0x27, 0x20, 0xb9, 0x21, 0xb0, 0x1f, 0xa8, - 0xd0, 0x17, 0xe0, 0x11, 0xb0, 0x13, 0xac, 0x5f, - 0x2a, 0xa9, 0x08, 0x39, 0x09, 0x29, 0xf0, 0x06, - 0xe0, 0x08, 0xb0, 0xce, 0x90, 0x0b, 0x8a, 0xd0, - 0x08, 0xa9, 0x02, 0x4c, 0xd2, 0x26, 0x4c, 0xc4, - 0x26, 0xa9, 0x00, 0x8d, 0x65, 0x2a, 0x8d, 0x74, - 0x2a, 0x8d, 0x66, 0x2a, 0x8d, 0x6c, 0x2a, 0x8d, - 0x6d, 0x2a, 0x20, 0xdc, 0x3f, 0xad, 0x5d, 0x2a, - 0x20, 0xa4, 0x21, 0xd0, 0x1f, 0xc9, 0x8d, 0xd0, - 0xf7, 0xae, 0x5f, 0x2a, 0xad, 0x65, 0x2a, 0x1d, - 0x0a, 0x29, 0x5d, 0x0a, 0x29, 0xd0, 0x93, 0xae, - - 0x63, 0x2a, 0xf0, 0x76, 0x8d, 0x63, 0x2a, 0x8e, - 0x5d, 0x2a, 0xd0, 0xdc, 0xa2, 0x0a, 0xdd, 0x40, - 0x29, 0xf0, 0x05, 0xca, 0xd0, 0xf8, 0xf0, 0xb6, - 0xbd, 0x4a, 0x29, 0x30, 0x47, 0x0d, 0x65, 0x2a, - 0x8d, 0x65, 0x2a, 0xca, 0x8e, 0x64, 0x2a, 0x20, - 0xb9, 0x21, 0xb0, 0xa2, 0xad, 0x64, 0x2a, 0x0a, - 0x0a, 0xa8, 0xa5, 0x45, 0xd0, 0x09, 0xa5, 0x44, - 0xd9, 0x55, 0x29, 0x90, 0x8c, 0xa5, 0x45, 0xd9, - 0x58, 0x29, 0x90, 0x0b, 0xd0, 0x83, 0xa5, 0x44, - 0xd9, 0x57, 0x29, 0x90, 0x02, 0xd0, 0xf5, 0xad, - 0x63, 0x2a, 0xd0, 0x94, 0x98, 0x4a, 0xa8, 0xa5, - 0x45, 0x99, 0x67, 0x2a, 0xa5, 0x44, 0x99, 0x66, - 0x2a, 0x4c, 0xe8, 0x20, 0x48, 0xa9, 0x80, 0x0d, - 0x65, 0x2a, 0x8d, 0x65, 0x2a, 0x68, 0x29, 0x7f, - 0x0d, 0x74, 0x2a, 0x8d, 0x74, 0x2a, 0xd0, 0xe9, - 0xf0, 0x9c, 0x20, 0x80, 0x21, 0x4c, 0x83, 0x1f, - 0x20, 0x5b, 0x27, 0x20, 0xae, 0x21, 0xad, 0x5f, - 0x2a, 0xaa, 0xbd, 0x1f, 0x1d, 0x48, 0xbd, 0x1e, - 0x1d, 0x48, 0x60, 0xae, 0x5d, 0x2a, 0xbd, 0x00, - 0x02, 0xc9, 0x8d, 0xf0, 0x06, 0xe8, 0x8e, 0x5d, - 0x2a, 0xc9, 0xac, 0x60, 0x20, 0x93, 0x21, 0xf0, - 0xfa, 0xc9, 0xa0, 0xf0, 0xf7, 0x60, 0xa9, 0x00, - 0xa0, 0x16, 0x99, 0xba, 0x35, 0x88, 0xd0, 0xfa, - 0x60, 0xa9, 0x00, 0x85, 0x44, 0x85, 0x45, 0x20, - 0xa4, 0x21, 0x08, 0xc9, 0xa4, 0xf0, 0x3c, 0x28, - 0x4c, 0xce, 0x21, 0x20, 0xa4, 0x21, 0xd0, 0x06, - 0xa6, 0x44, 0xa5, 0x45, 0x18, 0x60, 0x38, 0xe9, - 0xb0, 0x30, 0x21, 0xc9, 0x0a, 0xb0, 0x1d, 0x20, - 0xfe, 0x21, 0x65, 0x44, 0xaa, 0xa9, 0x00, 0x65, - 0x45, 0xa8, 0x20, 0xfe, 0x21, 0x20, 0xfe, 0x21, - 0x8a, 0x65, 0x44, 0x85, 0x44, 0x98, 0x65, 0x45, - 0x85, 0x45, 0x90, 0xcf, 0x38, 0x60, 0x06, 0x44, - - 0x26, 0x45, 0x60, 0x28, 0x20, 0xa4, 0x21, 0xf0, - 0xc5, 0x38, 0xe9, 0xb0, 0x30, 0xee, 0xc9, 0x0a, - 0x90, 0x08, 0xe9, 0x07, 0x30, 0xe6, 0xc9, 0x10, - 0xb0, 0xe2, 0xa2, 0x04, 0x20, 0xfe, 0x21, 0xca, - 0xd0, 0xfa, 0x05, 0x44, 0x85, 0x44, 0x4c, 0x04, - 0x22, 0xa5, 0x44, 0x4c, 0x95, 0xfe, 0xa5, 0x44, - 0x4c, 0x8b, 0xfe, 0xad, 0x5e, 0x2a, 0x0d, 0x74, - 0x2a, 0x8d, 0x5e, 0x2a, 0x60, 0x2c, 0x74, 0x2a, - 0x50, 0x03, 0x20, 0xc8, 0x1f, 0xa9, 0x70, 0x4d, - 0x74, 0x2a, 0x2d, 0x5e, 0x2a, 0x8d, 0x5e, 0x2a, - 0x60, 0xa9, 0x00, 0x8d, 0xb3, 0x2a, 0xa5, 0x44, - 0x48, 0x20, 0x16, 0x23, 0x68, 0x8d, 0x57, 0x2a, - 0x4c, 0xd4, 0x27, 0xa9, 0x05, 0x20, 0xaa, 0x22, - 0x20, 0x64, 0x27, 0xa0, 0x00, 0x98, 0x91, 0x40, - 0x60, 0xa9, 0x07, 0xd0, 0x02, 0xa9, 0x08, 0x20, - 0xaa, 0x22, 0x4c, 0xea, 0x22, 0xa9, 0x0c, 0xd0, - 0xf6, 0xad, 0x08, 0x1d, 0x8d, 0xbd, 0x35, 0xad, - 0x09, 0x1d, 0x8d, 0xbe, 0x35, 0xa9, 0x09, 0x8d, - 0x63, 0x2a, 0x20, 0xc8, 0x22, 0x4c, 0xea, 0x22, - 0x20, 0xa3, 0x22, 0x20, 0x8c, 0x26, 0xd0, 0xfb, - 0x4c, 0x46, 0x25, 0xa9, 0x00, 0x4c, 0xd5, 0x23, - 0xa9, 0x01, 0x8d, 0x63, 0x2a, 0xad, 0x6c, 0x2a, - 0xd0, 0x0a, 0xad, 0x6d, 0x2a, 0xd0, 0x05, 0xa9, - 0x01, 0x8d, 0x6c, 0x2a, 0xad, 0x6c, 0x2a, 0x8d, - 0xbd, 0x35, 0xad, 0x6d, 0x2a, 0x8d, 0xbe, 0x35, - 0x20, 0xea, 0x22, 0xa5, 0x45, 0xd0, 0x03, 0x4c, - 0xc8, 0x26, 0x85, 0x41, 0xa5, 0x44, 0x85, 0x40, - 0x20, 0x43, 0x27, 0x20, 0x4e, 0x27, 0x20, 0x1a, - 0x27, 0xad, 0x63, 0x2a, 0x8d, 0xbb, 0x35, 0x4c, - 0xa8, 0x26, 0xad, 0x75, 0x2a, 0xc9, 0xa0, 0xf0, - 0x25, 0x20, 0x64, 0x27, 0xb0, 0x3a, 0x20, 0xfc, - 0x22, 0x4c, 0xea, 0x22, 0x20, 0xaf, 0x27, 0xd0, - - 0x05, 0xa9, 0x00, 0x8d, 0xb3, 0x2a, 0xa0, 0x00, - 0x98, 0x91, 0x40, 0x20, 0x4e, 0x27, 0xa9, 0x02, - 0x8d, 0xbb, 0x35, 0x4c, 0xa8, 0x26, 0x20, 0x92, - 0x27, 0xd0, 0x05, 0x20, 0x9a, 0x27, 0xf0, 0x10, - 0x20, 0xaf, 0x27, 0xf0, 0xf6, 0x20, 0xaa, 0x27, - 0xf0, 0xf1, 0x20, 0xfc, 0x22, 0x4c, 0x16, 0x23, - 0x60, 0xa9, 0x09, 0x2d, 0x65, 0x2a, 0xc9, 0x09, - 0xf0, 0x03, 0x4c, 0x00, 0x20, 0xa9, 0x04, 0x20, - 0xd5, 0x23, 0xad, 0x73, 0x2a, 0xac, 0x72, 0x2a, - 0x20, 0xe0, 0x23, 0xad, 0x6d, 0x2a, 0xac, 0x6c, - 0x2a, 0x20, 0xe0, 0x23, 0xad, 0x73, 0x2a, 0xac, - 0x72, 0x2a, 0x4c, 0xff, 0x23, 0x20, 0xa8, 0x22, - 0xa9, 0x7f, 0x2d, 0xc2, 0x35, 0xc9, 0x04, 0xf0, - 0x03, 0x4c, 0xd0, 0x26, 0xa9, 0x04, 0x20, 0xd5, - 0x23, 0x20, 0x7a, 0x24, 0xaa, 0xad, 0x65, 0x2a, - 0x29, 0x01, 0xd0, 0x06, 0x8e, 0x72, 0x2a, 0x8c, - 0x73, 0x2a, 0x20, 0x7a, 0x24, 0xae, 0x72, 0x2a, - 0xac, 0x73, 0x2a, 0x4c, 0x71, 0x24, 0x20, 0x5d, - 0x23, 0x20, 0x51, 0x28, 0x6c, 0x72, 0x2a, 0xad, - 0xb6, 0x2a, 0xf0, 0x20, 0xa5, 0xd6, 0x10, 0x03, - 0x4c, 0xcc, 0x26, 0xa9, 0x02, 0x20, 0xd5, 0x23, - 0x38, 0xa5, 0xaf, 0xe5, 0x67, 0xa8, 0xa5, 0xb0, - 0xe5, 0x68, 0x20, 0xe0, 0x23, 0xa5, 0x68, 0xa4, - 0x67, 0x4c, 0xff, 0x23, 0xa9, 0x01, 0x20, 0xd5, - 0x23, 0x38, 0xa5, 0x4c, 0xe5, 0xca, 0xa8, 0xa5, - 0x4d, 0xe5, 0xcb, 0x20, 0xe0, 0x23, 0xa5, 0xcb, - 0xa4, 0xca, 0x4c, 0xff, 0x23, 0x8d, 0xc2, 0x35, - 0x48, 0x20, 0xa8, 0x22, 0x68, 0x4c, 0xc4, 0x27, - 0x8c, 0xc1, 0x35, 0x8c, 0xc3, 0x35, 0x8d, 0xc2, - 0x35, 0xa9, 0x04, 0x8d, 0xbb, 0x35, 0xa9, 0x01, - 0x8d, 0xbc, 0x35, 0x20, 0xa8, 0x26, 0xad, 0xc2, - 0x35, 0x8d, 0xc3, 0x35, 0x4c, 0xa8, 0x26, 0x8c, - - 0xc3, 0x35, 0x8d, 0xc4, 0x35, 0xa9, 0x02, 0x8d, - 0xbc, 0x35, 0x20, 0xa8, 0x26, 0x4c, 0xea, 0x22, - 0x4c, 0xd0, 0x26, 0x20, 0x16, 0x23, 0x20, 0xa8, - 0x22, 0xa9, 0x23, 0x2d, 0xc2, 0x35, 0xf0, 0xf0, - 0x8d, 0xc2, 0x35, 0xad, 0xb6, 0x2a, 0xf0, 0x28, - 0xa9, 0x02, 0x20, 0xb1, 0x24, 0x20, 0x7a, 0x24, - 0x18, 0x65, 0x67, 0xaa, 0x98, 0x65, 0x68, 0xc5, - 0x74, 0xb0, 0x70, 0x85, 0xb0, 0x85, 0x6a, 0x86, - 0xaf, 0x86, 0x69, 0xa6, 0x67, 0xa4, 0x68, 0x20, - 0x71, 0x24, 0x20, 0x51, 0x28, 0x6c, 0x60, 0x1d, - 0xa9, 0x01, 0x20, 0xb1, 0x24, 0x20, 0x7a, 0x24, - 0x38, 0xa5, 0x4c, 0xed, 0x60, 0x2a, 0xaa, 0xa5, - 0x4d, 0xed, 0x61, 0x2a, 0x90, 0x45, 0xa8, 0xc4, - 0x4b, 0x90, 0x40, 0xf0, 0x3e, 0x84, 0xcb, 0x86, - 0xca, 0x8e, 0xc3, 0x35, 0x8c, 0xc4, 0x35, 0x4c, - 0x0a, 0x24, 0xad, 0x0a, 0x1d, 0x8d, 0xc3, 0x35, - 0xad, 0x0b, 0x1d, 0x8d, 0xc4, 0x35, 0xa9, 0x00, - 0x8d, 0xc2, 0x35, 0xa9, 0x02, 0x8d, 0xc1, 0x35, - 0xa9, 0x03, 0x8d, 0xbb, 0x35, 0xa9, 0x02, 0x8d, - 0xbc, 0x35, 0x20, 0xa8, 0x26, 0xad, 0x61, 0x2a, - 0x8d, 0xc2, 0x35, 0xa8, 0xad, 0x60, 0x2a, 0x8d, - 0xc1, 0x35, 0x60, 0x20, 0xea, 0x22, 0x4c, 0xcc, - 0x26, 0xcd, 0xc2, 0x35, 0xf0, 0x1a, 0xae, 0x5f, - 0x2a, 0x8e, 0x62, 0x2a, 0x4a, 0xf0, 0x03, 0x4c, - 0x9e, 0x25, 0xa2, 0x1d, 0xbd, 0x75, 0x2a, 0x9d, - 0x93, 0x2a, 0xca, 0x10, 0xf7, 0x4c, 0x7a, 0x25, - 0x60, 0xad, 0xb6, 0x2a, 0xf0, 0x03, 0x8d, 0xb7, - 0x2a, 0x20, 0x13, 0x24, 0x20, 0xc8, 0x1f, 0x20, - 0x51, 0x28, 0x6c, 0x58, 0x1d, 0xa5, 0x4a, 0x85, - 0xcc, 0xa5, 0x4b, 0x85, 0xcd, 0x6c, 0x56, 0x1d, - 0x20, 0x16, 0x24, 0x20, 0xc8, 0x1f, 0x20, 0x51, - 0x28, 0x6c, 0x56, 0x1d, 0x20, 0x65, 0xd6, 0x85, - - 0x33, 0x85, 0xd8, 0x4c, 0xd2, 0xd7, 0x20, 0x65, - 0x0e, 0x85, 0x33, 0x85, 0xd8, 0x4c, 0xd4, 0x0f, - 0x20, 0x26, 0x25, 0xa9, 0x05, 0x8d, 0x52, 0x2a, - 0x4c, 0x83, 0x1f, 0x20, 0x26, 0x25, 0xa9, 0x01, - 0x8d, 0x51, 0x2a, 0x4c, 0x83, 0x1f, 0x20, 0x64, - 0x27, 0x90, 0x06, 0x20, 0xa3, 0x22, 0x4c, 0x34, - 0x25, 0x20, 0x4e, 0x27, 0xad, 0x65, 0x2a, 0x29, - 0x06, 0xf0, 0x13, 0xa2, 0x03, 0xbd, 0x6e, 0x2a, - 0x9d, 0xbd, 0x35, 0xca, 0x10, 0xf7, 0xa9, 0x0a, - 0x8d, 0xbb, 0x35, 0x20, 0xa8, 0x26, 0x60, 0xa9, - 0x40, 0x2d, 0x65, 0x2a, 0xf0, 0x05, 0xad, 0x66, - 0x2a, 0xd0, 0x05, 0xa9, 0xfe, 0x8d, 0x66, 0x2a, - 0xad, 0x0d, 0x1d, 0x8d, 0xbc, 0x35, 0xa9, 0x0b, - 0x20, 0xaa, 0x22, 0x4c, 0x97, 0x23, 0xa9, 0x06, - 0x20, 0xaa, 0x22, 0xad, 0xbf, 0x35, 0x8d, 0x66, - 0x2a, 0x60, 0xa9, 0x4c, 0x20, 0xb2, 0x25, 0xf0, - 0x2e, 0xa9, 0x00, 0x8d, 0xb6, 0x2a, 0xa0, 0x1e, - 0x20, 0x97, 0x20, 0xa2, 0x09, 0xbd, 0xb7, 0x2a, - 0x9d, 0x74, 0x2a, 0xca, 0xd0, 0xf7, 0xa9, 0xc0, - 0x8d, 0x51, 0x2a, 0x4c, 0xd1, 0x24, 0xa9, 0x20, - 0x20, 0xb2, 0x25, 0xf0, 0x05, 0xa9, 0x01, 0x4c, - 0xd2, 0x26, 0xa9, 0x00, 0x8d, 0xb7, 0x2a, 0x4c, - 0x84, 0x1d, 0xcd, 0x00, 0xe0, 0xf0, 0x0e, 0x8d, - 0x80, 0xc0, 0xcd, 0x00, 0xe0, 0xf0, 0x06, 0x8d, - 0x81, 0xc0, 0xcd, 0x00, 0xe0, 0x60, 0x20, 0xa3, - 0x22, 0xad, 0x4f, 0x2a, 0x8d, 0xb4, 0x2a, 0xad, - 0x50, 0x2a, 0x8d, 0xb5, 0x2a, 0xad, 0x75, 0x2a, - 0x8d, 0xb3, 0x2a, 0xd0, 0x0e, 0x20, 0x64, 0x27, - 0x90, 0x06, 0x20, 0xa3, 0x22, 0x4c, 0xeb, 0x25, - 0x20, 0x4e, 0x27, 0xad, 0x65, 0x2a, 0x29, 0x04, - 0xf0, 0x1b, 0xad, 0x6e, 0x2a, 0xd0, 0x08, 0xae, - 0x6f, 0x2a, 0xf0, 0x11, 0xce, 0x6f, 0x2a, 0xce, - - 0x6e, 0x2a, 0x20, 0x8c, 0x26, 0xf0, 0x38, 0xc9, - 0x8d, 0xd0, 0xf7, 0xf0, 0xe5, 0x60, 0x20, 0x5e, - 0x26, 0xb0, 0x66, 0xad, 0x5c, 0x2a, 0x8d, 0xc3, - 0x35, 0xa9, 0x04, 0x8d, 0xbb, 0x35, 0xa9, 0x01, - 0x8d, 0xbc, 0x35, 0x4c, 0xa8, 0x26, 0x20, 0x5e, - 0x26, 0xb0, 0x4e, 0xa9, 0x06, 0x8d, 0x52, 0x2a, - 0x20, 0x8c, 0x26, 0xd0, 0x0f, 0x20, 0xfc, 0x22, - 0xa9, 0x03, 0xcd, 0x52, 0x2a, 0xf0, 0xce, 0xa9, - 0x05, 0x4c, 0xd2, 0x26, 0xc9, 0xe0, 0x90, 0x02, - 0x29, 0x7f, 0x8d, 0x5c, 0x2a, 0xae, 0x5a, 0x2a, - 0xf0, 0x09, 0xca, 0xbd, 0x00, 0x02, 0x09, 0x80, - 0x9d, 0x00, 0x02, 0x4c, 0xb3, 0x1f, 0x48, 0xad, - 0xb6, 0x2a, 0xf0, 0x0e, 0xa6, 0x76, 0xe8, 0xf0, - 0x0d, 0xa6, 0x33, 0xe0, 0xdd, 0xf0, 0x07, 0x68, - 0x18, 0x60, 0xa5, 0xd9, 0x30, 0xf9, 0x68, 0x38, - 0x60, 0x20, 0xfc, 0x22, 0x20, 0x5b, 0x27, 0x4c, - 0xb3, 0x1f, 0x20, 0x9d, 0x26, 0x20, 0x4e, 0x27, - 0xa9, 0x03, 0xd0, 0xa1, 0xa9, 0x03, 0x8d, 0xbb, - 0x35, 0xa9, 0x01, 0x8d, 0xbc, 0x35, 0x20, 0xa8, - 0x26, 0xad, 0xc3, 0x35, 0x60, 0xad, 0xb5, 0x2a, - 0x85, 0x41, 0xad, 0xb4, 0x2a, 0x85, 0x40, 0x60, - 0x20, 0x06, 0x2b, 0x90, 0x16, 0x20, 0x64, 0x27, - 0xb0, 0x05, 0xa9, 0x00, 0xa8, 0x91, 0x40, 0xad, - 0xc5, 0x35, 0xc9, 0x05, 0xd0, 0x14, 0xa2, 0x00, - 0x8e, 0xc3, 0x35, 0x60, 0xa9, 0x0b, 0xd0, 0x0a, - 0xa9, 0x0c, 0xd0, 0x06, 0xa9, 0x0e, 0xd0, 0x02, - 0xa9, 0x0d, 0x8d, 0x5c, 0x2a, 0x20, 0xe6, 0x3f, - 0xad, 0xb6, 0x2a, 0xf0, 0x04, 0xa5, 0xd8, 0x30, - 0x0e, 0xa2, 0x00, 0x20, 0x02, 0x27, 0xae, 0x5c, - 0x2a, 0x20, 0x02, 0x27, 0x20, 0xc8, 0x1f, 0x20, - 0x51, 0x28, 0x20, 0x5e, 0x26, 0xae, 0x5c, 0x2a, - 0xa9, 0x03, 0xb0, 0x03, 0x6c, 0x5a, 0x1d, 0x6c, - - 0x5e, 0x1d, 0xbd, 0x3f, 0x2a, 0xaa, 0x8e, 0x63, - 0x2a, 0xbd, 0x71, 0x29, 0x48, 0x09, 0x80, 0x20, - 0xc5, 0x1f, 0xae, 0x63, 0x2a, 0xe8, 0x68, 0x10, - 0xed, 0x60, 0xad, 0x66, 0x2a, 0x8d, 0xbf, 0x35, - 0xad, 0x68, 0x2a, 0x8d, 0xc0, 0x35, 0xad, 0x6a, - 0x2a, 0x8d, 0xc1, 0x35, 0xad, 0x06, 0x1d, 0x8d, - 0xc3, 0x35, 0xad, 0x07, 0x1d, 0x8d, 0xc4, 0x35, - 0xa5, 0x40, 0x8d, 0x4f, 0x2a, 0xa5, 0x41, 0x8d, - 0x50, 0x2a, 0x60, 0xa0, 0x1d, 0xb9, 0x75, 0x2a, - 0x91, 0x40, 0x88, 0x10, 0xf8, 0x60, 0xa0, 0x1e, - 0xb1, 0x40, 0x99, 0xa9, 0x35, 0xc8, 0xc0, 0x26, - 0xd0, 0xf6, 0x60, 0xa0, 0x00, 0x8c, 0x51, 0x2a, - 0x8c, 0x52, 0x2a, 0x60, 0xa9, 0x00, 0x85, 0x45, - 0x20, 0x92, 0x27, 0x4c, 0x73, 0x27, 0x20, 0x9a, - 0x27, 0xf0, 0x1d, 0x20, 0xaa, 0x27, 0xd0, 0x0a, - 0xa5, 0x40, 0x85, 0x44, 0xa5, 0x41, 0x85, 0x45, - 0xd0, 0xec, 0xa0, 0x1d, 0xb1, 0x40, 0xd9, 0x75, - 0x2a, 0xd0, 0xe3, 0x88, 0x10, 0xf6, 0x18, 0x60, - 0x38, 0x60, 0xad, 0x00, 0x1d, 0xae, 0x01, 0x1d, - 0xd0, 0x0a, 0xa0, 0x25, 0xb1, 0x40, 0xf0, 0x09, - 0xaa, 0x88, 0xb1, 0x40, 0x86, 0x41, 0x85, 0x40, - 0x8a, 0x60, 0xa0, 0x00, 0xb1, 0x40, 0x60, 0xad, - 0xb3, 0x2a, 0xf0, 0x0e, 0xad, 0xb4, 0x2a, 0xc5, - 0x40, 0xd0, 0x08, 0xad, 0xb5, 0x2a, 0xc5, 0x41, - 0xf0, 0x01, 0xca, 0x60, 0x4d, 0xc2, 0x35, 0xf0, - 0x0a, 0x29, 0x7f, 0xf0, 0x06, 0x20, 0xea, 0x22, - 0x4c, 0xd0, 0x26, 0x60, 0x38, 0xad, 0x00, 0x1d, - 0x85, 0x40, 0xad, 0x01, 0x1d, 0x85, 0x41, 0xad, - 0x57, 0x2a, 0x8d, 0x63, 0x2a, 0xa0, 0x00, 0x98, - 0x91, 0x40, 0xa0, 0x1e, 0x38, 0xa5, 0x40, 0xe9, - 0x2d, 0x91, 0x40, 0x48, 0xa5, 0x41, 0xe9, 0x00, - 0xc8, 0x91, 0x40, 0xaa, 0xca, 0x68, 0x48, 0xc8, - - 0x91, 0x40, 0x8a, 0xc8, 0x91, 0x40, 0xaa, 0xca, - 0x68, 0x48, 0xc8, 0x91, 0x40, 0xc8, 0x8a, 0x91, - 0x40, 0xce, 0x63, 0x2a, 0xf0, 0x17, 0xaa, 0x68, - 0x38, 0xe9, 0x26, 0xc8, 0x91, 0x40, 0x48, 0x8a, - 0xe9, 0x00, 0xc8, 0x91, 0x40, 0x85, 0x41, 0x68, - 0x85, 0x40, 0x4c, 0xe5, 0x27, 0x48, 0xa9, 0x00, - 0xc8, 0x91, 0x40, 0xc8, 0x91, 0x40, 0xad, 0xb6, - 0x2a, 0xf0, 0x0b, 0x68, 0x85, 0x74, 0x85, 0x70, - 0x68, 0x85, 0x73, 0x85, 0x6f, 0x60, 0x68, 0x85, - 0x4d, 0x85, 0xcb, 0x68, 0x85, 0x4c, 0x85, 0xca, - 0x60, 0xa5, 0x39, 0xcd, 0x03, 0x1d, 0xf0, 0x12, - 0x8d, 0x56, 0x2a, 0xa5, 0x38, 0x8d, 0x55, 0x2a, - 0xad, 0x02, 0x1d, 0x85, 0x38, 0xad, 0x03, 0x1d, - 0x85, 0x39, 0xa5, 0x37, 0xcd, 0x05, 0x1d, 0xf0, - 0x12, 0x8d, 0x54, 0x2a, 0xa5, 0x36, 0x8d, 0x53, - 0x2a, 0xad, 0x04, 0x1d, 0x85, 0x36, 0xad, 0x05, - 0x1d, 0x85, 0x37, 0x60, 0x49, 0x4e, 0x49, 0xd4, - 0x4c, 0x4f, 0x41, 0xc4, 0x53, 0x41, 0x56, 0xc5, - 0x52, 0x55, 0xce, 0x43, 0x48, 0x41, 0x49, 0xce, - 0x44, 0x45, 0x4c, 0x45, 0x54, 0xc5, 0x4c, 0x4f, - 0x43, 0xcb, 0x55, 0x4e, 0x4c, 0x4f, 0x43, 0xcb, - 0x43, 0x4c, 0x4f, 0x53, 0xc5, 0x52, 0x45, 0x41, - 0xc4, 0x45, 0x58, 0x45, 0xc3, 0x57, 0x52, 0x49, - 0x54, 0xc5, 0x50, 0x4f, 0x53, 0x49, 0x54, 0x49, - 0x4f, 0xce, 0x4f, 0x50, 0x45, 0xce, 0x41, 0x50, - 0x50, 0x45, 0x4e, 0xc4, 0x52, 0x45, 0x4e, 0x41, - 0x4d, 0xc5, 0x43, 0x41, 0x54, 0x41, 0x4c, 0x4f, - 0xc7, 0x4d, 0x4f, 0xce, 0x4e, 0x4f, 0x4d, 0x4f, - 0xce, 0x50, 0x52, 0xa3, 0x49, 0x4e, 0xa3, 0x4d, - 0x41, 0x58, 0x46, 0x49, 0x4c, 0x45, 0xd3, 0x46, - 0xd0, 0x49, 0x4e, 0xd4, 0x42, 0x53, 0x41, 0x56, - 0xc5, 0x42, 0x4c, 0x4f, 0x41, 0xc4, 0x42, 0x52, - - 0x55, 0xce, 0x56, 0x45, 0x52, 0x49, 0x46, 0xd9, - 0x00, 0x21, 0x70, 0xa0, 0x70, 0xa1, 0x70, 0xa0, - 0x70, 0x20, 0x70, 0x20, 0x70, 0x20, 0x70, 0x20, - 0x70, 0x60, 0x00, 0x22, 0x06, 0x20, 0x74, 0x22, - 0x06, 0x22, 0x04, 0x23, 0x78, 0x22, 0x70, 0x30, - 0x70, 0x40, 0x70, 0x40, 0x80, 0x40, 0x80, 0x08, - 0x00, 0x08, 0x00, 0x04, 0x00, 0x40, 0x70, 0x40, - 0x00, 0x21, 0x79, 0x20, 0x71, 0x20, 0x71, 0x20, - 0x70, 0xd6, 0xc4, 0xd3, 0xcc, 0xd2, 0xc2, 0xc1, - 0xc3, 0xc9, 0xcf, 0x40, 0x20, 0x10, 0x08, 0x04, - 0x02, 0x01, 0xc0, 0xa0, 0x90, 0x00, 0x00, 0xfe, - 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x07, - 0x00, 0x01, 0x00, 0xff, 0x7f, 0x00, 0x00, 0xff, - 0x7f, 0x00, 0x00, 0xff, 0x7f, 0x00, 0x00, 0xff, - 0xff, 0x0d, 0x07, 0x8d, 0x4c, 0x41, 0x4e, 0x47, - 0x55, 0x41, 0x47, 0x45, 0x20, 0x4e, 0x4f, 0x54, - 0x20, 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, - 0x4c, 0xc5, 0x52, 0x41, 0x4e, 0x47, 0x45, 0x20, - 0x45, 0x52, 0x52, 0x4f, 0xd2, 0x57, 0x52, 0x49, - 0x54, 0x45, 0x20, 0x50, 0x52, 0x4f, 0x54, 0x45, - 0x43, 0x54, 0x45, 0xc4, 0x45, 0x4e, 0x44, 0x20, - 0x4f, 0x46, 0x20, 0x44, 0x41, 0x54, 0xc1, 0x46, - 0x49, 0x4c, 0x45, 0x20, 0x4e, 0x4f, 0x54, 0x20, - 0x46, 0x4f, 0x55, 0x4e, 0xc4, 0x56, 0x4f, 0x4c, - 0x55, 0x4d, 0x45, 0x20, 0x4d, 0x49, 0x53, 0x4d, - 0x41, 0x54, 0x43, 0xc8, 0x49, 0x2f, 0x4f, 0x20, - 0x45, 0x52, 0x52, 0x4f, 0xd2, 0x44, 0x49, 0x53, - 0x4b, 0x20, 0x46, 0x55, 0x4c, 0xcc, 0x46, 0x49, - 0x4c, 0x45, 0x20, 0x4c, 0x4f, 0x43, 0x4b, 0x45, - 0xc4, 0x53, 0x59, 0x4e, 0x54, 0x41, 0x58, 0x20, - 0x45, 0x52, 0x52, 0x4f, 0xd2, 0x4e, 0x4f, 0x20, - 0x42, 0x55, 0x46, 0x46, 0x45, 0x52, 0x53, 0x20, - - 0x41, 0x56, 0x41, 0x49, 0x4c, 0x41, 0x42, 0x4c, - 0xc5, 0x46, 0x49, 0x4c, 0x45, 0x20, 0x54, 0x59, - 0x50, 0x45, 0x20, 0x4d, 0x49, 0x53, 0x4d, 0x41, - 0x54, 0x43, 0xc8, 0x50, 0x52, 0x4f, 0x47, 0x52, - 0x41, 0x4d, 0x20, 0x54, 0x4f, 0x4f, 0x20, 0x4c, - 0x41, 0x52, 0x47, 0xc5, 0x4e, 0x4f, 0x54, 0x20, - 0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x20, 0x43, - 0x4f, 0x4d, 0x4d, 0x41, 0x4e, 0xc4, 0x8d, 0x00, - 0x03, 0x19, 0x19, 0x24, 0x33, 0x3e, 0x4c, 0x5b, - 0x64, 0x6d, 0x78, 0x84, 0x98, 0xaa, 0xbb, 0x2d, - 0x18, 0x00, 0x00, 0xf0, 0xfd, 0x1b, 0xfd, 0x03, - 0x03, 0xfb, 0x0a, 0x28, 0x8d, 0x0a, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc5, 0xcc, - 0xcc, 0xcf, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, - 0xa0, 0x03, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xc1, 0xd0, 0xd0, 0xcc, 0xc5, 0xd3, 0xcf, 0xc6, - 0xd4, 0xe8, 0x37, 0xbb, 0x33, 0xbb, 0x34, 0x00, - 0x40, 0x7e, 0x33, 0x21, 0x2b, 0x05, 0x2c, 0x57, - 0x2c, 0x6f, 0x2c, 0x2a, 0x2d, 0x97, 0x2d, 0xee, - 0x2c, 0xf5, 0x2c, 0x39, 0x2c, 0x11, 0x2d, 0x8d, - 0x2e, 0x17, 0x2d, 0x7e, 0x33, 0x7e, 0x33, 0x89, - 0x2c, 0x95, 0x2c, 0x86, 0x2c, 0x92, 0x2c, 0x7e, - 0x33, 0x7e, 0x33, 0xbd, 0x2c, 0xc9, 0x2c, 0xba, - 0x2c, 0xc6, 0x2c, 0x7e, 0x33, 0xe0, 0x00, 0xf0, - - 0x02, 0xa2, 0x02, 0x8e, 0x5f, 0x2a, 0xba, 0x8e, - 0x9b, 0x33, 0x20, 0x6a, 0x2e, 0xad, 0xbb, 0x35, - 0xc9, 0x0d, 0xb0, 0x0b, 0x0a, 0xaa, 0xbd, 0xca, - 0x2a, 0x48, 0xbd, 0xc9, 0x2a, 0x48, 0x60, 0x4c, - 0x63, 0x33, 0x20, 0x28, 0x2b, 0x4c, 0x7f, 0x33, - 0x20, 0xdc, 0x2b, 0xa9, 0x01, 0x8d, 0xe3, 0x35, - 0xae, 0xbe, 0x35, 0xad, 0xbd, 0x35, 0xd0, 0x05, - 0xe0, 0x00, 0xd0, 0x01, 0xe8, 0x8d, 0xe8, 0x35, - 0x8e, 0xe9, 0x35, 0x20, 0xc9, 0x31, 0x90, 0x5e, - 0x8e, 0x9c, 0x33, 0xae, 0x5f, 0x2a, 0xbd, 0x09, - 0x29, 0xae, 0x9c, 0x33, 0x4a, 0xb0, 0x0d, 0xad, - 0x51, 0x2a, 0xc9, 0xc0, 0xd0, 0x03, 0x4c, 0x5f, - 0x33, 0x4c, 0x73, 0x33, 0xa9, 0x00, 0x9d, 0xe8, - 0x34, 0xa9, 0x01, 0x9d, 0xe7, 0x34, 0x8e, 0x9c, - 0x33, 0x20, 0x44, 0x32, 0xae, 0x9c, 0x33, 0x9d, - 0xc7, 0x34, 0x8d, 0xd2, 0x35, 0x8d, 0xd4, 0x35, - 0xad, 0xf1, 0x35, 0x9d, 0xc6, 0x34, 0x8d, 0xd1, - 0x35, 0x8d, 0xd3, 0x35, 0xad, 0xc2, 0x35, 0x9d, - 0xc8, 0x34, 0x20, 0x37, 0x30, 0x20, 0x0c, 0x2f, - 0x20, 0xd6, 0x37, 0x20, 0x3a, 0x2f, 0xae, 0x9c, - 0x33, 0xa9, 0x06, 0x8d, 0xc5, 0x35, 0xbd, 0xc6, - 0x34, 0x8d, 0xd1, 0x35, 0xbd, 0xc7, 0x34, 0x8d, - 0xd2, 0x35, 0xbd, 0xc8, 0x34, 0x8d, 0xc2, 0x35, - 0x8d, 0xf6, 0x35, 0xbd, 0xe7, 0x34, 0x8d, 0xee, - 0x35, 0xbd, 0xe8, 0x34, 0x8d, 0xef, 0x35, 0x8e, - 0xd9, 0x35, 0xa9, 0xff, 0x8d, 0xe0, 0x35, 0x8d, - 0xe1, 0x35, 0xad, 0xe2, 0x33, 0x8d, 0xda, 0x35, - 0x18, 0x4c, 0x5e, 0x2f, 0xa9, 0x00, 0xaa, 0x9d, - 0xd1, 0x35, 0xe8, 0xe0, 0x2d, 0xd0, 0xf8, 0xad, - 0xbf, 0x35, 0x49, 0xff, 0x8d, 0xf9, 0x35, 0xad, - 0xc0, 0x35, 0x8d, 0xf8, 0x35, 0xad, 0xc1, 0x35, - 0x0a, 0x0a, 0x0a, 0x0a, 0xaa, 0x8e, 0xf7, 0x35, - - 0xa9, 0x11, 0x8d, 0xfa, 0x35, 0x60, 0x20, 0x1d, - 0x2f, 0x20, 0x34, 0x2f, 0x20, 0xc3, 0x32, 0xa9, - 0x02, 0x2d, 0xd5, 0x35, 0xf0, 0x21, 0x20, 0xf7, - 0x2f, 0xa9, 0x00, 0x18, 0x20, 0x11, 0x30, 0x38, - 0xce, 0xd8, 0x35, 0xd0, 0xf7, 0xae, 0xd9, 0x35, - 0xad, 0xee, 0x35, 0x9d, 0xe7, 0x34, 0xad, 0xef, - 0x35, 0x9d, 0xe8, 0x34, 0x20, 0x37, 0x30, 0x4c, - 0x7f, 0x33, 0x20, 0x28, 0x2b, 0xad, 0xf6, 0x35, - 0x30, 0x2b, 0xad, 0xbd, 0x35, 0x85, 0x42, 0xad, - 0xbe, 0x35, 0x85, 0x43, 0xae, 0x9c, 0x33, 0x20, - 0x1c, 0x32, 0x20, 0x37, 0x30, 0x4c, 0x7f, 0x33, - 0xad, 0xbc, 0x35, 0xc9, 0x05, 0xb0, 0x0b, 0x0a, - 0xaa, 0xbd, 0xe6, 0x2a, 0x48, 0xbd, 0xe5, 0x2a, - 0x48, 0x60, 0x4c, 0x67, 0x33, 0x4c, 0x7b, 0x33, - 0xad, 0xf6, 0x35, 0x30, 0xf8, 0xad, 0xbc, 0x35, - 0xc9, 0x05, 0xb0, 0xee, 0x0a, 0xaa, 0xbd, 0xf2, - 0x2a, 0x48, 0xbd, 0xf1, 0x2a, 0x48, 0x60, 0x20, - 0x00, 0x33, 0x20, 0xa8, 0x2c, 0x8d, 0xc3, 0x35, - 0x4c, 0x7f, 0x33, 0x20, 0x00, 0x33, 0x20, 0xb5, - 0x31, 0x20, 0xa8, 0x2c, 0x48, 0x20, 0xa2, 0x31, - 0xa0, 0x00, 0x68, 0x91, 0x42, 0x4c, 0x96, 0x2c, - 0x20, 0xb6, 0x30, 0xb0, 0x0b, 0xb1, 0x42, 0x48, - 0x20, 0x5b, 0x31, 0x20, 0x94, 0x31, 0x68, 0x60, - 0x4c, 0x6f, 0x33, 0x20, 0x00, 0x33, 0xad, 0xc3, - 0x35, 0x20, 0xda, 0x2c, 0x4c, 0x7f, 0x33, 0x20, - 0x00, 0x33, 0x20, 0xa2, 0x31, 0xa0, 0x00, 0xb1, - 0x42, 0x20, 0xda, 0x2c, 0x20, 0xb5, 0x31, 0x4c, - 0xca, 0x2c, 0x48, 0x20, 0xb6, 0x30, 0x68, 0x91, - 0x42, 0xa9, 0x40, 0x0d, 0xd5, 0x35, 0x8d, 0xd5, - 0x35, 0x20, 0x5b, 0x31, 0x4c, 0x94, 0x31, 0xa9, - 0x80, 0x8d, 0x9e, 0x33, 0xd0, 0x05, 0xa9, 0x00, - 0x8d, 0x9e, 0x33, 0x20, 0x28, 0x2b, 0xae, 0x9c, - - 0x33, 0xbd, 0xc8, 0x34, 0x29, 0x7f, 0x0d, 0x9e, - 0x33, 0x9d, 0xc8, 0x34, 0x20, 0x37, 0x30, 0x4c, - 0x7f, 0x33, 0x20, 0x00, 0x33, 0x4c, 0x7f, 0x33, - 0x20, 0x28, 0x2b, 0x20, 0xb6, 0x30, 0xb0, 0xef, - 0xee, 0xe4, 0x35, 0xd0, 0xf6, 0xee, 0xe5, 0x35, - 0x4c, 0x1b, 0x2d, 0x20, 0x28, 0x2b, 0xae, 0x9c, - 0x33, 0xbd, 0xc8, 0x34, 0x10, 0x03, 0x4c, 0x7b, - 0x33, 0xae, 0x9c, 0x33, 0xbd, 0xc6, 0x34, 0x8d, - 0xd1, 0x35, 0x9d, 0xe6, 0x34, 0xa9, 0xff, 0x9d, - 0xc6, 0x34, 0xbc, 0xc7, 0x34, 0x8c, 0xd2, 0x35, - 0x20, 0x37, 0x30, 0x18, 0x20, 0x5e, 0x2f, 0xb0, - 0x2a, 0x20, 0x0c, 0x2f, 0xa0, 0x0c, 0x8c, 0x9c, - 0x33, 0xb1, 0x42, 0x30, 0x0b, 0xf0, 0x09, 0x48, - 0xc8, 0xb1, 0x42, 0xa8, 0x68, 0x20, 0x89, 0x2d, - 0xac, 0x9c, 0x33, 0xc8, 0xc8, 0xd0, 0xe7, 0xad, - 0xd3, 0x35, 0xac, 0xd4, 0x35, 0x20, 0x89, 0x2d, - 0x38, 0xb0, 0xd1, 0x20, 0xfb, 0x2f, 0x4c, 0x7f, - 0x33, 0x38, 0x20, 0xdd, 0x32, 0xa9, 0x00, 0xa2, - 0x03, 0x9d, 0xf0, 0x35, 0xca, 0x10, 0xfa, 0x60, - 0x20, 0xdc, 0x2b, 0xa9, 0xff, 0x8d, 0xf9, 0x35, - 0x20, 0xf7, 0x2f, 0xa9, 0x16, 0x8d, 0x9d, 0x33, - 0x20, 0x2f, 0x2e, 0x20, 0x2f, 0x2e, 0xa2, 0x0b, - 0xbd, 0xaf, 0x33, 0x20, 0xed, 0xfd, 0xca, 0x10, - 0xf7, 0x86, 0x45, 0xad, 0xf6, 0x37, 0x85, 0x44, - 0x20, 0x42, 0x2e, 0x20, 0x2f, 0x2e, 0x20, 0x2f, - 0x2e, 0x18, 0x20, 0x11, 0x30, 0xb0, 0x5d, 0xa2, - 0x00, 0x8e, 0x9c, 0x33, 0xbd, 0xc6, 0x34, 0xf0, - 0x53, 0x30, 0x4a, 0xa0, 0xa0, 0xbd, 0xc8, 0x34, - 0x10, 0x02, 0xa0, 0xaa, 0x98, 0x20, 0xed, 0xfd, - 0xbd, 0xc8, 0x34, 0x29, 0x7f, 0xa0, 0x07, 0x0a, - 0x0a, 0xb0, 0x03, 0x88, 0xd0, 0xfa, 0xb9, 0xa7, - 0x33, 0x20, 0xed, 0xfd, 0xa9, 0xa0, 0x20, 0xed, - - 0xfd, 0xbd, 0xe7, 0x34, 0x85, 0x44, 0xbd, 0xe8, - 0x34, 0x85, 0x45, 0x20, 0x42, 0x2e, 0xa9, 0xa0, - 0x20, 0xed, 0xfd, 0xe8, 0xe8, 0xe8, 0xa0, 0x1d, - 0xbd, 0xc6, 0x34, 0x20, 0xed, 0xfd, 0xe8, 0x88, - 0x10, 0xf6, 0x20, 0x2f, 0x2e, 0x20, 0x30, 0x32, - 0x90, 0xa7, 0xb0, 0x9e, 0x4c, 0x7f, 0x33, 0xa9, - 0x8d, 0x20, 0xed, 0xfd, 0xce, 0x9d, 0x33, 0xd0, - 0x08, 0x20, 0x0c, 0xfd, 0xa9, 0x15, 0x8d, 0x9d, - 0x33, 0x60, 0xa0, 0x02, 0xa9, 0x00, 0x48, 0xa5, - 0x44, 0xd9, 0xa4, 0x33, 0x90, 0x12, 0xf9, 0xa4, - 0x33, 0x85, 0x44, 0xa5, 0x45, 0xe9, 0x00, 0x85, - 0x45, 0x68, 0x69, 0x00, 0x48, 0x4c, 0x47, 0x2e, - 0x68, 0x09, 0xb0, 0x20, 0xed, 0xfd, 0x88, 0x10, - 0xdb, 0x60, 0x20, 0x08, 0x2f, 0xa0, 0x00, 0x8c, - 0xc5, 0x35, 0xb1, 0x42, 0x99, 0xd1, 0x35, 0xc8, - 0xc0, 0x2d, 0xd0, 0xf6, 0x18, 0x60, 0x20, 0x08, - 0x2f, 0xa0, 0x00, 0xb9, 0xd1, 0x35, 0x91, 0x42, - 0xc8, 0xc0, 0x2d, 0xd0, 0xf6, 0x60, 0x20, 0xdc, - 0x2b, 0xa9, 0x04, 0x20, 0x58, 0x30, 0xad, 0xf9, - 0x35, 0x49, 0xff, 0x8d, 0xc1, 0x33, 0xa9, 0x11, - 0x8d, 0xeb, 0x33, 0xa9, 0x01, 0x8d, 0xec, 0x33, - 0xa2, 0x38, 0xa9, 0x00, 0x9d, 0xbb, 0x33, 0xe8, - 0xd0, 0xfa, 0xa2, 0x0c, 0xe0, 0x8c, 0xf0, 0x14, - 0xa0, 0x03, 0xb9, 0xa0, 0x33, 0x9d, 0xf3, 0x33, - 0xe8, 0x88, 0x10, 0xf6, 0xe0, 0x44, 0xd0, 0xec, - 0xa2, 0x48, 0xd0, 0xe8, 0x20, 0xfb, 0x2f, 0xa2, - 0x00, 0x8a, 0x9d, 0xbb, 0x34, 0xe8, 0xd0, 0xfa, - 0x20, 0x45, 0x30, 0xa9, 0x11, 0xac, 0xf0, 0x33, - 0x88, 0x88, 0x8d, 0xec, 0x37, 0x8d, 0xbc, 0x34, - 0x8c, 0xbd, 0x34, 0xc8, 0x8c, 0xed, 0x37, 0xa9, - 0x02, 0x20, 0x58, 0x30, 0xac, 0xbd, 0x34, 0x88, - 0x30, 0x05, 0xd0, 0xec, 0x98, 0xf0, 0xe6, 0x20, - - 0xc2, 0x37, 0x20, 0x4a, 0x37, 0x4c, 0x7f, 0x33, - 0xa2, 0x00, 0xf0, 0x06, 0xa2, 0x02, 0xd0, 0x02, - 0xa2, 0x04, 0xbd, 0xc7, 0x35, 0x85, 0x42, 0xbd, - 0xc8, 0x35, 0x85, 0x43, 0x60, 0x2c, 0xd5, 0x35, - 0x70, 0x01, 0x60, 0x20, 0xe4, 0x2f, 0xa9, 0x02, - 0x20, 0x52, 0x30, 0xa9, 0xbf, 0x2d, 0xd5, 0x35, - 0x8d, 0xd5, 0x35, 0x60, 0xad, 0xd5, 0x35, 0x30, - 0x01, 0x60, 0x20, 0x4b, 0x2f, 0xa9, 0x02, 0x20, - 0x52, 0x30, 0xa9, 0x7f, 0x2d, 0xd5, 0x35, 0x8d, - 0xd5, 0x35, 0x60, 0xad, 0xc9, 0x35, 0x8d, 0xf0, - 0x37, 0xad, 0xca, 0x35, 0x8d, 0xf1, 0x37, 0xae, - 0xd3, 0x35, 0xac, 0xd4, 0x35, 0x60, 0x08, 0x20, - 0x34, 0x2f, 0x20, 0x4b, 0x2f, 0x20, 0x0c, 0x2f, - 0x28, 0xb0, 0x09, 0xae, 0xd1, 0x35, 0xac, 0xd2, - 0x35, 0x4c, 0xb5, 0x2f, 0xa0, 0x01, 0xb1, 0x42, - 0xf0, 0x08, 0xaa, 0xc8, 0xb1, 0x42, 0xa8, 0x4c, - 0xb5, 0x2f, 0xad, 0xbb, 0x35, 0xc9, 0x04, 0xf0, - 0x02, 0x38, 0x60, 0x20, 0x44, 0x32, 0xa0, 0x02, - 0x91, 0x42, 0x48, 0x88, 0xad, 0xf1, 0x35, 0x91, - 0x42, 0x48, 0x20, 0x3a, 0x2f, 0x20, 0xd6, 0x37, - 0xa0, 0x05, 0xad, 0xde, 0x35, 0x91, 0x42, 0xc8, - 0xad, 0xdf, 0x35, 0x91, 0x42, 0x68, 0xaa, 0x68, - 0xa8, 0xa9, 0x02, 0xd0, 0x02, 0xa9, 0x01, 0x8e, - 0xd3, 0x35, 0x8c, 0xd4, 0x35, 0x20, 0x52, 0x30, - 0xa0, 0x05, 0xb1, 0x42, 0x8d, 0xdc, 0x35, 0x18, - 0x6d, 0xda, 0x35, 0x8d, 0xde, 0x35, 0xc8, 0xb1, - 0x42, 0x8d, 0xdd, 0x35, 0x6d, 0xdb, 0x35, 0x8d, - 0xdf, 0x35, 0x18, 0x60, 0x20, 0xe4, 0x2f, 0xa9, - 0x01, 0x4c, 0x52, 0x30, 0xac, 0xcb, 0x35, 0xad, - 0xcc, 0x35, 0x8c, 0xf0, 0x37, 0x8d, 0xf1, 0x37, - 0xae, 0xd6, 0x35, 0xac, 0xd7, 0x35, 0x60, 0xa9, - 0x01, 0xd0, 0x02, 0xa9, 0x02, 0xac, 0xc3, 0x2a, - - 0x8c, 0xf0, 0x37, 0xac, 0xc4, 0x2a, 0x8c, 0xf1, - 0x37, 0xae, 0xfa, 0x35, 0xa0, 0x00, 0x4c, 0x52, - 0x30, 0x08, 0x20, 0x45, 0x30, 0x28, 0xb0, 0x08, - 0xac, 0xbd, 0x33, 0xae, 0xbc, 0x33, 0xd0, 0x0a, - 0xae, 0xbc, 0x34, 0xd0, 0x02, 0x38, 0x60, 0xac, - 0xbd, 0x34, 0x8e, 0x97, 0x33, 0x8c, 0x98, 0x33, - 0xa9, 0x01, 0x20, 0x52, 0x30, 0x18, 0x60, 0x20, - 0x45, 0x30, 0xae, 0x97, 0x33, 0xac, 0x98, 0x33, - 0xa9, 0x02, 0x4c, 0x52, 0x30, 0xad, 0xc5, 0x2a, - 0x8d, 0xf0, 0x37, 0xad, 0xc6, 0x2a, 0x8d, 0xf1, - 0x37, 0x60, 0x8e, 0xec, 0x37, 0x8c, 0xed, 0x37, - 0x8d, 0xf4, 0x37, 0xc9, 0x02, 0xd0, 0x06, 0x0d, - 0xd5, 0x35, 0x8d, 0xd5, 0x35, 0xad, 0xf9, 0x35, - 0x49, 0xff, 0x8d, 0xeb, 0x37, 0xad, 0xf7, 0x35, - 0x8d, 0xe9, 0x37, 0xad, 0xf8, 0x35, 0x8d, 0xea, - 0x37, 0xad, 0xe2, 0x35, 0x8d, 0xf2, 0x37, 0xad, - 0xe3, 0x35, 0x8d, 0xf3, 0x37, 0xa9, 0x01, 0x8d, - 0xe8, 0x37, 0xac, 0xc1, 0x2a, 0xad, 0xc2, 0x2a, - 0x20, 0xb5, 0x37, 0xad, 0xf6, 0x37, 0x8d, 0xbf, - 0x35, 0xa9, 0xff, 0x8d, 0xeb, 0x37, 0xb0, 0x01, - 0x60, 0xad, 0xf5, 0x37, 0xa0, 0x07, 0xc9, 0x20, - 0xf0, 0x08, 0xa0, 0x04, 0xc9, 0x10, 0xf0, 0x02, - 0xa0, 0x08, 0x98, 0x4c, 0x85, 0x33, 0xad, 0xe4, - 0x35, 0xcd, 0xe0, 0x35, 0xd0, 0x08, 0xad, 0xe5, - 0x35, 0xcd, 0xe1, 0x35, 0xf0, 0x66, 0x20, 0x1d, - 0x2f, 0xad, 0xe5, 0x35, 0xcd, 0xdd, 0x35, 0x90, - 0x1c, 0xd0, 0x08, 0xad, 0xe4, 0x35, 0xcd, 0xdc, - 0x35, 0x90, 0x12, 0xad, 0xe5, 0x35, 0xcd, 0xdf, - 0x35, 0x90, 0x10, 0xd0, 0x08, 0xad, 0xe4, 0x35, - 0xcd, 0xde, 0x35, 0x90, 0x06, 0x20, 0x5e, 0x2f, - 0x90, 0xd7, 0x60, 0x38, 0xad, 0xe4, 0x35, 0xed, - 0xdc, 0x35, 0x0a, 0x69, 0x0c, 0xa8, 0x20, 0x0c, - - 0x2f, 0xb1, 0x42, 0xd0, 0x0f, 0xad, 0xbb, 0x35, - 0xc9, 0x04, 0xf0, 0x02, 0x38, 0x60, 0x20, 0x34, - 0x31, 0x4c, 0x20, 0x31, 0x8d, 0xd6, 0x35, 0xc8, - 0xb1, 0x42, 0x8d, 0xd7, 0x35, 0x20, 0xdc, 0x2f, - 0xad, 0xe4, 0x35, 0x8d, 0xe0, 0x35, 0xad, 0xe5, - 0x35, 0x8d, 0xe1, 0x35, 0x20, 0x10, 0x2f, 0xac, - 0xe6, 0x35, 0x18, 0x60, 0x8c, 0x9d, 0x33, 0x20, - 0x44, 0x32, 0xac, 0x9d, 0x33, 0xc8, 0x91, 0x42, - 0x8d, 0xd7, 0x35, 0x88, 0xad, 0xf1, 0x35, 0x91, - 0x42, 0x8d, 0xd6, 0x35, 0x20, 0x10, 0x2f, 0x20, - 0xd6, 0x37, 0xa9, 0xc0, 0x0d, 0xd5, 0x35, 0x8d, - 0xd5, 0x35, 0x60, 0xae, 0xea, 0x35, 0x8e, 0xbd, - 0x35, 0xae, 0xeb, 0x35, 0x8e, 0xbe, 0x35, 0xae, - 0xec, 0x35, 0xac, 0xed, 0x35, 0x8e, 0xbf, 0x35, - 0x8c, 0xc0, 0x35, 0xe8, 0xd0, 0x01, 0xc8, 0xcc, - 0xe9, 0x35, 0xd0, 0x11, 0xec, 0xe8, 0x35, 0xd0, - 0x0c, 0xa2, 0x00, 0xa0, 0x00, 0xee, 0xea, 0x35, - 0xd0, 0x03, 0xee, 0xeb, 0x35, 0x8e, 0xec, 0x35, - 0x8c, 0xed, 0x35, 0x60, 0xee, 0xe6, 0x35, 0xd0, - 0x08, 0xee, 0xe4, 0x35, 0xd0, 0x03, 0xee, 0xe5, - 0x35, 0x60, 0xac, 0xc3, 0x35, 0xae, 0xc4, 0x35, - 0x84, 0x42, 0x86, 0x43, 0xee, 0xc3, 0x35, 0xd0, - 0x03, 0xee, 0xc4, 0x35, 0x60, 0xac, 0xc1, 0x35, - 0xd0, 0x08, 0xae, 0xc2, 0x35, 0xf0, 0x07, 0xce, - 0xc2, 0x35, 0xce, 0xc1, 0x35, 0x60, 0x4c, 0x7f, - 0x33, 0x20, 0xf7, 0x2f, 0xad, 0xc3, 0x35, 0x85, - 0x42, 0xad, 0xc4, 0x35, 0x85, 0x43, 0xa9, 0x01, - 0x8d, 0x9d, 0x33, 0xa9, 0x00, 0x8d, 0xd8, 0x35, - 0x18, 0xee, 0xd8, 0x35, 0x20, 0x11, 0x30, 0xb0, - 0x51, 0xa2, 0x00, 0x8e, 0x9c, 0x33, 0xbd, 0xc6, - 0x34, 0xf0, 0x1f, 0x30, 0x22, 0xa0, 0x00, 0xe8, - 0xe8, 0xe8, 0xb1, 0x42, 0xdd, 0xc6, 0x34, 0xd0, - - 0x0a, 0xc8, 0xc0, 0x1e, 0xd0, 0xf3, 0xae, 0x9c, - 0x33, 0x18, 0x60, 0x20, 0x30, 0x32, 0x90, 0xdb, - 0xb0, 0xcf, 0xac, 0x9d, 0x33, 0xd0, 0xc1, 0xac, - 0x9d, 0x33, 0xd0, 0xef, 0xa0, 0x00, 0xe8, 0xe8, - 0xe8, 0xb1, 0x42, 0x9d, 0xc6, 0x34, 0xc8, 0xc0, - 0x1e, 0xd0, 0xf5, 0xae, 0x9c, 0x33, 0x38, 0x60, - 0x18, 0xad, 0x9c, 0x33, 0x69, 0x23, 0xaa, 0xe0, - 0xf5, 0x60, 0xa9, 0x00, 0xac, 0x9d, 0x33, 0xd0, - 0x97, 0x4c, 0x77, 0x33, 0xad, 0xf1, 0x35, 0xf0, - 0x21, 0xce, 0xf0, 0x35, 0x30, 0x17, 0x18, 0xa2, - 0x04, 0x3e, 0xf1, 0x35, 0xca, 0xd0, 0xfa, 0x90, - 0xf0, 0xee, 0xee, 0x35, 0xd0, 0x03, 0xee, 0xef, - 0x35, 0xad, 0xf0, 0x35, 0x60, 0xa9, 0x00, 0x8d, - 0xf1, 0x35, 0xa9, 0x00, 0x8d, 0x9e, 0x33, 0x20, - 0xf7, 0x2f, 0x18, 0xad, 0xeb, 0x33, 0x6d, 0xec, - 0x33, 0xf0, 0x09, 0xcd, 0xef, 0x33, 0x90, 0x14, - 0xa9, 0xff, 0xd0, 0x0a, 0xad, 0x9e, 0x33, 0xd0, - 0x37, 0xa9, 0x01, 0x8d, 0x9e, 0x33, 0x8d, 0xec, - 0x33, 0x18, 0x69, 0x11, 0x8d, 0xeb, 0x33, 0x8d, - 0xf1, 0x35, 0xa8, 0x0a, 0x0a, 0xa8, 0xa2, 0x04, - 0x18, 0xb9, 0xf6, 0x33, 0x9d, 0xf1, 0x35, 0xf0, - 0x06, 0x38, 0xa9, 0x00, 0x99, 0xf6, 0x33, 0x88, - 0xca, 0xd0, 0xee, 0x90, 0xbd, 0x20, 0xfb, 0x2f, - 0xad, 0xf0, 0x33, 0x8d, 0xf0, 0x35, 0xd0, 0x89, - 0x4c, 0x77, 0x33, 0xad, 0xf1, 0x35, 0xd0, 0x01, - 0x60, 0x48, 0x20, 0xf7, 0x2f, 0xac, 0xf0, 0x35, - 0x68, 0x18, 0x20, 0xdd, 0x32, 0xa9, 0x00, 0x8d, - 0xf1, 0x35, 0x4c, 0xfb, 0x2f, 0xa2, 0xfc, 0x7e, - 0xf6, 0x34, 0xe8, 0xd0, 0xfa, 0xc8, 0xcc, 0xf0, - 0x33, 0xd0, 0xf2, 0x0a, 0x0a, 0xa8, 0xf0, 0x0f, - 0xa2, 0x04, 0xbd, 0xf1, 0x35, 0x19, 0xf6, 0x33, - 0x99, 0xf6, 0x33, 0x88, 0xca, 0xd0, 0xf3, 0x60, - - 0xad, 0xbd, 0x35, 0x8d, 0xe6, 0x35, 0x8d, 0xea, - 0x35, 0xad, 0xbe, 0x35, 0x8d, 0xe4, 0x35, 0x8d, - 0xeb, 0x35, 0xa9, 0x00, 0x8d, 0xe5, 0x35, 0xa0, - 0x10, 0xaa, 0xad, 0xe6, 0x35, 0x4a, 0xb0, 0x03, - 0x8a, 0x90, 0x0e, 0x18, 0xad, 0xe5, 0x35, 0x6d, - 0xe8, 0x35, 0x8d, 0xe5, 0x35, 0x8a, 0x6d, 0xe9, - 0x35, 0x6a, 0x6e, 0xe5, 0x35, 0x6e, 0xe4, 0x35, - 0x6e, 0xe6, 0x35, 0x88, 0xd0, 0xdb, 0xad, 0xbf, - 0x35, 0x8d, 0xec, 0x35, 0x6d, 0xe6, 0x35, 0x8d, - 0xe6, 0x35, 0xad, 0xc0, 0x35, 0x8d, 0xed, 0x35, - 0x6d, 0xe4, 0x35, 0x8d, 0xe4, 0x35, 0xa9, 0x00, - 0x6d, 0xe5, 0x35, 0x8d, 0xe5, 0x35, 0x60, 0xa9, - 0x01, 0xd0, 0x22, 0xa9, 0x02, 0xd0, 0x1e, 0xa9, - 0x03, 0xd0, 0x1a, 0xa9, 0x04, 0xd0, 0x16, 0xa9, - 0x05, 0xd0, 0x12, 0xa9, 0x06, 0xd0, 0x0e, 0x4c, - 0xed, 0x3f, 0xea, 0xa9, 0x0a, 0xd0, 0x06, 0xad, - 0xc5, 0x35, 0x18, 0x90, 0x01, 0x38, 0x08, 0x8d, - 0xc5, 0x35, 0xa9, 0x00, 0x85, 0x48, 0x20, 0x7e, - 0x2e, 0x28, 0xae, 0x9b, 0x33, 0x9a, 0x60, 0x00, - 0x00, 0x00, 0x00, 0xf5, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0xf8, 0xff, 0x01, 0x0a, 0x64, 0xd4, - 0xc9, 0xc1, 0xc2, 0xd3, 0xd2, 0xc1, 0xc2, 0xa0, - 0xc5, 0xcd, 0xd5, 0xcc, 0xcf, 0xd6, 0xa0, 0xcb, - 0xd3, 0xc9, 0xc4, 0x02, 0x11, 0x0c, 0x03, 0x00, - 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x11, 0x01, 0x00, 0x00, 0x23, - 0x0d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, - - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0xff, - 0xf8, 0x00, 0x00, 0xff, 0xf8, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x1b, 0x01, 0x00, 0xfe, - 0x01, 0x06, 0x00, 0x75, 0x2a, 0x00, 0x00, 0x00, - 0x18, 0x00, 0x17, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, - 0x01, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -} // namespace DiskImgLib diff --git a/ciderpress/diskimg/DiskFS.cpp b/ciderpress/diskimg/DiskFS.cpp deleted file mode 100644 index 239a631..0000000 --- a/ciderpress/diskimg/DiskFS.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * DiskFS base class. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * A2File - * =========================================================================== - */ - -/* - * Set the quality level (a/k/a damage level) of a file. - * - * Refuse to "improve" the quality level of a file. - */ -void A2File::SetQuality(FileQuality quality) -{ - if (quality == kQualityGood && - (fFileQuality == kQualitySuspicious || fFileQuality == kQualityDamaged)) - { - assert(false); - return; - } - if (quality == kQualitySuspicious && fFileQuality == kQualityDamaged) { - //assert(false); - return; - } - - fFileQuality = quality; -} - -/* - * Reset the quality level after making repairs. - */ -void A2File::ResetQuality(void) -{ - fFileQuality = kQualityGood; -} - - -/* - * =========================================================================== - * DiskFS - * =========================================================================== - */ - -/* - * Set the DiskImg pointer. We add or subtract from the DiskImg's ref count - * so that it can be sure there are no DiskFS objects left dangling when the - * DiskImg is deleted. - */ -void DiskFS::SetDiskImg(DiskImg* pImg) -{ - if (pImg == NULL && fpImg == NULL) { - LOGI("SetDiskImg: no-op (both NULL)"); - return; - } else if (fpImg == pImg) { - LOGI("SetDiskImg: no-op (old == new)"); - return; - } - - if (fpImg != NULL) - fpImg->RemoveDiskFS(this); - if (pImg != NULL) - pImg->AddDiskFS(this); - fpImg = pImg; -} - -/* - * Flush changes to disk. - */ -DIError DiskFS::Flush(DiskImg::FlushMode mode) -{ - SubVolume* pSubVol = GetNextSubVolume(NULL); - DIError dierr; - - while (pSubVol != NULL) { - - // quick sanity check - assert(pSubVol->GetDiskFS()->GetDiskImg() == pSubVol->GetDiskImg()); - - dierr = pSubVol->GetDiskFS()->Flush(mode); // recurse - if (dierr != kDIErrNone) - return dierr; - - pSubVol = GetNextSubVolume(pSubVol); - } - - assert(fpImg != NULL); - - return fpImg->FlushImage(mode); -} - -/* - * Set the "read only" flag on our DiskImg and those of our sub-volumes. - */ -void DiskFS::SetAllReadOnly(bool val) -{ - SubVolume* pSubVol = GetNextSubVolume(NULL); - - /* put current volume in read-only mode */ - if (fpImg != NULL) - fpImg->SetReadOnly(val); - - /* handle our kids */ - while (pSubVol != NULL) { - // quick sanity check - assert(pSubVol->GetDiskFS()->GetDiskImg() == pSubVol->GetDiskImg()); - - //pSubVol->GetDiskImg()->SetReadOnly(val); - pSubVol->GetDiskFS()->SetAllReadOnly(val); // recurse - - pSubVol = GetNextSubVolume(pSubVol); - } -} - - -/* - * The file list looks something like this: - * - * volume-dir - * file1 - * file2 - * subdir1 - * subdir1:file1 - * subdir1:file2 - * subdir1:subsub1 - * subdir1:subsub1:file1 - * subdir1:subsub2 - * subdir1:subsub2:file1 - * subdir1:subsub2:file2 - * subdir1:file3 - * file3 - * - * Everything contained within a subdir comes after the subdir entry and - * before any entries from later subdirs at the same level. - * - * It's unclear whether a linear list or a hierarchical tree structure is - * the most appropriate way to hold the data. The tree is easier to update, - * but the linear list corresponds to the primary view in CiderPress, and - * lists are simpler and easier to manage. For now I'm sticking with a list. - * - * The files MUST be in the order in which they came from the disk. This - * doesn't matter most of the time, but for Pascal volumes it's essential - * for ensuring that the Write command doesn't run over the next file. - */ - -/* - * Add a file to the end of our list. - */ -void DiskFS::AddFileToList(A2File* pFile) -{ - assert(pFile->GetNext() == NULL); - - if (fpA2Head == NULL) { - assert(fpA2Tail == NULL); - fpA2Head = fpA2Tail = pFile; - } else { - pFile->SetPrev(fpA2Tail); - fpA2Tail->SetNext(pFile); - fpA2Tail = pFile; - } -} - -/* - * Insert a file into its appropriate place in the list, based on a file - * hierarchy. - * - * Pass in the thing to be added ("pFile") and the previous entry ("pPrev"). - * An empty hierarchic filesystem will have an entry for the volume dir, so - * we should never have an empty list or a NULL pPrev. - * - * The part where things go pear-shaped happens if "pPrev" is a subdirectory. - * If so, we need to come after all of the subdir's entries, including any - * entries for sub-subdirs. There's no graceful way to go about this in a - * linear list. - * - * (We'd love to be able to find the *next* entry and then back up one, - * but odds are that there isn't a "next" entry if we're busily creating - * files.) - */ -void DiskFS::InsertFileInList(A2File* pFile, A2File* pPrev) -{ - assert(pFile->GetNext() == NULL); - - if (fpA2Head == NULL) { - assert(pPrev == NULL); - fpA2Head = fpA2Tail = pFile; - return; - } else if (pPrev == NULL) { - // create two entries on DOS disk, delete first, add new file - pFile->SetNext(fpA2Head); - fpA2Head = pFile; - return; - } - - /* - * If we're inserting after the parent (i.e. we're the very first thing - * in a subdir) or after a plain file, just drop it in. - * - * If we're inserting after a subdir, go fish. - */ - if (pPrev->IsDirectory() && pFile->GetParent() != pPrev) { - pPrev = SkipSubdir(pPrev); - } - - pFile->SetNext(pPrev->GetNext()); - pPrev->SetNext(pFile); -} - -/* - * Skip over all entries in the subdir we're pointing to. - * - * The return value is the very last entry in the subdir. - */ -A2File* DiskFS::SkipSubdir(A2File* pSubdir) -{ - if (pSubdir->GetNext() == NULL) - return pSubdir; // end of list reached -- subdir is empty - - A2File* pCur = pSubdir; - A2File* pNext = NULL; - - assert(pCur != NULL); // at least one time through the loop - - while (pCur != NULL) { - pNext = pCur->GetNext(); - if (pNext == NULL) // end of list reached - return pCur; - - if (pNext->GetParent() != pSubdir) // end of dir reached - return pCur; - if (pNext->IsDirectory()) - pCur = SkipSubdir(pNext); // get last entry in dir - else - pCur = pNext; // advance forward one - } - - /* should never get here */ - assert(false); - return pNext; -} - -/* - * Delete a member from the list. - * - * We're currently singly-linked, making this rather expensive. - */ -void DiskFS::DeleteFileFromList(A2File* pFile) -{ - if (fpA2Head == pFile) { - /* delete the head of the list */ - fpA2Head = fpA2Head->GetNext(); - delete pFile; - } else { - A2File* pCur = fpA2Head; - while (pCur != NULL) { - if (pCur->GetNext() == pFile) { - /* found it */ - A2File* pNextNext = pCur->GetNext()->GetNext(); - delete pCur->GetNext(); - pCur->SetNext(pNextNext); - break; - } - pCur = pCur->GetNext(); - } - - if (pCur == NULL) { - LOGI("GLITCH: couldn't find element to delete!"); - assert(false); - } - } -} - - -/* - * Access the "next" pointer. - * - * Because we apparently can't declare an anonymous class as a friend - * in MSVC++6.0, this can't be an inline function. - */ -A2File* DiskFS::GetNextFile(A2File* pFile) const -{ - if (pFile == NULL) - return fpA2Head; - else - return pFile->GetNext(); -} - -/* - * Return the #of elements in the linear file list. - * - * Right now the only code that calls this is the disk info panel in - * CiderPress, so we don't need it to be efficient. - */ -long DiskFS::GetFileCount(void) const -{ - long count = 0; - - A2File* pFile = fpA2Head; - while (pFile != NULL) { - count++; - pFile = pFile->GetNext(); - } - - return count; -} - -/* - * Delete all entries in the list. - */ -void DiskFS::DeleteFileList(void) -{ - A2File* pFile; - A2File* pNext; - - pFile = fpA2Head; - while (pFile != NULL) { - pNext = pFile->GetNext(); - delete pFile; - pFile = pNext; - } -} - -/* - * Dump file list. - */ -void DiskFS::DumpFileList(void) -{ - A2File* pFile; - - LOGI("DiskFS file list contents:"); - - pFile = GetNextFile(NULL); - while (pFile != NULL) { - LOGI(" %s", pFile->GetPathName()); - pFile = GetNextFile(pFile); - } -} - -/* - * Run through the list of files and find one that matches (case-insensitive). - * - * This does not attempt to open files in sub-volumes. We could, but it's - * likely that the application has "decorated" the name in some fashion, - * e.g. by prepending the sub-volume's volume name to the filename. May - * be best to let the application dig for the sub-volume. - */ -A2File* DiskFS::GetFileByName(const char* fileName, StringCompareFunc func) -{ - A2File* pFile; - - if (func == NULL) - func = ::strcasecmp; - - pFile = GetNextFile(NULL); - while (pFile != NULL) { - if ((*func)(pFile->GetPathName(), fileName) == 0) - return pFile; - - pFile = GetNextFile(pFile); - } - - return NULL; -} - - -/* - * Add a sub-volume to the end of our list. - * - * Copies some parameters from "this" into pDiskFS, such as whether to - * scan for sub-volumes and the various DiskFS parameters. - * - * Note this happens AFTER the disk has been scanned. - */ -void DiskFS::AddSubVolumeToList(DiskImg* pDiskImg, DiskFS* pDiskFS) -{ - SubVolume* pSubVol; - - /* - * Check the arguments. - */ - if (pDiskImg == NULL || pDiskFS == NULL) { - LOGI(" DiskFS bogus sub volume ptrs %08lx %08lx", - (long) pDiskImg, (long) pDiskFS); - assert(false); - return; - } - if (pDiskImg == fpImg || pDiskFS == this) { - LOGI(" DiskFS attempt to add self to sub-vol list"); - assert(false); - return; - } - if (pDiskFS->GetDiskImg() == NULL) { - LOGI(" DiskFS lacks a DiskImg pointer"); - assert(false); - return; - } - pSubVol = fpSubVolumeHead; - while (pSubVol != NULL) { - if (pSubVol->GetDiskImg() == pDiskImg || - pSubVol->GetDiskFS() == pDiskFS) - { - LOGI(" DiskFS multiple adds on diskimg or diskfs"); - assert(false); - return; - } - pSubVol = pSubVol->GetNext(); - } - - assert(pDiskFS->GetDiskImg() == pDiskImg); - - /* - * Looks good. Add it. - */ - pSubVol = new SubVolume; - if (pSubVol == NULL) - return; - - pSubVol->Create(pDiskImg, pDiskFS); - - if (fpSubVolumeHead == NULL) { - assert(fpSubVolumeTail == NULL); - fpSubVolumeHead = fpSubVolumeTail = pSubVol; - } else { - pSubVol->SetPrev(fpSubVolumeTail); - fpSubVolumeTail->SetNext(pSubVol); - fpSubVolumeTail = pSubVol; - } - - /* make sure inheritable stuff gets copied */ - CopyInheritables(pDiskFS); -} - -/* - * Copy parameters to a sub-volume. - */ -void DiskFS::CopyInheritables(DiskFS* pNewFS) -{ - for (int i = 0; i < (int) NELEM(fParmTable); i++) - pNewFS->fParmTable[i] = fParmTable[i]; - - pNewFS->fScanForSubVolumes = fScanForSubVolumes; - -#if 0 - /* copy scan progress update stuff */ - pNewFS->fpScanProgressCallback = fpScanProgressCallback; - pNewFS->fpScanProgressCookie = fpScanProgressCookie; - pNewFS->fpScanCount = -1; - strcpy(pNewFS->fpScanMsg, "HEY"); -#endif -} - -/* - * Access the "next" pointer. - * - * Because we apparently can't declare an anonymous class as a friend - * in MSVC++6.0, this can't be an inline function. - */ -DiskFS::SubVolume* DiskFS::GetNextSubVolume(const SubVolume* pSubVol) const -{ - if (pSubVol == NULL) - return fpSubVolumeHead; - else - return pSubVol->GetNext(); -} - -/* - * Delete all entries in the list. - */ -void DiskFS::DeleteSubVolumeList(void) -{ - SubVolume* pSubVol; - SubVolume* pNext; - - pSubVol = fpSubVolumeHead; - while (pSubVol != NULL) { - pNext = pSubVol->GetNext(); - delete pSubVol; - pSubVol = pNext; - } -} - - -/* - * Get a parameter. - */ -long DiskFS::GetParameter(DiskFSParameter parm) -{ - assert(parm > kParmUnknown && parm < kParmMax); - return fParmTable[parm]; -} - -/* - * Set a parameter. - * - * The setting propagates to all sub-volumes. - */ -void DiskFS::SetParameter(DiskFSParameter parm, long val) -{ - assert(parm > kParmUnknown && parm < kParmMax); - fParmTable[parm] = val; - - SubVolume* pSubVol = GetNextSubVolume(NULL); - while (pSubVol != NULL) { - pSubVol->GetDiskFS()->SetParameter(parm, val); - pSubVol = GetNextSubVolume(pSubVol); - } -} - - -/* - * Scan for damaged or suspicious files. - */ -void DiskFS::ScanForDamagedFiles(bool* pDamaged, bool* pSuspicious) -{ - A2File* pFile; - - *pDamaged = *pSuspicious = false; - - pFile = GetNextFile(NULL); - while (pFile != NULL) { - if (pFile->GetQuality() == A2File::kQualityDamaged) - *pDamaged = true; - if (pFile->GetQuality() != A2File::kQualityGood) - *pSuspicious = true; - pFile = GetNextFile(pFile); - } -} diff --git a/ciderpress/diskimg/DiskImg.cpp b/ciderpress/diskimg/DiskImg.cpp deleted file mode 100644 index bcf4e8f..0000000 --- a/ciderpress/diskimg/DiskImg.cpp +++ /dev/null @@ -1,3492 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2009 by CiderPress authors. All Rights Reserved. - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of the DiskImg class. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" -#include "TwoImg.h" - - -/* - * =========================================================================== - * DiskImg - * =========================================================================== - */ - -/* - * Standard NibbleDescr profiles. - * - * These will be tried in the order in which they appear here. - * - * IMPORTANT: if you add or remove an entry, update the StdNibbleDescr enum - * in DiskImg.h. - * - * Formats that allow the data checksum to be ignored should NOT be written. - * It's possible that the DOS on the disk is ignoring the checksums, but - * it's more likely that they're using a non-standard seed, and the newly- - * written sectors will have the wrong checksum value. - * - * Non-standard headers are usually okay, because we don't rewrite the - * headers, just the sector contents. - */ -/*static*/ const DiskImg::NibbleDescr DiskImg::kStdNibbleDescrs[] = { - { - "DOS 3.3 Standard", - 16, - { 0xd5, 0xaa, 0x96 }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - true, // verify checksum - true, // verify track - 2, // epilog verify count - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - true, // verify checksum - 2, // epilog verify count - kNibbleEnc62, - kNibbleSpecialNone, - }, - { - "DOS 3.3 Patched", - 16, - { 0xd5, 0xaa, 0x96 }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - false, // verify checksum - false, // verify track - 0, // epilog verify count - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - true, // verify checksum - 0, // epilog verify count - kNibbleEnc62, - kNibbleSpecialNone, - }, - { - "DOS 3.3 Ignore Checksum", - 16, - { 0xd5, 0xaa, 0x96 }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - false, // verify checksum - false, // verify track - 0, // epilog verify count - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, // checksum seed - false, // verify checksum - 0, // epilog verify count - kNibbleEnc62, - kNibbleSpecialNone, - }, - { - "DOS 3.2 Standard", - 13, - { 0xd5, 0xaa, 0xb5 }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - true, - 2, - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - 2, - kNibbleEnc53, - kNibbleSpecialNone, - }, - { - "DOS 3.2 Patched", - 13, - { 0xd5, 0xaa, 0xb5 }, { 0xde, 0xaa, 0xeb }, - 0x00, - false, - false, - 0, - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - 0, - kNibbleEnc53, - kNibbleSpecialNone, - }, - { - "Muse DOS 3.2", // standard DOS 3.2 with doubled sectors - 13, - { 0xd5, 0xaa, 0xb5 }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - true, - 2, - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - 2, - kNibbleEnc53, - kNibbleSpecialMuse, - }, - { - "RDOS 3.3", // SSI 16-sector RDOS, with altered headers - 16, - { 0xd4, 0xaa, 0x96 }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - true, - 0, // epilog verify count - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - 2, - kNibbleEnc62, - kNibbleSpecialSkipFirstAddrByte, - /* odd tracks use d4aa96, even tracks use d5aa96 */ - }, - { - "RDOS 3.2", // SSI 13-sector RDOS, with altered headers - 13, - { 0xd4, 0xaa, 0xb7 }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - true, - 2, - { 0xd5, 0xaa, 0xad }, { 0xde, 0xaa, 0xeb }, - 0x00, - true, - 2, - kNibbleEnc53, - kNibbleSpecialNone, - }, - { - "Custom", // reserve space for empty slot - 0, - }, -}; -/*static*/ const DiskImg::NibbleDescr* -DiskImg::GetStdNibbleDescr(StdNibbleDescr idx) -{ - if ((int)idx < 0 || (int)idx >= (int) NELEM(kStdNibbleDescrs)) - return NULL; - return &kStdNibbleDescrs[(int)idx]; -} - - -/* - * Initialize the members during construction. - */ -DiskImg::DiskImg(void) -{ - assert(Global::GetAppInitCalled()); - - fOuterFormat = kOuterFormatUnknown; - fFileFormat = kFileFormatUnknown; - fPhysical = kPhysicalFormatUnknown; - fpNibbleDescr = NULL; - fOrder = kSectorOrderUnknown; - fFormat = kFormatUnknown; - - fFileSysOrder = kSectorOrderUnknown; - fSectorPairing = false; - fSectorPairOffset = -1; - - fpOuterGFD = NULL; - fpWrapperGFD = NULL; - fpDataGFD = NULL; - fpOuterWrapper = NULL; - fpImageWrapper = NULL; - fpParentImg = NULL; - fDOSVolumeNum = kVolumeNumNotSet; - fOuterLength = -1; - fWrappedLength = -1; - fLength = -1; - fExpandable = false; - fReadOnly = true; - fDirty = false; - - fHasSectors = false; - fHasBlocks = false; - fHasNibbles = false; - - fNumTracks = -1; - fNumSectPerTrack = -1; - fNumBlocks = -1; - - fpScanProgressCallback = NULL; - - /* - * Create a working copy of the nibble descr table. We want to leave - * open the possibility of applications editing or discarding entries, - * so we work off of a copy. - * - * Ideally we'd allow these to be set per-track, so that certain odd - * formats could be handled transparently (e.g. Muse tweaked DOS 3.2) - * for formatting as well as reading. - */ - assert(kStdNibbleDescrs[kNibbleDescrCustom].numSectors == 0); - assert(kNibbleDescrCustom == NELEM(kStdNibbleDescrs)-1); - fpNibbleDescrTable = new NibbleDescr[NELEM(kStdNibbleDescrs)]; - fNumNibbleDescrEntries = NELEM(kStdNibbleDescrs); - memcpy(fpNibbleDescrTable, kStdNibbleDescrs, sizeof(kStdNibbleDescrs)); - - fNibbleTrackBuf = NULL; - fNibbleTrackLoaded = -1; - - fNuFXCompressType = kNuThreadFormatLZW2; - - fNotes = NULL; - fpBadBlockMap = NULL; - fDiskFSRefCnt = 0; -} - -/* - * Throw away local storage. - */ -DiskImg::~DiskImg(void) -{ - if (fpDataGFD != NULL) { - LOGI("~DiskImg closing GenericFD(s)"); - } - (void) CloseImage(); - delete[] fpNibbleDescrTable; - delete[] fNibbleTrackBuf; - delete[] fNotes; - delete fpBadBlockMap; - - /* normally these will be closed, but perhaps not if something failed */ - if (fpOuterGFD != NULL) - delete fpOuterGFD; - if (fpWrapperGFD != NULL) - delete fpWrapperGFD; - if (fpDataGFD != NULL) - delete fpDataGFD; - if (fpOuterWrapper != NULL) - delete fpOuterWrapper; - if (fpImageWrapper != NULL) - delete fpImageWrapper; - - fDiskFSRefCnt = 100; // flag as freed -} - - -/* - * Set the nibble descr pointer. - */ -void DiskImg::SetNibbleDescr(int idx) -{ - assert(idx >= 0 && idx < kNibbleDescrMAX); - fpNibbleDescr = &fpNibbleDescrTable[idx]; -} - -/* - * Set up a custom nibble descriptor. - */ -void DiskImg::SetCustomNibbleDescr(const NibbleDescr* pDescr) -{ - if (pDescr == NULL) { - fpNibbleDescr = NULL; - } else { - assert(fpNibbleDescrTable != NULL); - //LOGI("Overwriting entry %d with new value (special=%d)", - // kNibbleDescrCustom, pDescr->special); - fpNibbleDescrTable[kNibbleDescrCustom] = *pDescr; - fpNibbleDescr = &fpNibbleDescrTable[kNibbleDescrCustom]; - } -} - - -/* - * Open a volume or a file on disk. - * - * For Windows, we need to handle logical/physical volumes specially. If - * the filename matches the appropriate pattern, use a different GFD. - */ -DIError DiskImg::OpenImage(const char* pathName, char fssep, bool readOnly) -{ - DIError dierr = kDIErrNone; - bool isWinDevice = false; - - if (fpDataGFD != NULL) { - LOGI(" DI already open!"); - return kDIErrAlreadyOpen; - } - LOGI(" DI OpenImage '%s' '%.1s' ro=%d", pathName, &fssep, readOnly); - - fReadOnly = readOnly; - -#ifdef _WIN32 - if ((fssep == '\0' || fssep == '\\') && - pathName[0] >= 'A' && pathName[0] <= 'Z' && - pathName[1] == ':' && pathName[2] == '\\' && - pathName[3] == '\0') - { - isWinDevice = true; // logical volume ("A:\") - } - if ((fssep == '\0' || fssep == '\\') && - isdigit(pathName[0]) && isdigit(pathName[1]) && - pathName[2] == ':' && pathName[3] == '\\' && - pathName[4] == '\0') - { - isWinDevice = true; // physical volume ("80:\") - } - if ((fssep == '\0' || fssep == '\\') && - strncmp(pathName, kASPIDev, strlen(kASPIDev)) == 0 && - pathName[strlen(pathName)-1] == '\\') - { - isWinDevice = true; // ASPI volume ("ASPI:x:y:z\") - } -#endif - - if (isWinDevice) { -#ifdef _WIN32 - GFDWinVolume* pGFDWinVolume = new GFDWinVolume; - - dierr = pGFDWinVolume->Open(pathName, fReadOnly); - if (dierr != kDIErrNone) { - delete pGFDWinVolume; - goto bail; - } - - fpWrapperGFD = pGFDWinVolume; - // Use a unique extension to skip some of the probing. - dierr = AnalyzeImageFile("CPDevice.cp-win-vol", '\0'); - if (dierr != kDIErrNone) - goto bail; -#endif - } else { - GFDFile* pGFDFile = new GFDFile; - - dierr = pGFDFile->Open(pathName, fReadOnly); - if (dierr != kDIErrNone) { - delete pGFDFile; - goto bail; - } - - //fImageFileName = new char[strlen(pathName) + 1]; - //strcpy(fImageFileName, pathName); - - fpWrapperGFD = pGFDFile; - pGFDFile = NULL; - - dierr = AnalyzeImageFile(pathName, fssep); - if (dierr != kDIErrNone) - goto bail; - } - - - assert(fpDataGFD != NULL); - -bail: - return dierr; -} - -DIError DiskImg::OpenImageFromBufferRO(const uint8_t* buffer, long length) { - return OpenImageFromBuffer(const_cast(buffer), length, true); -} - -DIError DiskImg::OpenImageFromBufferRW(uint8_t* buffer, long length) { - return OpenImageFromBuffer(buffer, length, false); -} - -/* - * Open from a buffer, which could point to unadorned ready-to-go content - * or to a preloaded image file. - */ -DIError DiskImg::OpenImageFromBuffer(uint8_t* buffer, long length, bool readOnly) -{ - if (fpDataGFD != NULL) { - LOGW(" DI already open!"); - return kDIErrAlreadyOpen; - } - LOGI(" DI OpenImage %08lx %ld ro=%d", (long) buffer, length, readOnly); - - DIError dierr; - GFDBuffer* pGFDBuffer; - - fReadOnly = readOnly; - pGFDBuffer = new GFDBuffer; - - dierr = pGFDBuffer->Open(buffer, length, false, false, readOnly); - if (dierr != kDIErrNone) { - delete pGFDBuffer; - return dierr; - } - - fpWrapperGFD = pGFDBuffer; - pGFDBuffer = NULL; - - dierr = AnalyzeImageFile("", '\0'); - if (dierr != kDIErrNone) - return dierr; - - assert(fpDataGFD != NULL); - return kDIErrNone; -} - -/* - * Open a range of blocks from an already-open disk image. This is only - * useful for things like UNIDOS volumes, which don't have an associated - * file in the image and are linear. - * - * The "read only" flag is inherited from the parent. - * - * For embedded images with visible file structure, we should be using - * an EmbeddedFD instead. [Note these were never implemented.] - * - * NOTE: there is an implicit ProDOS block ordering imposed on the parent - * image. It turns out that all of our current embedded parents use - * ProDOS-ordered blocks, so it works out okay, but the "linear" requirement - * above goes beyond just having contiguous blocks. - */ -DIError DiskImg::OpenImage(DiskImg* pParent, long firstBlock, long numBlocks) -{ - LOGI(" DI OpenImage parent=0x%08lx %ld %ld", (long) pParent, firstBlock, - numBlocks); - if (fpDataGFD != NULL) { - LOGI(" DW already open!"); - return kDIErrAlreadyOpen; - } - - if (pParent == NULL || firstBlock < 0 || numBlocks <= 0 || - firstBlock + numBlocks > pParent->GetNumBlocks()) - { - assert(false); - return kDIErrInvalidArg; - } - - fReadOnly = pParent->GetReadOnly(); // very important - - DIError dierr; - GFDGFD* pGFDGFD; - - pGFDGFD = new GFDGFD; - dierr = pGFDGFD->Open(pParent->fpDataGFD, firstBlock * kBlockSize, fReadOnly); - if (dierr != kDIErrNone) { - delete pGFDGFD; - return dierr; - } - - fpDataGFD = pGFDGFD; - assert(fpWrapperGFD == NULL); - - /* - * This replaces the call to "analyze image file" because we know we - * already have an open file with specific characteristics. - */ - //fOffset = pParent->fOffset + kBlockSize * firstBlock; - fLength = (di_off_t)numBlocks * kBlockSize; - fOuterLength = fWrappedLength = fLength; - fFileFormat = kFileFormatUnadorned; - fPhysical = pParent->fPhysical; - fOrder = pParent->fOrder; - - fpParentImg = pParent; - - return dierr; -} - -DIError DiskImg::OpenImage(DiskImg* pParent, long firstTrack, long firstSector, - long numSectors) -{ - LOGI(" DI OpenImage parent=0x%08lx %ld %ld %ld", (long) pParent, - firstTrack, firstSector, numSectors); - if (fpDataGFD != NULL) { - LOGW(" DI already open!"); - return kDIErrAlreadyOpen; - } - - if (pParent == NULL) - return kDIErrInvalidArg; - - int prntSectPerTrack = pParent->GetNumSectPerTrack(); - int lastTrack = firstTrack + - (numSectors + prntSectPerTrack-1) / prntSectPerTrack; - if (firstTrack < 0 || numSectors <= 0 || - lastTrack > pParent->GetNumTracks()) - { - return kDIErrInvalidArg; - } - - fReadOnly = pParent->GetReadOnly(); // very important - - DIError dierr; - GFDGFD* pGFDGFD; - - pGFDGFD = new GFDGFD; - dierr = pGFDGFD->Open(pParent->fpDataGFD, - kSectorSize * firstTrack * prntSectPerTrack, fReadOnly); - if (dierr != kDIErrNone) { - delete pGFDGFD; - return dierr; - } - - fpDataGFD = pGFDGFD; - assert(fpWrapperGFD == NULL); - - /* - * This replaces the call to "analyze image file" because we know we - * already have an open file with specific characteristics. - */ - assert(firstSector == 0); // else fOffset calculation breaks - //fOffset = pParent->fOffset + kSectorSize * firstTrack * prntSectPerTrack; - fLength = numSectors * kSectorSize; - fOuterLength = fWrappedLength = fLength; - fFileFormat = kFileFormatUnadorned; - fPhysical = pParent->fPhysical; - fOrder = pParent->fOrder; - - fpParentImg = pParent; - - return dierr; -} - - -/* - * Enable sector pairing. Useful for OzDOS. - */ -void DiskImg::SetPairedSectors(bool enable, int idx) -{ - fSectorPairing = enable; - fSectorPairOffset = idx; - - if (enable) { - assert(idx == 0 || idx == 1); - } -} - -/* - * Close the image, freeing resources. - * - * If we write to a child DiskImg, it's responsible for setting the "dirty" - * flag in its parent (and so on up the chain). That's necessary so that, - * when we close the file, changes made to a child DiskImg cause the parent - * to do any necessary recompression. - * - * [ This is getting called even when image creation failed with an error. - * This is probably the correct behavior, but we may want to be aborting the - * image creation instead of completing it. That's a higher-level decision - * though. ++ATM 20040506 ] - */ -DIError DiskImg::CloseImage(void) -{ - DIError dierr; - - LOGI("CloseImage 0x%p", this); - - /* check for DiskFS objects that still point to us */ - if (fDiskFSRefCnt != 0) { - LOGE("ERROR: CloseImage: fDiskFSRefCnt=%d", fDiskFSRefCnt); - assert(false); //DebugBreak(); - } - - /* - * Flush any changes. - */ - dierr = FlushImage(kFlushAll); - if (dierr != kDIErrNone) - return dierr; - - /* - * Clean up. Close GFD, OrigGFD, and OuterGFD. Delete ImageWrapper - * and OuterWrapper. - * - * In some cases we will have the file open more than once (e.g. a - * NuFX archive, which must be opened on disk). - */ - if (fpDataGFD != NULL) { - fpDataGFD->Close(); - delete fpDataGFD; - fpDataGFD = NULL; - } - if (fpWrapperGFD != NULL) { - fpWrapperGFD->Close(); - delete fpWrapperGFD; - fpWrapperGFD = NULL; - } - if (fpOuterGFD != NULL) { - fpOuterGFD->Close(); - delete fpOuterGFD; - fpOuterGFD = NULL; - } - delete fpImageWrapper; - fpImageWrapper = NULL; - delete fpOuterWrapper; - fpOuterWrapper = NULL; - - return dierr; -} - - -/* - * Flush data to disk. - * - * The only time this really needs to do anything on a disk image file is - * when we have compressed data (NuFX, DDD, .gz, .zip). The uncompressed - * wrappers either don't do anything ("unadorned") or just update some - * header fields (DiskCopy42). - * - * If "mode" is kFlushFastOnly, we only flush the formats that don't really - * need flushing. This is part of a scheme to keep the disk contents in a - * reasonable state on the off chance we crash with a modified file open. - * It also helps the user understand when changes are being made immediately - * vs. when they're written to memory and compressed later. We could just - * refuse to raise the "dirty" flag when modifying "simple" file formats, - * but that would change the meaning of the flag from "something has been - * changed" to "what's in the file and what's in memory differ". I want it - * to be a "dirty" flag. - */ -DIError DiskImg::FlushImage(FlushMode mode) -{ - DIError dierr = kDIErrNone; - - LOGI(" DI FlushImage (dirty=%d mode=%d)", fDirty, mode); - if (!fDirty) - return kDIErrNone; - if (fpDataGFD == NULL) { - /* - * This can happen if we tried to create a disk image but failed, e.g. - * couldn't create the output file because of access denied on the - * directory. There's no data, therefore nothing to flush, but the - * "dirty" flag is set because CreateImageCommon sets it almost - * immediately. - */ - LOGI(" (disk must've failed during creation)"); - fDirty = false; - return kDIErrNone; - } - - if (mode == kFlushFastOnly && - ((fpImageWrapper != NULL && !fpImageWrapper->HasFastFlush()) || - (fpOuterWrapper != NULL && !fpOuterWrapper->HasFastFlush()) )) - { - LOGI("DI fast flush requested, but one or both wrappers are slow"); - return kDIErrNone; - } - - /* - * Step 1: make sure any local caches have been flushed. - */ - /* (none) */ - - /* - * Step 2: push changes from fpDataGFD to fpWrapperGFD. This will - * cause ImageWrapper to rebuild itself (SHK, DDD, whatever). In - * some cases this amounts to copying the data on top of itself, - * which we can avoid easily. - * - * Embedded volumes don't have wrappers; when you write to an - * embedded volume, it passes straight through to the parent. - * - * (Note to self: formats like NuFX that write to a temp file and then - * rename over the old will close fpWrapperGFD and just access it - * directly. This is bad, because it doesn't allow them to have an - * "outer" format, but it's the way life is. The point is that it's - * okay for fpWrapperGFD to be non-NULL but represent a closed file, - * so long as the "Flush" function has it figured out.) - */ - if (fpWrapperGFD != NULL) { - LOGI(" DI flushing data changes to wrapper (fLen=%ld fWrapLen=%ld)", - (long) fLength, (long) fWrappedLength); - dierr = fpImageWrapper->Flush(fpWrapperGFD, fpDataGFD, fLength, - &fWrappedLength); - if (dierr != kDIErrNone) { - LOGI(" ERROR: wrapper flush failed (err=%d)", dierr); - return dierr; - } - /* flush the GFD in case it's a Win32 volume with block caching */ - dierr = fpWrapperGFD->Flush(); - } else { - assert(fpParentImg != NULL); - } - - /* - * Step 3: if we have an fpOuterGFD, rebuild the file with the data - * in fpWrapperGFD. - */ - if (fpOuterWrapper != NULL) { - LOGI(" DI saving wrapper to outer, fWrapLen=%ld", - (long) fWrappedLength); - assert(fpOuterGFD != NULL); - dierr = fpOuterWrapper->Save(fpOuterGFD, fpWrapperGFD, - fWrappedLength); - if (dierr != kDIErrNone) { - LOGI(" ERROR: outer save failed (err=%d)", dierr); - return dierr; - } - } - - fDirty = false; - return kDIErrNone; -} - - -/* - * Given the filename extension and a GFD, figure out what's inside. - * - * The filename extension should give us some idea what to expect: - * SHK, SDK, BXY - ShrinkIt compressed disk image - * GZ - gzip-compressed file (with something else inside) - * ZIP - ZIP archive with a single disk image inside - * DDD - DDD, DDD Pro, or DDD5.0 compressed image - * DSK - DiskCopy 4.2 or DO/PO - * DC - DiskCopy 4.2 (or 6?) - * DC6 - DiskCopy 6 (usually just raw sectors) - * DO, PO, D13, RAW? - DOS-order or ProDOS-order uncompressed - * IMG - Copy ][+ image (unadorned, physical sector order) - * HDV - virtual hard drive image - * NIB, RAW? - nibblized image - * (no extension) uncompressed - * cp-win-vol - our "magic" extension to indicate a Windows logical volume - * - * We can also examine the file length to see if it's a standard size - * (140K, 800K) and look for magic values in the header. - * - * If we can access the contents directly from disk, we do so. It's - * possibly more efficient to load the whole thing into memory, but if - * we have that much memory then the OS should cache it for us. (I have - * some 20MB disk images from my hard drive that shouldn't be loaded - * in their entirety. Certainly don't want to load a 512MB CFFA image.) - * - * On input, the following fields must be set: - * fpWrapperGFD - GenericFD for the file pointed to by "pathname" (or for a - * memory buffer if this is a sub-volume) - * - * On success, the following fields will be set: - * fWrappedLength, fOuterLength - set appropriately - * fpDataGFD - GFD for the raw data, possibly just a GFDGFD with an offset - * fLength - length of unadorned data in the file, or the length of - * data stored in fBuffer (test for fBuffer!=NULL) - * fFileFormat - set to the overall file format, mostly interesting - * for identification of the file "wrapper" - * fPhysicalFormat - set to the type of data this holds - * (maybe) fOrder - set when the file format or extension dictates, e.g. - * 2MG or *.po; not always reliable - * (maybe) fDOSVolumeNum - set to DOS volume number from wrapper - * - * This may set fReadOnly if one of the wrappers looks okay but is reporting - * a bad checksum. - */ -DIError DiskImg::AnalyzeImageFile(const char* pathName, char fssep) -{ - DIError dierr = kDIErrNone; - FileFormat probableFormat; - bool reliableExt; - const char* ext = FindExtension(pathName, fssep); - char* extBuf = NULL; // uses malloc/free - bool needExtFromOuter = false; - - if (ext != NULL) { - assert(*ext == '.'); - ext++; - } else - ext = ""; - - LOGI(" DI AnalyzeImageFile '%s' '%c' ext='%s'", - pathName, fssep, ext); - - /* sanity check: nobody should have configured these yet */ - assert(fOuterFormat == kOuterFormatUnknown); - assert(fFileFormat == kFileFormatUnknown); - assert(fOrder == kSectorOrderUnknown); - assert(fFormat == kFormatUnknown); - fLength = -1; - dierr = fpWrapperGFD->Seek(0, kSeekEnd); - if (dierr != kDIErrNone) { - LOGW(" DI Couldn't seek to end of wrapperGFD"); - goto bail; - } - fWrappedLength = fOuterLength = fpWrapperGFD->Tell(); - - /* quick test for zero-length files */ - if (fWrappedLength == 0) - return kDIErrUnrecognizedFileFmt; - - /* - * Start by checking for a zip/gzip "wrapper wrapper". We want to strip - * that away before we do anything else. Because web sites tend to - * gzip everything in sight whether it needs it or not, we treat this - * as a special case and assume that anything could be inside. - * - * Some cases are difficult to handle, e.g. ".SDK", since NufxLib - * doesn't let us open an archive that is sitting in memory. - * - * We could also handle disk images stored as ordinary files stored - * inside SHK. Not much point in handling multiple files down at - * this level though. - */ - if (strcasecmp(ext, "gz") == 0 && - OuterGzip::Test(fpWrapperGFD, fOuterLength) == kDIErrNone) - { - LOGI(" DI found gz outer wrapper"); - - fpOuterWrapper = new OuterGzip(); - if (fpOuterWrapper == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - fOuterFormat = kOuterFormatGzip; - - /* drop the ".gz" and get down to the next extension */ - ext = ""; - extBuf = strdup(pathName); - if (extBuf != NULL) { - char* localExt; - - localExt = (char*) FindExtension(extBuf, fssep); - if (localExt != NULL) - *localExt = '\0'; - localExt = (char*) FindExtension(extBuf, fssep); - if (localExt != NULL) { - ext = localExt; - assert(*ext == '.'); - ext++; - } - } - LOGI(" DI after gz, ext='%s'", ext == NULL ? "(NULL)" : ext); - - } else if (strcasecmp(ext, "zip") == 0) { - dierr = OuterZip::Test(fpWrapperGFD, fOuterLength); - if (dierr != kDIErrNone) - goto bail; - - LOGI(" DI found ZIP outer wrapper"); - - fpOuterWrapper = new OuterZip(); - if (fpOuterWrapper == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - fOuterFormat = kOuterFormatZip; - - needExtFromOuter = true; - - } else { - fOuterFormat = kOuterFormatNone; - } - - /* finish up outer wrapper stuff */ - if (fOuterFormat != kOuterFormatNone) { - GenericFD* pNewGFD = NULL; - dierr = fpOuterWrapper->Load(fpWrapperGFD, fOuterLength, fReadOnly, - &fWrappedLength, &pNewGFD); - if (dierr != kDIErrNone) { - LOGI(" DW outer prep failed"); - /* extensions are "reliable", so failure is unavoidable */ - goto bail; - } - - /* Load() sets this */ - if (fpOuterWrapper->IsDamaged()) { - AddNote(kNoteWarning, "The zip/gzip wrapper appears to be damaged."); - fReadOnly = true; - } - - /* shift GFDs */ - fpOuterGFD = fpWrapperGFD; - fpWrapperGFD = pNewGFD; - - if (needExtFromOuter) { - ext = fpOuterWrapper->GetExtension(); - if (ext == NULL) - ext = ""; - } - } - - /* - * Try to figure out what format the file is in. - * - * First pass, try only what the filename says it is. This way, if - * two file formats look alike, we have a good chance of getting it - * right. - * - * The "Test" functions have the complete file at their disposal. The - * file's length is stored in "fWrappedLength" for convenience. - */ - reliableExt = false; - probableFormat = kFileFormatUnknown; - if (strcasecmp(ext, "2mg") == 0 || strcasecmp(ext, "2img") == 0) { - reliableExt = true; - if (Wrapper2MG::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - probableFormat = kFileFormat2MG; - } else if (strcasecmp(ext, "shk") == 0 || strcasecmp(ext, "sdk") == 0 || - strcasecmp(ext, "bxy") == 0) - { - DIError dierr2; - reliableExt = true; - dierr2 = WrapperNuFX::Test(fpWrapperGFD, fWrappedLength); - if (dierr2 == kDIErrNone) - probableFormat = kFileFormatNuFX; - else if (dierr2 == kDIErrFileArchive) { - LOGI(" AnalyzeImageFile thinks it found a NuFX file archive"); - dierr = dierr2; - goto bail; - } - } else if (strcasecmp(ext, "hdv") == 0) { - /* usually just a "raw" disk, but check for Sim //e */ - if (WrapperSim2eHDV::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - probableFormat = kFileFormatSim2eHDV; - - /* ProDOS .hdv volumes can expand */ - fExpandable = true; - } else if (strcasecmp(ext, "dsk") == 0 || strcasecmp(ext, "dc") == 0) { - /* might be DiskCopy */ - if (WrapperDiskCopy42::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - probableFormat = kFileFormatDiskCopy42; - } else if (strcasecmp(ext, "ddd") == 0) { - /* do this after compressed formats but before unadorned */ - reliableExt = true; - if (WrapperDDD::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - probableFormat = kFileFormatDDD; - } else if (strcasecmp(ext, "app") == 0) { - reliableExt = true; - if (WrapperTrackStar::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - probableFormat = kFileFormatTrackStar; - } else if (strcasecmp(ext, "fdi") == 0) { - reliableExt = true; - if (WrapperFDI::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - probableFormat = kFileFormatFDI; - } else if (strcasecmp(ext, "img") == 0) { - if (WrapperUnadornedSector::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - { - probableFormat = kFileFormatUnadorned; - fPhysical = kPhysicalFormatSectors; - fOrder = kSectorOrderPhysical; - } - } else if (strcasecmp(ext, "nib") == 0 || strcasecmp(ext, "raw") == 0) { - if (WrapperUnadornedNibble::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - { - probableFormat = kFileFormatUnadorned; - fPhysical = kPhysicalFormatNib525_6656; - /* figure out NibbleFormat later */ - } - } else if (strcasecmp(ext, "do") == 0 || strcasecmp(ext, "po") == 0 || - strcasecmp(ext, "d13") == 0 || strcasecmp(ext, "dc6") == 0) - { - if (WrapperUnadornedSector::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - { - probableFormat = kFileFormatUnadorned; - fPhysical = kPhysicalFormatSectors; - if (strcasecmp(ext, "do") == 0 || strcasecmp(ext, "d13") == 0) - fOrder = kSectorOrderDOS; - else - fOrder = kSectorOrderProDOS; // po, dc6 - LOGI(" DI guessing order is %d by extension", fOrder); - } - } else if (strcasecmp(ext, "cp-win-vol") == 0) { - /* this is a Windows logical volume */ - reliableExt = true; - probableFormat = kFileFormatUnadorned; - fPhysical = kPhysicalFormatSectors; - fOrder = kSectorOrderProDOS; - } else { - /* no match on the filename extension; start guessing */ - } - - if (probableFormat != kFileFormatUnknown) { - /* - * Found a match. Use "probableFormat" to open the file. - */ - LOGI(" DI scored hit on extension '%s'", ext); - } else { - /* - * Didn't work. If the file extension was marked "reliable", then - * either we have the wrong extension on the file, or the contents - * are damaged. - * - * If the extension isn't reliable, or simply absent, then we have - * to probe through the formats we know and just hope for the best. - * - * If the "test" function returns with a checksum failure, we take - * it to mean that the format was positively identified, but the - * data inside is corrupted. This results in an immediate return - * with the checksum failure noted. Only a few wrapper formats - * have checksums embedded. (The "test" functions should only - * be looking at header checksums.) - */ - if (reliableExt) { - LOGI(" DI file extension '%s' did not match contents", ext); - dierr = kDIErrBadFileFormat; - goto bail; - } else { - LOGI(" DI extension '%s' not useful, probing formats", ext); - dierr = WrapperNuFX::Test(fpWrapperGFD, fWrappedLength); - if (dierr == kDIErrNone) { - probableFormat = kFileFormatNuFX; - goto gotit; - } else if (dierr == kDIErrFileArchive) - goto bail; // we know it's NuFX, we know we can't use it - else if (dierr == kDIErrBadChecksum) - goto bail; // right file type, bad data - - dierr = WrapperDiskCopy42::Test(fpWrapperGFD, fWrappedLength); - if (dierr == kDIErrNone) { - probableFormat = kFileFormatDiskCopy42; - goto gotit; - } else if (dierr == kDIErrBadChecksum) - goto bail; // right file type, bad data - - if (Wrapper2MG::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) { - probableFormat = kFileFormat2MG; - } else if (WrapperDDD::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) { - probableFormat = kFileFormatDDD; - } else if (WrapperSim2eHDV::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - { - probableFormat = kFileFormatSim2eHDV; - } else if (WrapperTrackStar::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - { - probableFormat = kFileFormatTrackStar; - } else if (WrapperFDI::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) - { - probableFormat = kFileFormatFDI; - } else if (WrapperUnadornedNibble::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) { - probableFormat = kFileFormatUnadorned; - fPhysical = kPhysicalFormatNib525_6656; // placeholder - } else if (WrapperUnadornedSector::Test(fpWrapperGFD, fWrappedLength) == kDIErrNone) { - probableFormat = kFileFormatUnadorned; - fPhysical = kPhysicalFormatSectors; - } -gotit: ; - } - } - - /* - * Either we recognize it or we don't. Finish opening the file by - * setting up "fLength" and "fPhysical" values, extracting data - * into a memory buffer if necessary. fpDataGFD is set up by the - * "prep" function. - * - * If we're lucky, this will also configure "fOrder" for us, which is - * important when we can't recognize the filesystem format (for correct - * operation of disk tools). - */ - switch (probableFormat) { - case kFileFormat2MG: - fpImageWrapper = new Wrapper2MG(); - break; - case kFileFormatDiskCopy42: - fpImageWrapper = new WrapperDiskCopy42(); - break; - case kFileFormatSim2eHDV: - fpImageWrapper = new WrapperSim2eHDV(); - break; - case kFileFormatTrackStar: - fpImageWrapper = new WrapperTrackStar(); - break; - case kFileFormatFDI: - fpImageWrapper = new WrapperFDI(); - fReadOnly = true; // writing to FDI not yet supported - break; - case kFileFormatNuFX: - fpImageWrapper = new WrapperNuFX(); - ((WrapperNuFX*)fpImageWrapper)->SetCompressType( - (NuThreadFormat) fNuFXCompressType); - break; - case kFileFormatDDD: - fpImageWrapper = new WrapperDDD(); - break; - case kFileFormatUnadorned: - if (IsSectorFormat(fPhysical)) - fpImageWrapper = new WrapperUnadornedSector(); - else if (IsNibbleFormat(fPhysical)) - fpImageWrapper = new WrapperUnadornedNibble(); - else { - assert(false); - } - break; - default: - LOGI(" DI couldn't figure out the file format"); - dierr = kDIErrUnrecognizedFileFmt; - break; - } - if (fpImageWrapper != NULL) { - assert(fpDataGFD == NULL); - dierr = fpImageWrapper->Prep(fpWrapperGFD, fWrappedLength, fReadOnly, - &fLength, &fPhysical, &fOrder, &fDOSVolumeNum, - &fpBadBlockMap, &fpDataGFD); - } else { - /* could be a mem alloc failure that didn't set dierr */ - if (dierr == kDIErrNone) - dierr = kDIErrGeneric; - } - - if (dierr != kDIErrNone) { - LOGI(" DI wrapper prep failed (err=%d)", dierr); - goto bail; - } - - /* check for non-fatal checksum failures, e.g. DiskCopy42 */ - if (fpImageWrapper->IsDamaged()) { - AddNote(kNoteWarning, "File checksum didn't match."); - fReadOnly = true; - } - - fFileFormat = probableFormat; - - assert(fLength >= 0); - assert(fpDataGFD != NULL); - assert(fOuterFormat != kOuterFormatUnknown); - assert(fFileFormat != kFileFormatUnknown); - assert(fPhysical != kPhysicalFormatUnknown); - -bail: - free(extBuf); - return dierr; -} - - -/* - * Try to figure out what we're looking at. - * - * Returns an error if we don't think this is even a disk image. If we - * just can't figure it out, we return success but with the format value - * set to "unknown". This gives the caller a chance to use "override" - * to help us find our way. - * - * On entry: - * fpDataGFD, fLength, and fFileFormat are defined - * fSectorPairing is specified - * fOrder has a semi-reliable guess at sector ordering - * On exit: - * fOrder and fFormat are set to the best of our ability - * fNumTracks, fNumSectPerTrack, and fNumBlocks are set - * fHasSectors, fHasTracks, and fHasNibbles are set - * fFileSysOrder is set - * fpNibbleDescr will be set for nibble images - */ -DIError DiskImg::AnalyzeImage(void) -{ - assert(fLength >= 0); - assert(fpDataGFD != NULL); - assert(fFileFormat != kFileFormatUnknown); - assert(fPhysical != kPhysicalFormatUnknown); - assert(fFormat == kFormatUnknown); - assert(fFileSysOrder == kSectorOrderUnknown); - assert(fNumTracks == -1); - assert(fNumSectPerTrack == -1); - assert(fNumBlocks == -1); - if (fpDataGFD == NULL) - return kDIErrInternal; - - /* - * Figure out how many tracks and sectors the image has. - * - * For an odd-sized ProDOS image, there will be no tracks and sectors. - */ - if (IsSectorFormat(fPhysical)) { - if (!fLength) { - LOGI(" DI zero-length disk images not allowed"); - return kDIErrOddLength; - } - - if (fLength == kD13Length) { - /* 13-sector .d13 image */ - fHasSectors = true; - fNumSectPerTrack = 13; - fNumTracks = kTrackCount525; - assert(!fHasBlocks); - } else if (fLength % (16 * kSectorSize) == 0) { - /* looks like a collection of 16-sector tracks */ - fHasSectors = true; - - fNumSectPerTrack = 16; - fNumTracks = (int) (fLength / (fNumSectPerTrack * kSectorSize)); - - /* sector pairing effectively cuts #of tracks in half */ - if (fSectorPairing) { - if ((fNumTracks & 0x01) != 0) { - LOGI(" DI error: bad attempt at sector pairing"); - assert(false); - fSectorPairing = false; - } - } - - if (fSectorPairing) - fNumTracks /= 2; - } else { - if (fSectorPairing) { - LOGI("GLITCH: sector pairing enabled, but fLength=%ld", - (long) fLength); - return kDIErrOddLength; - } - - assert(fNumTracks == -1); - assert(fNumSectPerTrack == -1); - assert((fLength % kBlockSize) == 0); - - fHasBlocks = true; - fNumBlocks = (long) (fLength / kBlockSize); - } - } else if (IsNibbleFormat(fPhysical)) { - fHasNibbles = fHasSectors = true; - - /* - * Figure out if it's 13-sector or 16-sector (or garbage). We - * have to make an assessment of the entire disk so we can declare - * it to be 13-sector or 16-sector, which is useful for DiskFS - * which will want to scan for DOS VTOCs and other goodies. We - * also want to provide a default NibbleDescr. - * - * Failing that, we still allow it to be opened for raw track access. - * - * This also sets fNumTracks, which could be more than 35 if we're - * working with a TrackStar or FDI image. - */ - DIError dierr; - dierr = AnalyzeNibbleData(); // sets nibbleDescr and DOS vol num - if (dierr == kDIErrNone) { - assert(fpNibbleDescr != NULL); - fNumSectPerTrack = fpNibbleDescr->numSectors; - fOrder = kSectorOrderPhysical; - - if (!fReadOnly && !fpNibbleDescr->dataVerifyChecksum) { - LOGI("DI nibbleDescr does not verify data checksum, disabling writes"); - AddNote(kNoteInfo, - "Sectors use non-standard data checksums; writing disabled."); - fReadOnly = true; - } - } else { - //assert(fpNibbleDescr == NULL); - fNumSectPerTrack = -1; - fOrder = kSectorOrderPhysical; - fHasSectors = false; - } - } else { - LOGI("Unsupported physical %d", fPhysical); - assert(false); - return kDIErrGeneric; - } - - /* - * Compute the number of blocks. For a 13-sector disk, block access - * is not possible. - * - * For nibble formats, we have to base the block count on the number - * of sectors rather than the file length. - */ - if (fHasSectors) { - assert(fNumSectPerTrack > 0); - if ((fNumSectPerTrack & 0x01) == 0) { - /* not a 13-sector disk, so define blocks in terms of sectors */ - /* (effects of sector pairing are already taken into account) */ - fHasBlocks = true; - fNumBlocks = (fNumTracks * fNumSectPerTrack) / 2; - } - } else if (fHasBlocks) { - if ((fLength % kBlockSize) == 0) { - /* not sector-oriented, so define blocks based on length */ - fHasBlocks = true; - fNumBlocks = (long) (fLength / kBlockSize); - - if (fSectorPairing) { - if ((fNumBlocks & 0x01) != 0) { - LOGI(" DI error: bad attempt at sector pairing (blk)"); - assert(false); - fSectorPairing = false; - } else - fNumBlocks /= 2; - } - - } else { - assert(false); - return kDIErrGeneric; - } - } else if (fHasNibbles) { - assert(fNumBlocks == -1); - } else { - LOGI(" DI none of fHasSectors/fHasBlocks/fHasNibbles are set"); - assert(false); - return kDIErrInternal; - } - - /* - * We've got the track/sector/block layout sorted out; now figure out - * what kind of filesystem we're dealing with. - */ - AnalyzeImageFS(); - - LOGI(" DI AnalyzeImage tracks=%ld sectors=%d blocks=%ld fileSysOrder=%d", - fNumTracks, fNumSectPerTrack, fNumBlocks, fFileSysOrder); - LOGI(" hasBlocks=%d hasSectors=%d hasNibbles=%d", - fHasBlocks, fHasSectors, fHasNibbles); - - return kDIErrNone; -} - -/* - * Try to figure out what filesystem exists on this disk image. - * - * We want to test for DOS before ProDOS, because sometimes they overlap (e.g. - * 800K ProDOS disk with five 160K DOS volumes on it). - * - * Sets fFormat, fOrder, and fFileSysOrder. - */ -void DiskImg::AnalyzeImageFS(void) -{ - /* - * In some circumstances it would be useful to have a set describing - * what filesystems we might expect to find, e.g. we're not likely to - * encounter RDOS embedded in a CF card. - */ - if (DiskFSMacPart::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatMacPart); - LOGI(" DI found MacPart, order=%d", fOrder); - } else if (DiskFSMicroDrive::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatMicroDrive); - LOGI(" DI found MicroDrive, order=%d", fOrder); - } else if (DiskFSFocusDrive::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatFocusDrive); - LOGI(" DI found FocusDrive, order=%d", fOrder); - } else if (DiskFSCFFA::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - // The CFFA format doesn't have a partition map, but we do insist - // on finding multiple volumes. It needs to come after MicroDrive, - // because a disk formatted for CFFA then subsequently partitioned - // for MicroDrive will still look like valid CFFA unless you zero - // out the blocks. - assert(fFormat == kFormatCFFA4 || fFormat == kFormatCFFA8); - LOGI(" DI found CFFA, order=%d", fOrder); - } else if (DiskFSFAT::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - // This is really just a trap to catch CFFA cards that were formatted - // for ProDOS and then re-formatted for MSDOS. As such it needs to - // come before the ProDOS test. It only works on larger volumes, - // and can be overridden, so it's pretty safe. - assert(fFormat == kFormatMSDOS); - LOGI(" DI found MSDOS, order=%d", fOrder); - } else if (DiskFSDOS33::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatDOS32 || fFormat == kFormatDOS33); - LOGI(" DI found DOS3.x, order=%d", fOrder); - if (fNumSectPerTrack == 13) - fFormat = kFormatDOS32; - } else if (DiskFSUNIDOS::TestWideFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - // Should only succeed on 400K embedded chunks. - assert(fFormat == kFormatDOS33); - fNumSectPerTrack = 32; - fNumTracks /= 2; - LOGI(" DI found 'wide' DOS3.3, order=%d", fOrder); - } else if (DiskFSUNIDOS::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatUNIDOS); - fNumSectPerTrack = 32; - fNumTracks /= 2; - LOGI(" DI found UNIDOS, order=%d", fOrder); - } else if (DiskFSOzDOS::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatOzDOS); - fNumSectPerTrack = 32; - fNumTracks /= 2; - LOGI(" DI found OzDOS, order=%d", fOrder); - } else if (DiskFSProDOS::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatProDOS); - LOGI(" DI found ProDOS, order=%d", fOrder); - } else if (DiskFSPascal::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatPascal); - LOGI(" DI found Pascal, order=%d", fOrder); - } else if (DiskFSCPM::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatCPM); - LOGI(" DI found CP/M, order=%d", fOrder); - } else if (DiskFSRDOS::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatRDOS33 || - fFormat == kFormatRDOS32 || - fFormat == kFormatRDOS3); - LOGI(" DI found RDOS 3.3, order=%d", fOrder); - } else if (DiskFSHFS::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatMacHFS); - LOGI(" DI found HFS, order=%d", fOrder); - } else if (DiskFSGutenberg::TestFS(this, &fOrder, &fFormat, DiskFS::kLeniencyNot) == kDIErrNone) - { - assert(fFormat == kFormatGutenberg); - LOGI(" DI found Gutenberg, order=%d", fOrder); - } else { - fFormat = kFormatUnknown; - LOGI(" DI no recognizeable filesystem found (fOrder=%d)", - fOrder); - } - - fFileSysOrder = CalcFSSectorOrder(); -} - - -/* - * Override the format determined by the analyzer. - * - * If they insist on the presence of a valid filesystem, check to make sure - * that filesystem actually exists. - * - * Note that this does not allow overriding the file structure, which must - * be clearly identifiable to be at all useful. If the file has no "wrapper" - * structure, the "unadorned" format should be specified, and the contents - * identified by the PhysicalFormat. - */ -DIError DiskImg::OverrideFormat(PhysicalFormat physical, FSFormat format, - SectorOrder order) -{ - DIError dierr = kDIErrNone; - SectorOrder newOrder; - FSFormat newFormat; - - LOGI(" DI override: physical=%d format=%d order=%d", - physical, format, order); - - if (!IsSectorFormat(physical) && !IsNibbleFormat(physical)) - return kDIErrUnsupportedPhysicalFmt; - - /* don't allow forcing physical format change */ - if (physical != fPhysical) - return kDIErrInvalidArg; - - /* optimization */ - if (physical == fPhysical && format == fFormat && order == fOrder) { - LOGI(" DI override matches existing, ignoring"); - return kDIErrNone; - } - - newOrder = order; - newFormat = format; - - switch (format) { - case kFormatDOS33: - case kFormatDOS32: - dierr = DiskFSDOS33::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - // Go ahead and allow the override even if the DOS version is wrong. - // So long as the sector count is correct, it's okay. - break; - case kFormatProDOS: - dierr = DiskFSProDOS::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatPascal: - dierr = DiskFSPascal::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatMacHFS: - dierr = DiskFSHFS::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatUNIDOS: - dierr = DiskFSUNIDOS::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatOzDOS: - dierr = DiskFSOzDOS::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatCFFA4: - case kFormatCFFA8: - dierr = DiskFSCFFA::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - // So long as it's CFFA, we allow the user to force it to be 4-mode - // or 8-mode. Don't require newFormat==format. - break; - case kFormatMacPart: - dierr = DiskFSMacPart::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatMicroDrive: - dierr = DiskFSMicroDrive::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatFocusDrive: - dierr = DiskFSFocusDrive::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatCPM: - dierr = DiskFSCPM::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatMSDOS: - dierr = DiskFSFAT::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - break; - case kFormatRDOS33: - case kFormatRDOS32: - case kFormatRDOS3: - dierr = DiskFSRDOS::TestFS(this, &newOrder, &newFormat, DiskFS::kLeniencyVery); - if (newFormat != format) - dierr = kDIErrFilesystemNotFound; // found RDOS, but wrong flavor - break; - case kFormatGenericPhysicalOrd: - case kFormatGenericProDOSOrd: - case kFormatGenericDOSOrd: - case kFormatGenericCPMOrd: - /* no discussion possible, since there's no FS to validate */ - newFormat = format; - newOrder = order; - break; - case kFormatUnknown: - /* only valid in rare situations, e.g. CFFA CreatePlaceholder */ - newFormat = format; - newOrder = order; - break; - default: - dierr = kDIErrUnsupportedFSFmt; - break; - } - - if (dierr != kDIErrNone) { - LOGI(" DI override failed"); - goto bail; - } - - /* - * We passed in "order" to TestFS. If it came back with something - * different, it means that it didn't like the new order value even - * when "leniency" was granted. - */ - if (newOrder != order) { - dierr = kDIErrBadOrdering; - goto bail; - } - - fFormat = format; - fOrder = newOrder; - fFileSysOrder = CalcFSSectorOrder(); - - LOGI(" DI override accepted"); - -bail: - return dierr; -} - -/* - * Figure out the sector ordering for this filesystem, so we can decide - * how the sectors need to be re-arranged when we're reading them. - * - * If the value returned by this function matches fOrder, then no swapping - * will be done. - * - * NOTE: this table is redundant with some knowledge embedded in the - * individual "TestFS" functions. - */ -DiskImg::SectorOrder DiskImg::CalcFSSectorOrder(void) const -{ - /* in the absence of information, just leave it alone */ - if (fFormat == kFormatUnknown || fOrder == kSectorOrderUnknown) { - LOGI(" DI WARNING: FindSectorOrder but format not known"); - return fOrder; - } - - assert(fOrder == kSectorOrderPhysical || fOrder == kSectorOrderCPM || - fOrder == kSectorOrderProDOS || fOrder == kSectorOrderDOS); - - switch (fFormat) { - case kFormatGenericPhysicalOrd: - case kFormatRDOS32: - case kFormatRDOS3: - return kSectorOrderPhysical; - - case kFormatGenericDOSOrd: - case kFormatDOS33: - case kFormatDOS32: - case kFormatUNIDOS: - case kFormatOzDOS: - case kFormatGutenberg: - return kSectorOrderDOS; - - case kFormatGenericCPMOrd: - case kFormatCPM: - return kSectorOrderCPM; - - case kFormatGenericProDOSOrd: - case kFormatProDOS: - case kFormatRDOS33: - case kFormatPascal: - case kFormatMacHFS: - case kFormatMacMFS: - case kFormatLisa: - case kFormatMSDOS: - case kFormatISO9660: - case kFormatCFFA4: - case kFormatCFFA8: - case kFormatMacPart: - case kFormatMicroDrive: - case kFormatFocusDrive: - return kSectorOrderProDOS; - - default: - assert(false); - return fOrder; - } -} - -/* - * Based on the disk format, figure out if we should prefer blocks or - * sectors when examining disk contents. - */ -bool DiskImg::ShowAsBlocks(void) const -{ - if (!fHasBlocks) - return false; - - /* in the absence of information, assume sectors */ - if (fFormat == kFormatUnknown) { - if (fOrder == kSectorOrderProDOS) - return true; - else - return false; - } - - switch (fFormat) { - case kFormatGenericPhysicalOrd: - case kFormatGenericDOSOrd: - case kFormatDOS33: - case kFormatDOS32: - case kFormatRDOS3: - case kFormatRDOS33: - case kFormatUNIDOS: - case kFormatOzDOS: - case kFormatGutenberg: - return false; - - case kFormatGenericProDOSOrd: - case kFormatGenericCPMOrd: - case kFormatProDOS: - case kFormatPascal: - case kFormatMacHFS: - case kFormatMacMFS: - case kFormatLisa: - case kFormatCPM: - case kFormatMSDOS: - case kFormatISO9660: - case kFormatCFFA4: - case kFormatCFFA8: - case kFormatMacPart: - case kFormatMicroDrive: - case kFormatFocusDrive: - return true; - - default: - assert(false); - return false; - } -} - - -/* - * Format an image with the requested fileystem format. This only works if - * the matching DiskFS supports formatting of disks. - */ -DIError DiskImg::FormatImage(FSFormat format, const char* volName) -{ - DIError dierr = kDIErrNone; - DiskFS* pDiskFS = NULL; - FSFormat savedFormat; - - LOGI(" DI FormatImage '%s'", volName); - - /* - * Open a temporary DiskFS for the requested format. We do this via the - * standard OpenAppropriate call, so we temporarily switch our format - * out. (We will eventually replace it, but we want to make sure that - * local error handling works correctly, so we restore it for now.) - */ - savedFormat = fFormat; - fFormat = format; - pDiskFS = OpenAppropriateDiskFS(false); - fFormat = savedFormat; - - if (pDiskFS == NULL) { - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - dierr = pDiskFS->Format(this, volName); - if (dierr != kDIErrNone) - goto bail; - - LOGI("DI format successful"); - fFormat = format; - -bail: - delete pDiskFS; - return dierr; -} - -/* - * Clear an image to zeros, usually done as a prelude to a higher-level format. - * - * BUG: this should also handle the track/sector case. - * - * HEY: this is awfully slow on large disks... should have some sort of - * optimized path that just writes to the GFD or something. Maybe even just - * a "ZeroBlock" instead of "WriteBlock" so we can memset instead of memcpy? - */ -DIError DiskImg::ZeroImage(void) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlockSize]; - long block; - - LOGI(" DI ZeroImage (%ld blocks)", GetNumBlocks()); - memset(blkBuf, 0, sizeof(blkBuf)); - - for (block = 0; block < GetNumBlocks(); block++) { - dierr = WriteBlock(block, blkBuf); - if (dierr != kDIErrNone) - break; - } - - return dierr; -} - - -/* - * Set the "scan progress" function. - * - * We want to use the same function for our sub-volumes too. - */ -void DiskImg::SetScanProgressCallback(ScanProgressCallback func, void* cookie) -{ - if (fpParentImg != NULL) { - /* unexpected, but perfectly okay */ - DebugBreak(); - } - - fpScanProgressCallback = func; - fScanProgressCookie = cookie; - fScanCount = 0; - fScanMsg[0] = '\0'; - fScanLastMsgWhen = time(NULL); -} - -/* - * Update the progress. Call with a string at the start of a volume, then - * call with a NULL pointer every time we add a file. - */ -bool DiskImg::UpdateScanProgress(const char* newStr) -{ - ScanProgressCallback func = fpScanProgressCallback; - DiskImg* pImg = this; - bool result = true; - - /* search up the tree to find a progress updater */ - while (func == NULL) { - pImg = pImg->fpParentImg; - if (pImg == NULL) - return result; // none defined, bail out - func = pImg->fpScanProgressCallback; - } - - time_t now = time(NULL); - - if (newStr == NULL) { - fScanCount++; - //if ((fScanCount % 100) == 0) - if (fScanLastMsgWhen != now) { - result = (*func)(fScanProgressCookie, - fScanMsg, fScanCount); - fScanLastMsgWhen = now; - } - } else { - fScanCount = 0; - strncpy(fScanMsg, newStr, sizeof(fScanMsg)); - fScanMsg[sizeof(fScanMsg)-1] = '\0'; - result = (*func)(fScanProgressCookie, fScanMsg, - fScanCount); - fScanLastMsgWhen = now; - } - - return result; -} - - -/* - * ========================================================================== - * Block/track/sector I/O - * ========================================================================== - */ - -/* - * Handle sector order conversions. - */ -DIError DiskImg::CalcSectorAndOffset(long track, int sector, SectorOrder imageOrder, - SectorOrder fsOrder, di_off_t* pOffset, int* pNewSector) -{ - if (!fHasSectors) - return kDIErrUnsupportedAccess; - - /* - * Sector order conversions. No table is needed for Copy ][+ format, - * which is equivalent to "physical". - */ - static const int raw2dos[16] = { - 0, 7, 14, 6, 13, 5, 12, 4, 11, 3, 10, 2, 9, 1, 8, 15 - }; - static const int dos2raw[16] = { - 0, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 15 - }; - static const int raw2prodos[16] = { - 0, 8, 1, 9, 2, 10, 3, 11, 4, 12, 5, 13, 6, 14, 7, 15 - }; - static const int prodos2raw[16] = { - 0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13, 15 - }; - static const int raw2cpm[16] = { - 0, 11, 6, 1, 12, 7, 2, 13, 8, 3, 14, 9, 4, 15, 10, 5 - }; - static const int cpm2raw[16] = { - 0, 3, 6, 9, 12, 15, 2, 5, 8, 11, 14, 1, 4, 7, 10, 13 - }; - - if (track < 0 || track >= fNumTracks) { - LOGI(" DI read invalid track %ld", track); - return kDIErrInvalidTrack; - } - if (sector < 0 || sector >= fNumSectPerTrack) { - LOGI(" DI read invalid sector %d", sector); - return kDIErrInvalidSector; - } - - di_off_t offset; - int newSector = -1; - - /* - * 16-sector disks write sectors in ascending order and then remap - * them with a translation table. - */ - if (fNumSectPerTrack == 16 || fNumSectPerTrack == 32) { - if (fSectorPairing) { - assert(fSectorPairOffset == 0 || fSectorPairOffset == 1); - // this pushes "track" beyond fNumTracks - track *= 2; - if (sector >= 16) { - track++; - sector -= 16; - } - offset = track * fNumSectPerTrack * kSectorSize; - - sector = sector * 2 + fSectorPairOffset; - if (sector >= 16) { - offset += 16*kSectorSize; - sector -= 16; - } - } else { - offset = track * fNumSectPerTrack * kSectorSize; - if (sector >= 16) { - offset += 16*kSectorSize; - sector -= 16; - } - } - assert(sector >= 0 && sector < 16); - - /* convert request to "raw" sector number */ - switch (fsOrder) { - case kSectorOrderProDOS: - newSector = prodos2raw[sector]; - break; - case kSectorOrderDOS: - newSector = dos2raw[sector]; - break; - case kSectorOrderCPM: - newSector = cpm2raw[sector]; - break; - case kSectorOrderPhysical: // used for Copy ][+ - newSector = sector; - break; - case kSectorOrderUnknown: - // should never happen; fall through to "default" - default: - assert(false); - newSector = sector; - break; - } - - /* convert "raw" request to the image's ordering */ - switch (imageOrder) { - case kSectorOrderProDOS: - newSector = raw2prodos[newSector]; - break; - case kSectorOrderDOS: - newSector = raw2dos[newSector]; - break; - case kSectorOrderCPM: - newSector = raw2cpm[newSector]; - break; - case kSectorOrderPhysical: - //newSector = newSector; - break; - case kSectorOrderUnknown: - // should never happen; fall through to "default" - default: - assert(false); - //newSector = newSector; - break; - } - - if (imageOrder == fsOrder) { - assert(sector == newSector); - } - - offset += newSector * kSectorSize; - } else if (fNumSectPerTrack == 13) { - /* sector skew has no meaning, so assume no translation */ - offset = track * fNumSectPerTrack * kSectorSize; - newSector = sector; - offset += newSector * kSectorSize; - if (imageOrder != fsOrder) { - /* translation expected */ - LOGI("NOTE: CalcSectorAndOffset for nspt=13 with img=%d fs=%d", - imageOrder, fsOrder); - } - } else { - assert(false); // should not be here - - /* try to do something reasonable */ - assert(imageOrder == fsOrder); - offset = (di_off_t)track * fNumSectPerTrack * kSectorSize; - offset += sector * kSectorSize; - } - - *pOffset = offset; - *pNewSector = newSector; - return kDIErrNone; -} - -/* - * Determine whether an image uses a linear mapping. This allows us to - * optimize block reads & writes, very useful when dealing with logical - * volumes under Windows (which also use 512-byte blocks). - * - * The "imageOrder" argument usually comes from fOrder, and "fsOrder" - * comes from "fFileSysOrder". - */ -inline bool DiskImg::IsLinearBlocks(SectorOrder imageOrder, SectorOrder fsOrder) -{ - /* - * Any time fOrder==fFileSysOrder, we know that we have a linear - * mapping. This holds true for reading ProDOS blocks from a ".po" - * file or reading DOS sectors from a ".do" file. - */ - return (IsSectorFormat(fPhysical) && fHasBlocks && - imageOrder == fsOrder); -} - -/* - * Read the specified track and sector, adjusting for sector ordering as - * appropriate. - * - * Copies 256 bytes into "*buf". - * - * Returns 0 on success, nonzero on failure. - */ -DIError DiskImg::ReadTrackSectorSwapped(long track, int sector, void* buf, - SectorOrder imageOrder, SectorOrder fsOrder) -{ - DIError dierr; - di_off_t offset; - int newSector = -1; - - if (buf == NULL) - return kDIErrInvalidArg; - -#if 0 // Pre-d13 - if (fNumSectPerTrack == 13) { - /* no sector skewing possible for 13-sector disks */ - assert(fHasNibbles); - - return ReadNibbleSector(track, sector, buf, fpNibbleDescr); - } -#endif - - dierr = CalcSectorAndOffset(track, sector, imageOrder, fsOrder, - &offset, &newSector); - if (dierr != kDIErrNone) - return dierr; - - if (IsSectorFormat(fPhysical)) { - assert(offset+kSectorSize <= fLength); - - //LOGI(" DI t=%d s=%d", track, - // (offset - track * fNumSectPerTrack * kSectorSize) / kSectorSize); - - dierr = CopyBytesOut(buf, offset, kSectorSize); - } else if (IsNibbleFormat(fPhysical)) { - if (imageOrder != kSectorOrderPhysical) { - LOGI(" NOTE: nibble imageOrder is %d (expected %d)", - imageOrder, kSectorOrderPhysical); - } - dierr = ReadNibbleSector(track, newSector, buf, fpNibbleDescr); - } else { - assert(false); - dierr = kDIErrInternal; - } - - return dierr; -} - -/* - * Write the specified track and sector, adjusting for sector ordering as - * appropriate. - * - * Copies 256 bytes out of "buf". - * - * Returns 0 on success, nonzero on failure. - */ -DIError DiskImg::WriteTrackSector(long track, int sector, const void* buf) -{ - DIError dierr; - di_off_t offset; - int newSector = -1; - - if (buf == NULL) - return kDIErrInvalidArg; - if (fReadOnly) - return kDIErrAccessDenied; - -#if 0 // Pre-d13 - if (fNumSectPerTrack == 13) { - /* no sector skewing possible for 13-sector disks */ - assert(fHasNibbles); - - return WriteNibbleSector(track, sector, buf, fpNibbleDescr); - } -#endif - - dierr = CalcSectorAndOffset(track, sector, fOrder, fFileSysOrder, - &offset, &newSector); - if (dierr != kDIErrNone) - return dierr; - - if (IsSectorFormat(fPhysical)) { - assert(offset+kSectorSize <= fLength); - - //LOGI(" DI t=%d s=%d", track, - // (offset - track * fNumSectPerTrack * kSectorSize) / kSectorSize); - - dierr = CopyBytesIn(buf, offset, kSectorSize); - } else if (IsNibbleFormat(fPhysical)) { - if (fOrder != kSectorOrderPhysical) { - LOGI(" NOTE: nibble fOrder is %d (expected %d)", - fOrder, kSectorOrderPhysical); - } - dierr = WriteNibbleSector(track, newSector, buf, fpNibbleDescr); - } else { - assert(false); - dierr = kDIErrInternal; - } - - return dierr; -} - -/* - * Read a 512-byte block. - * - * Copies 512 bytes into "*buf". - */ -DIError DiskImg::ReadBlockSwapped(long block, void* buf, SectorOrder imageOrder, - SectorOrder fsOrder) -{ - if (!fHasBlocks) - return kDIErrUnsupportedAccess; - if (block < 0 || block >= fNumBlocks) - return kDIErrInvalidBlock; - if (buf == NULL) - return kDIErrInvalidArg; - - DIError dierr; - long track, blkInTrk; - - /* if we have a bad block map, check it */ - if (CheckForBadBlocks(block, 1)) { - dierr = kDIErrReadFailed; - goto bail; - } - - if (fHasSectors && !IsLinearBlocks(imageOrder, fsOrder)) { - /* run it through the t/s call so we handle DOS ordering */ - track = block / (fNumSectPerTrack/2); - blkInTrk = block - (track * (fNumSectPerTrack/2)); - dierr = ReadTrackSectorSwapped(track, blkInTrk*2, buf, - imageOrder, fsOrder); - if (dierr != kDIErrNone) - return dierr; - dierr = ReadTrackSectorSwapped(track, blkInTrk*2+1, - (char*)buf+kSectorSize, imageOrder, fsOrder); - } else if (fHasBlocks) { - /* no sectors, so no swapping; must be linear blocks */ - if (imageOrder != fsOrder) { - LOGI(" DI NOTE: ReadBlockSwapped on non-sector (%d/%d)", - imageOrder, fsOrder); - } - dierr = CopyBytesOut(buf, (di_off_t) block * kBlockSize, kBlockSize); - } else { - assert(false); - dierr = kDIErrInternal; - } - -bail: - return dierr; -} - -/* - * Read multiple blocks. - * - * IMPORTANT: this returns immediately when a read fails. The buffer will - * probably not contain data from all readable sectors. The application is - * expected to retry the blocks individually. - */ -DIError DiskImg::ReadBlocks(long startBlock, int numBlocks, void* buf) -{ - DIError dierr = kDIErrNone; - - assert(fHasBlocks); - assert(startBlock >= 0); - assert(numBlocks > 0); - assert(buf != NULL); - - if (startBlock < 0 || numBlocks + startBlock > GetNumBlocks()) { - assert(false); - return kDIErrInvalidArg; - } - - /* if we have a bad block map, check it */ - if (CheckForBadBlocks(startBlock, numBlocks)) { - dierr = kDIErrReadFailed; - goto bail; - } - - if (!IsLinearBlocks(fOrder, fFileSysOrder)) { - /* - * This isn't a collection of linear blocks, so we need to read it one - * block at a time with sector swapping. This almost certainly means - * that we're not reading from physical media, so performance shouldn't - * be an issue. - */ - if (startBlock == 0) { - LOGI(" ReadBlocks: nonlinear, not trying"); - } - while (numBlocks--) { - dierr = ReadBlock(startBlock, buf); - if (dierr != kDIErrNone) - goto bail; - startBlock++; - buf = (uint8_t*)buf + kBlockSize; - } - } else { - if (startBlock == 0) { - LOGI(" ReadBlocks: doing big linear reads"); - } - dierr = CopyBytesOut(buf, - (di_off_t) startBlock * kBlockSize, numBlocks * kBlockSize); - } - -bail: - return dierr; -} - -/* - * Check to see if any blocks in a range of blocks show up in the bad - * block map. This is primarily useful for 3.5" disk images converted - * from nibble images, because we convert them directly to "cooked" - * 512-byte blocks. - * - * Returns "true" if we found bad blocks, "false" if not. - */ -bool DiskImg::CheckForBadBlocks(long startBlock, int numBlocks) -{ - int i; - - if (fpBadBlockMap == NULL) - return false; - - for (i = startBlock; i < startBlock+numBlocks; i++) { - if (fpBadBlockMap->IsSet(i)) - return true; - } - return false; -} - -/* - * Write a block of data to a DiskImg. - * - * Returns immediately when a block write fails. Does not try to write all - * blocks before returning failure. - */ -DIError DiskImg::WriteBlock(long block, const void* buf) -{ - if (!fHasBlocks) - return kDIErrUnsupportedAccess; - if (block < 0 || block >= fNumBlocks) - return kDIErrInvalidBlock; - if (buf == NULL) - return kDIErrInvalidArg; - if (fReadOnly) - return kDIErrAccessDenied; - - DIError dierr; - long track, blkInTrk; - - if (fHasSectors && !IsLinearBlocks(fOrder, fFileSysOrder)) { - /* run it through the t/s call so we handle DOS ordering */ - track = block / (fNumSectPerTrack/2); - blkInTrk = block - (track * (fNumSectPerTrack/2)); - dierr = WriteTrackSector(track, blkInTrk*2, buf); - if (dierr != kDIErrNone) - return dierr; - dierr = WriteTrackSector(track, blkInTrk*2+1, (char*)buf+kSectorSize); - } else if (fHasBlocks) { - /* no sectors, so no swapping; must be linear blocks */ - if (fOrder != fFileSysOrder) { - LOGI(" DI NOTE: WriteBlock on non-sector (%d/%d)", - fOrder, fFileSysOrder); - } - dierr = CopyBytesIn(buf, (di_off_t)block * kBlockSize, kBlockSize); - } else { - assert(false); - dierr = kDIErrInternal; - } - return dierr; -} - -/* - * Write multiple blocks. - */ -DIError DiskImg::WriteBlocks(long startBlock, int numBlocks, const void* buf) -{ - DIError dierr = kDIErrNone; - - assert(fHasBlocks); - assert(startBlock >= 0); - assert(numBlocks > 0); - assert(buf != NULL); - - if (startBlock < 0 || numBlocks + startBlock > GetNumBlocks()) { - assert(false); - return kDIErrInvalidArg; - } - - if (!IsLinearBlocks(fOrder, fFileSysOrder)) { - /* - * This isn't a collection of linear blocks, so we need to write it - * one block at a time with sector swapping. This almost certainly - * means that we're not reading from physical media, so performance - * shouldn't be an issue. - */ - if (startBlock == 0) { - LOGI(" WriteBlocks: nonlinear, not trying"); - } - while (numBlocks--) { - dierr = WriteBlock(startBlock, buf); - if (dierr != kDIErrNone) - goto bail; - startBlock++; - buf = (uint8_t*)buf + kBlockSize; - } - } else { - if (startBlock == 0) { - LOGI(" WriteBlocks: doing big linear writes"); - } - dierr = CopyBytesIn(buf, - (di_off_t) startBlock * kBlockSize, numBlocks * kBlockSize); - } - -bail: - return dierr; -} - - -/* - * Copy a chunk of bytes out of the disk image. - * - * (This is the lowest-level read routine in this class.) - */ -DIError DiskImg::CopyBytesOut(void* buf, di_off_t offset, int size) const -{ - DIError dierr; - - dierr = fpDataGFD->Seek(offset, kSeekSet); - if (dierr != kDIErrNone) { - LOGI(" DI seek off=%ld failed (err=%d)", (long) offset, dierr); - return dierr; - } - - dierr = fpDataGFD->Read(buf, size); - if (dierr != kDIErrNone) { - LOGI(" DI read off=%ld size=%d failed (err=%d)", - (long) offset, size, dierr); - return dierr; - } - - return kDIErrNone; -} - -/* - * Copy a chunk of bytes into the disk image. - * - * Sets the "dirty" flag. - * - * (This is the lowest-level write routine in DiskImg.) - */ -DIError DiskImg::CopyBytesIn(const void* buf, di_off_t offset, int size) -{ - DIError dierr; - - if (fReadOnly) { - DebugBreak(); - return kDIErrAccessDenied; - } - assert(fpDataGFD != NULL); // somebody closed the image? - - dierr = fpDataGFD->Seek(offset, kSeekSet); - if (dierr != kDIErrNone) { - LOGI(" DI seek off=%ld failed (err=%d)", (long) offset, dierr); - return dierr; - } - - dierr = fpDataGFD->Write(buf, size); - if (dierr != kDIErrNone) { - LOGI(" DI write off=%ld size=%d failed (err=%d)", - (long) offset, size, dierr); - return dierr; - } - - /* set the dirty flag here and everywhere above */ - DiskImg* pImg = this; - while (pImg != NULL) { - pImg->fDirty = true; - pImg = pImg->fpParentImg; - } - - return kDIErrNone; -} - - -/* - * =========================================================================== - * Image creation - * =========================================================================== - */ - -/* - * Create a disk image with the specified parameters. - * - * "storageName" and "pNibbleDescr" may be NULL. - */ -DIError DiskImg::CreateImage(const char* pathName, const char* storageName, - OuterFormat outerFormat, FileFormat fileFormat, PhysicalFormat physical, - const NibbleDescr* pNibbleDescr, SectorOrder order, - FSFormat format, long numBlocks, bool skipFormat) -{ - assert(fpDataGFD == NULL); // should not be open already! - - if (numBlocks <= 0) { - LOGI("ERROR: bad numBlocks %ld", numBlocks); - assert(false); - return kDIErrInvalidCreateReq; - } - - fOuterFormat = outerFormat; - fFileFormat = fileFormat; - fPhysical = physical; - SetCustomNibbleDescr(pNibbleDescr); - fOrder = order; - fFormat = format; - - fNumBlocks = numBlocks; - fHasBlocks = true; - - return CreateImageCommon(pathName, storageName, skipFormat); -} - -DIError DiskImg::CreateImage(const char* pathName, const char* storageName, - OuterFormat outerFormat, FileFormat fileFormat, PhysicalFormat physical, - const NibbleDescr* pNibbleDescr, SectorOrder order, - FSFormat format, long numTracks, long numSectPerTrack, bool skipFormat) -{ - assert(fpDataGFD == NULL); // should not be open already! - - if (numTracks <= 0 || numSectPerTrack == 0) { - LOGI("ERROR: bad tracks/sectors %ld/%ld", numTracks, numSectPerTrack); - assert(false); - return kDIErrInvalidCreateReq; - } - - fOuterFormat = outerFormat; - fFileFormat = fileFormat; - fPhysical = physical; - SetCustomNibbleDescr(pNibbleDescr); - fOrder = order; - fFormat = format; - - fNumTracks = numTracks; - fNumSectPerTrack = numSectPerTrack; - fHasSectors = true; - if (numSectPerTrack < 0) { - /* nibble image with non-standard formatting */ - if (!IsNibbleFormat(fPhysical)) { - LOGI("Whoa: expected nibble format here"); - assert(false); - return kDIErrInvalidCreateReq; - } - LOGI("Sector image w/o sectors, switching to nibble mode"); - fHasNibbles = true; - fHasSectors = false; - fpNibbleDescr = NULL; - } - - return CreateImageCommon(pathName, storageName, skipFormat); -} - -/* - * Do the actual disk image creation. - */ -DIError DiskImg::CreateImageCommon(const char* pathName, const char* storageName, - bool skipFormat) -{ - DIError dierr; - - /* - * Step 1: figure out fHasBlocks/fHasSectors/fHasNibbles and any - * other misc fields. - * - * If the disk is a nibble image expected to have a particular - * volume number, it should have already been set by the application. - */ - if (fHasBlocks) { - if ((fNumBlocks % 8) == 0) { - fHasSectors = true; - fNumSectPerTrack = 16; - fNumTracks = fNumBlocks / 8; - } else { - LOGI("NOTE: sector access to new image not possible"); - } - } else if (fHasSectors) { - if ((fNumSectPerTrack & 0x01) == 0) { - fHasBlocks = true; - fNumBlocks = (fNumTracks * fNumSectPerTrack) / 2; - } else { - LOGI("NOTE: block access to new image not possible"); - } - } - if (fHasSectors && fPhysical != kPhysicalFormatSectors) - fHasNibbles = true; - assert(fHasBlocks || fHasSectors || fHasNibbles); - - fFileSysOrder = CalcFSSectorOrder(); - fReadOnly = false; - fDirty = true; - - /* - * Step 2: check for invalid arguments and bad combinations. - */ - dierr = ValidateCreateFormat(); - if (dierr != kDIErrNone) { - LOGE("ERROR: CIC arg validation failed, bailing"); - goto bail; - } - - /* - * Step 3: create the destination file. Put this into fpWrapperGFD - * or fpOuterGFD. - * - * The file must not already exist. - * - * THOUGHT: should allow creation of an in-memory disk image. This won't - * work for NuFX, but will work for pretty much everything else. - */ - LOGI(" CIC: creating '%s'", pathName); - int fd; - fd = open(pathName, O_CREAT | O_EXCL, 0644); - if (fd < 0) { - dierr = (DIError) errno; - LOGE("ERROR: unable to create file '%s' (errno=%d)", - pathName, dierr); - goto bail; - } - close(fd); - - GFDFile* pGFDFile; - pGFDFile = new GFDFile; - - dierr = pGFDFile->Open(pathName, false); - if (dierr != kDIErrNone) { - delete pGFDFile; - goto bail; - } - - if (fOuterFormat == kOuterFormatNone) - fpWrapperGFD = pGFDFile; - else - fpOuterGFD = pGFDFile; - pGFDFile = NULL; - - /* - * Step 4: if we have an outer GFD and therefore don't currently have - * an fpWrapperGFD, create an expandable memory buffer to use. - * - * We want to take a guess at how big the image will be, so compute - * fLength now. - * - * Create an OuterWrapper as needed. - */ - if (IsSectorFormat(fPhysical)) { - if (fHasBlocks) - fLength = (di_off_t) GetNumBlocks() * kBlockSize; - else - fLength = (di_off_t) GetNumTracks() * GetNumSectPerTrack() * kSectorSize; - } else { - assert(IsNibbleFormat(fPhysical)); - fLength = GetNumTracks() * GetNibbleTrackAllocLength(); - } - assert(fLength > 0); - - if (fpWrapperGFD == NULL) { - /* shift GFDs and create a new memory GFD, pre-sized */ - GFDBuffer* pGFDBuffer = new GFDBuffer; - - /* use fLength as a starting point for buffer size; this may expand */ - dierr = pGFDBuffer->Open(NULL, fLength, true, true, false); - if (dierr != kDIErrNone) { - delete pGFDBuffer; - goto bail; - } - - fpWrapperGFD = pGFDBuffer; - pGFDBuffer = NULL; - } - - /* create an fpOuterWrapper struct */ - switch (fOuterFormat) { - case kOuterFormatNone: - break; - case kOuterFormatGzip: - fpOuterWrapper = new OuterGzip; - if (fpOuterWrapper == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - break; - case kOuterFormatZip: - fpOuterWrapper = new OuterZip; - if (fpOuterWrapper == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - break; - default: - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - /* - * Step 5: tell the ImageWrapper to write itself into the GFD, passing - * in the blank memory buffer. - * - * - Unadorned formats copy from memory buffer to fpWrapperGFD on disk. - * (With gz, fpWrapperGFD is actually a memory buffer.) fpDataGFD - * becomes an offset into the file. - * - 2MG writes header into GFD and follows it with all data; DC42 - * and Sim2e do similar things. - * - NuFX reopens pathName as SHK file (fpWrapperGFD must point to a - * file) and accesses the archive through an fpArchive. fpDataGFD - * is created as a memory buffer and the blank image is copied in. - * - DDD leaves fpWrapperGFD alone and copies the blank image into a - * new buffer for fpDataGFD. - * - * Sets fWrappedLength when possible, determined from fPhysical and - * either fNumBlocks or fNumTracks. Creates fpDataGFD, often as a - * GFDGFD offset into fpWrapperGFD. - */ - switch (fFileFormat) { - case kFileFormat2MG: - fpImageWrapper = new Wrapper2MG(); - break; - case kFileFormatDiskCopy42: - fpImageWrapper = new WrapperDiskCopy42(); - fpImageWrapper->SetStorageName(storageName); - break; - case kFileFormatSim2eHDV: - fpImageWrapper = new WrapperSim2eHDV(); - break; - case kFileFormatTrackStar: - fpImageWrapper = new WrapperTrackStar(); - fpImageWrapper->SetStorageName(storageName); - break; - case kFileFormatFDI: - fpImageWrapper = new WrapperFDI(); - break; - case kFileFormatNuFX: - fpImageWrapper = new WrapperNuFX(); - fpImageWrapper->SetStorageName(storageName); - ((WrapperNuFX*)fpImageWrapper)->SetCompressType( - (NuThreadFormat) fNuFXCompressType); - break; - case kFileFormatDDD: - fpImageWrapper = new WrapperDDD(); - break; - case kFileFormatUnadorned: - if (IsSectorFormat(fPhysical)) - fpImageWrapper = new WrapperUnadornedSector(); - else if (IsNibbleFormat(fPhysical)) - fpImageWrapper = new WrapperUnadornedNibble(); - else { - assert(false); - } - break; - default: - assert(fpImageWrapper == NULL); - break; - } - - if (fpImageWrapper == NULL) { - LOGW(" DI couldn't figure out the file format"); - dierr = kDIErrUnrecognizedFileFmt; - goto bail; - } - - /* create the wrapper, write the header, and create fpDataGFD */ - assert(fpDataGFD == NULL); - dierr = fpImageWrapper->Create(fLength, fPhysical, fOrder, - fDOSVolumeNum, fpWrapperGFD, &fWrappedLength, &fpDataGFD); - if (dierr != kDIErrNone) { - LOGE("ImageWrapper Create failed, err=%d", dierr); - goto bail; - } - assert(fpDataGFD != NULL); - - /* - * Step 6: "format" fpDataGFD. - * - * Note we don't specify an ordering to the "create blank" functions. - * Either it's sectors, in which case it's all zeroes, or it's nibbles, - * in which case it's always in physical order. - * - * If we're formatting for nibbles, and the application hasn't specified - * a disk volume number, use the default (254). - */ - if (fPhysical == kPhysicalFormatSectors) - dierr = FormatSectors(fpDataGFD, skipFormat); // zero out the image - else { - assert(!skipFormat); // don't skip low-level nibble formatting! - if (fDOSVolumeNum == kVolumeNumNotSet) { - fDOSVolumeNum = kDefaultNibbleVolumeNum; - LOGD(" Using default nibble volume num"); - } - - dierr = FormatNibbles(fpDataGFD); // write basic nibble stuff - } - - - /* - * We're done! - * - * Quick sanity check... - */ - if (fOuterFormat != kOuterFormatNone) { - assert(fpOuterGFD != NULL); - assert(fpWrapperGFD != NULL); - assert(fpDataGFD != NULL); - } - -bail: - return dierr; -} - -/* - * Check that the requested format is one we can create. - * - * We don't allow .SDK.GZ or 6384-byte nibble 2MG. 2MG sector images - * must be in DOS or ProDOS order. - * - * Only "generic" FS formats may be used. The application may choose - * to call AnalyzeImage later on to set the actual FS once data has - * been written. - */ -DIError DiskImg::ValidateCreateFormat(void) const -{ - /* - * Check for invalid arguments. - */ - if (fHasBlocks && fNumBlocks >= 4194304) { // 2GB or larger? - if (fFileFormat != kFileFormatUnadorned) { - LOGW("CreateImage: images >= 2GB can only be unadorned"); - return kDIErrInvalidCreateReq; - } - } - if (fOuterFormat == kOuterFormatUnknown || - fFileFormat == kFileFormatUnknown || - fPhysical == kPhysicalFormatUnknown || - fOrder == kSectorOrderUnknown || - fFormat == kFormatUnknown) - { - LOGW("CreateImage: ambiguous format"); - return kDIErrInvalidCreateReq; - } - if (fOuterFormat != kOuterFormatNone && - fOuterFormat != kOuterFormatGzip && - fOuterFormat != kOuterFormatZip) - { - LOGW("CreateImage: unsupported outer format %d", fOuterFormat); - return kDIErrInvalidCreateReq; - } - if (fFileFormat != kFileFormatUnadorned && - fFileFormat != kFileFormat2MG && - fFileFormat != kFileFormatDiskCopy42 && - fFileFormat != kFileFormatSim2eHDV && - fFileFormat != kFileFormatTrackStar && - fFileFormat != kFileFormatFDI && - fFileFormat != kFileFormatNuFX && - fFileFormat != kFileFormatDDD) - { - LOGW("CreateImage: unsupported file format %d", fFileFormat); - return kDIErrInvalidCreateReq; - } - if (fFormat != kFormatGenericPhysicalOrd && - fFormat != kFormatGenericProDOSOrd && - fFormat != kFormatGenericDOSOrd && - fFormat != kFormatGenericCPMOrd) - { - LOGW("CreateImage: may only use 'generic' formats"); - return kDIErrInvalidCreateReq; - } - - /* - * Check for invalid combinations. - */ - if (fPhysical != kPhysicalFormatSectors) { - if (fOrder != kSectorOrderPhysical) { - LOGW("CreateImage: nibble images are always 'physical' order"); - return kDIErrInvalidCreateReq; - } - - if (GetHasSectors() == false && GetHasNibbles() == false) { - LOGW("CreateImage: must set hasSectors(%d) or hasNibbles(%d)", - GetHasSectors(), GetHasNibbles()); - return kDIErrInvalidCreateReq; - } - - if (fpNibbleDescr == NULL && GetNumSectPerTrack() > 0) { - LOGW("CreateImage: must provide NibbleDescr for non-sector"); - return kDIErrInvalidCreateReq; - } - - if (fpNibbleDescr != NULL && - fpNibbleDescr->numSectors != GetNumSectPerTrack()) - { - LOGW("CreateImage: ?? nd->numSectors=%d, GetNumSectPerTrack=%d", - fpNibbleDescr->numSectors, GetNumSectPerTrack()); - return kDIErrInvalidCreateReq; - } - - if (fpNibbleDescr != NULL && ( - (fpNibbleDescr->numSectors == 13 && - fpNibbleDescr->encoding != kNibbleEnc53) || - (fpNibbleDescr->numSectors == 16 && - fpNibbleDescr->encoding != kNibbleEnc62)) - ) - { - LOGW("CreateImage: sector count/encoding mismatch"); - return kDIErrInvalidCreateReq; - } - - if (GetNumTracks() != kTrackCount525 && - !(GetNumTracks() == 40 && fFileFormat == kFileFormatTrackStar)) - { - LOGW("CreateImage: unexpected track count %ld", GetNumTracks()); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormat2MG) { - if (fPhysical != kPhysicalFormatSectors && - fPhysical != kPhysicalFormatNib525_6656) - { - LOGW("CreateImage: 2MG can't handle physical %d", fPhysical); - return kDIErrInvalidCreateReq; - } - - if (fPhysical == kPhysicalFormatSectors && - (fOrder != kSectorOrderProDOS && - fOrder != kSectorOrderDOS)) - { - LOGW("CreateImage: 2MG requires DOS or ProDOS ordering"); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormatNuFX) { - if (fOuterFormat != kOuterFormatNone) { - LOGW("CreateImage: can't mix NuFX and outer wrapper"); - return kDIErrInvalidCreateReq; - } - if (fPhysical != kPhysicalFormatSectors) { - LOGW("CreateImage: NuFX physical must be sectors"); - return kDIErrInvalidCreateReq; - } - if (fOrder != kSectorOrderProDOS) { - LOGW("CreateImage: NuFX is always ProDOS-order"); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormatDiskCopy42) { - if (fPhysical != kPhysicalFormatSectors) { - LOGW("CreateImage: DC42 physical must be sectors"); - return kDIErrInvalidCreateReq; - } - if ((GetHasBlocks() && GetNumBlocks() != 1600) || - (GetHasSectors() && - (GetNumTracks() != 200 || GetNumSectPerTrack() != 16))) - { - LOGW("CreateImage: DC42 only for 800K disks"); - return kDIErrInvalidCreateReq; - } - if (fOrder != kSectorOrderProDOS && - fOrder != kSectorOrderDOS) // used for UNIDOS disks?? - { - LOGW("CreateImage: DC42 is always ProDOS or DOS"); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormatSim2eHDV) { - if (fPhysical != kPhysicalFormatSectors) { - LOGW("CreateImage: Sim2eHDV physical must be sectors"); - return kDIErrInvalidCreateReq; - } - if (fOrder != kSectorOrderProDOS) { - LOGW("CreateImage: Sim2eHDV is always ProDOS-order"); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormatTrackStar) { - if (fPhysical != kPhysicalFormatNib525_Var) { - LOGW("CreateImage: TrackStar physical must be var-nibbles"); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormatFDI) { - if (fPhysical != kPhysicalFormatNib525_Var) { - LOGW("CreateImage: FDI physical must be var-nibbles"); - return kDIErrInvalidCreateReq; - } - } - if (fFileFormat == kFileFormatDDD) { - if (fPhysical != kPhysicalFormatSectors) { - LOGW("CreateImage: DDD physical must be sectors"); - return kDIErrInvalidCreateReq; - } - if (fOrder != kSectorOrderDOS) { - LOGW("CreateImage: DDD is always DOS-order"); - return kDIErrInvalidCreateReq; - } - if (!GetHasSectors() || GetNumTracks() != 35 || - GetNumSectPerTrack() != 16) - { - LOGW("CreateImage: DDD is only for 16-sector 35-track disks"); - return kDIErrInvalidCreateReq; - } - } - - return kDIErrNone; -} - -/* - * Create a blank image for physical=="sectors". - * - * fLength must be a multiple of 256. - * - * If "quickFormat" is set, only the very last sector is written (to set - * the EOF on the file). - */ -DIError DiskImg::FormatSectors(GenericFD* pGFD, bool quickFormat) const -{ - DIError dierr = kDIErrNone; - char sctBuf[kSectorSize]; - di_off_t length; - - assert(fLength > 0 && (fLength & 0xff) == 0); - - //if (!(fLength & 0x01)) - // return FormatBlocks(pGFD); - - memset(sctBuf, 0, sizeof(sctBuf)); - pGFD->Rewind(); - - if (quickFormat) { - dierr = pGFD->Seek(fLength - sizeof(sctBuf), kSeekSet); - if (dierr != kDIErrNone) { - LOGI(" FormatSectors: GFD seek %ld failed (err=%d)", - (long) fLength - sizeof(sctBuf), dierr); - goto bail; - } - dierr = pGFD->Write(sctBuf, sizeof(sctBuf), NULL); - if (dierr != kDIErrNone) { - LOGI(" FormatSectors: GFD quick write failed (err=%d)", dierr); - goto bail; - } - } else { - for (length = fLength ; length > 0; length -= sizeof(sctBuf)) { - dierr = pGFD->Write(sctBuf, sizeof(sctBuf), NULL); - if (dierr != kDIErrNone) { - LOGI(" FormatSectors: GFD write failed (err=%d)", dierr); - goto bail; - } - } - assert(length == 0); - } - - -bail: - return dierr; -} - -#if 0 // didn't help -/* - * Create a blank image for physical=="sectors". This is called from - * FormatSectors when it looks like we're formatting entire blocks. - */ -DIError -DiskImg::FormatBlocks(GenericFD* pGFD) const -{ - DIError dierr; - char blkBuf[kBlockSize]; - long length; - time_t start, end; - - assert(fLength > 0 && (fLength & 0x1ff) == 0); - - start = time(NULL); - - memset(blkBuf, 0, sizeof(blkBuf)); - pGFD->Rewind(); - - for (length = fLength ; length > 0; length -= sizeof(blkBuf)) { - dierr = pGFD->Write(blkBuf, sizeof(blkBuf), NULL); - if (dierr != kDIErrNone) { - LOGI(" FormatBlocks: GFD write failed (err=%d)", dierr); - return dierr; - } - } - assert(length == 0); - - end = time(NULL); - LOGI("FormatBlocks complete, time=%ld", end - start); - - return kDIErrNone; -} -#endif - - -/* - * =========================================================================== - * Utility functions - * =========================================================================== - */ - -/* - * Add a note to this disk image. - * - * This is how we communicate cautions and warnings to the user. Use - * linefeeds ('\n') to indicate line breaks. - * - * The maximum length of a single note is set by the size of "buf". - */ -void DiskImg::AddNote(NoteType type, const char* fmt, ...) -{ - char buf[512]; - char* cp = buf; - int maxLen = sizeof(buf); - va_list args; - int len; - - /* - * Prepend a string that highlights the note. - */ - switch (type) { - case kNoteWarning: - strcpy(cp, "- WARNING: "); - break; - default: - strcpy(cp, "- "); - break; - } - len = strlen(cp); - cp += len; - maxLen -= len; - - /* - * Add the note. - */ - va_start(args, fmt); -#if defined(HAVE_VSNPRINTF) - (void) vsnprintf(cp, maxLen, fmt, args); -#elif defined(HAVE__VSNPRINTF) - (void) _vsnprintf(cp, maxLen, fmt, args); -#else -# error "hosed" -#endif - va_end(args); - - buf[sizeof(buf)-2] = '\0'; // leave room for additional '\n' - len = strlen(buf); - if (len > 0 && buf[len-1] != '\n') { - buf[len] = '\n'; - buf[len+1] = '\0'; - len++; - } - - LOGD("+++ adding note '%s'", buf); - - if (fNotes == NULL) { - fNotes = new char[len +1]; - if (fNotes == NULL) { - LOGW("Unable to create notes[%d]", len+1); - assert(false); - return; - } - strcpy(fNotes, buf); - } else { - int existingLen = strlen(fNotes); - char* newNotes = new char[existingLen + len +1]; - if (newNotes == NULL) { - LOGW("Unable to create newNotes[%d]", existingLen+len+1); - assert(false); - return; - } - strcpy(newNotes, fNotes); - strcpy(newNotes + existingLen, buf); - delete[] fNotes; - fNotes = newNotes; - } -} - -/* - * Return a string with the notes in it. - */ -const char* DiskImg::GetNotes(void) const -{ - if (fNotes == NULL) - return ""; - else - return fNotes; -} - - -/* - * Get length and offset of tracks in a nibble image. This is necessary - * because of formats with variable-length tracks (e.g. TrackStar). - */ -int DiskImg::GetNibbleTrackLength(long track) const -{ - assert(fpImageWrapper != NULL); - return fpImageWrapper->GetNibbleTrackLength(fPhysical, track); -} - -int DiskImg::GetNibbleTrackOffset(long track) const -{ - assert(fpImageWrapper != NULL); - return fpImageWrapper->GetNibbleTrackOffset(fPhysical, track); -} - - -/* - * Return a new object with the appropriate DiskFS sub-class. - * - * If the image hasn't been analyzed, or was analyzed to no avail, "NULL" - * is returned unless "allowUnknown" is set to "true". In that case, a - * DiskFSUnknown is returned. - * - * This doesn't inspire the DiskFS to do any processing, just creates the - * new object. - */ -DiskFS* DiskImg::OpenAppropriateDiskFS(bool allowUnknown) -{ - DiskFS* pDiskFS = NULL; - - /* - * Create an appropriate DiskFS object. - */ - switch (GetFSFormat()) { - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - pDiskFS = new DiskFSDOS33(); - break; - case DiskImg::kFormatProDOS: - pDiskFS = new DiskFSProDOS(); - break; - case DiskImg::kFormatPascal: - pDiskFS = new DiskFSPascal(); - break; - case DiskImg::kFormatMacHFS: - pDiskFS = new DiskFSHFS(); - break; - case DiskImg::kFormatUNIDOS: - pDiskFS = new DiskFSUNIDOS(); - break; - case DiskImg::kFormatOzDOS: - pDiskFS = new DiskFSOzDOS(); - break; - case DiskImg::kFormatCFFA4: - case DiskImg::kFormatCFFA8: - pDiskFS = new DiskFSCFFA(); - break; - case DiskImg::kFormatMacPart: - pDiskFS = new DiskFSMacPart(); - break; - case DiskImg::kFormatMicroDrive: - pDiskFS = new DiskFSMicroDrive(); - break; - case DiskImg::kFormatFocusDrive: - pDiskFS = new DiskFSFocusDrive(); - break; - case DiskImg::kFormatCPM: - pDiskFS = new DiskFSCPM(); - break; - case DiskImg::kFormatMSDOS: - pDiskFS = new DiskFSFAT(); - break; - case DiskImg::kFormatRDOS33: - case DiskImg::kFormatRDOS32: - case DiskImg::kFormatRDOS3: - pDiskFS = new DiskFSRDOS(); - break; - case DiskImg::kFormatGutenberg: - pDiskFS = new DiskFSGutenberg(); - break; - - default: - LOGI("WARNING: unhandled DiskFS case %d", GetFSFormat()); - assert(false); - /* fall through */ - case DiskImg::kFormatGenericPhysicalOrd: - case DiskImg::kFormatGenericProDOSOrd: - case DiskImg::kFormatGenericDOSOrd: - case DiskImg::kFormatGenericCPMOrd: - case DiskImg::kFormatUnknown: - if (allowUnknown) { - pDiskFS = new DiskFSUnknown(); - break; - } - } - - return pDiskFS; -} - - -/* - * Fill an array with SectorOrder values. The ordering specified by "first" - * will come first. Unused entries will be set to "unknown" and should be - * ignored. - * - * "orderArray" must have kSectorOrderMax elements. - */ -/*static*/ void DiskImg::GetSectorOrderArray(SectorOrder* orderArray, - SectorOrder first) -{ - // init array - for (int i = 0; i < kSectorOrderMax; i++) - orderArray[i] = (SectorOrder) i; - - // pull the best-guess ordering to the front - assert(orderArray[0] == kSectorOrderUnknown); - - orderArray[0] = first; - orderArray[(int) first] = kSectorOrderUnknown; - - // don't bother checking CP/M sector order - orderArray[kSectorOrderCPM] = kSectorOrderUnknown; -} - - -/* - * Return a short string describing "format". - * - * These are semi-duplicated in ImageFormatDialog.cpp in CiderPress. - */ -/*static*/ const char* DiskImg::ToStringCommon(int format, - const ToStringLookup* pTable, int tableSize) -{ - for (int i = 0; i < tableSize; i++) { - if (pTable[i].format == format) - return pTable[i].str; - } - - assert(false); - return "(unknown)"; -} - -/*static*/ const char* DiskImg::ToString(OuterFormat format) -{ - static const ToStringLookup kOuterFormats[] = { - { DiskImg::kOuterFormatUnknown, "Unknown format" }, - { DiskImg::kOuterFormatNone, "(none)" }, - { DiskImg::kOuterFormatCompress, "UNIX compress" }, - { DiskImg::kOuterFormatGzip, "gzip" }, - { DiskImg::kOuterFormatBzip2, "bzip2" }, - { DiskImg::kOuterFormatZip, "Zip archive" }, - }; - - return ToStringCommon(format, kOuterFormats, NELEM(kOuterFormats)); -} - -/*static*/ const char* DiskImg::ToString(FileFormat format) -{ - static const ToStringLookup kFileFormats[] = { - { DiskImg::kFileFormatUnknown, "Unknown format" }, - { DiskImg::kFileFormatUnadorned, "Unadorned raw data" }, - { DiskImg::kFileFormat2MG, "2MG" }, - { DiskImg::kFileFormatNuFX, "NuFX (ShrinkIt)" }, - { DiskImg::kFileFormatDiskCopy42, "DiskCopy 4.2" }, - { DiskImg::kFileFormatDiskCopy60, "DiskCopy 6.0" }, - { DiskImg::kFileFormatDavex, "Davex volume image" }, - { DiskImg::kFileFormatSim2eHDV, "Sim //e HDV" }, - { DiskImg::kFileFormatTrackStar, "TrackStar image" }, - { DiskImg::kFileFormatFDI, "FDI image" }, - { DiskImg::kFileFormatDDD, "DDD" }, - { DiskImg::kFileFormatDDDDeluxe, "DDDDeluxe" }, - }; - - return ToStringCommon(format, kFileFormats, NELEM(kFileFormats)); -}; - -/*static*/ const char* DiskImg::ToString(PhysicalFormat format) -{ - static const ToStringLookup kPhysicalFormats[] = { - { DiskImg::kPhysicalFormatUnknown, "Unknown format" }, - { DiskImg::kPhysicalFormatSectors, "Sectors" }, - { DiskImg::kPhysicalFormatNib525_6656, "Raw nibbles (6656-byte)" }, - { DiskImg::kPhysicalFormatNib525_6384, "Raw nibbles (6384-byte)" }, - { DiskImg::kPhysicalFormatNib525_Var, "Raw nibbles (variable len)" }, - }; - - return ToStringCommon(format, kPhysicalFormats, NELEM(kPhysicalFormats)); -}; - -/*static*/ const char* DiskImg::ToString(SectorOrder format) -{ - static const ToStringLookup kSectorOrders[] = { - { DiskImg::kSectorOrderUnknown, "Unknown ordering" }, - { DiskImg::kSectorOrderProDOS, "ProDOS block ordering" }, - { DiskImg::kSectorOrderDOS, "DOS sector ordering" }, - { DiskImg::kSectorOrderCPM, "CP/M block ordering" }, - { DiskImg::kSectorOrderPhysical, "Physical sector ordering" }, - }; - - return ToStringCommon(format, kSectorOrders, NELEM(kSectorOrders)); -}; - -/*static*/ const char* DiskImg::ToString(FSFormat format) -{ - static const ToStringLookup kFSFormats[] = { - { DiskImg::kFormatUnknown, "Unknown" }, - { DiskImg::kFormatProDOS, "ProDOS" }, - { DiskImg::kFormatDOS33, "DOS 3.3" }, - { DiskImg::kFormatDOS32, "DOS 3.2" }, - { DiskImg::kFormatPascal, "Pascal" }, - { DiskImg::kFormatMacHFS, "HFS" }, - { DiskImg::kFormatMacMFS, "MFS" }, - { DiskImg::kFormatLisa, "Lisa" }, - { DiskImg::kFormatCPM, "CP/M" }, - { DiskImg::kFormatMSDOS, "MS-DOS FAT" }, - { DiskImg::kFormatISO9660, "ISO-9660" }, - { DiskImg::kFormatRDOS33, "RDOS 3.3 (16-sector)" }, - { DiskImg::kFormatRDOS32, "RDOS 3.2 (13-sector)" }, - { DiskImg::kFormatRDOS3, "RDOS 3 (cracked 13-sector)" }, - { DiskImg::kFormatGenericDOSOrd, "Generic DOS sectors" }, - { DiskImg::kFormatGenericProDOSOrd, "Generic ProDOS blocks" }, - { DiskImg::kFormatGenericPhysicalOrd, "Generic raw sectors" }, - { DiskImg::kFormatGenericCPMOrd, "Generic CP/M blocks" }, - { DiskImg::kFormatUNIDOS, "UNIDOS (400K DOS x2)" }, - { DiskImg::kFormatOzDOS, "OzDOS (400K DOS x2)" }, - { DiskImg::kFormatCFFA4, "CFFA (4 or 6 partitions)" }, - { DiskImg::kFormatCFFA8, "CFFA (8 partitions)" }, - { DiskImg::kFormatMacPart, "Macintosh partitioned disk" }, - { DiskImg::kFormatMicroDrive, "MicroDrive partitioned disk" }, - { DiskImg::kFormatFocusDrive, "FocusDrive partitioned disk" }, - }; - - return ToStringCommon(format, kFSFormats, NELEM(kFSFormats)); -}; - - -/* - * strerror() equivalent for DiskImg errors. - */ -const char* DiskImgLib::DIStrError(DIError dierr) -{ - if (dierr > 0) { - const char* msg; - msg = strerror(dierr); - if (msg != NULL) - return msg; - } - - /* - * BUG: this should be set up as per-thread storage in an MT environment. - * I would be more inclined to worry about this if I was expecting - * to hit "default:". So long as valid values are passed in, and the - * switch statement is kept up to date, we should never have cause - * to return this. - * - * An easier solution, should this present a problem for someone, would - * be to have the function return NULL or "unknown error" when the - * error value isn't recognized. I'd recommend leaving it as-is for - * debug builds, though, as it's helpful to know *which* error is not - * recognized. - */ - static char defaultMsg[32]; - - switch (dierr) { - case kDIErrNone: - return "(no error)"; - - case kDIErrAccessDenied: - return "access denied"; - case kDIErrVWAccessForbidden: - return "for safety, write access to this volume is forbidden"; - case kDIErrSharingViolation: - return "file is already open and cannot be shared"; - case kDIErrNoExclusiveAccess: - return "couldn't get exclusive access"; - case kDIErrWriteProtected: - return "write protected"; - case kDIErrCDROMNotSupported: - return "access to CD-ROM drives is not supported"; - case kDIErrASPIFailure: - return "an ASPI request failed"; - case kDIErrSPTIFailure: - return "an SPTI request failed"; - case kDIErrSCSIFailure: - return "a SCSI request failed"; - case kDIErrDeviceNotReady: - return "device not ready"; - - case kDIErrFileNotFound: - return "file not found"; - case kDIErrForkNotFound: - return "fork not found"; - case kDIErrAlreadyOpen: - return "an image is already open"; - case kDIErrFileOpen: - return "file is open"; - case kDIErrNotReady: - return "object not ready"; - case kDIErrFileExists: - return "file already exists"; - case kDIErrDirectoryExists: - return "directory already exists"; - - case kDIErrEOF: - return "end of file reached"; - case kDIErrReadFailed: - return "read failed"; - case kDIErrWriteFailed: - return "write failed"; - case kDIErrDataUnderrun: - return "tried to read past end of file"; - case kDIErrDataOverrun: - return "tried to write past end of file"; - case kDIErrGenericIO: - return "I/O error"; - - case kDIErrOddLength: - return "image size is wrong"; - case kDIErrUnrecognizedFileFmt: - return "not a recognized disk image format"; - case kDIErrBadFileFormat: - return "image file contents aren't in expected format"; - case kDIErrUnsupportedFileFmt: - return "file format not supported"; - case kDIErrUnsupportedPhysicalFmt: - return "physical format not supported"; - case kDIErrUnsupportedFSFmt: - return "filesystem type not supported"; - case kDIErrBadOrdering: - return "bad sector ordering"; - case kDIErrFilesystemNotFound: - return "specified filesystem not found"; - case kDIErrUnsupportedAccess: - return "the method of access used isn't supported for this image"; - case kDIErrUnsupportedImageFeature: - return "image file uses features that CiderPress doesn't support"; - - case kDIErrInvalidTrack: - return "invalid track number"; - case kDIErrInvalidSector: - return "invalid sector number"; - case kDIErrInvalidBlock: - return "invalid block number"; - case kDIErrInvalidIndex: - return "invalid index number"; - - case kDIErrDirectoryLoop: - return "disk directory structure has an infinite loop"; - case kDIErrFileLoop: - return "file structure has an infinite loop"; - case kDIErrBadDiskImage: - return "the filesystem on this image appears damaged"; - case kDIErrBadFile: - return "file structure appears damaged"; - case kDIErrBadDirectory: - return "a directory appears damaged"; - case kDIErrBadPartition: - return "bad partition"; - - case kDIErrFileArchive: - return "this looks like a file archive, not a disk archive"; - case kDIErrUnsupportedCompression: - return "compression method not supported"; - case kDIErrBadChecksum: - return "checksum doesn't match, data may be corrupted"; - case kDIErrBadCompressedData: - return "the compressed data is corrupted"; - case kDIErrBadArchiveStruct: - return "archive may be damaged"; - - case kDIErrBadNibbleSectors: - return "couldn't read sectors from this image"; - case kDIErrSectorUnreadable: - return "sector not readable"; - case kDIErrInvalidDiskByte: - return "found invalid nibble image disk byte"; - case kDIErrBadRawData: - return "couldn't convert raw data to nibble data"; - - case kDIErrInvalidFileName: - return "invalid file name"; - case kDIErrDiskFull: - return "disk full"; - case kDIErrVolumeDirFull: - return "volume directory is full"; - case kDIErrInvalidCreateReq: - return "invalid disk image create request"; - case kDIErrTooBig: - return "size is larger than we can handle"; - - case kDIErrGeneric: - return "DiskImg generic error"; - case kDIErrInternal: - return "DiskImg internal error"; - case kDIErrMalloc: - return "memory allocation failure"; - case kDIErrInvalidArg: - return "invalid argument"; - case kDIErrNotSupported: - return "feature not supported"; - case kDIErrCancelled: - return "cancelled by user"; - - case kDIErrNufxLibInitFailed: - return "NufxLib initialization failed"; - - default: - sprintf(defaultMsg, "(error=%d)", dierr); - return defaultMsg; - } -} - -/* - * High ASCII conversion table, from Technical Note PT515, - * "Apple File Exchange Q&As". The table is available in a hopelessly - * blurry PDF or a pair of GIFs created with small fonts, but I think I - * have mostly captured it. - */ -/*static*/ const uint8_t DiskImg::kMacHighASCII[128+1] = - "AACENOUaaaaaaceeeeiiiinooooouuuu" // 0x80 - 0x9f - "tocL$oPBrct'.=AO%+<>YudsPpSaoOao" // 0xa0 - 0xbf - "?!-vf=d<>. AAOOo--\"\"''/oyY/o<> f" // 0xc0 - 0xdf - "|*,,%AEAEEIIIIOOaOUUUi^~-,**,\"? "; // 0xe0 - 0xff - - -/* - * Hack for Win32 systems. See Win32BlockIO.cpp for commentary. - */ -bool DiskImgLib::gAllowWritePhys0 = false; -/*static*/ void DiskImg::SetAllowWritePhys0(bool val) { - DiskImgLib::gAllowWritePhys0 = val; -} diff --git a/ciderpress/diskimg/DiskImg.h b/ciderpress/diskimg/DiskImg.h deleted file mode 100644 index 78d81b9..0000000 --- a/ciderpress/diskimg/DiskImg.h +++ /dev/null @@ -1,1676 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2009 by CiderPress authors. All Rights Reserved. - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Public declarations for the DiskImg library. - * - * Everything is wrapped in the "DiskImgLib" namespace. Either prefix - * all references with "DiskImgLib::", or add "using namespace DiskImgLib" - * to all C++ source files that make use of it. - * - * Under Linux, this should be compiled with -D_FILE_OFFSET_BITS=64. - * - * These classes are not thread-safe with respect to access to a single - * disk image. Writing to the same disk image from multiple threads - * simultaneously is bound to end in disaster. Simultaneous access to - * different objects will work, though modifying the same disk image - * file from multiple objects will lead to unpredictable results. - */ -#ifndef DISKIMG_DISKIMG_H -#define DISKIMG_DISKIMG_H - -#include -#include -#include -#include - -//#define EXCISE_GPL_CODE - -/* Windows DLL stuff */ -#ifdef _WIN32 -# ifdef DISKIMG_EXPORTS -# define DISKIMG_API __declspec(dllexport) -# else -# define DISKIMG_API __declspec(dllimport) -# endif -#else -# define DISKIMG_API -#endif - -namespace DiskImgLib { - -/* compiled-against versions; call DiskImg::GetVersion for linked-against */ -#define kDiskImgVersionMajor 5 -#define kDiskImgVersionMinor 0 -#define kDiskImgVersionBug 1 - - -/* - * Errors from the various disk image classes. - */ -typedef enum DIError { - kDIErrNone = 0, - - /* I/O request errors (should renumber/rename to match GS/OS errors?) */ - kDIErrAccessDenied = -10, - kDIErrVWAccessForbidden = -11, // write access to volume forbidden - kDIErrSharingViolation = -12, // file is in use and not shareable - kDIErrNoExclusiveAccess = -13, // couldn't get exclusive access - kDIErrWriteProtected = -14, // disk is write protected - kDIErrCDROMNotSupported = -15, // access to CD-ROM drives not supptd - kDIErrASPIFailure = -16, // generic ASPI failure result - kDIErrSPTIFailure = -17, // generic SPTI failure result - kDIErrSCSIFailure = -18, // generic SCSI failure result - kDIErrDeviceNotReady = -19, // floppy or CD-ROM drive has no media - - kDIErrFileNotFound = -20, - kDIErrForkNotFound = -21, // requested fork does not exist - kDIErrAlreadyOpen = -22, // already open, can't open a 2nd time - kDIErrFileOpen = -23, // file is open, can't delete it - kDIErrNotReady = -24, - kDIErrFileExists = -25, // file already exists - kDIErrDirectoryExists = -26, // directory already exists - - kDIErrEOF = -30, // end-of-file reached - kDIErrReadFailed = -31, - kDIErrWriteFailed = -32, - kDIErrDataUnderrun = -33, // tried to read off end of the image - kDIErrDataOverrun = -34, // tried to write off end of the image - kDIErrGenericIO = -35, // generic I/O error - - kDIErrOddLength = -40, // image size not multiple of sectors - kDIErrUnrecognizedFileFmt = -41, // file format just not recognized - kDIErrBadFileFormat = -42, // filename ext doesn't match contents - kDIErrUnsupportedFileFmt = -43, // recognized but not supported - kDIErrUnsupportedPhysicalFmt = -44, // (same) - kDIErrUnsupportedFSFmt = -45, // (and again) - kDIErrBadOrdering = -46, // requested sector ordering no good - kDIErrFilesystemNotFound = -47, // requested filesystem isn't there - kDIErrUnsupportedAccess = -48, // e.g. read sectors from blocks-only - kDIErrUnsupportedImageFeature = -49, // e.g. FDI image w/Amiga sectors - - kDIErrInvalidTrack = -50, // request for invalid track number - kDIErrInvalidSector = -51, // request for invalid sector number - kDIErrInvalidBlock = -52, // request for invalid block number - kDIErrInvalidIndex = -53, // request with an invalid index - - kDIErrDirectoryLoop = -60, // directory chain points into itself - kDIErrFileLoop = -61, // file sector or block alloc loops - kDIErrBadDiskImage = -62, // the FS on the disk image is damaged - kDIErrBadFile = -63, // bad file on disk image - kDIErrBadDirectory = -64, // bad dir on disk image - kDIErrBadPartition = -65, // bad partition on multi-part format - - kDIErrFileArchive = -70, // file archive, not disk archive - kDIErrUnsupportedCompression = -71, // compression method is not supported - kDIErrBadChecksum = -72, // image file's checksum is bad - kDIErrBadCompressedData = -73, // data can't even be unpacked - kDIErrBadArchiveStruct = -74, // bad archive structure - - kDIErrBadNibbleSectors = -80, // can't read sectors from this image - kDIErrSectorUnreadable = -81, // requested sector not readable - kDIErrInvalidDiskByte = -82, // invalid byte for encoding type - kDIErrBadRawData = -83, // couldn't get correct nibbles - - kDIErrInvalidFileName = -90, // tried to create file with bad name - kDIErrDiskFull = -91, // no space left on disk - kDIErrVolumeDirFull = -92, // no more entries in volume dir - kDIErrInvalidCreateReq = -93, // CreateImage request was flawed - kDIErrTooBig = -94, // larger than we want to handle - - /* higher-level errors */ - kDIErrGeneric = -101, - kDIErrInternal = -102, - kDIErrMalloc = -103, - kDIErrInvalidArg = -104, - kDIErrNotSupported = -105, // feature not currently supported - kDIErrCancelled = -106, // an operation was cancelled by user - - kDIErrNufxLibInitFailed = -110, -} DIError; - -/* return a string describing the error */ -DISKIMG_API const char* DIStrError(DIError dierr); - - -/* exact definition of off_t varies, so just define our own */ -#ifdef _ULONGLONG_ - typedef LONGLONG di_off_t; -#else - typedef off_t di_off_t; -#endif - -/* common definition of "whence" for seeks */ -enum DIWhence { - kSeekSet = SEEK_SET, - kSeekCur = SEEK_CUR, - kSeekEnd = SEEK_END -}; - -/* try to load ASPI under Win2K; if successful, SPTI should be disabled */ -const bool kAlwaysTryASPI = false; -/* ASPI device "filenames" look like "ASPI:x:y:z\" */ -DISKIMG_API extern const char* kASPIDev; - -/* some nibble-encoding constants */ -const int kTrackLenNib525 = 6656; -const int kTrackLenNb2525 = 6384; -const int kTrackLenTrackStar525 = 6525; // max len of data in TS image -const int kTrackAllocSize = 6656; // max 5.25 nibble track len; for buffers -const int kTrackCount525 = 35; // expected #of tracks on 5.25 img -const int kMaxNibbleTracks525 = 40; // max #of tracks on 5.25 nibble img -const int kDefaultNibbleVolumeNum = 254; -const int kBlockSize = 512; // block size for DiskImg interfaces -const int kSectorSize = 256; // sector size (1/2 block) -const int kD13Length = 256 * 13 * 35; // length of a .d13 image - -/* largest expanse we allow access to on a volume (8GB in 512-byte blocks) */ -const long kVolumeMaxBlocks = 8*1024*(1024*1024 / kBlockSize); - -/* largest .gz file we'll open (uncompressed size) */ -const long kGzipMax = 32*1024*1024; - -/* forward and external class definitions */ -class DiskFS; -class A2File; -class A2FileDescr; -class GenericFD; -class OuterWrapper; -class ImageWrapper; -class CircularBufferAccess; -class ASPI; -class LinearBitmap; - - -/* - * Library-global data functions. - * - * This class is just a namespace clumper. Do not instantiate. - */ -class DISKIMG_API Global { -public: - // one-time DLL initialization; use SetDebugMsgHandler first - static DIError AppInit(void); - // one-time DLL cleanup - static DIError AppCleanup(void); - - // return the DiskImg version number - static void GetVersion(int32_t* pMajor, int32_t* pMinor, int32_t* pBug); - - static bool GetAppInitCalled(void) { return fAppInitCalled; } - static bool GetHasSPTI(void); - static bool GetHasASPI(void); - - // return a pointer to our global ASPI instance, or NULL - static ASPI* GetASPI(void) { return fpASPI; } - // shortcut for fpASPI->GetVersion() - static unsigned long GetASPIVersion(void); - - // pointer to the debug message handler - typedef void (*DebugMsgHandler)(const char* file, int line, const char* msg); - static DebugMsgHandler gDebugMsgHandler; - - static DebugMsgHandler SetDebugMsgHandler(DebugMsgHandler handler); - static void PrintDebugMsg(const char* file, int line, const char* fmt, ...) - #if defined(__GNUC__) - __attribute__ ((format(printf, 3, 4))) - #endif - ; - -private: - // no instantiation allowed - Global(void) {} - ~Global(void) {} - - // make sure app calls AppInit - static bool fAppInitCalled; - - static ASPI* fpASPI; -}; - -extern bool gAllowWritePhys0; // ugh -- see Win32BlockIO.cpp - - -/* - * Disk I/O class, roughly equivalent to a GS/OS disk device driver. - * - * Abstracts away the file's source (file on disk, file in memory) and - * storage format (DOS order, ProDOS order, nibble). Will also cope - * with common disk compression and wrapper formats (Mac DiskCopy, 2MG, - * ShrinkIt, etc) if handed a file on disk. - * - * Images may be embedded within other images, e.g. UNIDOS and storage - * type $04 pascal volumes. - * - * THOUGHT: we need a list(?) of pointers from here back to the DiskFS - * so that any modifications here will "wake" the DiskFS and sub-volumes. - * We also need a "dirty" flag so things like CloseNufx can know not to - * re-do work when Closing after a Flush. Also DiskFS can alert us to - * any locally cached stuff, and we can tell them to flush everything. - * (Possibly useful when doing disk updates, so stuff can be trivially - * un-done. Currently CiderPress checks the filename manually after - * each write, but that's generally less reliable than having the knowledge - * contained in the DiskImg.) - * - * THOUGHT: need a ReadRawTrack that gets raw nibblized data. For a - * nibblized image it returns the data, for a sector image it generates - * the raw data. - * - * THOUGHT: we could reduce the risk of problems and increase performance - * for physical media with a "copy on write" scheme. We'd create a shadow - * array of modified blocks, and write them at Flush time. This would - * provide an instantaneous "revert" feature, and prevent formats like - * DiskCopy42 (which has a CRC in its header) from being inconsistent for - * long stretches. - */ -class DISKIMG_API DiskImg { -public: - // create DiskImg object - DiskImg(void); - virtual ~DiskImg(void); - - /* - * Types describing an image file. - * - * The file itself is described by an external parameter ("file source") - * that is either the name of the file, a memory buffer, or an EFD - * (EmbeddedFileDescriptor). - */ - typedef enum { // format of the "wrapper wrapper" - kOuterFormatUnknown = 0, - kOuterFormatNone = 1, // (plain) - kOuterFormatCompress = 2, // .xx.Z - kOuterFormatGzip = 3, // .xx.gz - kOuterFormatBzip2 = 4, // .xx.bz2 - kOuterFormatZip = 10, // .zip - } OuterFormat; - typedef enum { // format of the image "wrapper" - kFileFormatUnknown = 0, - kFileFormatUnadorned = 1, // .po, .do, ,nib, .raw, .d13 - kFileFormat2MG = 2, // .2mg, .2img, $e0/0130 - kFileFormatDiskCopy42 = 3, // .dsk/.disk, maybe .dc - kFileFormatDiskCopy60 = 4, // .dc6 (often just raw format) - kFileFormatDavex = 5, // $e0/8004 - kFileFormatSim2eHDV = 6, // .hdv - kFileFormatTrackStar = 7, // .app (40-track or 80-track) - kFileFormatFDI = 8, // .fdi (5.25" or 3.5") - kFileFormatNuFX = 20, // .shk, .sdk, .bxy - kFileFormatDDD = 21, // .ddd - kFileFormatDDDDeluxe = 22, // $DD, .ddd - } FileFormat; - typedef enum { // format of the image data stream - kPhysicalFormatUnknown = 0, - kPhysicalFormatSectors = 1, // sequential 256-byte sectors (13/16/32) - kPhysicalFormatNib525_6656 = 2, // 5.25" disk ".nib" (6656 bytes/track) - kPhysicalFormatNib525_6384 = 3, // 5.25" disk ".nb2" (6384 bytes/track) - kPhysicalFormatNib525_Var = 4, // 5.25" disk (variable len, e.g. ".app") - } PhysicalFormat; - typedef enum { // sector ordering for "sector" format images - kSectorOrderUnknown = 0, - kSectorOrderProDOS = 1, // written as series of ProDOS blocks - kSectorOrderDOS = 2, // written as series of DOS sectors - kSectorOrderCPM = 3, // written as series of 1K CP/M blocks - kSectorOrderPhysical = 4, // written as un-interleaved sectors - kSectorOrderMax, // (used for array sizing) - } SectorOrder; - typedef enum { // main filesystem format (based on NuFX enum) - kFormatUnknown = 0, - kFormatProDOS = 1, - kFormatDOS33 = 2, - kFormatDOS32 = 3, - kFormatPascal = 4, - kFormatMacHFS = 5, - kFormatMacMFS = 6, - kFormatLisa = 7, - kFormatCPM = 8, - //kFormatCharFST - kFormatMSDOS = 10, // any FAT filesystem - //kFormatHighSierra - kFormatISO9660 = 12, - //kFormatAppleShare - kFormatRDOS33 = 20, // 16-sector RDOS disk - kFormatRDOS32 = 21, // 13-sector RDOS disk - kFormatRDOS3 = 22, // 13-sector RDOS disk converted to 16 - // "generic" formats *must* be in their own "decade" - kFormatGenericPhysicalOrd = 30, // unknown, but physical-sector-ordered - kFormatGenericProDOSOrd = 31, // unknown, but ProDOS-block-ordered - kFormatGenericDOSOrd = 32, // unknown, but DOS-sector-ordered - kFormatGenericCPMOrd = 33, // unknown, but CP/M-block-ordered - kFormatUNIDOS = 40, // two 400K DOS 3.3 volumes - kFormatOzDOS = 41, // two 400K DOS 3.3 volumes, weird order - kFormatCFFA4 = 42, // CFFA image with 4 or 6 partitions - kFormatCFFA8 = 43, // CFFA image with 8 partitions - kFormatMacPart = 44, // Macintosh-style partitioned disk - kFormatMicroDrive = 45, // ///SHH Systeme's MicroDrive format - kFormatFocusDrive = 46, // Parsons Engineering FocusDrive format - kFormatGutenberg = 47, // Gutenberg word processor format - - // try to keep this in an unsigned char, e.g. for CP clipboard - } FSFormat; - - /* - * Nibble encode/decode description. Use no pointers here, so we - * store as an array and resize at will. - * - * Should we define an enum to describe whether address and data - * headers are standard or some wacky variant? - */ - enum { - kNibbleAddrPrologLen = 3, // d5 aa 96 - kNibbleAddrEpilogLen = 3, // de aa eb - kNibbleDataPrologLen = 3, // d5 aa ad - kNibbleDataEpilogLen = 3, // de aa eb - }; - typedef enum { - kNibbleEncUnknown = 0, - kNibbleEnc44, - kNibbleEnc53, - kNibbleEnc62, - } NibbleEnc; - typedef enum { - kNibbleSpecialNone = 0, - kNibbleSpecialMuse, // doubled sector numbers on tracks > 2 - kNibbleSpecialSkipFirstAddrByte, - } NibbleSpecial; - typedef struct { - char description[32]; - short numSectors; // 13 or 16 (or 18?) - - uint8_t addrProlog[kNibbleAddrPrologLen]; - uint8_t addrEpilog[kNibbleAddrEpilogLen]; - uint8_t addrChecksumSeed; - bool addrVerifyChecksum; - bool addrVerifyTrack; - int addrEpilogVerifyCount; - - uint8_t dataProlog[kNibbleDataPrologLen]; - uint8_t dataEpilog[kNibbleDataEpilogLen]; - uint8_t dataChecksumSeed; - bool dataVerifyChecksum; - int dataEpilogVerifyCount; - - NibbleEnc encoding; - NibbleSpecial special; - } NibbleDescr; - - - static inline bool IsSectorFormat(PhysicalFormat fmt) { - return (fmt == kPhysicalFormatSectors); - } - static inline bool IsNibbleFormat(PhysicalFormat fmt) { - return (fmt == kPhysicalFormatNib525_6656 || - fmt == kPhysicalFormatNib525_6384 || - fmt == kPhysicalFormatNib525_Var); - } - - // file is on disk; stuff like 2MG headers will be identified and stripped - DIError OpenImage(const char* filename, char fssep, bool readOnly); - // file is in memory; provide a pointer to the data start and buffer size - DIError OpenImageFromBufferRO(const uint8_t* buffer, long length); - // file is in memory; provide a pointer to the data start and buffer size - DIError OpenImageFromBufferRW(uint8_t* buffer, long length); - // file is a range of blocks on an open block-oriented disk image - DIError OpenImage(DiskImg* pParent, long firstBlock, long numBlocks); - // file is a range of tracks/sectors on an open sector-oriented disk image - DIError OpenImage(DiskImg* pParent, long firstTrack, long firstSector, - long numSectors); - - // create a new, blank image file - DIError CreateImage(const char* pathName, const char* storageName, - OuterFormat outerFormat, FileFormat fileFormat, - PhysicalFormat physical, const NibbleDescr* pNibbleDescr, - SectorOrder order, FSFormat format, - long numBlocks, bool skipFormat); - DIError CreateImage(const char* pathName, const char* storageName, - OuterFormat outerFormat, FileFormat fileFormat, - PhysicalFormat physical, const NibbleDescr* pNibbleDescr, - SectorOrder order, FSFormat format, - long numTracks, long numSectPerTrack, bool skipFormat); - - // flush any changes to disk; slow recompress only for "kFlushAll" - typedef enum { kFlushUnknown=0, kFlushFastOnly=1, kFlushAll=2 } FlushMode; - DIError FlushImage(FlushMode mode); - // close the image, freeing up any resources in use - DIError CloseImage(void); - // raise/lower refCnt (may want to track pointers someday) - void AddDiskFS(DiskFS* pDiskFS) { fDiskFSRefCnt++; } - void RemoveDiskFS(DiskFS* pDiskFS) { - assert(fDiskFSRefCnt > 0); - fDiskFSRefCnt--; - } - - // (re-)format this image in the specified FS format - DIError FormatImage(FSFormat format, const char* volName); - // reset all blocks/sectors to zeroes - DIError ZeroImage(void); - - // configure for paired sectors (OzDOS) - void SetPairedSectors(bool enable, int idx); - - // identify sector ordering and disk format - // (may want a version that takes "hints" for special disks?) - DIError AnalyzeImage(void); - // figure out what FS and sector ordering is on the disk image - void AnalyzeImageFS(void); - bool ShowAsBlocks(void) const; - // overrule the analyzer (generally not recommended) -- does not - // override FileFormat, which is very reliable - DIError OverrideFormat(PhysicalFormat physical, FSFormat format, - SectorOrder order); - - // Create a DiskFS that matches this DiskImg. Must be called after - // AnalayzeImage, or you will always get a DiskFSUnknown. The DiskFS - // must be freed with "delete" when no longer needed. - DiskFS* OpenAppropriateDiskFS(bool allowUnknown = false); - - // Add information or a warning to the list of notes. Use linefeeds to - // indicate line breaks. This is currently append-only. - typedef enum { kNoteInfo, kNoteWarning } NoteType; - void AddNote(NoteType type, const char* fmt, ...) - #if defined(__GNUC__) - __attribute__ ((format(printf, 3, 4))) - #endif - ; - const char* GetNotes(void) const; - - // simple accessors - OuterFormat GetOuterFormat(void) const { return fOuterFormat; } - FileFormat GetFileFormat(void) const { return fFileFormat; } - PhysicalFormat GetPhysicalFormat(void) const { return fPhysical; } - SectorOrder GetSectorOrder(void) const { return fOrder; } - FSFormat GetFSFormat(void) const { return fFormat; } - long GetNumTracks(void) const { return fNumTracks; } - int GetNumSectPerTrack(void) const { return fNumSectPerTrack; } - long GetNumBlocks(void) const { return fNumBlocks; } - bool GetReadOnly(void) const { return fReadOnly; } - bool GetDirtyFlag(void) const { return fDirty; } - - // set read-only flag; don't use this (open with correct setting; - // this was added as safety hack for the volume copier) - void SetReadOnly(bool val) { fReadOnly = val; } - - // read a 256-byte sector - // NOTE to self: this function should not be available for odd-sized - // volumes, e.g. a ProDOS /RAM or /RAM5 stored with Davex. Need some way - // to communicate that to disk editor so it knows to grey-out the - // selection checkbox and/or not use "show as sectors" as default. - virtual DIError ReadTrackSector(long track, int sector, void* buf) { - return ReadTrackSectorSwapped(track, sector, buf, fOrder, - fFileSysOrder); - } - DIError ReadTrackSectorSwapped(long track, int sector, - void* buf, SectorOrder imageOrder, SectorOrder fsOrder); - // write a 256-byte sector - virtual DIError WriteTrackSector(long track, int sector, const void* buf); - - // read a 512-byte block - virtual DIError ReadBlock(long block, void* buf) { - return ReadBlockSwapped(block, buf, fOrder, fFileSysOrder); - } - DIError ReadBlockSwapped(long block, void* buf, SectorOrder imageOrder, - SectorOrder fsOrder); - // read multiple blocks - virtual DIError ReadBlocks(long startBlock, int numBlocks, void* buf); - // check our virtual bad block map - bool CheckForBadBlocks(long startBlock, int numBlocks); - // write a 512-byte block - virtual DIError WriteBlock(long block, const void* buf); - // write multiple blocks - virtual DIError WriteBlocks(long startBlock, int numBlocks, const void* buf); - - // read an entire nibblized track - virtual DIError ReadNibbleTrack(long track, uint8_t* buf, - long* pTrackLen); - // write a track; trackLen must be <= those in image - virtual DIError WriteNibbleTrack(long track, const uint8_t* buf, - long trackLen); - - // save the current image as a 2MG file - //DIError Write2MG(const char* filename); - - // need to treat the DOS volume number as meta-data for some disks - short GetDOSVolumeNum(void) const { return fDOSVolumeNum; } - void SetDOSVolumeNum(short val) { fDOSVolumeNum = val; } - enum { kVolumeNumNotSet = -1 }; - - // some simple getters - bool GetHasSectors(void) const { return fHasSectors; } - bool GetHasBlocks(void) const { return fHasBlocks; } - bool GetHasNibbles(void) const { return fHasNibbles; } - bool GetIsEmbedded(void) const { return fpParentImg != NULL; } - - // return the current NibbleDescr - const NibbleDescr* GetNibbleDescr(void) const { return fpNibbleDescr; } - // set the NibbleDescr; we do this by copying the entry into our table - // (could improve by doing memcmp on available entries?) - void SetNibbleDescr(int idx); - void SetCustomNibbleDescr(const NibbleDescr* pDescr); - const NibbleDescr* GetNibbleDescrTable(int* pCount) const { - *pCount = fNumNibbleDescrEntries; - return fpNibbleDescrTable; - } - - // set the NuFX compression type, used when compressing or re-compressing; - // must be set before image is opened or created - void SetNuFXCompressionType(int val) { fNuFXCompressType = val; } - - /* - * Set up a progress callback to use when scanning a disk volume. Pass - * NULL for "func" to disable. - * - * The callback function is expected to return "true" if all is well. - * If it returns false, kDIErrCancelled will eventually come back. - */ - typedef bool (*ScanProgressCallback)(void* cookie, const char* str, - int count); - void SetScanProgressCallback(ScanProgressCallback func, void* cookie); - /* update status dialog during disk scan; called from DiskFS code */ - bool UpdateScanProgress(const char* newStr); - - /* - * Static utility functions. - */ - // returns "true" if the files on this image have DOS structure, i.e. - // simple file types and high-ASCII text files - static bool UsesDOSFileStructure(FSFormat format) { - return (format == kFormatDOS33 || - format == kFormatDOS32 || - format == kFormatGutenberg || - format == kFormatUNIDOS || - format == kFormatOzDOS || - format == kFormatRDOS33 || - format == kFormatRDOS32 || - format == kFormatRDOS3); - } - // returns "true" if we can open files on the specified filesystem - static bool CanOpenFiles(FSFormat format) { - return (format == kFormatProDOS || - format == kFormatDOS33 || - format == kFormatDOS32 || - format == kFormatPascal || - format == kFormatCPM || - format == kFormatRDOS33 || - format == kFormatRDOS32 || - format == kFormatRDOS3); - } - // returns "true" if we can create subdirectories on this filesystem - static bool IsHierarchical(FSFormat format) { - return (format == kFormatProDOS || - format == kFormatMacHFS || - format == kFormatMSDOS); - } - // returns "true" if we can create resource forks on this filesystem - static bool HasResourceForks(FSFormat format) { - return (format == kFormatProDOS || - format == kFormatMacHFS); - } - // returns "true" if the format is one of the "generics" - static bool IsGenericFormat(FSFormat format) { - return (format / 10 == DiskImg::kFormatGenericDOSOrd / 10); - } - - /* this must match DiskImg::kStdNibbleDescrs table */ - enum StdNibbleDescr { - kNibbleDescrDOS33Std = 0, - kNibbleDescrDOS33Patched, - kNibbleDescrDOS33IgnoreChecksum, - kNibbleDescrDOS32Std, - kNibbleDescrDOS32Patched, - kNibbleDescrMuse32, - kNibbleDescrRDOS33, - kNibbleDescrRDOS32, - kNibbleDescrCustom, // slot for custom entry - - kNibbleDescrMAX // must be last - }; - static const NibbleDescr* GetStdNibbleDescr(StdNibbleDescr idx); - // call this once, at DLL initialization time - static void CalcNibbleInvTables(void); - // calculate block number from cyl/head/sect on 3.5" disk - static int CylHeadSect35ToBlock(int cyl, int head, int sect); - // unpack nibble data from a 3.5" disk track - static DIError UnpackNibbleTrack35(const uint8_t* nibbleBuf, - long nibbleLen, uint8_t* outputBuf, int cyl, int head, - LinearBitmap* pBadBlockMap); - // compute the #of sectors per track for cylinder N (0-79) - static int SectorsPerTrack35(int cylinder); - - // get the order in which we test for sector ordering - static void GetSectorOrderArray(SectorOrder* orderArray, SectorOrder first); - - // utility function used by HFS filename normalizer; available to apps - static inline uint8_t MacToASCII(uint8_t uch) { - if (uch < 0x20) - return '?'; - else if (uch < 0x80) - return uch; - else - return kMacHighASCII[uch - 0x80]; - } - - // Allow write access to physical disk 0. This is usually the boot disk, - // but with some BIOS the first IDE drive is always physical 0 even if - // you're booting from SATA. This only has meaning under Win32. - static void SetAllowWritePhys0(bool val); - - /* - * Get string constants for enumerated values. - */ - typedef struct { int format; const char* str; } ToStringLookup; - static const char* ToStringCommon(int format, const ToStringLookup* pTable, - int tableSize); - static const char* ToString(OuterFormat format); - static const char* ToString(FileFormat format); - static const char* ToString(PhysicalFormat format); - static const char* ToString(SectorOrder format); - static const char* ToString(FSFormat format); - -private: - /* - * Fundamental disk image identification. - */ - OuterFormat fOuterFormat; // e.g. gzip - FileFormat fFileFormat; - PhysicalFormat fPhysical; - const NibbleDescr* fpNibbleDescr; // only used for "nibble" images - SectorOrder fOrder; // only used for "sector" images - FSFormat fFormat; - - /* - * This affects how the DiskImg responds to requests for reading - * a track or sector. - * - * "fFileSysOrder", together with with "fOrder", determines how - * sector numbers are translated. It describes the order that the - * DiskFS filesystem expects things to be in. If the image isn't - * sector-addressable, then it is assumed to be in linear block order. - * - * If "fSectorPairing" is set, the DiskImg treats the disk as if - * it were in OzDOS format, with one sector from two different - * volumes in a single 512-byte block. - */ - SectorOrder fFileSysOrder; - bool fSectorPairing; - int fSectorPairOffset; // which image (should be 0 or 1) - - /* - * Internal state. - */ - GenericFD* fpOuterGFD; // outer wrapper, if any (.gz only) - GenericFD* fpWrapperGFD; // Apple II image file - GenericFD* fpDataGFD; // raw Apple II data - OuterWrapper* fpOuterWrapper; // needed for outer .gz wrapper - ImageWrapper* fpImageWrapper; // disk image wrapper (2MG, SHK, etc) - DiskImg* fpParentImg; // set for embedded volumes - short fDOSVolumeNum; // specified by some wrapper formats - di_off_t fOuterLength; // total len of file - di_off_t fWrappedLength; // len of file after Outer wrapper removed - di_off_t fLength; // len of disk image (w/o wrappers) - bool fExpandable; // ProDOS .hdv can expand - bool fReadOnly; // allow writes to this image? - bool fDirty; // have we modified this image? - //bool fIsEmbedded; // is this image embedded in another? - - bool fHasSectors; // image is sector-addressable - bool fHasBlocks; // image is block-addressable - bool fHasNibbles; // image is nibble-addressable - - long fNumTracks; // for sector-addressable images - int fNumSectPerTrack; // (ditto) - long fNumBlocks; // for 512-byte block-addressable images - - uint8_t* fNibbleTrackBuf; // allocated on heap - int fNibbleTrackLoaded; // track currently in buffer - - int fNuFXCompressType; // used when compressing a NuFX image - - char* fNotes; // warnings and FYIs about DiskImg/DiskFS - - LinearBitmap* fpBadBlockMap; // used for 3.5" nibble images - - int fDiskFSRefCnt; // #of DiskFS objects pointing at us - - /* - * NibbleDescr entries. There are several standard ones, and we want - * to allow applications to define additional ones. - */ - NibbleDescr* fpNibbleDescrTable; - int fNumNibbleDescrEntries; - - /* static table of default values */ - static const NibbleDescr kStdNibbleDescrs[]; - - DIError OpenImageFromBuffer(uint8_t* buffer, long length, bool readOnly); - DIError CreateImageCommon(const char* pathName, const char* storageName, - bool skipFormat); - DIError ValidateCreateFormat(void) const; - DIError FormatSectors(GenericFD* pGFD, bool quickFormat) const; - //DIError FormatBlocks(GenericFD* pGFD) const; - - DIError CopyBytesOut(void* buf, di_off_t offset, int size) const; - DIError CopyBytesIn(const void* buf, di_off_t offset, int size); - DIError AnalyzeImageFile(const char* pathName, char fssep); - // Figure out the sector ordering for this filesystem, so we can decide - // how the sectors need to be re-arranged when we're reading them. - SectorOrder CalcFSSectorOrder(void) const; - // Handle sector order calculations. - DIError CalcSectorAndOffset(long track, int sector, SectorOrder ImageOrder, - SectorOrder fsOrder, di_off_t* pOffset, int* pNewSector); - inline bool IsLinearBlocks(SectorOrder imageOrder, SectorOrder fsOrder); - - /* - * Progress update during the filesystem scan. This only exists in the - * topmost DiskImg. (This is arguably more appropriate in DiskFS, but - * DiskFS objects don't have a notion of "parent" and are somewhat more - * ephemeral.) - */ - ScanProgressCallback fpScanProgressCallback; - void* fScanProgressCookie; - int fScanCount; - char fScanMsg[128]; - time_t fScanLastMsgWhen; - - /* - * 5.25" nibble image access. - */ - enum { - kDataSize62 = 343, // 342 bytes + checksum byte - kChunkSize62 = 86, // (0x56) - - kDataSize53 = 411, // 410 bytes + checksum byte - kChunkSize53 = 51, // (0x33) - kThreeSize = (kChunkSize53 * 3) + 1, // same as 410 - 256 - }; - DIError ReadNibbleSector(long track, int sector, void* buf, - const NibbleDescr* pNibbleDescr); - DIError WriteNibbleSector(long track, int sector, const void* buf, - const NibbleDescr* pNibbleDescr); - void DumpNibbleDescr(const NibbleDescr* pNibDescr) const; - int GetNibbleTrackLength(long track) const; - int GetNibbleTrackOffset(long track) const; - int GetNibbleTrackFormatLength(void) const { - /* return length to use when formatting for 16 sectors */ - if (fPhysical == kPhysicalFormatNib525_6656) - return kTrackLenNib525; - else if (fPhysical == kPhysicalFormatNib525_6384) - return kTrackLenNb2525; - else if (fPhysical == kPhysicalFormatNib525_Var) { - if (fFileFormat == kFileFormatTrackStar || - fFileFormat == kFileFormatFDI) - { - return kTrackLenNb2525; // use minimum possible - } - } - assert(false); - return -1; - } - int GetNibbleTrackAllocLength(void) const { - /* return length to allocate when creating an image */ - if (fPhysical == kPhysicalFormatNib525_Var && - (fFileFormat == kFileFormatTrackStar || - fFileFormat == kFileFormatFDI)) - { - // use maximum possible - return kTrackLenTrackStar525; - } - return GetNibbleTrackFormatLength(); - } - DIError LoadNibbleTrack(long track, long* pTrackLen); - DIError SaveNibbleTrack(void); - int FindNibbleSectorStart(const CircularBufferAccess& buffer, - int track, int sector, const NibbleDescr* pNibbleDescr, int* pVol); - void DecodeAddr(const CircularBufferAccess& buffer, int offset, - short* pVol, short* pTrack, short* pSector, short* pChksum); - inline uint16_t ConvFrom44(uint8_t val1, uint8_t val2) { - return ((val1 << 1) | 0x01) & val2; - } - DIError DecodeNibbleData(const CircularBufferAccess& buffer, int idx, - uint8_t* sctBuf, const NibbleDescr* pNibbleDescr); - void EncodeNibbleData(const CircularBufferAccess& buffer, int idx, - const uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) const; - DIError DecodeNibble62(const CircularBufferAccess& buffer, int idx, - uint8_t* sctBuf, const NibbleDescr* pNibbleDescr); - void EncodeNibble62(const CircularBufferAccess& buffer, int idx, - const uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) const; - DIError DecodeNibble53(const CircularBufferAccess& buffer, int idx, - uint8_t* sctBuf, const NibbleDescr* pNibbleDescr); - void EncodeNibble53(const CircularBufferAccess& buffer, int idx, - const uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) const; - int TestNibbleTrack(int track, const NibbleDescr* pNibbleDescr, int* pVol); - DIError AnalyzeNibbleData(void); - inline uint8_t Conv44(uint16_t val, bool first) const { - if (first) - return (val >> 1) | 0xaa; - else - return val | 0xaa; - } - DIError FormatNibbles(GenericFD* pGFD) const; - - static const uint8_t kMacHighASCII[]; - - /* - * 3.5" nibble access - */ - static int FindNextSector35(const CircularBufferAccess& buffer, int start, - int cyl, int head, int* pSector); - static bool DecodeNibbleSector35(const CircularBufferAccess& buffer, - int start, uint8_t* sectorBuf, uint8_t* readChecksum, - uint8_t* calcChecksum); - static bool UnpackChecksum35(const CircularBufferAccess& buffer, - int offset, uint8_t* checksumBuf); - static void EncodeNibbleSector35(const uint8_t* sectorData, - uint8_t* outBuf); - - /* static data tables */ - static uint8_t kDiskBytes53[32]; - static uint8_t kDiskBytes62[64]; - static uint8_t kInvDiskBytes53[256]; - static uint8_t kInvDiskBytes62[256]; - enum { kInvInvalidValue = 0xff }; - -private: // some C++ stuff to block behavior we don't support - DiskImg& operator=(const DiskImg&); - DiskImg(const DiskImg&); -}; - - - -/* - * Disk filesystem class, roughly equivalent to a GS/OS FST. This is an - * abstract base class. - * - * Static functions know how to access a DiskImg and figure out what kind - * of image we have. Once known, the appropriate sub-class can be - * instantiated. - * - * We maintain a linear list of files to make it easy for applications to - * traverse the full set of files. Sometimes this makes it hard for us to - * update internally (especially HFS). With some minor cleverness it - * should be possible to switch to a tree structure while retaining the - * linear "get next" API. This would be a big help for ProDOS and HFS. - * - * NEED: some notification mechanism for changes to files and/or block - * editing of the disk (especially with regard to open sub-volumes). If - * a disk volume open for file-by-file viewing is modified with the disk - * editor, we should close the file when the disk editor exits. - * - * NEED? disk utilities, such as "disk crunch", for Pascal volumes. Could - * be written externally, but might as well keep fs knowledge embedded. - * - * MISSING: there is no way to override the image analyzer when working - * with sub-volumes. Actually, there is; it just has to happen *after* - * the DiskFS has been created. We should provide an approach that either - * stifles the DiskFS creation, or allows us to override and replace the - * internal DiskFS so we can pop up a sub-volume list, show what we *think* - * is there, and then let the user pick a volume and pick overrides (mainly - * for use in the disk editor). Not sure if we want the changes to "stick"; - * we probably do. Q: does the "scan for sub-volumes" attribute propagate - * recursively to each sub-sub-volume? Probably. - * - * NOTE to self: should make "test" functions more lax when called - * from here, on the assumption that the caller is knowledgeable. Perhaps - * an independent "strictness" variable that can be cranked down through - * multiple calls to AnalyzeImage?? - */ -class DISKIMG_API DiskFS { -public: - /* - * Information about volume usage. - * - * Each "chunk" is a track/sector on a DOS disk or a block on a ProDOS - * or Pascal disk. CP/M really ought to use 1K blocks, but for - * convenience we're just using 512-byte blocks (it's up to the CP/M - * code to set two "chunks" per block). - * - * NOTE: the current DOS/ProDOS/Pascal code is sloppy when it comes to - * keeping this structure up to date. HFS doesn't use it at all. This - * has always been a low-priority feature. - */ - class DISKIMG_API VolumeUsage { - public: - VolumeUsage(void) { - fByBlocks = false; - fTotalChunks = -1; - fNumSectors = -1; - //fFreeChunks = -1; - fList = NULL; - fListSize = -1; - } - ~VolumeUsage(void) { - delete[] fList; - } - - /* - * These values MUST fit in five bits. - * - * Suggested disk map colors: - * 0 = unknown (color-key pink) - * 1 = conflict (medium-red) - * 2 = boot loader (dark-blue) - * 3 = volume dir (light-blue) - * 4 = subdir (medium-blue) - * 5 = user data (medium-green) - * 6 = user index blocks (light-green) - * 7 = embedded filesys (yellow) - * - * THOUGHT: Add flag for I/O error (nibble images) -- requires - * automatic disk verify pass. (Or maybe could be done manually - * on request?) - * - * unused --> black - * marked-used-but-not-used --> dark-red - * used-but-not-marked-used --> orange - */ - typedef enum { - kChunkPurposeUnknown = 0, - kChunkPurposeConflict = 1, // two or more different things - kChunkPurposeSystem = 2, // boot blocks, volume bitmap - kChunkPurposeVolumeDir = 3, // volume dir (or only dir) - kChunkPurposeSubdir = 4, // ProDOS sub-directory - kChunkPurposeUserData = 5, // file on this filesystem - kChunkPurposeFileStruct = 6, // index blocks, T/S lists - kChunkPurposeEmbedded = 7, // embedded filesystem - // how about: outside range claimed by disk, e.g. fTotalBlocks on - // 800K ProDOS disk in a 32MB CFFA volume? - } ChunkPurpose; - - typedef struct ChunkState { - bool isUsed; - bool isMarkedUsed; - ChunkPurpose purpose; // only valid if isUsed is set - } ChunkState; - - // initialize, configuring for either blocks or sectors - DIError Create(long numBlocks); - DIError Create(long numTracks, long numSectors); - bool GetInitialized(void) const { return (fList != NULL); } - - // return the number of chunks on this disk - long GetNumChunks(void) const { return fTotalChunks; } - - // return the number of unallocated chunks, taking into account - // both the free-chunk bitmap (if any) and the actual usage - long GetActualFreeChunks(void) const; - - // return the state of the specified chunk - DIError GetChunkState(long block, ChunkState* pState) const; - DIError GetChunkState(long track, long sector, - ChunkState* pState) const; - - // set the state of a particular chunk (should only be done by - // the DiskFS sub-classes) - DIError SetChunkState(long block, const ChunkState* pState); - DIError SetChunkState(long track, long sector, - const ChunkState* pState); - - void Dump(void) const; // debugging - - private: - DIError GetChunkStateIdx(int idx, ChunkState* pState) const; - DIError SetChunkStateIdx(int idx, const ChunkState* pState); - inline char StateToChar(ChunkState* pState) const; - - /* - * Chunk state is stored as a set of bits in one byte: - * - * 0-4: how is block used (only has meaning if bit 6 is set) - * 5: for nibble images, indicates the block or sector is unreadable - * 6: is block used by something (0=no, 1=yes) - * 7: is block marked "in use" by system map (0=no, 1=yes) - * - * [ Consider reducing "purpose" to 0-3 and adding bad block bit for - * nibble images and physical media.] - */ - enum { - kChunkPurposeMask = 0x1f, // ChunkPurpose enum - kChunkDamagedFlag = 0x20, - kChunkUsedFlag = 0x40, - kChunkMarkedUsedFlag = 0x80, - }; - - bool fByBlocks; - long fTotalChunks; - long fNumSectors; // only valid if !fByBlocks - //long fFreeChunks; - uint8_t* fList; - int fListSize; - }; // end of VolumeUsage class - - /* - * List of sub-volumes. The SubVolume owns the DiskImg and DiskFS - * that are handed to it, so they can be deleted when the SubVolume - * is deleted as part of destroying the parent. - */ - class SubVolume { - public: - SubVolume(void) : fpDiskImg(NULL), fpDiskFS(NULL), - fpPrev(NULL), fpNext(NULL) {} - ~SubVolume(void) { - delete fpDiskFS; // must close first; may need flush to DiskImg - delete fpDiskImg; - } - - void Create(DiskImg* pDiskImg, DiskFS* pDiskFS) { - assert(pDiskImg != NULL); - assert(pDiskFS != NULL); - fpDiskImg = pDiskImg; - fpDiskFS = pDiskFS; - } - - DiskImg* GetDiskImg(void) const { return fpDiskImg; } - DiskFS* GetDiskFS(void) const { return fpDiskFS; } - - SubVolume* GetPrev(void) const { return fpPrev; } - void SetPrev(SubVolume* pSubVol) { fpPrev = pSubVol; } - SubVolume* GetNext(void) const { return fpNext; } - void SetNext(SubVolume* pSubVol) { fpNext = pSubVol; } - - private: - DiskImg* fpDiskImg; - DiskFS* fpDiskFS; - - SubVolume* fpPrev; - SubVolume* fpNext; - }; // end of SubVolume class - - - - /* - * Start of DiskFS declarations. - */ -public: - typedef enum SubScanMode { - kScanSubUnknown = 0, - kScanSubDisabled, - kScanSubEnabled, - kScanSubContainerOnly, - } SubScanMode; - - - DiskFS(void) { - fpA2Head = fpA2Tail = NULL; - fpSubVolumeHead = fpSubVolumeTail = NULL; - fpImg = NULL; - fScanForSubVolumes = kScanSubDisabled; - - fParmTable[kParm_CreateUnique] = 0; - fParmTable[kParmProDOS_AllowLowerCase] = 1; - fParmTable[kParmProDOS_AllocSparse] = 1; - } - virtual ~DiskFS(void) { - DeleteSubVolumeList(); - DeleteFileList(); - SetDiskImg(NULL); - } - - /* - * Static FSFormat-analysis functions, called by the DiskImg AnalyzeImage - * function. Capable of determining with a high degree of accuracy - * what format the disk is in, yet remaining flexible enough to - * function correctly with variations (like DOS3.3 disks with - * truncated TOCs and ProDOS images from hard drives). - * - * The "pOrder" and "pFormat" arguments are in/out. Set them to the - * appropriate "unknown" enum value on entry. If something is known - * of the order or format, put that in instead; in some cases this will - * bias the proceedings, which is useful for efficiency and for making - * overrides work correctly. - * - * On success, these return kDIErrNone and set "*pOrder". On failure, - * they return nonzero and leave "*pOrder" unmodified. - * - * Each DiskFS sub-class should declare a TestFS function. It's not - * virtual void here because, since it's called before the DiskFS is - * created, it must be static. - */ - typedef enum FSLeniency { kLeniencyNot, kLeniencyVery } FSLeniency; - //static DIError TestFS(const DiskImg* pImg, DiskImg::SectorOrder* pOrder, - // DiskImg::FSFormat* pFormat, FSLeniency leniency); - - /* - * Load the disk contents and (if enabled) scan for sub-volumes. - * - * If "headerOnly" is set, we just do a quick scan of the volume header - * to get basic information. The deep file scan is skipped (but can - * be done later). (Sub-classes can choose to ignore the flag and - * always do the full scan; this is an optimization.) Guaranteed to - * set the volume name and volume block/sector count. - * - * If a progress callback is set up, this can return with a "cancelled" - * result, which should not be treated as a failure. - */ - typedef enum { kInitUnknown = 0, kInitHeaderOnly, kInitFull } InitMode; - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) = 0; - - /* - * Format the disk with the appropriate filesystem, creating all filesystem - * structures and (when appropriate) boot blocks. - */ - virtual DIError Format(DiskImg* pDiskImg, const char* volName) - { return kDIErrNotSupported; } - - /* - * Pass in a full path to normalize, and a buffer to copy the output - * into. On entry "pNormalizedBufLen" holds the length of the buffer. - * On exit, it holds the size of the buffer required to hold the - * normalized string. If the buffer is NULL or isn't big enough, no part - * of the normalized path will be copied into the buffer, and a specific - * error (kDIErrDataOverrun) will be returned. - * - * Generally speaking, the normalization process involves separating - * the pathname into its components, modifying each filename as needed - * to make it legal on the current filesystem, and then reassembling - * the components. - * - * The input and output strings are Mac OS Roman. - */ - virtual DIError NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) - { return kDIErrNotSupported; } - - - /* - * Create a file. The CreateParms struct specifies the full set of file - * details. To remain FS-agnostic, use the NufxLib constants - * (kNuStorageDirectory, kNuAccessUnlocked, etc). They match up with - * their ProDOS equivalents, and I promise to make them work right. - * - * On success, the file exists as a fully-formed, zero-length file. A - * pointer to the relevant A2File structure is returned. - */ - enum { - /* valid values for CreateParms; must match ProDOS enum */ - kStorageSeedling = 1, - kStorageExtended = 5, - kStorageDirectory = 13, - }; - typedef struct CreateParms { - const char* pathName; // full pathname for file on disk image - char fssep; - int storageType; // determines normal, subdir, or forked - uint32_t fileType; - uint32_t auxType; - uint32_t access; - time_t createWhen; - time_t modWhen; - } CreateParms; - virtual DIError CreateFile(const CreateParms* pParms, A2File** ppNewFile) - { return kDIErrNotSupported; } - - /* - * Delete a file from the disk. - */ - virtual DIError DeleteFile(A2File* pFile) - { return kDIErrNotSupported; } - - /* - * Rename a file. - */ - virtual DIError RenameFile(A2File* pFile, const char* newName) - { return kDIErrNotSupported; } - - /* - * Alter file attributes. - */ - virtual DIError SetFileInfo(A2File* pFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) - { return kDIErrNotSupported; } - - /* - * Rename a volume. Also works for changing the disk volume number. - */ - virtual DIError RenameVolume(const char* newName) - { return kDIErrNotSupported; } - - - // Accessor - DiskImg* GetDiskImg(void) const { return fpImg; } - - // Test file and volume names (and volume numbers) - // [these need to be static functions for some things... hmm] - //virtual bool IsValidFileName(const char* name) const { return false; } - //virtual bool IsValidVolumeName(const char* name) const { return false; } - - // Return the disk volume name, or NULL if there isn't one. - virtual const char* GetVolumeName(void) const = 0; - - // Return a printable string identifying the FS type and volume - virtual const char* GetVolumeID(void) const = 0; - - // Return the "bare" volume name. For formats where the volume name - // is actually a number (e.g. DOS 3.3), this returns just the number. - // For formats without a volume name or number (e.g. CP/M), this returns - // NULL, indicating that any attempt to change the volume name will fail. - virtual const char* GetBareVolumeName(void) const = 0; - - // Returns "false" if we only support read-only access to this FS type - virtual bool GetReadWriteSupported(void) const = 0; - - // Returns "true" if the filesystem shows evidence of damage. - virtual bool GetFSDamaged(void) const = 0; - - // Returns number of blocks recognized by the filesystem, or -1 if the - // FS isn't block-oriented (e.g. DOS 3.2/3.3) - virtual long GetFSNumBlocks(void) const { return -1; } - - // Get the next file in the list. Start by passing in NULL to get the - // head of the list. Returns NULL when the end of the list is reached. - A2File* GetNextFile(A2File* pCurrent) const; - - // Get a count of the files and directories on this disk. - long GetFileCount(void) const; - - /* - * Find a file by case-insensitive pathname. Assumes fssep=':'. The - * compare function can be overridden for systems like HFS, where "case - * insensitive" has a different meaning because of the native - * character set. - * - * The A2File* returned should not be deleted. - */ - typedef int (*StringCompareFunc)(const char* str1, const char* str2); - A2File* GetFileByName(const char* pathName, StringCompareFunc func = NULL); - - // This controls scanning for sub-volumes; must be set before Initialize(). - SubScanMode GetScanForSubVolumes(void) const { return fScanForSubVolumes; } - void SetScanForSubVolumes(SubScanMode val) { fScanForSubVolumes = val; } - - // some constants for non-ProDOS filesystems to use - enum { kFileAccessLocked = 0x01, kFileAccessUnlocked = 0xc3 }; - - - /* - * We use this as a filename separator character (i.e. between pathname - * components) in all filenames. It's useful to standardize on this - * so that behavior is consistent across all disk and archive formats. - * - * The choice of ':' is good because it's invalid in filenames on - * Windows, Mac OS, GS/OS, and pretty much anywhere else we could be - * running except for UNIX. It's valid under DOS 3.3, but since you - * can't have subdirectories under DOS there's no risk of confusion. - */ - enum { kDIFssep = ':' }; - - - /* - * Return the volume use map. This is a non-const function because - * it might need to do a "just-in-time" usage map update. It returns - * const to keep non-DiskFS classes from altering the map. - */ - const VolumeUsage* GetVolumeUsageMap(void) { - if (fVolumeUsage.GetInitialized()) - return &fVolumeUsage; - else - return NULL; - } - - /* - * Return the total space and free space, in either blocks or sectors - * as appropriate. "*pUnitSize" will be kBlockSize or kSectorSize. - */ - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const = 0; - - - /* - * Get the next volume in the list. Start by passing in NULL to get the - * head of the list. Returns NULL when the end of the list is reached. - */ - SubVolume* GetNextSubVolume(const SubVolume* pCurrent) const; - - /* - * Set some parameters to tell the DiskFS how to operate. Some of - * these are FS-specific, some may be general. - * - * Parameters are set in the current object and all sub-volume objects. - * - * The enum is part of the interface and must be rigidly defined, but - * it is also used to size an array so it can't be too sparse. - */ - typedef enum DiskFSParameter { - kParmUnknown = 0, - - kParm_CreateUnique = 1, // make new filenames unique - - kParmProDOS_AllowLowerCase = 10, // allow lower case and spaces - kParmProDOS_AllocSparse = 11, // don't store empty blocks - - kParmMax // must be last entry - } DiskFSParameter; - long GetParameter(DiskFSParameter parm); - void SetParameter(DiskFSParameter parm, long val); - - /* - * Flush changed data. - * - * The individual filesystems shouldn't generally do any caching; if - * they do, we would want a virtual "FlushFS()" that gets called by - * Flush. The better answer is to cache in DiskImg, which works for - * everything, and knows if the underlying storage is already in RAM. - * - * For the most part this just needs to recursively flush the DiskImg - * objects in all of the sub-volumes and then the current volume. This - * is a no-op in most cases, but if the archive is compressed this will - * cause a new, compressed archive to be created. - * - * The "mode" value determines whether or not we do "heavy" flushes. It's - * very handy to be able to do "slow" flushes for anything that is being - * written directly to disk (as opposed to being run through Deflate), - * so that the UI doesn't indicate that they're partially written when - * in fact they're fully written. - */ - DIError Flush(DiskImg::FlushMode mode); - - /* - * Set the read-only flag on our DiskImg and those of our subvolumes. - * Used to ensure that a DiskFS with un-flushed data can be deleted - * without corrupting the volume. - */ - void SetAllReadOnly(bool val); - - // debug dump - void DumpFileList(void); - -protected: - /* - * Set the DiskImg pointer. Updates the reference count in DiskImg. - */ - void SetDiskImg(DiskImg* pImg); - - // once added, we own the pDiskImg and the pDiskFS (DO NOT pass the - // same DiskImg or DiskFS in more than once!). Note this copies the - // fParmTable and other stuff (fScanForSubVolumes) from parent to child. - void AddSubVolumeToList(DiskImg* pDiskImg, DiskFS* pDiskFS); - // add files to fpA2Head/fpA2Tail - void AddFileToList(A2File* pFile); - // only need for hierarchical filesystems; insert file after pPrev - void InsertFileInList(A2File* pFile, A2File* pPrev); - // delete an entry - void DeleteFileFromList(A2File* pFile); - - // scan for damaged or suspicious files - void ScanForDamagedFiles(bool* pDamaged, bool* pSuspicious); - - // pointer to the DiskImg structure underlying this filesystem - DiskImg* fpImg; - - VolumeUsage fVolumeUsage; - SubScanMode fScanForSubVolumes; - - -private: - A2File* SkipSubdir(A2File* pSubdir); - void CopyInheritables(DiskFS* pNewFS); - void DeleteFileList(void); - void DeleteSubVolumeList(void); - - long fParmTable[kParmMax]; // for DiskFSParameter - - A2File* fpA2Head; - A2File* fpA2Tail; - SubVolume* fpSubVolumeHead; - SubVolume* fpSubVolumeTail; - -private: - DiskFS& operator=(const DiskFS&); - DiskFS(const DiskFS&); -}; - - -/* - * Apple II file class, representing a file on an Apple II volume. This is an - * abstract base class. - * - * There is a different sub-class for each filesystem type. The A2File object - * encapsulates all of the knowledge required to read a file from a disk - * image. - * - * The prev/next pointers, used to maintain a linked list of files, are only - * accessible from DiskFS functions. At some point we may want to rearrange - * the way this is handled, e.g. by not maintaining a list at all, so it's - * important that everything go through DiskFS requests. - * - * The FSFormat is made an explicit member, because sub-classes may not - * understand exactly where the file came from (e.g. was it DOS3.2 or - * DOS 3.3). Somebody might care. - * - * - * NOTE TO SELF: open files need to be generalized. Right now the internal - * implementations only allow a single open, which is okay for our purposes - * but bad for a general FS implementation. As it stands, you can't even - * open both forks at the same time on ProDOS/HFS. - * - * UMMM: The handling of "damaged" files could be more consistent. - */ -class DISKIMG_API A2File { -public: - friend class DiskFS; - - A2File(DiskFS* pDiskFS) : fpDiskFS(pDiskFS) { - fpPrev = fpNext = NULL; - fFileQuality = kQualityGood; - } - virtual ~A2File(void) {} - - /* - * All Apple II files have certain characteristics, of which ProDOS - * is roughly a superset. (Yes, you can have HFS on a IIgs, but - * all that fancy creator type stuff is decidedly Mac-centric. Still, - * we want to assume 4-byte file and aux types.) - * - * NEED: something distinguishing between files and disk images? - * - * NOTE: there is no guarantee that GetPathName will return unique values - * (duplicates are possible). We don't guarantee that you won't get an - * empty string back (it's valid to have an empty filename in the dir in - * DOS 3.3, and it's possible for other filesystems to be damaged). - * - * The filename returned is defined to be null-terminated Mac OS Roman. - * For most filesystems this is automatic, as filenames are restricted - * to ASCII, but for DOS 3.3 it requires stripping high bits. It also - * means that embedded nulls in HFS filenames (which are discouraged but - * allowed) will be lost. - * - * We do guarantee that the contents of subdirectories are grouped - * together. This makes it much easier to construct a hierarchy out of - * the linear list. This becomes important when dynamically adding - * files to a disk. - */ - virtual const char* GetFileName(void) const = 0; // name of this file - virtual const char* GetPathName(void) const = 0; // full path - virtual char GetFssep(void) const = 0; // '\0' if none - virtual uint32_t GetFileType(void) const = 0; - virtual uint32_t GetAuxType(void) const = 0; - virtual uint32_t GetAccess(void) const = 0; // ProDOS-style perms - virtual time_t GetCreateWhen(void) const = 0; - virtual time_t GetModWhen(void) const = 0; - virtual di_off_t GetDataLength(void) const = 0; // len of data fork - virtual di_off_t GetDataSparseLength(void) const = 0; // len w/o sparse areas - virtual di_off_t GetRsrcLength(void) const = 0; // len or -1 if no rsrc - virtual di_off_t GetRsrcSparseLength(void) const = 0; // len or -1 if no rsrc - virtual DiskImg::FSFormat GetFSFormat(void) const { - return fpDiskFS->GetDiskImg()->GetFSFormat(); - } - virtual bool IsDirectory(void) const { return false; } - virtual bool IsVolumeDirectory(void) const { return false; } - - /* - * Open a file. Treat the A2FileDescr like an fd. - */ - virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork = false) = 0; - - /* - * This is called by the A2FileDescr object when somebody invokes Close(). - * The A2File object should remove the A2FileDescr from its list of open - * files and delete the storage. The implementation must not call the - * A2FileDescr's Close function, since that would cause a recursive loop. - * - * This should not be called by an application. - */ - virtual void CloseDescr(A2FileDescr* pOpenFile) = 0; - - /* - * This is only useful for hierarchical filesystems like ProDOS, - * where the order of items in the linear list is significant. It - * allows an unambiguous determination of which subdir a file resides - * in, even if somebody has sector-edited the filesystem so that two - * subdirs have the same name. (It's also a bit speedier to compare - * than pathname substrings would be.) - */ - virtual A2File* GetParent(void) const { return NULL; } - - /* - * Returns "true" if either fork of the file is open, "false" if not. - */ - virtual bool IsFileOpen(void) const = 0; - - virtual void Dump(void) const = 0; // debugging - - typedef enum FileQuality { - kQualityUnknown = 0, - kQualityGood, - kQualitySuspicious, - kQualityDamaged, - } FileQuality; - virtual FileQuality GetQuality(void) const { return fFileQuality; } - virtual void SetQuality(FileQuality quality); - virtual void ResetQuality(void); - - DiskFS* GetDiskFS(void) const { return fpDiskFS; } - -protected: - DiskFS* fpDiskFS; - virtual void SetParent(A2File* pParent) { /* do nothing */ } - -private: - A2File* GetPrev(void) const { return fpPrev; } - void SetPrev(A2File* pFile) { fpPrev = pFile; } - A2File* GetNext(void) const { return fpNext; } - void SetNext(A2File* pFile) { fpNext = pFile; } - - // Set when file structure is damaged and application should not try - // to open the file. - FileQuality fFileQuality; - - A2File* fpPrev; - A2File* fpNext; - - -private: - A2File& operator=(const A2File&); - A2File(const A2File&); -}; - - -/* - * Abstract representation of an open file. This contains all active state - * and all information required to read and write a file. - * - * Do not delete these objects; instead, invoke the Close method, so that they - * can be removed from the parents' list of open files. - * TODO: consider making the destructor "protected" - */ -class DISKIMG_API A2FileDescr { -public: - A2FileDescr(A2File* pFile) : fpFile(pFile), fProgressUpdateFunc(NULL) {} - virtual ~A2FileDescr(void) { fpFile = NULL; /*paranoia*/ } - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) = 0; - virtual DIError Write(const void* buf, size_t len, size_t* pActual = NULL) = 0; - virtual DIError Seek(di_off_t offset, DIWhence whence) = 0; - virtual di_off_t Tell(void) = 0; - virtual DIError Close(void) = 0; - - virtual DIError GetStorage(long sectorIdx, long* pTrack, long* pSector) - const = 0; - virtual DIError GetStorage(long blockIdx, long* pBlock) - const = 0; - virtual long GetSectorCount(void) const = 0; - virtual long GetBlockCount(void) const = 0; - - virtual DIError Rewind(void) { return Seek(0, kSeekSet); } - - A2File* GetFile(void) const { return fpFile; } - - /* - * Progress update callback mechanism. Pass in the length or (for writes) - * expected length of the file. This invokes the callback with the - * lengths and some pointers. - * - * If the progress callback returns "true", progress continues. If it - * returns "false", the read or write function will return with - * kDIErrCancelled. - */ - typedef bool (*ProgressUpdater)(A2FileDescr* pFile, di_off_t max, - di_off_t current, void* vState); - void SetProgressUpdater(ProgressUpdater func, di_off_t max, void* state) { - fProgressUpdateFunc = func; - fProgressUpdateMax = max; - fProgressUpdateState = state; - } - void ClearProgressUpdater(void) { - fProgressUpdateFunc = NULL; - } - -protected: - A2File* fpFile; - - /* - * Internal utility functions for mapping blocks to sectors and vice-versa. - */ - virtual void TrackSectorToBlock(long track, long sector, long* pBlock, - bool* pSecondHalf) const - { - int numSectPerTrack = fpFile->GetDiskFS()->GetDiskImg()->GetNumSectPerTrack(); - assert(track < fpFile->GetDiskFS()->GetDiskImg()->GetNumTracks()); - assert(sector < numSectPerTrack); - long dblBlock = track * numSectPerTrack + sector; - *pBlock = dblBlock / 2; - *pSecondHalf = (dblBlock & 0x01) != 0; - } - virtual void BlockToTrackSector(long block, bool secondHalf, long* pTrack, - long* pSector) const - { - assert(block < fpFile->GetDiskFS()->GetDiskImg()->GetNumBlocks()); - int numSectPerTrack = fpFile->GetDiskFS()->GetDiskImg()->GetNumSectPerTrack(); - int dblBlock = block * 2; - if (secondHalf) - dblBlock++; - *pTrack = dblBlock / numSectPerTrack; - *pSector = dblBlock % numSectPerTrack; - } - - /* - * Call this from FS-specific read/write functions on successful - * completion (and perhaps more often for filesystems with potentially - * large files, e.g. ProDOS/HFS). - * - * Test the return value; if "false", user wishes to cancel the op, and - * long read or write calls should return immediately. - */ - inline bool UpdateProgress(di_off_t current) { - if (fProgressUpdateFunc != NULL) { - return (*fProgressUpdateFunc)(this, fProgressUpdateMax, current, - fProgressUpdateState); - } else { - return true; - } - } - -private: - A2FileDescr& operator=(const A2FileDescr&); - A2FileDescr(const A2FileDescr&); - - /* storage for progress update goodies */ - ProgressUpdater fProgressUpdateFunc; - di_off_t fProgressUpdateMax; - void* fProgressUpdateState; -}; - -} // namespace DiskImgLib - -#endif /*DISKIMG_DISKIMG_H*/ diff --git a/ciderpress/diskimg/DiskImgDetail.h b/ciderpress/diskimg/DiskImgDetail.h deleted file mode 100644 index 122c461..0000000 --- a/ciderpress/diskimg/DiskImgDetail.h +++ /dev/null @@ -1,3231 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2009 by CiderPress authors. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Sub-classes of the base classes defined in DiskImg.h. - * - * Most applications will not need to include this file, because the - * polymorphic interfaces do everything they need. If something needs to - * examine the actual directory structure of a file, it can do so through - * these declarations. - */ -#ifndef DISKIMG_DISKIMGDETAIL_H -#define DISKIMG_DISKIMGDETAIL_H - -#include "../nufxlib/NufxLib.h" -#include "../zlib/zlib.h" - -#include "DiskImg.h" - -#ifndef EXCISE_GPL_CODE -# include "libhfs/hfs.h" -#endif - -namespace DiskImgLib { - -/* - * =========================================================================== - * Outer wrappers - * =========================================================================== - */ - -/* - * Outer wrapper class, representing a compression utility or archive - * format that must be stripped away so we can get to the Apple II stuff. - * - * Outer wrappers usually have a filename embedded in them, representing - * the original name of the file. We want to use the extension from this - * name when evaluating the file contents. Usually. - */ -class OuterWrapper { -public: - OuterWrapper(void) {} - virtual ~OuterWrapper(void) {} - - // all sub-classes should have one of these - //static DIError Test(GenericFD* pGFD, long outerLength); - - // open the file and prepare to access it; fills out return values - // NOTE: pGFD must be a GFDFile. - virtual DIError Load(GenericFD* pOuterGFD, di_off_t outerLength, bool readOnly, - di_off_t* pWrapperLength, GenericFD** ppWrapperGFD) = 0; - - virtual DIError Save(GenericFD* pOuterGFD, GenericFD* pWrapperGFD, - di_off_t wrapperLength) = 0; - - // set on recoverable errors, like a CRC failure - virtual bool IsDamaged(void) const = 0; - - // indicate that we don't have a "fast" flush - virtual bool HasFastFlush(void) const { return false; } - - virtual const char* GetExtension(void) const = 0; - -private: - OuterWrapper& operator=(const OuterWrapper&); - OuterWrapper(const OuterWrapper&); -}; - -class OuterGzip : public OuterWrapper { -public: - OuterGzip(void) { fWrapperDamaged = false; } - virtual ~OuterGzip(void) {} - - static DIError Test(GenericFD* pGFD, di_off_t outerLength); - virtual DIError Load(GenericFD* pGFD, di_off_t outerLength, bool readOnly, - di_off_t* pTotalLength, GenericFD** ppNewGFD) override; - virtual DIError Save(GenericFD* pOuterGFD, GenericFD* pWrapperGFD, - di_off_t wrapperLength) override; - - virtual bool IsDamaged(void) const override { return fWrapperDamaged; } - - virtual const char* GetExtension(void) const override { return NULL; } - -private: - DIError ExtractGzipImage(gzFile gzfp, char** pBuf, di_off_t* pLength); - DIError CloseGzip(void); - - // Largest possible ProDOS volume; quite a bit to hold in RAM. Add a - // little extra for .hdv format. - enum { kMaxUncompressedSize = kGzipMax +256 }; - - bool fWrapperDamaged; -}; - -class OuterZip : public OuterWrapper { -public: - OuterZip(void) : fStoredFileName(NULL), fExtension(NULL) {} - virtual ~OuterZip(void) { - delete[] fStoredFileName; - delete[] fExtension; - } - - static DIError Test(GenericFD* pGFD, di_off_t outerLength); - virtual DIError Load(GenericFD* pGFD, di_off_t outerLength, bool readOnly, - di_off_t* pTotalLength, GenericFD** ppNewGFD) override; - virtual DIError Save(GenericFD* pOuterGFD, GenericFD* pWrapperGFD, - di_off_t wrapperLength) override; - - virtual bool IsDamaged(void) const override { return false; } - - virtual const char* GetExtension(void) const override { return fExtension; } - -private: - class LocalFileHeader { - public: - LocalFileHeader(void) : - fVersionToExtract(0), - fGPBitFlag(0), - fCompressionMethod(0), - fLastModFileTime(0), - fLastModFileDate(0), - fCRC32(0), - fCompressedSize(0), - fUncompressedSize(0), - fFileNameLength(0), - fExtraFieldLength(0), - fFileName(NULL) - {} - virtual ~LocalFileHeader(void) { delete[] fFileName; } - - DIError Read(GenericFD* pGFD); - DIError Write(GenericFD* pGFD); - void SetFileName(const char* name); - - // uint32_t fSignature; - uint16_t fVersionToExtract; - uint16_t fGPBitFlag; - uint16_t fCompressionMethod; - uint16_t fLastModFileTime; - uint16_t fLastModFileDate; - uint32_t fCRC32; - uint32_t fCompressedSize; - uint32_t fUncompressedSize; - uint16_t fFileNameLength; - uint16_t fExtraFieldLength; - uint8_t* fFileName; - // extra field - - enum { - kSignature = 0x04034b50, - kLFHLen = 30, // LocalFileHdr len, excl. var fields - }; - - void Dump(void) const; - }; - - class CentralDirEntry { - public: - CentralDirEntry(void) : - fVersionMadeBy(0), - fVersionToExtract(0), - fGPBitFlag(0), - fCompressionMethod(0), - fLastModFileTime(0), - fLastModFileDate(0), - fCRC32(0), - fCompressedSize(0), - fUncompressedSize(0), - fFileNameLength(0), - fExtraFieldLength(0), - fFileCommentLength(0), - fDiskNumberStart(0), - fInternalAttrs(0), - fExternalAttrs(0), - fLocalHeaderRelOffset(0), - fFileName(NULL), - fFileComment(NULL) - {} - virtual ~CentralDirEntry(void) { - delete[] fFileName; - delete[] fFileComment; - } - - DIError Read(GenericFD* pGFD); - DIError Write(GenericFD* pGFD); - void SetFileName(const char* name); - - // uint32_t fSignature; - uint16_t fVersionMadeBy; - uint16_t fVersionToExtract; - uint16_t fGPBitFlag; - uint16_t fCompressionMethod; - uint16_t fLastModFileTime; - uint16_t fLastModFileDate; - uint32_t fCRC32; - uint32_t fCompressedSize; - uint32_t fUncompressedSize; - uint16_t fFileNameLength; - uint16_t fExtraFieldLength; - uint16_t fFileCommentLength; - uint16_t fDiskNumberStart; - uint16_t fInternalAttrs; - uint32_t fExternalAttrs; - uint32_t fLocalHeaderRelOffset; - uint8_t* fFileName; - // extra field - uint8_t* fFileComment; // alloc with new[] - - void Dump(void) const; - - enum { - kSignature = 0x02014b50, - kCDELen = 46, // CentralDirEnt len, excl. var fields - }; - }; - - class EndOfCentralDir { - public: - EndOfCentralDir(void) : - fDiskNumber(0), - fDiskWithCentralDir(0), - fNumEntries(0), - fTotalNumEntries(0), - fCentralDirSize(0), - fCentralDirOffset(0), - fCommentLen(0) - {} - virtual ~EndOfCentralDir(void) {} - - DIError ReadBuf(const uint8_t* buf, int len); - DIError Write(GenericFD* pGFD); - - // uint32_t fSignature; - uint16_t fDiskNumber; - uint16_t fDiskWithCentralDir; - uint16_t fNumEntries; - uint16_t fTotalNumEntries; - uint32_t fCentralDirSize; - uint32_t fCentralDirOffset; // offset from first disk - uint16_t fCommentLen; - // archive comment - - enum { - kSignature = 0x06054b50, - kEOCDLen = 22, // EndOfCentralDir len, excl. comment - }; - - void Dump(void) const; - }; - - enum { - kDataDescriptorSignature = 0x08074b50, - - kMaxCommentLen = 65535, // longest possible in ushort - kMaxEOCDSearch = kMaxCommentLen + EndOfCentralDir::kEOCDLen, - - kZipFssep = '/', - kDefaultVersion = 20, - kMaxUncompressedSize = kGzipMax +256, - }; - enum { - kCompressStored = 0, // no compression - //kCompressShrunk = 1, - //kCompressImploded = 6, - kCompressDeflated = 8, // standard deflate - }; - - static DIError ReadCentralDir(GenericFD* pGFD, di_off_t outerLength, - CentralDirEntry* pDirEntry); - DIError ExtractZipEntry(GenericFD* pOuterGFD, CentralDirEntry* pCDE, - uint8_t** pBuf, di_off_t* pLength); - DIError InflateGFDToBuffer(GenericFD* pGFD, unsigned long compSize, - unsigned long uncompSize, uint8_t* buf); - DIError DeflateGFDToGFD(GenericFD* pDst, GenericFD* pSrc, di_off_t length, - di_off_t* pCompLength, uint32_t* pCRC); - -private: - void SetExtension(const char* ext); - void SetStoredFileName(const char* name); - void GetMSDOSTime(uint16_t* pDate, uint16_t* pTime); - void DOSTime(time_t when, uint16_t* pDate, uint16_t* pTime); - - char* fStoredFileName; - char* fExtension; -}; - - -/* - * =========================================================================== - * Image wrappers - * =========================================================================== - */ - -/* - * Image wrapper class, representing the format of the Windows files. - * Might be "raw" data, might be data with a header, might be a complex - * or compressed format that must be extracted to a buffer. - */ -class ImageWrapper { -public: - ImageWrapper(void) {} - virtual ~ImageWrapper(void) {} - - // all sub-classes should have one of these - // static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - - // open the file and prepare to access it; fills out return values - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) = 0; - - // fill out the wrapper, using the specified parameters - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) = 0; - - // push altered data to the wrapper GFD - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) = 0; - - // set the storage name (used by some formats) - virtual void SetStorageName(const char* name) { - // default implementation - assert(false); - } - - // indicate that we have a "fast" flush - virtual bool HasFastFlush(void) const = 0; - - // set by "Prep" on recoverable errors, like a CRC failure, for some fmts - virtual bool IsDamaged(void) const { return false; } - - // if this wrapper format includes a file comment, return it - //virtual const char* GetComment(void) const { return NULL; } - - /* - * Some additional goodies required for accessing variable-length nibble - * tracks in TrackStar images. A default implementation is provided and - * used for everything but TrackStar. - */ - virtual int GetNibbleTrackLength(DiskImg::PhysicalFormat physical, int track) const - { - if (physical == DiskImg::kPhysicalFormatNib525_6656) - return kTrackLenNib525; - else if (physical == DiskImg::kPhysicalFormatNib525_6384) - return kTrackLenNb2525; - else { - assert(false); - return -1; - } - } - virtual void SetNibbleTrackLength(int track, int length) { /*do nothing*/ } - virtual int GetNibbleTrackOffset(DiskImg::PhysicalFormat physical, int track) const - { - if (physical == DiskImg::kPhysicalFormatNib525_6656 || - physical == DiskImg::kPhysicalFormatNib525_6384) - { - /* fixed-length tracks */ - return GetNibbleTrackLength(physical, 0) * track; - } else { - assert(false); - return -1; - } - } - // TrackStar images can have more, but otherwise all nibble images have 35 - virtual int GetNibbleNumTracks(void) const - { - return kTrackCount525; - } - -private: - ImageWrapper& operator=(const ImageWrapper&); - ImageWrapper(const ImageWrapper&); -}; - - -class Wrapper2MG : public ImageWrapper { -public: - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return true; } - //virtual const char* GetComment(void) const { return NULL; } - // (need to hold TwoImgHeader in the struct, rather than as temp, or - // need to copy the comment out into Wrapper2MG storage e.g. StorageName) -}; - -class WrapperNuFX : public ImageWrapper { -public: - WrapperNuFX(void) : fpArchive(NULL), fThreadIdx(0), fStorageName(NULL), - fCompressType(kNuThreadFormatLZW2) - {} - virtual ~WrapperNuFX(void) { CloseNuFX(); delete[] fStorageName; } - - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return false; } - - void SetStorageName(const char* name) { - delete[] fStorageName; - if (name != NULL) { - fStorageName = new char[strlen(name)+1]; - strcpy(fStorageName, name); - } else - fStorageName = NULL; - } - void SetCompressType(NuThreadFormat format) { fCompressType = format; } - -private: - enum { kDefaultStorageFssep = ':' }; - static NuResult ErrMsgHandler(NuArchive* pArchive, void* vErrorMessage); - static DIError OpenNuFX(const char* pathName, NuArchive** ppArchive, - NuThreadIdx* pThreadIdx, long* pLength, bool readOnly); - DIError GetNuFXDiskImage(NuArchive* pArchive, NuThreadIdx threadIdx, - long length, char** ppData); - static char* GenTempPath(const char* path); - DIError CloseNuFX(void); - void UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime); - - NuArchive* fpArchive; - NuThreadIdx fThreadIdx; - char* fStorageName; - NuThreadFormat fCompressType; -}; - -class WrapperDiskCopy42 : public ImageWrapper { -public: - WrapperDiskCopy42(void) : fStorageName(NULL), fBadChecksum(false) - {} - virtual ~WrapperDiskCopy42(void) { delete[] fStorageName; } - - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - void SetStorageName(const char* name) { - delete[] fStorageName; - if (name != NULL) { - fStorageName = new char[strlen(name)+1]; - strcpy(fStorageName, name); - } else - fStorageName = NULL; - } - - virtual bool HasFastFlush(void) const override { return false; } - virtual bool IsDamaged(void) const override { return fBadChecksum; } - -private: - typedef struct DC42Header DC42Header; - static void DumpHeader(const DC42Header* pHeader); - void InitHeader(DC42Header* pHeader); - static int ReadHeader(GenericFD* pGFD, DC42Header* pHeader); - DIError WriteHeader(GenericFD* pGFD, const DC42Header* pHeader); - static DIError ComputeChecksum(GenericFD* pGFD, - uint32_t* pChecksum); - - char* fStorageName; - bool fBadChecksum; -}; - -class WrapperDDD : public ImageWrapper { -public: - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return false; } - - enum { - kMaxDDDZeroCount = 4, // 3 observed, 4 suspected - }; - -private: - class BitBuffer; - enum { - kNumTracks = 35, - kNumSectors = 16, - kSectorSize = 256, - kTrackLen = kNumSectors * kSectorSize, - }; - - static DIError CheckForRuns(GenericFD* pGFD); - static DIError Unpack(GenericFD* pGFD, GenericFD** ppNewGFD, - short* pDiskVolNum); - - static DIError UnpackDisk(GenericFD* pGFD, GenericFD* pNewGFD, - short* pDiskVolNum); - static bool UnpackTrack(BitBuffer* pBitBuffer, uint8_t* trackBuf); - static DIError PackDisk(GenericFD* pSrcGFD, GenericFD* pWrapperGFD, - short diskVolNum); - static void PackTrack(const uint8_t* trackBuf, BitBuffer* pBitBuf); - static void ComputeFreqCounts(const uint8_t* trackBuf, - uint16_t* freqCounts); - static void ComputeFavorites(uint16_t* freqCounts, - uint8_t* favorites); - - short fDiskVolumeNum; -}; - -class WrapperSim2eHDV : public ImageWrapper { -public: - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return true; } -}; - -class WrapperTrackStar : public ImageWrapper { -public: - enum { - kTrackStarNumTracks = 40, - kFileTrackStorageLen = 6656, - kMaxTrackLen = kFileTrackStorageLen - (128+1+2), // header + footer - kCommentFieldLen = 0x2e, - }; - - WrapperTrackStar(void) : fStorageName(NULL) { - memset(&fNibbleTrackInfo, 0, sizeof(fNibbleTrackInfo)); - fNibbleTrackInfo.numTracks = -1; - } - virtual ~WrapperTrackStar(void) { delete[] fStorageName; } - - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return false; } - - virtual void SetStorageName(const char* name) override - { - delete[] fStorageName; - if (name != NULL) { - fStorageName = new char[strlen(name)+1]; - strcpy(fStorageName, name); - } else - fStorageName = NULL; - } - -private: - static DIError VerifyTrack(int track, const uint8_t* trackBuf); - DIError Unpack(GenericFD* pGFD, GenericFD** ppNewGFD); - DIError UnpackDisk(GenericFD* pGFD, GenericFD* pNewGFD); - - int fImageTracks; - char* fStorageName; - - /* - * Data structure for managing nibble images with variable-length tracks. - */ - typedef struct { - int numTracks; // should be 35 or 40 - int length[kMaxNibbleTracks525]; - int offset[kMaxNibbleTracks525]; - } NibbleTrackInfo; - NibbleTrackInfo fNibbleTrackInfo; // count and lengths for variable formats - - // nibble images can have variable-length data fields - virtual int GetNibbleTrackLength(DiskImg::PhysicalFormat physical, int track) const - { - assert(physical == DiskImg::kPhysicalFormatNib525_Var); - assert(fNibbleTrackInfo.numTracks > 0); - - return fNibbleTrackInfo.length[track]; - } - virtual void SetNibbleTrackLength(int track, int length); -#if 0 - { - assert(track >= 0); - assert(length > 0 && length <= kMaxTrackLen); - assert(track < fNibbleTrackInfo.numTracks); - - fNibbleTrackInfo.length[track] = length; - } -#endif - virtual int GetNibbleTrackOffset(DiskImg::PhysicalFormat physical, int track) const - { - assert(physical == DiskImg::kPhysicalFormatNib525_Var); - assert(fNibbleTrackInfo.numTracks > 0); - - return fNibbleTrackInfo.offset[track]; - } - virtual int GetNibbleNumTracks(void) const - { - return kTrackStarNumTracks; - } -}; - -class WrapperFDI : public ImageWrapper { -public: - WrapperFDI(void) {} - virtual ~WrapperFDI(void) {} - - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return false; } - - enum { - kSignatureLen = 27, - kCreatorLen = 30, - kCommentLen = 80, - }; - -private: - static const char* kFDIMagic; - - /* what type of disk is this? */ - typedef enum DiskType { - kDiskType8 = 0, - kDiskType525 = 1, - kDiskType35 = 2, - kDiskType3 = 3 - } DiskType; - - /* - * Contents of FDI header. - */ - typedef struct FDIHeader { - char signature[kSignatureLen+1]; - char creator[kCreatorLen+1]; - // CR + LF - char comment[kCommentLen+1]; - // MS-DOS EOF - uint16_t version; - uint16_t lastTrack; - uint8_t lastHead; - uint8_t type; // DiskType enum - uint8_t rotSpeed; - uint8_t flags; - uint8_t tpi; - uint8_t headWidth; - uint16_t reserved; - // track descriptors follow, at byte 152 - } FDIHeader; - - /* - * Header for pulse-index streams track. - */ - typedef struct PulseIndexHeader { - long numPulses; - long avgStreamLen; - int avgStreamCompression; - long minStreamLen; - int minStreamCompression; - long maxStreamLen; - int maxStreamCompression; - long idxStreamLen; - int idxStreamCompression; - - uint32_t* avgStream; // 4 bytes/pulse - uint32_t* minStream; // 4 bytes/pulse; optional - uint32_t* maxStream; // 4 bytes/pulse; optional - uint32_t* idxStream; // 2 bytes/pulse; optional? - } PulseIndexHeader; - - enum { - kTrackDescrOffset = 152, - kMaxHeads = 2, - kMaxHeaderBlockTracks = 180, // max 90 double-sided cylinders - kMinHeaderLen = 512, - kMinVersion = 0x0200, // v2.0 - - kMaxNibbleTracks35 = 80, // 80 double-sided tracks - kNibbleBufLen = 10240, // max seems to be a little under 10K - kBitBufferSize = kNibbleBufLen + (kNibbleBufLen / 4), - - kMaxSectors35 = 12, // max #of sectors per track - //kBytesPerSector35 = 512, // bytes per sector on 3.5" disk - - kPulseStreamDataOffset = 16, // start of header to avg stream - - kBitRate525 = 250000, // 250Kbits/sec - }; - - /* meaning of the two-bit compression format value */ - typedef enum CompressedFormat { - kCompUncompressed = 0, - kCompHuffman = 1, - } CompressedFormat; - - /* node in the Huffman tree */ - typedef struct HuffNode { - uint16_t val; - struct HuffNode* left; - struct HuffNode* right; - } HuffNode; - - /* - * Keep a copy of the header around while we work. None of the formats - * we're interested in have more than kMaxHeaderBlockTracks tracks in - * them, so we don't need anything beyond the initial 512-byte header. - */ - uint8_t fHeaderBuf[kMinHeaderLen]; - - static void UnpackHeader(const uint8_t* headerBuf, FDIHeader* hdr); - static void DumpHeader(const FDIHeader* pHdr); - - DIError Unpack525(GenericFD* pGFD, GenericFD** ppNewGFD, int numCyls, - int numHeads); - DIError Unpack35(GenericFD* pGFD, GenericFD** ppNewGFD, int numCyls, - int numHeads, LinearBitmap** ppBadBlockMap); - DIError PackDisk(GenericFD* pSrcGFD, GenericFD* pWrapperGFD); - - DIError UnpackDisk525(GenericFD* pGFD, GenericFD* pNewGFD, int numCyls, - int numHeads); - DIError UnpackDisk35(GenericFD* pGFD, GenericFD* pNewGFD, int numCyls, - int numHeads, LinearBitmap* pBadBlockMap); - void GetTrackInfo(int trk, int* pType, int* pLength256); - - int BitRate35(int trk); - void FixBadNibbles(uint8_t* nibbleBuf, long nibbleLen); - bool DecodePulseTrack(const uint8_t* inputBuf, long inputLen, - int bitRate, uint8_t* nibbleBuf, long* pNibbleLen); - bool UncompressPulseStream(const uint8_t* inputBuf, long inputLen, - uint32_t* outputBuf, long numPulses, int format, int bytesPerPulse); - bool ExpandHuffman(const uint8_t* inputBuf, long inputLen, - uint32_t* outputBuf, long numPulses); - const uint8_t* HuffExtractTree(const uint8_t* inputBuf, - HuffNode* pNode, uint8_t* pBits, uint8_t* pBitMask); - const uint8_t* HuffExtractValues16(const uint8_t* inputBuf, - HuffNode* pNode); - const uint8_t* HuffExtractValues8(const uint8_t* inputBuf, - HuffNode* pNode); - void HuffFreeNodes(HuffNode* pNode); - uint32_t HuffSignExtend16(uint32_t val); - uint32_t HuffSignExtend8(uint32_t val); - bool ConvertPulseStreamsToNibbles(PulseIndexHeader* pHdr, int bitRate, - uint8_t* nibbleBuf, long* pNibbleLen); - bool ConvertPulsesToBits(const uint32_t* avgStream, - const uint32_t* minStream, const uint32_t* maxStream, - const uint32_t* idxStream, int numPulses, int maxIndex, - int indexOffset, uint32_t totalAvg, int bitRate, - uint8_t* outputBuf, int* pOutputLen); - int MyRand(void); - bool ConvertBitsToNibbles(const uint8_t* bitBuffer, int bitCount, - uint8_t* nibbleBuf, long* pNibbleLen); - - - int fImageTracks; - char* fStorageName; - - - /* - * Data structure for managing nibble images with variable-length tracks. - */ - typedef struct { - int numTracks; // expect 35 or 40 for 5.25" - int length[kMaxNibbleTracks525]; - int offset[kMaxNibbleTracks525]; - } NibbleTrackInfo; - NibbleTrackInfo fNibbleTrackInfo; // count and lengths for variable formats - - // nibble images can have variable-length data fields - virtual int GetNibbleTrackLength(DiskImg::PhysicalFormat physical, int track) const - { - assert(physical == DiskImg::kPhysicalFormatNib525_Var); - assert(fNibbleTrackInfo.numTracks > 0); - - return fNibbleTrackInfo.length[track]; - } - virtual void SetNibbleTrackLength(int track, int length); - virtual int GetNibbleTrackOffset(DiskImg::PhysicalFormat physical, int track) const - { - assert(physical == DiskImg::kPhysicalFormatNib525_Var); - assert(fNibbleTrackInfo.numTracks > 0); - - return fNibbleTrackInfo.offset[track]; - } - virtual int GetNibbleNumTracks(void) const - { - return fNibbleTrackInfo.numTracks; - } -}; - - -class WrapperUnadornedNibble : public ImageWrapper { -public: - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return true; } -}; - -class WrapperUnadornedSector : public ImageWrapper { -public: - static DIError Test(GenericFD* pGFD, di_off_t wrappedLength); - virtual DIError Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) override; - virtual DIError Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) override; - virtual DIError Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) override; - virtual bool HasFastFlush(void) const override { return true; } -}; - - -/* - * =========================================================================== - * Non-FS DiskFSs - * =========================================================================== - */ - -/* - * A "raw" disk, i.e. no filesystem is known. Useful as a placeholder - * for applications that demand a DiskFS object even when the filesystem - * isn't known. - */ -class DISKIMG_API DiskFSUnknown : public DiskFS { -public: - DiskFSUnknown(void) : DiskFS() { - strcpy(fDiskVolumeName, "[Unknown]"); - strcpy(fDiskVolumeID, "Unknown FS"); - } - virtual ~DiskFSUnknown(void) {} - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) { - SetDiskImg(pImg); - return kDIErrNone; - } - - virtual const char* GetVolumeName(void) const override { return fDiskVolumeName; } - virtual const char* GetVolumeID(void) const override { return fDiskVolumeID; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - - // Use this if *something* is known about the filesystem, e.g. the - // partition type on a MacPart disk. - void SetVolumeInfo(const char* descr) { - if (strlen(descr) > kMaxVolumeName) - return; - - fDiskVolumeName[0] = '['; - strcpy(fDiskVolumeName+1, descr); - strcat(fDiskVolumeName, "]"); - strcpy(fDiskVolumeID, "Unknown FS - "); - strcat(fDiskVolumeID, descr); - } - -private: - enum { kMaxVolumeName = 64 }; - - char fDiskVolumeName[kMaxVolumeName+3]; - char fDiskVolumeID[kMaxVolumeName + 20]; -}; - - -/* - * Generic "container" DiskFS class. Contains some common functions shared - * among classes that are just containers for other filesystems. This class - * is not expected to be instantiated. - * - * TODO: create a common OpenSubVolume() function. - */ -class DISKIMG_API DiskFSContainer : public DiskFS { -public: - DiskFSContainer(void) : DiskFS() {} - virtual ~DiskFSContainer(void) {} - -protected: - virtual const char* GetDebugName(void) = 0; - virtual DIError CreatePlaceholder(long startBlock, long numBlocks, - const char* partName, const char* partType, - DiskImg** ppNewImg, DiskFS** ppNewFS); - virtual void SetVolumeUsageMap(void); -}; - -/* - * UNIDOS disk, an 800K floppy with two 400K DOS 3.3 volumes on it. - * - * The disk itself has no files; instead, it has two embedded sub-volumes. - */ -class DISKIMG_API DiskFSUNIDOS : public DiskFSContainer { -public: - DiskFSUNIDOS(void) : DiskFSContainer() {} - virtual ~DiskFSUNIDOS(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - static DIError TestWideFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "[UNIDOS]"; } - virtual const char* GetVolumeID(void) const override { return "[UNIDOS]"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - virtual const char* GetDebugName(void) override { return "UNIDOS"; } - DIError Initialize(void); - DIError OpenSubVolume(int idx); -}; - -/* - * OzDOS disk, an 800K floppy with two 400K DOS 3.3 volumes on it. They - * put the files for disk 1 in the odd sectors and the files for disk 2 - * in the even sectors (the top and bottom halves of a 512-byte block). - * - * The disk itself has no files; instead, it has two embedded sub-volumes. - * Because of the funky layout, we have to use the "sector pairing" feature - * of DiskImg to treat this like a DOS 3.3 disk. - */ -class DISKIMG_API DiskFSOzDOS : public DiskFSContainer { -public: - DiskFSOzDOS(void) : DiskFSContainer() {} - virtual ~DiskFSOzDOS(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "[OzDOS]"; } - virtual const char* GetVolumeID(void) const override { return "[OzDOS]"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - virtual const char* GetDebugName(void) override { return "OzDOS"; } - DIError Initialize(void); - DIError OpenSubVolume(int idx); -}; - -/* - * CFFA volume. A potentially very large volume with multiple partitions. - * - * This DiskFS is just a container that describes the position and sizes - * of the sub-volumes. - */ -class DISKIMG_API DiskFSCFFA : public DiskFSContainer { -public: - DiskFSCFFA(void) : DiskFSContainer() {} - virtual ~DiskFSCFFA(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "[CFFA]"; } - virtual const char* GetVolumeID(void) const override { return "[CFFA]"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - virtual const char* GetDebugName(void) override { return "CFFA"; } - - static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, - DiskImg::FSFormat* pFormatFound); - static DIError OpenSubVolume(DiskImg* pImg, long startBlock, - long numBlocks, bool scanOnly, DiskImg** ppNewImg, DiskFS** ppNewFS); - DIError Initialize(void); - DIError FindSubVolumes(void); - DIError AddVolumeSeries(int start, int count, long blocksPerVolume, - long& startBlock, long& totalBlocksLeft); - - enum { - kMinInterestingBlocks = 65536 + 1024, // less than this, ignore - kEarlyVolExpectedSize = 65536, // 32MB in 512-byte blocks - kOneGB = 1024*1024*(1024/512), // 1GB in 512-byte blocks - }; -}; - - -/* - * Macintosh-style partitioned disk image. - * - * This DiskFS is just a container that describes the position and sizes - * of the sub-volumes. - */ -class DISKIMG_API DiskFSMacPart : public DiskFSContainer { -public: - DiskFSMacPart(void) : DiskFSContainer() {} - virtual ~DiskFSMacPart(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "[MacPartition]"; } - virtual const char* GetVolumeID(void) const override { return "[MacPartition]"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - virtual const char* GetDebugName(void) override { return "MacPart"; } - - struct PartitionMap; // fwd - struct DriverDescriptorRecord; // fwd - static void UnpackDDR(const uint8_t* buf, - DriverDescriptorRecord* pDDR); - static void DumpDDR(const DriverDescriptorRecord* pDDR); - static void UnpackPartitionMap(const uint8_t* buf, - PartitionMap* pMap); - static void DumpPartitionMap(long block, const PartitionMap* pMap); - - static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder); - DIError OpenSubVolume(const PartitionMap* pMap); - DIError Initialize(void); - DIError FindSubVolumes(void); - - enum { - kMinInterestingBlocks = 2048, // less than this, ignore - kDDRSignature = 0x4552, // 'ER' - kPartitionSignature = 0x504d, // 'PM' - }; -}; - - -/* - * Partitioning for Joachim Lange's MicroDrive card. - * - * This DiskFS is just a container that describes the position and sizes - * of the sub-volumes. - */ -class DISKIMG_API DiskFSMicroDrive : public DiskFSContainer { -public: - DiskFSMicroDrive(void) : DiskFSContainer() {} - virtual ~DiskFSMicroDrive(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "[MicroDrive]"; } - virtual const char* GetVolumeID(void) const override { return "[MicroDrive]"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - virtual const char* GetDebugName(void) override { return "MicroDrive"; } - - struct PartitionMap; // fwd - static void UnpackPartitionMap(const uint8_t* buf, - PartitionMap* pMap); - static void DumpPartitionMap(const PartitionMap* pMap); - - static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder); - DIError OpenSubVolume(long startBlock, long numBlocks); - DIError OpenVol(int idx, long startBlock, long numBlocks); - DIError Initialize(void); - DIError FindSubVolumes(void); - - enum { - kMinInterestingBlocks = 2048, // less than this, ignore - kPartitionSignature = 0xccca, // 'JL' in little-endian high-ASCII - }; -}; - - -/* - * Partitioning for Parsons Engineering FocusDrive card. - * - * This DiskFS is just a container that describes the position and sizes - * of the sub-volumes. - */ -class DISKIMG_API DiskFSFocusDrive : public DiskFSContainer { -public: - DiskFSFocusDrive(void) : DiskFSContainer() {} - virtual ~DiskFSFocusDrive(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "[FocusDrive]"; } - virtual const char* GetVolumeID(void) const override { return "[FocusDrive]"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - virtual const char* GetDebugName(void) override { return "FocusDrive"; } - - struct PartitionMap; // fwd - static void UnpackPartitionMap(const uint8_t* buf, - const uint8_t* nameBuf, PartitionMap* pMap); - static void DumpPartitionMap(const PartitionMap* pMap); - - static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder); - DIError OpenSubVolume(long startBlock, long numBlocks, - const char* name); - DIError OpenVol(int idx, long startBlock, long numBlocks, - const char* name); - DIError Initialize(void); - DIError FindSubVolumes(void); - - enum { - kMinInterestingBlocks = 2048, // less than this, ignore - }; -}; - - -/* - * =========================================================================== - * DOS 3.2/3.3 - * =========================================================================== - */ - -class A2FileDOS; - -/* - * DOS 3.2/3.3 disk. - */ -class DISKIMG_API DiskFSDOS33 : public DiskFS { -public: - DiskFSDOS33(void) : DiskFS() { - fVTOCLoaded = false; - fDiskIsGood = false; - } - virtual ~DiskFSDOS33(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(initMode); - } - virtual DIError Format(DiskImg* pDiskImg, const char* volName) override; - - virtual const char* GetVolumeName(void) const override { return fDiskVolumeName; } - virtual const char* GetVolumeID(void) const override { return fDiskVolumeID; } - virtual const char* GetBareVolumeName(void) const override { - // this is fragile -- skip over the "DOS" part, return 3 digits - assert(strlen(fDiskVolumeName) > 3); - return fDiskVolumeName+3; - } - virtual bool GetReadWriteSupported(void) const override { return true; } - virtual bool GetFSDamaged(void) const override { return !fDiskIsGood; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override; - virtual DIError NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) override; - virtual DIError CreateFile(const CreateParms* pParms, A2File** ppNewFile) override; - virtual DIError DeleteFile(A2File* pFile) override; - virtual DIError RenameFile(A2File* pFile, const char* newName) override; - virtual DIError SetFileInfo(A2File* pFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) override; - virtual DIError RenameVolume(const char* newName) override; - - /* - * Unique to DOS 3.3 disks. - */ - int GetDiskVolumeNum(void) const { return fDiskVolumeNum; } - void SetDiskVolumeNum(int val); - - static bool IsValidFileName(const char* name); - static bool IsValidVolumeName(const char* name); - - // utility function - static void LowerASCII(uint8_t* buf, long len); - static void ReplaceFssep(char* str, char replacement); - - enum { - kMinTracks = 17, // need to put the catalog track here - kMaxTracks = 50, - kMaxCatalogSectors = 64, // two tracks on a 32-sector disk - }; - - /* a T/S pair */ - typedef struct TrackSector { - char track; - char sector; - } TrackSector; - - friend class A2FDDOS; // for Write - -private: - DIError Initialize(InitMode initMode); - DIError ReadVTOC(void); - void UpdateVolumeNum(void); - void DumpVTOC(void); - void SetSectorUsage(long track, long sector, - VolumeUsage::ChunkPurpose purpose); - void FixVolumeUsageMap(void); - DIError ReadCatalog(void); - DIError ProcessCatalogSector(int catTrack, int catSect, - const uint8_t* sctBuf); - DIError GetFileLengths(void); - DIError ComputeLength(A2FileDOS* pFile, const TrackSector* tsList, - int tsCount); - DIError TrimLastSectorUp(A2FileDOS* pFile, TrackSector lastTS); - void MarkFileUsage(A2FileDOS* pFile, TrackSector* tsList, int tsCount, - TrackSector* indexList, int indexCount); - //DIError TrimLastSectorDown(A2FileDOS* pFile, uint16_t* tsBuf, - // int maxZeroCount); - void DoNormalizePath(const char* name, char fssep, char* outBuf); - DIError MakeFileNameUnique(char* fileName); - DIError GetFreeCatalogEntry(TrackSector* pCatSect, int* pCatEntry, - uint8_t* sctBuf, A2FileDOS** ppPrevEntry); - void CreateDirEntry(uint8_t* sctBuf, int catEntry, - const char* fileName, TrackSector* pTSSect, uint8_t fileType, - int access); - void FreeTrackSectors(TrackSector* pList, int count); - - bool CheckDiskIsGood(void); - - DIError WriteDOSTracks(int sectPerTrack); - - DIError ScanVolBitmap(void); - DIError LoadVolBitmap(void); - DIError SaveVolBitmap(void); - void FreeVolBitmap(void); - DIError AllocSector(TrackSector* pTS); - DIError CreateEmptyBlockMap(bool withDOS); - bool GetSectorUseEntry(long track, int sector) const; - void SetSectorUseEntry(long track, int sector, bool inUse); - inline uint32_t GetVTOCEntry(const uint8_t* pVTOC, long track) const; - - // Largest interesting volume is 400K (50 tracks, 32 sectors), but - // we may be looking at it in 16-sector mode, so max tracks is 100. - enum { - kMaxInterestingTracks = 100, - kSectorSize = 256, - kDefaultVolumeNum = 254, - kMaxExtensionLen = 4, // used when normalizing; ".gif" is 4 - }; - - // DOS track images, for initializing disk images - static const uint8_t gDOS33Tracks[]; - static const uint8_t gDOS32Tracks[]; - - /* some fields from the VTOC */ - int fFirstCatTrack; - int fFirstCatSector; - int fVTOCVolumeNumber; - int fVTOCNumTracks; - int fVTOCNumSectors; - - /* private data */ - int fDiskVolumeNum; // usually 254 - char fDiskVolumeName[7]; // "DOS" + num, e.g. "DOS001", "DOS254" - char fDiskVolumeID[32]; // sizeof "DOS 3.3 Volume " +3 +1 - uint8_t fVTOC[kSectorSize]; - bool fVTOCLoaded; - - /* - * There are some things we need to be careful of when reading the - * catalog track, like bad links and infinite loops. By storing a list - * of known good catalog sectors, we only have to handle that stuff once. - * The catalog doesn't grow or shrink, so this never needs to be updated. - */ - TrackSector fCatalogSectors[kMaxCatalogSectors]; - - bool fDiskIsGood; -}; - -/* - * File descriptor for an open DOS file. - */ -class DISKIMG_API A2FDDOS : public A2FileDescr { -public: - A2FDDOS(A2File* pFile) : A2FileDescr(pFile) { - fTSList = NULL; - fIndexList = NULL; - fOffset = 0; - fModified = false; - } - virtual ~A2FDDOS(void) { - delete[] fTSList; - delete[] fIndexList; - //fTSList = fIndexList = NULL; - } - - //typedef DiskFSDOS33::TrackSector TrackSector; - - friend class A2FileDOS; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: - typedef DiskFSDOS33::TrackSector TrackSector; - - TrackSector* fTSList; // T/S entries for data sectors - int fTSCount; - TrackSector* fIndexList; // T/S entries for T/S list sectors - int fIndexCount; - di_off_t fOffset; // current position in file - - di_off_t fOpenEOF; // how big the file currently is - long fOpenSectorsUsed; // how many sectors it occupies - bool fModified; // if modified, update stuff on Close - - void DumpTSList(void) const; -}; - -/* - * Holds DOS files. Works for DOS33, DOS32, and "wide" DOS implementations. - * - * The embedded address and length fields found in Applesoft, Integer, and - * Binary files are quietly skipped over with the fDataOffset field when - * files are read. - * - * THOUGHT: have "get filename" and "get raw filename" interfaces? There - * are no directories, so maybe we don't care about "raw pathname"?? Might - * be better to always return the "raw" value and let the caller deal with - * things like high ASCII. - */ -class DISKIMG_API A2FileDOS : public A2File { -public: - A2FileDOS(DiskFS* pDiskFS); - virtual ~A2FileDOS(void); - - // assorted constants - enum { - kMaxFileName = 30, - }; - typedef enum { - kTypeUnknown = -1, - kTypeText = 0x00, // 'T' - kTypeInteger = 0x01, // 'I' - kTypeApplesoft = 0x02, // 'A' - kTypeBinary = 0x04, // 'B' - kTypeS = 0x08, // 'S' - kTypeReloc = 0x10, // 'R' - kTypeA = 0x20, // 'A' - kTypeB = 0x40, // 'B' - - kTypeLocked = 0x80 // bitwise OR with previous values - } FileType; - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fFileName; } - virtual char GetFssep(void) const override { return '\0'; } - virtual uint32_t GetFileType(void) const override; - virtual uint32_t GetAuxType(void) const override { return fAuxType; } - virtual uint32_t GetAccess(void) const override; - virtual time_t GetCreateWhen(void) const override { return 0; } - virtual time_t GetModWhen(void) const override { return 0; } - virtual di_off_t GetDataLength(void) const override { return fLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fSparseLength; } - virtual di_off_t GetRsrcLength(void) const override { return -1; } - virtual di_off_t GetRsrcSparseLength(void) const override { return -1; } - - virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - void Dump(void) const; - - friend class DiskFSDOS33; - friend class A2FDDOS; - -private: - typedef DiskFSDOS33::TrackSector TrackSector; - - /* - * Contents of directory entry. - * - * We don't hold deleted or unused entries, so fTSListTrack is always - * valid. - */ - short fTSListTrack; // (could use TrackSector here) - short fTSListSector; - uint16_t fLengthInSectors; - bool fLocked; - char fFileName[kMaxFileName+1]; // "fixed" version - FileType fFileType; - - TrackSector fCatTS; // track/sector for our catalog entry - int fCatEntryNum; // entry number within cat sector - - // these are computed or determined from the file contents - uint16_t fAuxType; // addr for bin, etc. - uint16_t fDataOffset; // 0/2/4, for 'A'/'B'/'I' with embedded len - di_off_t fLength; // file length, in bytes - di_off_t fSparseLength; // file length, factoring sparse out - - void FixFilename(void); - - DIError LoadTSList(TrackSector** pTSList, int* pTSCount, - TrackSector** pIndexList = NULL, int* pIndexCount = NULL); - static FileType ConvertFileType(long prodosType, di_off_t fileLen); - static bool IsValidType(long prodosType); - static void MakeDOSName(char* buf, const char* name); - static void TrimTrailingSpaces(char* filename); - - DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList, - int* pLastNonZero); - - A2FDDOS* fpOpenFile; -}; - - -/* - * =========================================================================== - * ProDOS - * =========================================================================== - */ - -class A2FileProDOS; - -/* - * ProDOS disk. - * - * THOUGHT: it would be undesirable for the CiderPress UI, but it would - * make things somewhat easier internally if we treated the volume dir - * like a subdirectory under which everything else sits, instead of special- - * casing it like we do. This is awkward because volume dirs have names - * under ProDOS, giving every pathname an extra component that they don't - * really need. We can never treat the volume dir purely as a subdir, - * because it can't expand beyond 51 files, but the storage_type in the - * header is sufficient to identify it as such (assuming the disk isn't - * broken). Certain operations, such as changing the file type or aux type, - * simply aren't possible on a volume dir, and deleting a volume dir doesn't - * make sense. So in some respects we simply trade one kind of special-case - * behavior for another. - */ -class DISKIMG_API DiskFSProDOS : public DiskFS { -public: - DiskFSProDOS(void) : fBitMapPointer(0), fTotalBlocks(0), fBlockUseMap(NULL) - {} - virtual ~DiskFSProDOS(void) { - if (fBlockUseMap != NULL) { - assert(false); // unexpected - delete[] fBlockUseMap; - } - } - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(initMode); - } - virtual DIError Format(DiskImg* pDiskImg, const char* volName) override; - virtual DIError NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) override; - virtual DIError CreateFile(const CreateParms* pParms, - A2File** ppNewFile) override; - virtual DIError DeleteFile(A2File* pFile) override; - virtual DIError RenameFile(A2File* pFile, const char* newName) override; - virtual DIError SetFileInfo(A2File* pFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) override; - virtual DIError RenameVolume(const char* newName) override; - - // assorted constants - enum { - kMaxVolumeName = 15, - }; - typedef uint32_t ProDate; - - virtual const char* GetVolumeName(void) const override { return fVolumeName; } - virtual const char* GetVolumeID(void) const override { return fVolumeID; } - virtual const char* GetBareVolumeName(void) const override { return fVolumeName; } - virtual bool GetReadWriteSupported(void) const override { return true; } - virtual bool GetFSDamaged(void) const override { return !fDiskIsGood; } - virtual long GetFSNumBlocks(void) const override { return fTotalBlocks; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override; - - //A2FileProDOS* GetVolDir(void) const { return fpVolDir; } - - static bool IsValidFileName(const char* name); - static bool IsValidVolumeName(const char* name); - static uint16_t GenerateLowerCaseBits(const char* upperName, - const char* lowerName, bool forAppleWorks); - static void GenerateLowerCaseName(const char* upperName, - char* lowerNameNoTerm, uint16_t lcFlags, bool fromAppleWorks); - - friend class A2FDProDOS; - -private: - struct DirHeader; - - enum { kMaxExtensionLen = 4 }; // used when normalizing; ".gif" is 4 - - DIError Initialize(InitMode initMode); - DIError LoadVolHeader(void); - void SetVolumeID(void); - void DumpVolHeader(void); - DIError ScanVolBitmap(void); - DIError LoadVolBitmap(void); - DIError SaveVolBitmap(void); - void FreeVolBitmap(void); - long AllocBlock(void); - int GetNumBitmapBlocks(void) const { - /* use fTotalBlocks rather than GetNumBlocks() */ - assert(fTotalBlocks > 0); - const int kBitsPerBlock = 512 * 8; - int numBlocks = (fTotalBlocks + kBitsPerBlock-1) / kBitsPerBlock; - return numBlocks; - } - DIError CreateEmptyBlockMap(void); - bool GetBlockUseEntry(long block) const; - void SetBlockUseEntry(long block, bool inUse); - bool ScanForExtraEntries(void) const; - - void SetBlockUsage(long block, VolumeUsage::ChunkPurpose purpose); - DIError GetDirHeader(const uint8_t* blkBuf, DirHeader* pHeader); - DIError RecursiveDirAdd(A2File* pParent, uint16_t dirBlock, - const char* basePath, int depth); - DIError SlurpEntries(A2File* pParent, const DirHeader* pHeader, - const uint8_t* blkBuf, bool skipFirst, int* pCount, - const char* basePath, uint16_t thisBlock, int depth); - DIError ReadExtendedInfo(A2FileProDOS* pFile); - DIError ScanFileUsage(void); - void ScanBlockList(long blockCount, uint16_t* blockList, - long indexCount, uint16_t* indexList, long* pSparseCount); - DIError ScanForSubVolumes(void); - DIError FindSubVolume(long blockStart, long blockCount, - DiskImg** ppDiskImg, DiskFS** ppDiskFS); - void MarkSubVolumeBlocks(long block, long count); - - A2File* FindFileByKeyBlock(A2File* pStart, uint16_t keyBlock); - DIError AllocInitialFileStorage(const CreateParms* pParms, - const char* upperName, uint16_t dirBlock, int dirEntrySlot, - long* pKeyBlock, int* pBlocksUsed, int* pNewEOF); - DIError WriteBootBlocks(void); - DIError DoNormalizePath(const char* path, char fssep, - char** pNormalizedPath); - void UpperCaseName(char* upperName, const char* name); - bool CheckDiskIsGood(void); - DIError AllocDirEntry(A2FileDescr* pOpenSubdir, uint8_t** ppDir, - long* pDirLen, uint8_t** ppDirEntry, uint16_t* pDirKeyBlock, - int* pDirEntrySlot, uint16_t* pDirBlock); - uint8_t* GetPrevDirEntry(uint8_t* buf, uint8_t* ptr); - DIError MakeFileNameUnique(const uint8_t* dirBuf, long dirLen, - char* fileName); - bool NameExistsInDir(const uint8_t* dirBuf, long dirLen, - const char* fileName); - - DIError FreeBlocks(long blockCount, uint16_t* blockList); - DIError RegeneratePathName(A2FileProDOS* pFile); - - /* some items from the volume header */ - char fVolumeName[kMaxVolumeName+1]; - char fVolumeID[kMaxVolumeName + 16]; // add "ProDOS /" - uint8_t fAccess; - ProDate fCreateWhen; - ProDate fModWhen; - uint16_t fBitMapPointer; - uint16_t fTotalBlocks; - //uint16_t fPrevBlock; - //uint16_t fNextBlock; - //uint8_t fVersion; - //uint8_t fMinVersion; - //uint8_t fEntryLength; - //uint8_t fEntriesPerBlock; - uint16_t fVolDirFileCount; - -// A2FileProDOS* fpVolDir; // a "fake" file entry for the volume dir - - /* - * This is a working copy of the block use map from blocks 6+. It should - * be loaded when we're about to modify files on the disk and freed - * immediately afterward. The goal is to facilitate speedy updates to the - * bitmap without creating problems if the application decides to modify - * one of the bitmap blocks directly (e.g. with the disk sector editor). - * It should never be held across calls. - */ - uint8_t* fBlockUseMap; - - /* - * Set this if the disk is "perfect". If it's not, we disallow write - * access for safety reasons. - */ - bool fDiskIsGood; - - /* set if something fixes damage so CheckDiskIsGood can't see it */ - bool fEarlyDamage; -}; - -/* - * File descriptor for an open ProDOS file. - * - * This only represents one fork. - */ -class DISKIMG_API A2FDProDOS : public A2FileDescr { -public: - A2FDProDOS(A2File* pFile) : A2FileDescr(pFile), fModified(false), - fBlockList(NULL), fOffset(0) - {} - virtual ~A2FDProDOS(void) { - delete[] fBlockList; - fBlockList = NULL; - } - - friend class A2FileProDOS; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - - void DumpBlockList(void) const; - -private: - bool IsEmptyBlock(const uint8_t* blk); - DIError WriteDirectory(const void* buf, size_t len, size_t* pActual); - - /* state for open files */ - bool fModified; - long fBlockCount; - uint16_t* fBlockList; - di_off_t fOpenEOF; // current EOF - uint16_t fOpenBlocksUsed; // #of block used by open piece - int fOpenStorageType; - bool fOpenRsrcFork; // is this the resource fork? - di_off_t fOffset; // current file offset -}; - -/* - * Holds a ProDOS file. - */ -class DISKIMG_API A2FileProDOS : public A2File { -public: - A2FileProDOS(DiskFS* pDiskFS) : A2File(pDiskFS) { - fPathName = NULL; - fSparseDataEof = fSparseRsrcEof = -1; - fpOpenFile = NULL; - fParentDirBlock = 0; - fParentDirIdx = -1; - fpParent = NULL; - } - virtual ~A2FileProDOS(void) { - delete fpOpenFile; - delete[] fPathName; - } - - typedef DiskFSProDOS::ProDate ProDate; - - /* assorted constants */ - enum { - kMaxFileName = 15, - kFssep = ':', - kInvalidBlockNum = 1, // boot block, can't be in file - kMaxBlocksPerIndex = 256, - }; - /* ProDOS access permissions */ - enum { - kAccessRead = 0x01, - kAccessWrite = 0x02, - kAccessInvisible = 0x04, - kAccessBackup = 0x20, - kAccessRename = 0x40, - kAccessDelete = 0x80 - }; - /* contents of a directory entry */ - typedef struct DirEntry { - int storageType; - char fileName[kMaxFileName+1]; // shows lower case - uint8_t fileType; - uint16_t keyPointer; - uint16_t blocksUsed; - uint32_t eof; - ProDate createWhen; - uint8_t version; - uint8_t minVersion; - uint8_t access; - uint16_t auxType; - ProDate modWhen; - uint16_t headerPointer; - } DirEntry; - typedef struct ExtendedInfo { - uint8_t storageType; - uint16_t keyBlock; - uint16_t blocksUsed; - uint32_t eof; - } ExtendedInfo; - typedef enum StorageType { - kStorageDeleted = 0, /* indicates deleted file */ - kStorageSeedling = 1, /* <= 512 bytes */ - kStorageSapling = 2, /* < 128KB */ - kStorageTree = 3, /* < 16MB */ - kStoragePascalVolume = 4, /* see ProDOS technote 25 */ - kStorageExtended = 5, /* forked */ - kStorageDirectory = 13, - kStorageSubdirHeader = 14, - kStorageVolumeDirHeader = 15, - } StorageType; - - static bool IsRegularFile(int type) { - return (type == kStorageSeedling || type == kStorageSapling || - type == kStorageTree); - } - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fDirEntry.fileName; } - virtual const char* GetPathName(void) const override { return fPathName; } - virtual char GetFssep(void) const override { return kFssep; } - virtual uint32_t GetFileType(void) const override { return fDirEntry.fileType; } - virtual uint32_t GetAuxType(void) const override { return fDirEntry.auxType; } - virtual uint32_t GetAccess(void) const override { return fDirEntry.access; } - virtual time_t GetCreateWhen(void) const override; - virtual time_t GetModWhen(void) const override; - virtual di_off_t GetDataLength(void) const override { - if (GetQuality() == kQualityDamaged) - return 0; - if (fDirEntry.storageType == kStorageExtended) - return fExtData.eof; - else - return fDirEntry.eof; - } - virtual di_off_t GetRsrcLength(void) const override { - if (fDirEntry.storageType == kStorageExtended) { - if (GetQuality() == kQualityDamaged) - return 0; - else - return fExtRsrc.eof; - } else - return -1; - } - virtual di_off_t GetDataSparseLength(void) const override { - if (GetQuality() == kQualityDamaged) - return 0; - else - return fSparseDataEof; - } - virtual di_off_t GetRsrcSparseLength(void) const override { - if (GetQuality() == kQualityDamaged) - return 0; - else - return fSparseRsrcEof; - } - virtual bool IsDirectory(void) const override { - return (fDirEntry.storageType == kStorageDirectory || - fDirEntry.storageType == kStorageVolumeDirHeader); - } - virtual bool IsVolumeDirectory(void) const override { - return (fDirEntry.storageType == kStorageVolumeDirHeader); - } - - virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - virtual void SetParent(A2File* pParent) override { fpParent = pParent; } - virtual A2File* GetParent(void) const override { return fpParent; } - - static char NameToLower(char ch); - static void InitDirEntry(DirEntry* pEntry, const uint8_t* entryBuf); - - virtual void Dump(void) const override; - - /* directory entry contents for this file */ - DirEntry fDirEntry; - - /* pointer to directory entry (update dir if file size or dates change) */ - uint16_t fParentDirBlock; // directory block - int fParentDirIdx; // index in dir block - - /* these are only valid if storageType == kStorageExtended */ - ExtendedInfo fExtData; - ExtendedInfo fExtRsrc; - - void SetPathName(const char* basePath, const char* fileName); - static time_t ConvertProDate(ProDate proDate); - static ProDate ConvertProDate(time_t unixDate); - - /* returns "true" if AppleWorks aux type is used for lower-case name */ - static bool UsesAppleWorksAuxType(uint8_t fileType) { - return (fileType >= 0x19 && fileType <= 0x1b); - } - -#if 0 - /* change fPathName; should only be used by DiskFS rename */ - void SetPathName(const char* name) { - delete[] fPathName; - if (name == NULL) { - fPathName = NULL; - } else { - fPathName = new char[strlen(name)+1]; - if (fPathName != NULL) - strcpy(fPathName, name); - } - } -#endif - - DIError LoadBlockList(int storageType, uint16_t keyBlock, - long eof, long* pBlockCount, uint16_t** pBlockList, - long* pIndexBlockCount=NULL, uint16_t** pIndexBlockList=NULL); - DIError LoadDirectoryBlockList(uint16_t keyBlock, - long eof, long* pBlockCount, uint16_t** pBlockList); - - /* fork lengths without sparseness */ - di_off_t fSparseDataEof; - di_off_t fSparseRsrcEof; - -private: - DIError LoadIndexBlock(uint16_t block, uint16_t* list, - int maxCount); - DIError ValidateBlockList(const uint16_t* list, long count); - - char* fPathName; // full pathname to file on this volume - - A2FDProDOS* fpOpenFile; // only one fork can be open at a time - A2File* fpParent; -}; - - -/* - * =========================================================================== - * Pascal - * =========================================================================== - */ - -/* - * Pascal disk. - * - * There is no allocation map or file index blocks, just a linear collection - * of files with contiguous blocks. - */ -class A2FilePascal; - -class DISKIMG_API DiskFSPascal : public DiskFS { -public: - DiskFSPascal(void) : fDirectory(NULL) {} - virtual ~DiskFSPascal(void) { - if (fDirectory != NULL) { - assert(false); // unexpected - delete[] fDirectory; - } - } - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - virtual DIError Format(DiskImg* pDiskImg, const char* volName) override; - - // assorted constants - enum { - kMaxVolumeName = 7, - kDirectoryEntryLen = 26, - }; - typedef uint16_t PascalDate; - - virtual const char* GetVolumeName(void) const override { return fVolumeName; } - virtual const char* GetVolumeID(void) const override { return fVolumeID; } - virtual const char* GetBareVolumeName(void) const override { return fVolumeName; } - virtual bool GetReadWriteSupported(void) const override { return true; } - virtual bool GetFSDamaged(void) const override { return !fDiskIsGood; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override; - virtual DIError NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) override; - virtual DIError CreateFile(const CreateParms* pParms, A2File** ppNewFile) override; - virtual DIError DeleteFile(A2File* pFile) override; - virtual DIError RenameFile(A2File* pFile, const char* newName) override; - virtual DIError SetFileInfo(A2File* pFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) override; - virtual DIError RenameVolume(const char* newName) override; - - static bool IsValidVolumeName(const char* name); - static bool IsValidFileName(const char* name); - - uint16_t GetTotalBlocks(void) const { return fTotalBlocks; } - - friend class A2FDPascal; - -private: - DIError Initialize(void); - DIError LoadVolHeader(void); - void SetVolumeID(void); - void DumpVolHeader(void); - DIError LoadCatalog(void); - DIError SaveCatalog(void); - void FreeCatalog(void); - DIError ProcessCatalog(void); - DIError ScanFileUsage(void); - void SetBlockUsage(long block, VolumeUsage::ChunkPurpose purpose); - DIError WriteBootBlocks(void); - bool CheckDiskIsGood(void); - void DoNormalizePath(const char* name, char fssep, char* outBuf); - DIError MakeFileNameUnique(char* fileName); - DIError FindLargestFreeArea(int *pPrevIdx, A2FilePascal** ppPrevFile); - uint8_t* FindDirEntry(A2FilePascal* pFile); - - enum { kMaxExtensionLen = 5 }; // used when normalizing; ".code" is 4 - - /* some items from the volume header */ - uint16_t fStartBlock; // first block of dir hdr; always 2 - uint16_t fNextBlock; // i.e. first block with data - char fVolumeName[kMaxVolumeName+1]; - char fVolumeID[kMaxVolumeName + 16]; // add "Pascal ___:" - uint16_t fTotalBlocks; - uint16_t fNumFiles; - PascalDate fAccessWhen; // PascalDate last access - PascalDate fDateSetWhen; // PascalDate last date setting - uint16_t fStuff1; // - uint16_t fStuff2; // - - /* other goodies */ - bool fDiskIsGood; - bool fEarlyDamage; - - /* - * Pascal disks have one fixed-size directory. The contents aren't - * divided into blocks, which means you can't always edit an entry - * by loading a single block from disk and writing it back. Also, - * deleted entries are squeezed out, so if we delete an entry we - * have to reshuffle the entries below it. - * - * We want to keep the copy on disk synced up, so we don't hold on - * to this longer than necessary. Possibly less efficient that way; - * if it becomes a problem it's easy enough to change the behavior. - */ - uint8_t* fDirectory; -}; - -/* - * File descriptor for an open Pascal file. - */ -class DISKIMG_API A2FDPascal : public A2FileDescr { -public: - A2FDPascal(A2File* pFile) : A2FileDescr(pFile) { - fOffset = 0; - } - virtual ~A2FDPascal(void) { - /* nothing to clean up */ - } - - friend class A2FilePascal; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: - di_off_t fOffset; // where we are - di_off_t fOpenEOF; // how big the file currently is - long fOpenBlocksUsed; // how many blocks it occupies - bool fModified; // if modified, update dir on Close -}; - -/* - * File on a Pascal disk. - */ -class DISKIMG_API A2FilePascal : public A2File { -public: - A2FilePascal(DiskFS* pDiskFS) : A2File(pDiskFS) { - fpOpenFile = NULL; - } - virtual ~A2FilePascal(void) { - /* this comes back and calls CloseDescr */ - if (fpOpenFile != NULL) - fpOpenFile->Close(); - } - - typedef DiskFSPascal::PascalDate PascalDate; - - // assorted constants - enum { - kMaxFileName = 15, - }; - typedef enum FileType { - kTypeUntyped = 0, // NON - kTypeXdsk = 1, // BAD (bad blocks) - kTypeCode = 2, // PCD - kTypeText = 3, // PTX - kTypeInfo = 4, // ? - kTypeData = 5, // PDA - kTypeGraf = 6, // ? - kTypeFoto = 7, // FOT? (hires image) - kTypeSecurdir = 8 // ?? - } FileType; - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fFileName; } - virtual char GetFssep(void) const override { return '\0'; } - virtual uint32_t GetFileType(void) const override; - virtual uint32_t GetAuxType(void) const override { return 0; } - virtual uint32_t GetAccess(void) const override { return DiskFS::kFileAccessUnlocked; } - virtual time_t GetCreateWhen(void) const override { return 0; } - virtual time_t GetModWhen(void) const override; - virtual di_off_t GetDataLength(void) const override { return fLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fLength; } - virtual di_off_t GetRsrcLength(void) const override { return -1; } - virtual di_off_t GetRsrcSparseLength(void) const override { return -1; } - - virtual DIError Open(A2FileDescr** pOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - - virtual void Dump(void) const override; - - static time_t ConvertPascalDate(PascalDate pascalDate); - static A2FilePascal::PascalDate ConvertPascalDate(time_t unixDate); - static A2FilePascal::FileType ConvertFileType(long prodosType); - - /* fields pulled out of directory block */ - uint16_t fStartBlock; - uint16_t fNextBlock; - FileType fFileType; - char fFileName[kMaxFileName+1]; - uint16_t fBytesRemaining; - PascalDate fModWhen; - - /* derived fields */ - di_off_t fLength; - - /* note to self: don't try to store a directory offset here; they shift - every time you add or delete a file */ - -private: - A2FileDescr* fpOpenFile; -}; - - -/* - * =========================================================================== - * CP/M - * =========================================================================== - */ - -/* - * CP/M disk. - * - * We really ought to be using 1K blocks here, since that's the native - * CP/M format, but there's little value in making an exception for such - * a rarely used Apple II format. - * - * There is no allocation map or file index blocks, just a single 2K - * directory filled with files that have up to 16 1K blocks each. If - * a file is longer than 16K, a second entry with the identical name - * and user number is made. These "extents" may be sparse, so it's - * necessary to use the "records" field to determine the actual file length. - */ -class A2FileCPM; -class DISKIMG_API DiskFSCPM : public DiskFS { -public: - DiskFSCPM(void) : fDiskIsGood(false) {} - virtual ~DiskFSCPM(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return "CP/M"; } - virtual const char* GetVolumeID(void) const override { return "CP/M"; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return !fDiskIsGood; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - - // assorted constants - enum { - kDirectoryEntryLen = 32, - kVolDirBlock = 24, // ProDOS block where volume dir starts - kDirFileNameLen = 11, // 8+3 without the '.' - kFullDirSize = 2048, // blocks 0 and 1 - kDirEntryBlockCount = 16, // #of blocks held in dir slot - kNumDirEntries = kFullDirSize/kDirectoryEntryLen, - kExtentsInLowByte = 32, - - kDirEntryFlagContinued = 0x8000, // "flags" word - }; - - // Contents of the raw 32-byte directory entry. - // - // From http://www.seasip.demon.co.uk/Cpm/format31.html - // - // UU F1 F2 F3 F4 F5 F6 F7 F8 T1 T2 T3 EX S1 S2 RC .FILENAMETYP.... - // AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL AL ................ - // - // If the high bit of T1 is set, the file is read-only. If the high - // bit of T2 is set, the file is a "system" file. - // - // An entry with UU=0x20 indicates a CP/M 3.1 disk label entry. - // An entry with UU=0x21 indicates a time stamp entry (2.x or 3.x). - // - // Files larger than (1024 * 16) have multiple "extent" entries, i.e. - // entries with the same user number and file name. - typedef struct DirEntry { - uint8_t userNumber; // 0-15 or 0-31 (usually 0), e5=unused - uint8_t fileName[kDirFileNameLen+1]; - uint16_t extent; // extent (EX + S2 * 32) - uint8_t S1; // Last Record Byte Count (app-specific) - uint8_t records; // #of 128-byte records in this extent - uint8_t blocks[kDirEntryBlockCount]; - bool readOnly; - bool system; - bool badBlockList; // set if block list is damaged - } DirEntry; - - static long CPMToProDOSBlock(long cpmBlock) { - return kVolDirBlock + (cpmBlock*2); - } - -private: - DIError Initialize(void); - DIError ReadCatalog(void); - DIError ScanFileUsage(void); - void SetBlockUsage(long block, VolumeUsage::ChunkPurpose purpose); - void FormatName(char* dstBuf, const char* srcBuf); - DIError ComputeLength(A2FileCPM* pFile); - bool CheckDiskIsGood(void); - - // the full set of raw dir entries - DirEntry fDirEntry[kNumDirEntries]; - - bool fDiskIsGood; -}; - -/* - * File descriptor for an open CP/M file. - */ -class DISKIMG_API A2FDCPM : public A2FileDescr { -public: - A2FDCPM(A2File* pFile) : A2FileDescr(pFile) { - //fOpen = false; - fBlockList = NULL; - } - virtual ~A2FDCPM(void) { - delete fBlockList; - fBlockList = NULL; - } - - friend class A2FileCPM; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: - //bool fOpen; - di_off_t fOffset; - long fBlockCount; - uint8_t* fBlockList; -}; - -/* - * File on a CP/M disk. - */ -class DISKIMG_API A2FileCPM : public A2File { -public: - typedef DiskFSCPM::DirEntry DirEntry; - - A2FileCPM(DiskFS* pDiskFS, DirEntry* pDirEntry) : - A2File(pDiskFS), fpDirEntry(pDirEntry) - { - fDirIdx = -1; - fpOpenFile = NULL; - } - virtual ~A2FileCPM(void) { - delete fpOpenFile; - } - - // assorted constants - enum { - kMaxFileName = 12, // 8+3 including '.' - }; - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fFileName; } - virtual char GetFssep(void) const override { return '\0'; } - virtual uint32_t GetFileType(void) const override { return 0; } - virtual uint32_t GetAuxType(void) const override { return 0; } - virtual uint32_t GetAccess(void) const override { - if (fReadOnly) - return DiskFS::kFileAccessLocked; - else - return DiskFS::kFileAccessUnlocked; - } - virtual time_t GetCreateWhen(void) const override { return 0; } - virtual time_t GetModWhen(void) const override { return 0; } - virtual di_off_t GetDataLength(void) const override { return fLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fLength; } - virtual di_off_t GetRsrcLength(void) const override { return -1; } - virtual di_off_t GetRsrcSparseLength(void) const override { return -1; } - - virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - virtual void Dump(void) const override; - - /* fields pulled out of directory block */ - char fFileName[kMaxFileName+1]; - bool fReadOnly; - - /* derived fields */ - di_off_t fLength; - int fDirIdx; // index into fDirEntry for part #1 - - DIError GetBlockList(long* pBlockCount, uint8_t* blockBuf) const; - -private: - const DirEntry* fpDirEntry; - A2FileDescr* fpOpenFile; -}; - - -/* - * =========================================================================== - * RDOS - * =========================================================================== - */ - -/* - * RDOS disk. - * - * There is no allocation map or file index blocks, just a linear collection - * of files with contiguous sectors. Very similar to Pascal. - * - * The one interesting quirk is the "converted 13-sector disk" format, where - * only 13 of 16 sectors are actually used. The linear sector addressing - * must take that into account. - */ -class A2FileRDOS; -class DISKIMG_API DiskFSRDOS : public DiskFS { -public: - DiskFSRDOS(void) {} - virtual ~DiskFSRDOS(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - virtual const char* GetVolumeName(void) const override { return fVolumeName; } - virtual const char* GetVolumeID(void) const override { return fVolumeName; } - virtual const char* GetBareVolumeName(void) const override { return NULL; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - - int GetOurSectPerTrack(void) const { return fOurSectPerTrack; } - -private: - static DIError TestCommon(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - FSLeniency leniency, DiskImg::FSFormat* pFormatFound); - - DIError Initialize(void); - DIError ReadCatalog(void); - DIError ScanFileUsage(void); - void SetSectorUsage(long track, long sector, - VolumeUsage::ChunkPurpose purpose); - - char fVolumeName[10]; // e.g. "RDOS 3.3" - int fOurSectPerTrack; -}; - -/* - * File descriptor for an open RDOS file. - */ -class DISKIMG_API A2FDRDOS : public A2FileDescr { -public: - A2FDRDOS(A2File* pFile) : A2FileDescr(pFile) { - fOffset = 0; - } - virtual ~A2FDRDOS(void) { - /* nothing to clean up */ - } - - friend class A2FileRDOS; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: - /* RDOS is unique in that it can put 13-sector disks on 16-sector tracks */ - inline int GetOurSectPerTrack(void) const { - DiskFSRDOS* pDiskFS = (DiskFSRDOS*) fpFile->GetDiskFS(); - return pDiskFS->GetOurSectPerTrack(); - } - - //bool fOpen; - di_off_t fOffset; -}; - -/* - * File on an RDOS disk. - */ -class DISKIMG_API A2FileRDOS : public A2File { -public: - A2FileRDOS(DiskFS* pDiskFS) : A2File(pDiskFS) { - //fOpen = false; - fpOpenFile = NULL; - } - virtual ~A2FileRDOS(void) { - delete fpOpenFile; - } - - // assorted constants - enum { - kMaxFileName = 24, - }; - typedef enum FileType { - kTypeUnknown = 0, - kTypeApplesoft, // 'A' - kTypeBinary, // 'B' - kTypeText, // 'T' - } FileType; - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fFileName; } - virtual char GetFssep(void) const override { return '\0'; } - virtual uint32_t GetFileType(void) const override; - virtual uint32_t GetAuxType(void) const override { return fLoadAddr; } - virtual uint32_t GetAccess(void) const override { return DiskFS::kFileAccessUnlocked; } - virtual time_t GetCreateWhen(void) const override { return 0; } - virtual time_t GetModWhen(void) const override { return 0; }; - virtual di_off_t GetDataLength(void) const override { return fLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fLength; } - virtual di_off_t GetRsrcLength(void) const override { return -1; } - virtual di_off_t GetRsrcSparseLength(void) const override { return -1; } - - virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - void FixFilename(void); - virtual void Dump(void) const override; - - /* fields pulled out of directory block */ - char fFileName[kMaxFileName+1]; - FileType fFileType; - uint16_t fNumSectors; - uint16_t fLoadAddr; - uint16_t fLength; - uint16_t fStartSector; - -private: - void TrimTrailingSpaces(char* filename); - - A2FileDescr* fpOpenFile; -}; - - -/* - * =========================================================================== - * HFS - * =========================================================================== - */ - -/* - * HFS disk. - */ -class A2FileHFS; -class DISKIMG_API DiskFSHFS : public DiskFS { -public: - DiskFSHFS(void) { - fLocalTimeOffset = -1; - fDiskIsGood = true; -#ifndef EXCISE_GPL_CODE - fHfsVol = NULL; -#endif - } - virtual ~DiskFSHFS(void) { -#ifndef EXCISE_GPL_CODE - hfs_callback_close(fHfsVol); - fHfsVol = (hfsvol*) 0xcdaaaacd; -#endif - } - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(initMode); - } - -#ifndef EXCISE_GPL_CODE - /* these are optional, defined as no-ops in the parent class */ - virtual DIError Format(DiskImg* pDiskImg, const char* volName) override; - virtual DIError NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) override; - virtual DIError CreateFile(const CreateParms* pParms, A2File** ppNewFile) override; - virtual DIError DeleteFile(A2File* pFile) override; - virtual DIError RenameFile(A2File* pFile, const char* newName) override; - virtual DIError SetFileInfo(A2File* pFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) override; - virtual DIError RenameVolume(const char* newName); -#endif - - // assorted constants - enum { - kMaxVolumeName = 27, - kMaxExtensionLen = 4, // used when normalizing; ".gif" is 4 - }; - - /* mandatory functions */ - virtual const char* GetVolumeName(void) const override { return fVolumeName; } - virtual const char* GetVolumeID(void) const override { return fVolumeID; } - virtual const char* GetBareVolumeName(void) const override { return fVolumeName; } - virtual bool GetReadWriteSupported(void) const override { return true; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual long GetFSNumBlocks(void) const override { return fTotalBlocks; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override; - -#ifndef EXCISE_GPL_CODE - hfsvol* GetHfsVol(void) const { return fHfsVol; } -#endif - - // utility function, used by app - static bool IsValidVolumeName(const char* name); - static bool IsValidFileName(const char* name); - -private: - enum { - // Macintosh 32-bit dates start in 1904, everybody else starts in - // 1970. Take the Mac date and adjust it 66 years plus 17 leap days. - // The annoying part is that HFS stores dates in local time, which - // means it's impossible to know absolutely when a file was modified. - // libhfs converts timestamps to the current time zone, so that a - // file written January 1st 2006 at 6pm in London will appear to have - // been written January 1st 2006 at 6pm in San Francisco if you - // happen to be sitting in California. - // - // This was fixed in HFS+, but we have to deal with it for now. The - // value below converts the date to local time in Greenwich; the - // current GMT offset and daylight saving time must be added to it. - // - // Curiously, the volume dates shown by Cmd-I on the volume on my - // Quadra are off by an hour, even though the file dates match. - kDateTimeOffset = (1970 - 1904) * 60 * 60 * 24 * 365 + - (60 * 60 * 24 * 17), - - kExpectedMinBlocks = 1440, // ignore volumes under 720K - }; - - struct MasterDirBlock; // fwd - static void UnpackMDB(const uint8_t* buf, MasterDirBlock* pMDB); - static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder); - - DIError Initialize(InitMode initMode); - DIError LoadVolHeader(void); - void SetVolumeID(void); - void DumpVolHeader(void); - void SetVolumeUsageMap(void); - -#ifdef EXCISE_GPL_CODE - void CreateFakeFile(void); -#else - DIError RecursiveDirAdd(A2File* pParent, const char* basePath, int depth); - //void Sanitize(uint8_t* str); - DIError DoNormalizePath(const char* path, char fssep, - char** pNormalizedPath); - static int CompareMacFileNames(const char* str1, const char* str2); - DIError RegeneratePathName(A2FileHFS* pFile); - DIError MakeFileNameUnique(const char* pathName, char** pUniqueName); - - /* libhfs stuff */ - static unsigned long LibHFSCB(void* vThis, int op, unsigned long arg1, - void* arg2); - hfsvol* fHfsVol; -#endif - - - /* some items from the volume header */ - char fVolumeName[kMaxVolumeName+1]; - char fVolumeID[kMaxVolumeName + 8]; // add "HFS :" - uint32_t fTotalBlocks; - uint32_t fAllocationBlockSize; - uint32_t fNumAllocationBlocks; - uint32_t fCreatedDateTime; - uint32_t fModifiedDateTime; - uint32_t fNumFiles; - uint32_t fNumDirectories; - - long fLocalTimeOffset; - bool fDiskIsGood; -}; - -/* - * File descriptor for an open HFS file. - */ -class DISKIMG_API A2FDHFS : public A2FileDescr { -public: -#ifdef EXCISE_GPL_CODE - A2FDHFS(A2File* pFile, void* unused) - : A2FileDescr(pFile), fOffset(0) - {} -#else - A2FDHFS(A2File* pFile, hfsfile* pHfsFile) - : A2FileDescr(pFile), fHfsFile(pHfsFile), fModified(false) - {} -#endif - virtual ~A2FDHFS(void) { -#ifndef EXCISE_GPL_CODE - if (fHfsFile != NULL) - hfs_close(fHfsFile); -#endif - } - - friend class A2FileHFS; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: -#ifdef EXCISE_GPL_CODE - di_off_t fOffset; -#else - hfsfile* fHfsFile; - bool fModified; -#endif -}; - -/* - * File on an HFS disk. - */ -class DISKIMG_API A2FileHFS : public A2File { -public: - A2FileHFS(DiskFS* pDiskFS) : A2File(pDiskFS) { - fPathName = NULL; - fpOpenFile = NULL; -#ifdef EXCISE_GPL_CODE - fFakeFileBuf = NULL; -#else - //fOrigPathName = NULL; -#endif - } - virtual ~A2FileHFS(void) { - delete fpOpenFile; - delete[] fPathName; -#ifdef EXCISE_GPL_CODE - delete[] fFakeFileBuf; -#else - //delete[] fOrigPathName; -#endif - } - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fPathName; } - virtual char GetFssep(void) const override { return kFssep; } - virtual uint32_t GetFileType(void) const override; - virtual uint32_t GetAuxType(void) const override; - virtual uint32_t GetAccess(void) const override { return fAccess; } - virtual time_t GetCreateWhen(void) const override { return fCreateWhen; } - virtual time_t GetModWhen(void) const override { return fModWhen; } - virtual di_off_t GetDataLength(void) const override { return fDataLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fDataLength; } - virtual di_off_t GetRsrcLength(void) const override { return fRsrcLength; } - virtual di_off_t GetRsrcSparseLength(void) const override { return fRsrcLength; } - virtual bool IsDirectory(void) const override { return fIsDir; } - virtual bool IsVolumeDirectory(void) const override { return fIsVolumeDir; } - - virtual DIError Open(A2FileDescr** pOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - enum { - kMaxFileName = 31, - kFssep = ':', - kPdosType = 0x70646f73, // 'pdos' - }; - - void SetPathName(const char* basePath, const char* fileName); - virtual void Dump(void) const override; - -#ifdef EXCISE_GPL_CODE - void SetFakeFile(void* buf, long len) { - assert(len > 0); - if (fFakeFileBuf != NULL) - delete[] fFakeFileBuf; - fFakeFileBuf = new char[len]; - memcpy(fFakeFileBuf, buf, len); - fDataLength = len; - } - const void* GetFakeFileBuf(void) const { return fFakeFileBuf; } -#else - void InitEntry(const hfsdirent* dirEntry); - void SetOrigPathName(const char* pathName); - virtual void SetParent(A2File* pParent) override { fpParent = pParent; } - virtual A2File* GetParent(void) const override { return fpParent; } - char* GetLibHFSPathName(void) const; - static void ConvertTypeToHFS(uint32_t fileType, uint32_t auxType, - char* pType, char* pCreator); -#endif - - bool fIsDir; - bool fIsVolumeDir; - uint32_t fType; - uint32_t fCreator; - char fFileName[kMaxFileName+1]; - char* fPathName; - di_off_t fDataLength; - di_off_t fRsrcLength; - time_t fCreateWhen; - time_t fModWhen; - uint32_t fAccess; - -private: -#ifdef EXCISE_GPL_CODE - char* fFakeFileBuf; -#else - //char* fOrigPathName; - A2File* fpParent; -#endif - A2FileDescr* fpOpenFile; // only one fork can be open at a time -}; - - -/* - * =========================================================================== - * Gutenberg - * =========================================================================== - */ - -class A2FileGutenberg; - -/* - * Gutenberg disk. - */ -class DISKIMG_API DiskFSGutenberg : public DiskFS { -public: - DiskFSGutenberg(void) : DiskFS() { - fVTOCLoaded = false; - fDiskIsGood = false; - } - virtual ~DiskFSGutenberg(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(initMode); - } - - virtual const char* GetVolumeName(void) const override { return fDiskVolumeName; } - virtual const char* GetVolumeID(void) const override { return fDiskVolumeID; } - virtual const char* GetBareVolumeName(void) const override { - return fDiskVolumeName; - } - virtual bool GetReadWriteSupported(void) const override { return true; } - virtual bool GetFSDamaged(void) const override { return !fDiskIsGood; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override; - - static bool IsValidFileName(const char* name); - static bool IsValidVolumeName(const char* name); - - // utility function - static void LowerASCII(uint8_t* buf, long len); - static void ReplaceFssep(char* str, char replacement); - - enum { - kMinTracks = 17, // need to put the catalog track here - kMaxTracks = 50, - kMaxCatalogSectors = 64, // two tracks on a 32-sector disk - }; - - /* a T/S pair */ - typedef struct TrackSector { - char track; - char sector; - } TrackSector; - - friend class A2FDGutenberg; // for Write - -private: - DIError Initialize(InitMode initMode); - DIError ReadVTOC(void); - void UpdateVolumeNum(void); - void DumpVTOC(void); - void SetSectorUsage(long track, long sector, - VolumeUsage::ChunkPurpose purpose); - void FixVolumeUsageMap(void); - DIError ReadCatalog(void); - DIError ProcessCatalogSector(int catTrack, int catSect, - const uint8_t* sctBuf); - DIError GetFileLengths(void); - DIError ComputeLength(A2FileGutenberg* pFile, const TrackSector* tsList, - int tsCount); - DIError TrimLastSectorUp(A2FileGutenberg* pFile, TrackSector lastTS); - void MarkFileUsage(A2FileGutenberg* pFile, TrackSector* tsList, int tsCount, - TrackSector* indexList, int indexCount); - DIError MakeFileNameUnique(char* fileName); - DIError GetFreeCatalogEntry(TrackSector* pCatSect, int* pCatEntry, - uint8_t* sctBuf, A2FileGutenberg** ppPrevEntry); - void CreateDirEntry(uint8_t* sctBuf, int catEntry, - const char* fileName, TrackSector* pTSSect, uint8_t fileType, - int access); - void FreeTrackSectors(TrackSector* pList, int count); - - bool CheckDiskIsGood(void); - - DIError WriteDOSTracks(int sectPerTrack); - - DIError ScanVolBitmap(void); - DIError LoadVolBitmap(void); - DIError SaveVolBitmap(void); - void FreeVolBitmap(void); - DIError AllocSector(TrackSector* pTS); - DIError CreateEmptyBlockMap(bool withDOS); - bool GetSectorUseEntry(long track, int sector) const; - void SetSectorUseEntry(long track, int sector, bool inUse); - inline uint32_t GetVTOCEntry(const uint8_t* pVTOC, long track) const; - - // Largest interesting volume is 400K (50 tracks, 32 sectors), but - // we may be looking at it in 16-sector mode, so max tracks is 100. - enum { - kMaxInterestingTracks = 100, - kSectorSize = 256, - kDefaultVolumeNum = 254, - kMaxExtensionLen = 4, // used when normalizing; ".gif" is 4 - }; - - /* some fields from the VTOC */ - int fFirstCatTrack; - int fFirstCatSector; - int fVTOCVolumeNumber; - int fVTOCNumTracks; - int fVTOCNumSectors; - - /* private data */ - char fDiskVolumeName[10]; // - char fDiskVolumeID[11+12+1]; // sizeof "Gutenberg: " + 12 + null - uint8_t fVTOC[kSectorSize]; - bool fVTOCLoaded; - - /* - * There are some things we need to be careful of when reading the - * catalog track, like bad links and infinite loops. By storing a list - * of known good catalog sectors, we only have to handle that stuff once. - * The catalog doesn't grow or shrink, so this never needs to be updated. - */ - TrackSector fCatalogSectors[kMaxCatalogSectors]; - - bool fDiskIsGood; -}; - -/* - * File descriptor for an open Gutenberg file. - */ -class DISKIMG_API A2FDGutenberg : public A2FileDescr { -public: - A2FDGutenberg(A2File* pFile) : A2FileDescr(pFile) { - fOffset = 0; - fModified = false; - } - virtual ~A2FDGutenberg(void) { - } - - friend class A2FileGutenberg; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: - typedef DiskFSGutenberg::TrackSector TrackSector; - - int fTSCount; - di_off_t fOffset; // current position in file - - di_off_t fOpenEOF; // how big the file currently is - long fOpenSectorsUsed; // how many sectors it occupies - bool fModified; // if modified, update stuff on Close - - void DumpTSList(void) const; -}; - -/* - * Holds Gutenberg files. - * - */ -class DISKIMG_API A2FileGutenberg : public A2File { -public: - A2FileGutenberg(DiskFS* pDiskFS); - virtual ~A2FileGutenberg(void); - - // assorted constants - enum { - kMaxFileName = 12, - }; - typedef enum { - kTypeText = 0x00, // 'T' - } FileType; - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fFileName; } - virtual char GetFssep(void) const override { return '\0'; } - virtual uint32_t GetFileType(void) const override; - virtual uint32_t GetAuxType(void) const override { return fAuxType; } - virtual uint32_t GetAccess(void) const override { return DiskFS::kFileAccessUnlocked; } - virtual time_t GetCreateWhen(void) const override { return 0; } - virtual time_t GetModWhen(void) const override { return 0; } - virtual di_off_t GetDataLength(void) const override { return fLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fSparseLength; } - virtual di_off_t GetRsrcLength(void) const override { return -1; } - virtual di_off_t GetRsrcSparseLength(void) const override { return -1; } - - virtual DIError Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - void Dump(void) const; - - typedef DiskFSGutenberg::TrackSector TrackSector; - - /* - * Contents of directory entry. - * - * We don't hold deleted or unused entries, so fTSListTrack is always - * valid. - */ - short fTrack; // (could use TrackSector here) - short fSector; - uint16_t fLengthInSectors; - bool fLocked; - char fFileName[kMaxFileName+1]; // "fixed" version - FileType fFileType; - - TrackSector fCatTS; // track/sector for our catalog entry - int fCatEntryNum; // entry number within cat sector - - // these are computed or determined from the file contents - uint16_t fAuxType; // addr for bin, etc. - short fDataOffset; // for 'A'/'B'/'I' with embedded len - di_off_t fLength; // file length, in bytes - di_off_t fSparseLength; // file length, factoring sparse out - - void FixFilename(void); - - static FileType ConvertFileType(long prodosType, di_off_t fileLen); - static bool IsValidType(long prodosType); - static void MakeDOSName(char* buf, const char* name); - static void TrimTrailingSpaces(char* filename); - -private: - DIError ExtractTSPairs(const uint8_t* sctBuf, TrackSector* tsList, - int* pLastNonZero); - - A2FDGutenberg* fpOpenFile; -}; - - -/* - * =========================================================================== - * FAT (including FAT12, FAT16, and FAT32) - * =========================================================================== - */ - -/* - * MS-DOS FAT disk. - * - * This is currently just the minimum necessary to properly recognize - * the disk. - */ -class A2FileFAT; -class DISKIMG_API DiskFSFAT : public DiskFS { -public: - DiskFSFAT(void) {} - virtual ~DiskFSFAT(void) {} - - static DIError TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency); - - virtual DIError Initialize(DiskImg* pImg, InitMode initMode) override { - SetDiskImg(pImg); - return Initialize(); - } - - // assorted constants - enum { - kMaxVolumeName = 11, - }; - - virtual const char* GetVolumeName(void) const override { return fVolumeName; } - virtual const char* GetVolumeID(void) const override { return fVolumeID; } - virtual const char* GetBareVolumeName(void) const override { return fVolumeName; } - virtual bool GetReadWriteSupported(void) const override { return false; } - virtual bool GetFSDamaged(void) const override { return false; } - virtual long GetFSNumBlocks(void) const override { return fTotalBlocks; } - virtual DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const override - { return kDIErrNotSupported; } - -private: - enum { - kExpectedMinBlocks = 720, // ignore volumes under 360K - }; - - struct MasterBootRecord; // fwd - struct BootSector; - static bool UnpackMBR(const uint8_t* buf, MasterBootRecord* pOut); - static bool UnpackBootSector(const uint8_t* buf, BootSector* pOut); - static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder); - - DIError Initialize(void); - DIError LoadVolHeader(void); - void DumpVolHeader(void); - void SetVolumeUsageMap(void); - void CreateFakeFile(void); - - /* some items from the volume header */ - char fVolumeName[kMaxVolumeName+1]; - char fVolumeID[kMaxVolumeName + 8]; // add "FAT %s:" - uint32_t fTotalBlocks; -}; - -/* - * File descriptor for an open FAT file. - */ -class DISKIMG_API A2FDFAT : public A2FileDescr { -public: - A2FDFAT(A2File* pFile) : A2FileDescr(pFile) { - fOffset = 0; - } - virtual ~A2FDFAT(void) { - /* nothing to clean up */ - } - - friend class A2FileFAT; - - virtual DIError Read(void* buf, size_t len, size_t* pActual = NULL) override; - virtual DIError Write(const void* buf, size_t len, - size_t* pActual = NULL) override; - virtual DIError Seek(di_off_t offset, DIWhence whence) override; - virtual di_off_t Tell(void) override; - virtual DIError Close(void) override; - - virtual long GetSectorCount(void) const override; - virtual long GetBlockCount(void) const override; - virtual DIError GetStorage(long sectorIdx, long* pTrack, - long* pSector) const override; - virtual DIError GetStorage(long blockIdx, long* pBlock) const override; - -private: - di_off_t fOffset; -}; - -/* - * File on a FAT disk. - */ -class DISKIMG_API A2FileFAT : public A2File { -public: - A2FileFAT(DiskFS* pDiskFS) : A2File(pDiskFS) { - fFakeFileBuf = NULL; - //fFakeFileLen = -1; - fpOpenFile = NULL; - } - virtual ~A2FileFAT(void) { - delete fpOpenFile; - delete[] fFakeFileBuf; - } - - /* - * Implementations of standard interfaces. - */ - virtual const char* GetFileName(void) const override { return fFileName; } - virtual const char* GetPathName(void) const override { return fFileName; } - virtual char GetFssep(void) const override { return '\0'; } - virtual uint32_t GetFileType(void) const override { return 0; }; - virtual uint32_t GetAuxType(void) const override { return 0; } - virtual uint32_t GetAccess(void) const override { return DiskFS::kFileAccessUnlocked; } - virtual time_t GetCreateWhen(void) const override { return 0; } - virtual time_t GetModWhen(void) const override { return 0; } - virtual di_off_t GetDataLength(void) const override { return fLength; } - virtual di_off_t GetDataSparseLength(void) const override { return fLength; } - virtual di_off_t GetRsrcLength(void) const override { return -1; } - virtual di_off_t GetRsrcSparseLength(void) const override { return -1; } - - virtual DIError Open(A2FileDescr** pOpenFile, bool readOnly, - bool rsrcFork = false) override; - virtual void CloseDescr(A2FileDescr* pOpenFile) override { - assert(pOpenFile == fpOpenFile); - delete fpOpenFile; - fpOpenFile = NULL; - } - virtual bool IsFileOpen(void) const override { return fpOpenFile != NULL; } - - enum { kMaxFileName = 31 }; - - virtual void Dump(void) const override; - - void SetFakeFile(void* buf, long len) { - assert(len > 0); - if (fFakeFileBuf != NULL) - delete[] fFakeFileBuf; - fFakeFileBuf = new char[len]; - memcpy(fFakeFileBuf, buf, len); - fLength = len; - } - const void* GetFakeFileBuf(void) const { return fFakeFileBuf; } - - char fFileName[kMaxFileName+1]; - di_off_t fLength; - -private: - char* fFakeFileBuf; - //long fFakeFileLen; - A2FileDescr* fpOpenFile; -}; - -} // namespace DiskImgLib - -#endif /*DISKIMG_DISKIMGDETAIL_H*/ diff --git a/ciderpress/diskimg/DiskImgPriv.h b/ciderpress/diskimg/DiskImgPriv.h deleted file mode 100644 index a506ab0..0000000 --- a/ciderpress/diskimg/DiskImgPriv.h +++ /dev/null @@ -1,348 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Declarations common within but private to the DiskImg library. - * - * External code should not include this. - */ -#ifndef DISKIMG_DISKIMGPRIV_H -#define DISKIMG_DISKIMGPRIV_H - -#include "DiskImgDetail.h" -#include -#include -// "GenericFD.h" included at end - -using namespace DiskImgLib; // make life easy for all internal code - -namespace DiskImgLib { - -/* - * Debug logging macros. - * - * The macro choice implies a severity level, but we don't currently - * support that in the callback interface, so it's not used. - */ -#define DLOG_BASE(file, line, format, ...) \ - Global::PrintDebugMsg((file), (line), (format), ##__VA_ARGS__) - -//#ifdef SHOW_LOGV -# define LOGV(format, ...) DLOG_BASE(__FILE__, __LINE__, (format), ##__VA_ARGS__) -//#else -//# define LOGV(format, ...) ((void) 0) -//#endif -#define LOGD(format, ...) DLOG_BASE(__FILE__, __LINE__, (format), ##__VA_ARGS__) -#define LOGI(format, ...) DLOG_BASE(__FILE__, __LINE__, (format), ##__VA_ARGS__) -#define LOGW(format, ...) DLOG_BASE(__FILE__, __LINE__, (format), ##__VA_ARGS__) -#define LOGE(format, ...) DLOG_BASE(__FILE__, __LINE__, (format), ##__VA_ARGS__) - -/* put this in to break on interesting events when built debug */ -#if defined(_DEBUG) -# define DebugBreak() { assert(false); } -#else -# define DebugBreak() ((void) 0) -#endif - -/* - * Standard goodies. - */ -#define NELEM(x) (sizeof(x) / sizeof(x[0])) - -#define ErrnoOrGeneric() (errno != 0 ? (DIError) errno : kDIErrGeneric) - - -/* filename manipulation functions */ -const char* FilenameOnly(const char* pathname, char fssep); -const char* FindExtension(const char* pathname, char fssep); -char* StrcpyNew(const char* str); - -/* get/set integer values out of a memory buffer */ -uint16_t GetShortLE(const uint8_t* buf); -uint32_t GetLongLE(const uint8_t* buf); -uint16_t GetShortBE(const uint8_t* buf); -uint32_t GetLongBE(const uint8_t* buf); -uint32_t Get24BE(const uint8_t* ptr); -void PutShortLE(uint8_t* ptr, uint16_t val); -void PutLongLE(uint8_t* ptr, uint32_t val); -void PutShortBE(uint8_t* ptr, uint16_t val); -void PutLongBE(uint8_t* ptr, uint32_t val); - -/* little-endian read/write, for file headers (mainly 2MG and DC42) */ -DIError ReadShortLE(GenericFD* pGFD, uint16_t* pBuf); -DIError ReadLongLE(GenericFD* pGFD, uint32_t* pBuf); -DIError WriteShortLE(FILE* fp, uint16_t val); -DIError WriteLongLE(FILE* fp, uint32_t val); -DIError WriteShortLE(GenericFD* pGFD, uint16_t val); -DIError WriteLongLE(GenericFD* pGFD, uint32_t val); -DIError WriteShortBE(GenericFD* pGFD, uint16_t val); -DIError WriteLongBE(GenericFD* pGFD, uint32_t val); - -#ifdef _WIN32 -/* Windows helpers */ -DIError LastErrorToDIError(void); -bool IsWin9x(void); -#endif - - -/* - * Provide access to a buffer of data as if it were a circular buffer. - * Access is through the C array operator ([]). - * - * This DOES NOT own the array it is handed, and will not try to - * free it. - */ -class CircularBufferAccess { -public: - CircularBufferAccess(uint8_t* buf, long len) : - fBuf(buf), fLen(len) - { assert(fLen > 0); assert(fBuf != NULL); } - CircularBufferAccess(const uint8_t* buf, long len) : - fBuf(const_cast(buf)), fLen(len) - { assert(fLen > 0); assert(fBuf != NULL); } - ~CircularBufferAccess(void) {} - - /* - * Be circular. Assume that we won't stray far past the end, so - * it's cheaper to subtract than mod. - */ - uint8_t& operator[](int idx) const { - if (idx < 0) { - assert(false); - } - while (idx >= fLen) - idx -= fLen; - return fBuf[idx]; - } - - //uint8_t* GetPointer(int idx) const { - // while (idx >= fLen) - // idx -= fLen; - // return &fBuf[idx]; - //} - - int Normalize(int idx) const { - while (idx >= fLen) - idx -= fLen; - return idx; - } - - long GetSize(void) const { - return fLen; - } - -private: - uint8_t* fBuf; - long fLen; -}; - -/* - * Manage an output buffer into which we write one bit at a time. - * - * Bits fill in from the MSB to the LSB. If we write 10 bits, the - * output buffer will look like this: - * - * xxxxxxxx xx000000 - * - * Call WriteBit() repeatedly. When done, call Finish() to write any pending - * data and return the number of bits in the buffer. - */ -class BitOutputBuffer { -public: - /* pass in the output buffer and the output buffer's size */ - BitOutputBuffer(uint8_t* buf, int size) { - fBufStart = fBuf = buf; - fBufSize = size; - fBitMask = 0x80; - fByte = 0; - fOverflow = false; - } - virtual ~BitOutputBuffer(void) {} - - /* write a single bit */ - void WriteBit(int val) { - if (fBuf - fBufStart >= fBufSize) { - if (!fOverflow) { - LOGI("Overran bit output buffer"); - DebugBreak(); - fOverflow = true; - } - return; - } - - if (val) - fByte |= fBitMask; - fBitMask >>= 1; - if (fBitMask == 0) { - *fBuf++ = fByte; - fBitMask = 0x80; - fByte = 0; - } - } - - /* flush pending bits; returns length in bits (or -1 on overrun) */ - int Finish(void) { - int outputBits; - - if (fOverflow) - return -1; - - outputBits = (fBuf - fBufStart) * 8; - - if (fBitMask != 0x80) { - *fBuf++ = fByte; - - assert(fBitMask != 0); - while (fBitMask != 0x80) { - outputBits++; - fBitMask <<= 1; - } - } - return outputBits; - } - -private: - uint8_t* fBufStart; - uint8_t* fBuf; - int fBufSize; - uint8_t fBitMask; - uint8_t fByte; - bool fOverflow; -}; - -/* - * Extract data from the buffer one bit or one byte at a time. - */ -class BitInputBuffer { -public: - BitInputBuffer(const uint8_t* buf, int bitCount) { - fBufStart = fBuf = buf; - fBitCount = bitCount; - fCurrentBit = 0; - fBitPosn = 7; - fBitsConsumed = 0; - } - virtual ~BitInputBuffer(void) {} - - /* - * Get the next bit. Returns 0 or 1. - * - * If we wrapped around to the start of the buffer, and "pWrap" is - * non-null, set "*pWrap". (This does *not* set it to "false" if we - * don't wrap.) - */ - uint8_t GetBit(bool* pWrap) { - uint8_t val; - - //assert(fBitPosn == 7 - (fCurrentBit & 0x07)); - - if (fCurrentBit == fBitCount) { - /* end reached, wrap to start */ - fCurrentBit = 0; - fBitPosn = 7; - fBuf = fBufStart; - //fByte = *fBuf++; - if (pWrap != NULL) - *pWrap = true; - } - - val = (*fBuf >> fBitPosn) & 0x01; - - fCurrentBit++; - fBitPosn--; - if (fBitPosn < 0) { - fBitPosn = 7; - fBuf++; - } - - fBitsConsumed++; - return val; - } - - /* - * Get the next 8 bits. - */ - uint8_t GetByte(bool* pWrap) { - uint8_t val; - int i; - - if (true || fCurrentBit > fBitCount-8) { - /* near end, use single-bit function iteratively */ - val = 0; - for (i = 0; i < 8; i++) - val = (val << 1) | GetBit(pWrap); - } else { - /* room to spare, grab it in one or two chunks */ - assert(false); - } - return val; - } - - /* - * Set the start position. - */ - void SetStartPosition(int bitOffset) { - assert(bitOffset >= 0 && bitOffset < fBitCount); - fCurrentBit = bitOffset; - fBitPosn = 7 - (bitOffset & 0x07); // mod 8, 0 to MSB - fBuf = fBufStart + (bitOffset >> 3); // div 8 - } - - /* used to ensure we consume exactly 100% of bits */ - void ResetBitsConsumed(void) { fBitsConsumed = 0; } - int GetBitsConsumed(void) const { return fBitsConsumed; } - -private: - const uint8_t* fBufStart; - const uint8_t* fBuf; - int fBitCount; // #of bits in buffer - int fCurrentBit; // where we are in buffer - int fBitPosn; // which bit to access within byte - //uint8_t fByte; - - int fBitsConsumed; // sanity check - all bits used? -}; - -/* - * Linear bitmap. Suitable for use as a bad block map. - */ -class LinearBitmap { -public: - LinearBitmap(int numBits) { - assert(numBits > 0); - fBits = new uint8_t[(numBits + 7) / 8]; - memset(fBits, 0, (numBits + 7) / 8); - fNumBits = numBits; - } - ~LinearBitmap(void) { - delete[] fBits; - } - - /* - * Set or get the status of bit N. - */ - bool IsSet(int bit) const { - assert(bit >= 0 && bit < fNumBits); - return ((fBits[bit >> 3] >> (bit & 0x07)) & 0x01) != 0; - } - void Set(int bit) { - assert(bit >= 0 && bit < fNumBits); - fBits[bit >> 3] |= 1 << (bit & 0x07); - } - -private: - uint8_t* fBits; - int fNumBits; -}; - - -} // namespace DiskImgLib - -/* - * Most of the code needs these. - */ -#include "GenericFD.h" - -#endif /*DISKIMG_DISKIMGPRIV_H*/ diff --git a/ciderpress/diskimg/FAT.cpp b/ciderpress/diskimg/FAT.cpp deleted file mode 100644 index 7b7e27c..0000000 --- a/ciderpress/diskimg/FAT.cpp +++ /dev/null @@ -1,507 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of the Windows FAT filesystem. - * - * Right now we just try to identify that a disk is in a PC format rather - * than Apple II. The trick here is to figure out whether block 0 is a - * Master Boot Record or merely a Boot Sector. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSFAT - * =========================================================================== - */ - -const int kBlkSize = 512; -const long kBootBlock = 0; -const uint16_t kSignature = 0xaa55; // MBR or boot sector -const int kSignatureOffset = 0x1fe; -const uint8_t kOpcodeMumble = 0x33; // seen on 2nd drive -const uint8_t kOpcodeBranch = 0xeb; -const uint8_t kOpcodeSetInt = 0xfa; - -typedef struct PartitionTableEntry { - uint8_t driveNum; // dl (0x80 or 0x00) - uint8_t startHead; // dh - uint8_t startSector; // cl (&0x3f=sector, +two hi bits cyl) - uint8_t startCylinder; // ch (low 8 bits of 10-bit cylinder) - uint8_t type; // partition type - uint8_t endHead; // dh - uint8_t endSector; // cl - uint8_t endCylinder; // ch - uint32_t startLBA; // in blocks - uint32_t size; // in blocks -} PartitionTableEntry; - -/* - * Definition of a Master Boot Record, which is block 0 of a physical volume. - */ -typedef struct DiskFSFAT::MasterBootRecord { - /* - * Begins immediately with code, usually 0xfa (set interrupt flag) or - * 0xeb (relative branch). - */ - uint8_t firstByte; - - /* - * Partition table starts at 0x1be. Four entries, each 16 bytes. - */ - PartitionTableEntry parTab[4]; -} MasterBootRecord; - -/* - * Definition of a boot sector, which is block 0 of a logical volume. - */ -typedef struct DiskFSFAT::BootSector { - /* - * The first few bytes of the boot sector is called the BIOS Parameter - * Block, or BPB. - */ - uint8_t jump[3]; // usually EB XX 90 - uint8_t oemName[8]; // e.g. "MSWIN4.1" or "MSDOS5.0" - uint16_t bytesPerSector; // usually (always?) 512 - uint8_t sectPerCluster; - uint16_t reservedSectors; - uint8_t numFAT; - uint16_t numRootDirEntries; - uint16_t numSectors; // if set, ignore numSectorsHuge - uint8_t mediaType; - uint16_t numFATSectors; - uint16_t sectorsPerTrack; - uint16_t numHeads; - uint32_t numHiddenSectors; - uint32_t numSectorsHuge; // only if numSectors==0 - /* - * This next part can start immediately after the above (at 0x24) for - * FAT12/FAT16, or somewhat later (0x42) for FAT32. It doesn't seem - * to exist for NTFS. Probably safest to assume it doesn't exist. - * - * The only way to be sure of what we're dealing with is to know the - * partition type, but if this is our block 0 then we can't know what - * that is. - */ - uint8_t driveNum; - uint8_t reserved; - uint8_t signature; // 0x29 - uint32_t volumeID; - uint8_t volumeLabel[11]; // e.g. "FUBAR " - uint8_t fileSysType[8]; // e.g. "FAT12 " - - /* - * Code follows. Signature 0xaa55 in the last two bytes. - */ -} BootSector; - -// some values for MediaType -enum MediaType { - kMediaTypeLarge = 0xf0, // 1440KB or 2800KB 3.5" disk - kMediaTypeHardDrive = 0xf8, - kMediaTypeMedium = 0xf9, // 720KB 3.5" disk or 1.2MB 5.25" disk - kMediaTypeSmall = 0xfd, // 360KB 5.25" disk -}; - - -/* - * Unpack the MBR. - * - * Returns "true" if this looks like an MBR, "false" otherwise. - */ -/*static*/ bool DiskFSFAT::UnpackMBR(const uint8_t* buf, MasterBootRecord* pOut) -{ - const uint8_t* ptr; - int i; - - pOut->firstByte = buf[0x00]; - - ptr = &buf[0x1be]; - for (i = 0; i < 4; i++) { - pOut->parTab[i].driveNum = ptr[0x00]; - pOut->parTab[i].startHead = ptr[0x01]; - pOut->parTab[i].startSector = ptr[0x02]; - pOut->parTab[i].startCylinder = ptr[0x03]; - pOut->parTab[i].type = ptr[0x04]; - pOut->parTab[i].endHead = ptr[0x05]; - pOut->parTab[i].endSector = ptr[0x06]; - pOut->parTab[i].endCylinder = ptr[0x07]; - pOut->parTab[i].startLBA = GetLongLE(&ptr[0x08]); - pOut->parTab[i].size = GetLongLE(&ptr[0x0c]); - - ptr += 16; - } - - if (pOut->firstByte != kOpcodeBranch && - pOut->firstByte != kOpcodeSetInt && - pOut->firstByte != kOpcodeMumble) - return false; - bool foundActive = false; - for (i = 0; i < 4; i++) { - if (pOut->parTab[i].driveNum == 0x80) - foundActive = true; - else if (pOut->parTab[i].driveNum != 0x00) - return false; // must be 0x00 or 0x80 - } - // CFFA cards don't seem to set the "active" flag - if (false && !foundActive) - return false; - return true; -} - -/* - * Unpack the boot sector. - * - * Returns "true" if this looks like a boot sector, "false" otherwise. - */ -/*static*/ bool DiskFSFAT::UnpackBootSector(const uint8_t* buf, BootSector* pOut) -{ - memcpy(pOut->jump, &buf[0x00], sizeof(pOut->jump)); - memcpy(pOut->oemName, &buf[0x03], sizeof(pOut->oemName)); - pOut->bytesPerSector = GetShortLE(&buf[0x0b]); - pOut->sectPerCluster = buf[0x0d]; - pOut->reservedSectors = GetShortLE(&buf[0x0e]); - pOut->numFAT = buf[0x10]; - pOut->numRootDirEntries = GetShortLE(&buf[0x11]); - pOut->numSectors = GetShortLE(&buf[0x13]); - pOut->mediaType = buf[0x15]; - pOut->numFATSectors = GetShortLE(&buf[0x16]); - pOut->sectorsPerTrack = GetShortLE(&buf[0x18]); - pOut->numHeads = GetShortLE(&buf[0x1a]); - pOut->numHiddenSectors = GetLongLE(&buf[0x1c]); - pOut->numSectorsHuge = GetLongLE(&buf[0x20]); - - if (pOut->jump[0] != kOpcodeBranch && pOut->jump[0] != kOpcodeSetInt) - return false; - if (pOut->bytesPerSector != 512) - return false; - return true; -} - -/* - * See if this looks like a FAT volume. - */ -/*static*/ DIError DiskFSFAT::TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - MasterBootRecord mbr; - BootSector bs; - - dierr = pImg->ReadBlockSwapped(kBootBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - /* - * Both MBR and boot sectors have the same signature in block 0. - */ - if (GetShortLE(&blkBuf[kSignatureOffset]) != kSignature) { - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* - * Decode it as an MBR and as a partition table. Figure out which - * one makes sense. If neither make sense, fail. - */ - bool hasMBR, hasBS; - hasMBR = UnpackMBR(blkBuf, &mbr); - hasBS = UnpackBootSector(blkBuf, &bs); - LOGI(" FAT hasMBR=%d hasBS=%d", hasMBR, hasBS); - - if (!hasMBR && !hasBS) { - dierr = kDIErrFilesystemNotFound; - goto bail; - } - if (hasMBR) { - LOGI(" FAT partition table found:"); - for (int i = 0; i < 4; i++) { - LOGI(" %d: type=0x%02x start LBA=%-9u size=%u", - i, mbr.parTab[i].type, - mbr.parTab[i].startLBA, mbr.parTab[i].size); - } - } - if (hasBS) { - LOGI(" FAT boot sector found:"); - LOGI(" OEMName is '%.8s'", bs.oemName); - } - - // looks good! - -bail: - return dierr; -} - -/* - * Test to see if the image is a FAT disk. - */ -/*static*/ DIError DiskFSFAT::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - /* must be block format, should be at least 360K */ - if (!pImg->GetHasBlocks() || pImg->GetNumBlocks() < kExpectedMinBlocks) - return kDIErrFilesystemNotFound; - if (pImg->GetIsEmbedded()) // don't look for FAT inside CFFA! - return kDIErrFilesystemNotFound; - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i]) == kDIErrNone) { - *pOrder = ordering[i]; - *pFormat = DiskImg::kFormatMSDOS; - return kDIErrNone; - } - } - - LOGI(" FAT didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -/* - * Get things rolling. - */ -DIError DiskFSFAT::Initialize(void) -{ - DIError dierr = kDIErrNone; - - strcpy(fVolumeName, "[MS-DOS]"); // max 11 chars - strcpy(fVolumeID, "FATxx [MS-DOS]"); - - // take the easy way out - fTotalBlocks = fpImg->GetNumBlocks(); - - CreateFakeFile(); - - SetVolumeUsageMap(); - - return dierr; -} - - -/* - * Blank out the volume usage map. - */ -void DiskFSFAT::SetVolumeUsageMap(void) -{ - VolumeUsage::ChunkState cstate; - long block; - - fVolumeUsage.Create(fpImg->GetNumBlocks()); - - cstate.isUsed = true; - cstate.isMarkedUsed = true; - cstate.purpose = VolumeUsage::kChunkPurposeUnknown; - - for (block = fTotalBlocks-1; block >= 0; block--) - fVolumeUsage.SetChunkState(block, &cstate); -} - - -/* - * Fill a buffer with some interesting stuff, and add it to the file list. - */ -void DiskFSFAT::CreateFakeFile(void) -{ - A2FileFAT* pFile; - char buf[768]; // currently running about 430 - static const char* kFormatMsg = -"The FAT12/16/32 and NTFS filesystems are not supported. CiderPress knows\r\n" -"how to recognize MS-DOS and Windows volumes so that it can identify\r\n" -"PC data on removable media, but it does not know how to view or extract\r\n" -"files from them.\r\n" -"\r\n" -"Some information about this FAT volume:\r\n" -"\r\n" -" Volume name : '%s'\r\n" -" Volume size : %ld blocks (%.2fMB)\r\n" -"\r\n" -"(CiderPress limits itself to 8GB, so larger volume sizes may not be shown.)\r\n" -; - long capacity; - - capacity = fTotalBlocks; - - memset(buf, 0, sizeof(buf)); - snprintf(buf, NELEM(buf)-1, kFormatMsg, - fVolumeName, - capacity, - (double) capacity / 2048.0); - - pFile = new A2FileFAT(this); - pFile->SetFakeFile(buf, strlen(buf)); - strcpy(pFile->fFileName, "(not supported)"); - - AddFileToList(pFile); -} - - -/* - * =========================================================================== - * A2FileFAT - * =========================================================================== - */ - -/* - * Dump the contents of the A2File structure. - */ -void A2FileFAT::Dump(void) const -{ - LOGD("A2FileFAT '%s'", fFileName); -} - -/* - * Not a whole lot to do. - */ -DIError A2FileFAT::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - A2FDFAT* pOpenFile = NULL; - - if (fpOpenFile != NULL) - return kDIErrAlreadyOpen; - if (rsrcFork) - return kDIErrForkNotFound; - assert(readOnly == true); - - pOpenFile = new A2FDFAT(this); - - fpOpenFile = pOpenFile; - *ppOpenFile = pOpenFile; - - return kDIErrNone; -} - - -/* - * =========================================================================== - * A2FDFAT - * =========================================================================== - */ - -/* - * Read a chunk of data from the fake file. - */ -DIError A2FDFAT::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" FAT reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - - A2FileFAT* pFile = (A2FileFAT*) fpFile; - - /* don't allow them to read past the end of the file */ - if (fOffset + (long)len > pFile->fLength) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (size_t) (pFile->fLength - fOffset); - } - if (pActual != NULL) - *pActual = len; - - memcpy(buf, pFile->GetFakeFileBuf(), len); - - fOffset += len; - - return kDIErrNone; -} - -/* - * Write data at the current offset. - */ -DIError A2FDFAT::Write(const void* buf, size_t len, size_t* pActual) -{ - return kDIErrNotSupported; -} - -/* - * Seek to a new offset. - */ -DIError A2FDFAT::Seek(di_off_t offset, DIWhence whence) -{ - di_off_t fileLen = ((A2FileFAT*) fpFile)->fLength; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fileLen) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fileLen) - return kDIErrInvalidArg; - fOffset = fileLen + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fileLen - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fileLen); - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDFAT::Tell(void) -{ - return fOffset; -} - -/* - * Release file state, and tell our parent to destroy us. - */ -DIError A2FDFAT::Close(void) -{ - fpFile->CloseDescr(this); - return kDIErrNone; -} - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDFAT::GetSectorCount(void) const -{ - A2FileFAT* pFile = (A2FileFAT*) fpFile; - return (long) ((pFile->fLength+255) / 256); -} - -long A2FDFAT::GetBlockCount(void) const -{ - A2FileFAT* pFile = (A2FileFAT*) fpFile; - return (long) ((pFile->fLength+511) / 512); -} - -/* - * Return the Nth track/sector in this file. - */ -DIError A2FDFAT::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - return kDIErrNotSupported; -} - -/* - * Return the Nth 512-byte block in this file. - */ -DIError A2FDFAT::GetStorage(long blockIdx, long* pBlock) const -{ - return kDIErrNotSupported; -} diff --git a/ciderpress/diskimg/FDI.cpp b/ciderpress/diskimg/FDI.cpp deleted file mode 100644 index 671c7ab..0000000 --- a/ciderpress/diskimg/FDI.cpp +++ /dev/null @@ -1,1522 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for Formatted Disk Image (FDI) format. - * - * Based on the v2.0 spec and "fdi2raw.c". The latter was released under - * version 2 of the GPL, so this code may be subject to it. - * - * (Note: I tend to abuse the term "nibble" here. Instead of 4 bits, I - * use it to refer to 8 bits of "nibblized" data. Sorry.) - * - * THOUGHT: we have access to the self-sync byte data. We could use this - * to pretty easily convert a track to 6656-byte format, which would allow - * conversion to .NIB instead of .APP. This would probably need to be - * specified as a global preference (how to open .FDI), though we could - * just drag the self-sync flags around in a parallel data structure and - * invent a format-conversion API. The former seems easier, and should - * be easy to explain in the UI. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * FDI compression functions - * =========================================================================== - */ - -/* - * Pack a disk image with FDI. - */ -DIError WrapperFDI::PackDisk(GenericFD* pSrcGFD, GenericFD* pWrapperGFD) -{ - DIError dierr = kDIErrGeneric; // not yet - return dierr; -} - - -/* - * =========================================================================== - * FDI expansion functions - * =========================================================================== - */ - -/* - * Unpack an FDI-encoded disk image from "pGFD" to a new memory buffer - * created in "*ppNewGFD". The output is a collection of variable-length - * nibble tracks. - * - * "pNewGFD" will need to hold (kTrackAllocSize * numCyls * numHeads) - * bytes of data. - * - * Fills in "fNibbleTrackInfo". - */ -DIError WrapperFDI::UnpackDisk525(GenericFD* pGFD, GenericFD* pNewGFD, - int numCyls, int numHeads) -{ - DIError dierr = kDIErrNone; - uint8_t nibbleBuf[kNibbleBufLen]; - uint8_t* inputBuf = NULL; - bool goodTracks[kMaxNibbleTracks525]; - int inputBufLen = -1; - int badTracks = 0; - int trk, type, length256; - long nibbleLen; - bool result; - - assert(numHeads == 1); - memset(goodTracks, false, sizeof(goodTracks)); - - dierr = pGFD->Seek(kMinHeaderLen, kSeekSet); - if (dierr != kDIErrNone) { - LOGI("FDI: track seek failed (offset=%d)", kMinHeaderLen); - goto bail; - } - - for (trk = 0; trk < numCyls * numHeads; trk++) { - GetTrackInfo(trk, &type, &length256); - LOGI("%2d.%d: t=0x%02x l=%d (%d)", trk / numHeads, trk % numHeads, - type, length256, length256 * 256); - - /* if we have data to read, read it */ - if (length256 > 0) { - if (length256 * 256 > inputBufLen) { - /* allocate or increase the size of the input buffer */ - delete[] inputBuf; - inputBufLen = length256 * 256; - inputBuf = new uint8_t[inputBufLen]; - if (inputBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - } - - dierr = pGFD->Read(inputBuf, length256 * 256); - if (dierr != kDIErrNone) - goto bail; - } else { - assert(type == 0x00); - } - - /* figure out what we want to do with this track */ - switch (type) { - case 0x00: - /* blank track */ - badTracks++; - memset(nibbleBuf, 0xff, sizeof(nibbleBuf)); - nibbleLen = kTrackLenNb2525; - break; - case 0x80: - case 0x90: - case 0xa0: - case 0xb0: - /* low-level pulse-index */ - nibbleLen = kNibbleBufLen; - result = DecodePulseTrack(inputBuf, length256*256, kBitRate525, - nibbleBuf, &nibbleLen); - if (!result) { - /* something failed in the decoder; fake it */ - badTracks++; - memset(nibbleBuf, 0xff, sizeof(nibbleBuf)); - nibbleLen = kTrackLenNb2525; - } else { - goodTracks[trk] = true; - } - if (nibbleLen > kTrackAllocSize) { - LOGI(" FDI: decoded %ld nibbles, buffer is only %d", - nibbleLen, kTrackAllocSize); - dierr = kDIErrBadRawData; - goto bail; - } - break; - default: - LOGI("FDI: unexpected track type 0x%04x", type); - dierr = kDIErrUnsupportedImageFeature; - goto bail; - } - - fNibbleTrackInfo.offset[trk] = trk * kTrackAllocSize; - fNibbleTrackInfo.length[trk] = nibbleLen; - FixBadNibbles(nibbleBuf, nibbleLen); - dierr = pNewGFD->Seek(fNibbleTrackInfo.offset[trk], kSeekSet); - if (dierr != kDIErrNone) - goto bail; - dierr = pNewGFD->Write(nibbleBuf, nibbleLen); - if (dierr != kDIErrNone) - goto bail; - LOGI(" FDI: track %d: wrote %ld nibbles", trk, nibbleLen); - - //offset += 256 * length256; - //break; // DEBUG DEBUG - } - - LOGI(" FDI: %d of %d tracks bad or blank", - badTracks, numCyls * numHeads); - if (badTracks > (numCyls * numHeads) / 2) { - LOGI("FDI: too many bad tracks"); - dierr = kDIErrBadRawData; - goto bail; - } - - /* - * For convenience we want this to be 35 or 40 tracks. Start by - * reducing trk to 35 if there are no good tracks at 35+. - */ - bool want40; - int i; - - want40 = false; - for (i = kTrackCount525; i < kMaxNibbleTracks525; i++) { - if (goodTracks[i]) { - want40 = true; - break; - } - } - if (!want40 && trk > kTrackCount525) { - LOGI(" FDI: no good tracks past %d, reducing from %d", - kTrackCount525, trk); - trk = kTrackCount525; // nothing good out there, roll back - } - - /* - * Now pad us *up* to 35 if we have fewer than that. - */ - memset(nibbleBuf, 0xff, sizeof(nibbleBuf)); - for ( ; trk < kMaxNibbleTracks525; trk++) { - if (trk == kTrackCount525) - break; - - fNibbleTrackInfo.offset[trk] = trk * kTrackAllocSize; - fNibbleTrackInfo.length[trk] = kTrackLenNb2525; - fNibbleTrackInfo.numTracks++; - - dierr = pNewGFD->Seek(fNibbleTrackInfo.offset[trk], kSeekSet); - if (dierr != kDIErrNone) - goto bail; - dierr = pNewGFD->Write(nibbleBuf, nibbleLen); - if (dierr != kDIErrNone) - goto bail; - } - - assert(trk == kTrackCount525 || trk == kMaxNibbleTracks525); - fNibbleTrackInfo.numTracks = trk; - -bail: - delete[] inputBuf; - return dierr; -} - -/* - * Unpack an FDI-encoded disk image from "pGFD" to 800K of ProDOS-ordered - * 512-byte blocks in "pNewGFD". - * - * We could keep the 12-byte "tags" on each block, but they were never - * really used in the Apple II world. - * - * We also need to set up a "bad block" map to identify parts that we had - * trouble unpacking. - */ -DIError WrapperFDI::UnpackDisk35(GenericFD* pGFD, GenericFD* pNewGFD, int numCyls, - int numHeads, LinearBitmap* pBadBlockMap) -{ - DIError dierr = kDIErrNone; - uint8_t nibbleBuf[kNibbleBufLen]; - uint8_t* inputBuf = NULL; - uint8_t outputBuf[kMaxSectors35 * kBlockSize]; // 6KB - int inputBufLen = -1; - int badTracks = 0; - int trk, type, length256; - long nibbleLen; - bool result; - - assert(numHeads == 2); - - dierr = pGFD->Seek(kMinHeaderLen, kSeekSet); - if (dierr != kDIErrNone) { - LOGI("FDI: track seek failed (offset=%d)", kMinHeaderLen); - goto bail; - } - - pNewGFD->Rewind(); - - for (trk = 0; trk < numCyls * numHeads; trk++) { - GetTrackInfo(trk, &type, &length256); - LOGI("%2d.%d: t=0x%02x l=%d (%d)", trk / numHeads, trk % numHeads, - type, length256, length256 * 256); - - /* if we have data to read, read it */ - if (length256 > 0) { - if (length256 * 256 > inputBufLen) { - /* allocate or increase the size of the input buffer */ - delete[] inputBuf; - inputBufLen = length256 * 256; - inputBuf = new uint8_t[inputBufLen]; - if (inputBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - } - - dierr = pGFD->Read(inputBuf, length256 * 256); - if (dierr != kDIErrNone) - goto bail; - } else { - assert(type == 0x00); - } - - /* figure out what we want to do with this track */ - switch (type) { - case 0x00: - /* blank track */ - badTracks++; - memset(nibbleBuf, 0xff, sizeof(nibbleBuf)); - nibbleLen = kTrackLenNb2525; - break; - case 0x80: - case 0x90: - case 0xa0: - case 0xb0: - /* low-level pulse-index */ - nibbleLen = kNibbleBufLen; - result = DecodePulseTrack(inputBuf, length256*256, - BitRate35(trk/numHeads), nibbleBuf, &nibbleLen); - if (!result) { - /* something failed in the decoder; fake it */ - badTracks++; - memset(nibbleBuf, 0xff, sizeof(nibbleBuf)); - nibbleLen = kTrackLenNb2525; - } - if (nibbleLen > kNibbleBufLen) { - LOGI(" FDI: decoded %ld nibbles, buffer is only %d", - nibbleLen, kTrackAllocSize); - dierr = kDIErrBadRawData; - goto bail; - } - break; - default: - LOGI("FDI: unexpected track type 0x%04x", type); - dierr = kDIErrUnsupportedImageFeature; - goto bail; - } - - LOGI(" FDI: track %d got %ld nibbles", trk, nibbleLen); - - /* - fNibbleTrackInfo.offset[trk] = trk * kTrackAllocSize; - fNibbleTrackInfo.length[trk] = nibbleLen; - dierr = pNewGFD->Seek(fNibbleTrackInfo.offset[trk], kSeekSet); - if (dierr != kDIErrNone) - goto bail; - dierr = pNewGFD->Write(nibbleBuf, nibbleLen); - if (dierr != kDIErrNone) - goto bail; - */ - - dierr = DiskImg::UnpackNibbleTrack35(nibbleBuf, nibbleLen, outputBuf, - trk / numHeads, trk % numHeads, pBadBlockMap); - if (dierr != kDIErrNone) - goto bail; - - dierr = pNewGFD->Write(outputBuf, - kBlockSize * DiskImg::SectorsPerTrack35(trk / numHeads)); - if (dierr != kDIErrNone) { - LOGI("FDI: failed writing disk blocks (%d * %d)", - kBlockSize, DiskImg::SectorsPerTrack35(trk / numHeads)); - goto bail; - } - } - - //fNibbleTrackInfo.numTracks = numCyls * numHeads; - -bail: - delete[] inputBuf; - return dierr; -} - -/* - * Return the approximate bit rate for the specified cylinder, in bits/sec. - */ -int WrapperFDI::BitRate35(int cyl) -{ - if (cyl >= 0 && cyl <= 15) - return 375000; // 394rpm - else if (cyl <= 31) - return 343750; // 429rpm - else if (cyl <= 47) - return 312500; // 472rpm - else if (cyl <= 63) - return 281250; // 525rpm - else if (cyl <= 79) - return 250000; // 590rpm - else { - LOGI(" FDI: invalid 3.5 cylinder %d", cyl); - return 250000; - } -} - -/* - * Fix any obviously-bad nibble values. - * - * This should be unlikely, but if we find several zeroes in a row due to - * garbled data from the drive, it can happen. We clean it up here so that, - * when we convert to another format (e.g. TrackStar), we don't flunk a - * simple high-bit screening test. - * - * (We could be more rigorous and test against valid disk bytes, but that's - * probably excessive.) - */ -void WrapperFDI::FixBadNibbles(uint8_t* nibbleBuf, long nibbleLen) -{ - int badCount = 0; - - while (nibbleLen--) { - if ((*nibbleBuf & 0x80) == 0) { - badCount++; - *nibbleBuf = 0xff; - } - nibbleBuf++; - } - - if (badCount != 0) { - LOGI(" FDI: fixed %d bad nibbles", badCount); - } -} - - -/* - * Get the info for the Nth track. The track number is used as an index - * into the track descriptor table. - * - * Returns the track type and amount of data (/256). - */ -void WrapperFDI::GetTrackInfo(int trk, int* pType, int* pLength256) -{ - uint16_t trackDescr; - trackDescr = fHeaderBuf[kTrackDescrOffset + trk * 2] << 8 | - fHeaderBuf[kTrackDescrOffset + trk * 2 +1]; - - *pType = (trackDescr & 0xff00) >> 8; - *pLength256 = trackDescr & 0x00ff; - - switch (trackDescr & 0xf000) { - case 0x0000: - /* high-level type */ - switch (trackDescr & 0xff00) { - case 0x0000: - /* blank track */ - break; - default: - /* miscellaneous high-level type */ - break; - } - break; - case 0x8000: - case 0x9000: - case 0xa000: - case 0xb000: - /* low-level type, length is 14 bits */ - *pType = (trackDescr & 0xc000) >> 8; - *pLength256 = trackDescr & 0x3fff; - break; - case 0xc000: - case 0xd000: - /* mid-level format, value in 0n00 holds a bit rate index */ - break; - case 0xe000: - case 0xf000: - /* raw MFM; for 0xf000, the value in 0n00 holds a bit rate index */ - break; - default: - LOGI("Unexpected trackDescr 0x%04x", trackDescr); - *pType = 0x7e; // return an invalid value - *pLength256 = 0; - break; - } -} - - -/* - * Convert a track encoded as one or more pulse streams to nibbles. - * - * This decompresses the pulse streams in "inputBuf", then converts them - * to nibble form in "nibbleBuf". - * - * "*pNibbleLen" should hold the maximum size of the buffer. On success, - * it will hold the actual number of bytes used. - * - * Returns "true" on success, "false" on failure. - */ -bool WrapperFDI::DecodePulseTrack(const uint8_t* inputBuf, long inputLen, - int bitRate, uint8_t* nibbleBuf, long* pNibbleLen) -{ - const int kSizeValueMask = 0x003fffff; - const int kSizeCompressMask = 0x00c00000; - const int kSizeCompressShift = 22; - PulseIndexHeader hdr; - uint32_t val; - bool result = false; - - memset(&hdr, 0, sizeof(hdr)); - - hdr.numPulses = GetLongBE(&inputBuf[0x00]); - val = Get24BE(&inputBuf[0x04]); - hdr.avgStreamLen = val & kSizeValueMask; - hdr.avgStreamCompression = (val & kSizeCompressMask) >> kSizeCompressShift; - val = Get24BE(&inputBuf[0x07]); - hdr.minStreamLen = val & kSizeValueMask; - hdr.minStreamCompression = (val & kSizeCompressMask) >> kSizeCompressShift; - val = Get24BE(&inputBuf[0x0a]); - hdr.maxStreamLen = val & kSizeValueMask; - hdr.maxStreamCompression = (val & kSizeCompressMask) >> kSizeCompressShift; - val = Get24BE(&inputBuf[0x0d]); - hdr.idxStreamLen = val & kSizeValueMask; - hdr.idxStreamCompression = (val & kSizeCompressMask) >> kSizeCompressShift; - - if (hdr.numPulses < 64 || hdr.numPulses > 131072) { - /* should be about 40,000 */ - LOGI(" FDI: bad pulse count %ld in track", hdr.numPulses); - return false; - } - - /* advance past the 16 hdr bytes; now pointing at "average" stream */ - inputBuf += kPulseStreamDataOffset; - - LOGI(" pulses: %ld", hdr.numPulses); - //LOGI(" avg: len=%d comp=%d", hdr.avgStreamLen, hdr.avgStreamCompression); - //LOGI(" min: len=%d comp=%d", hdr.minStreamLen, hdr.minStreamCompression); - //LOGI(" max: len=%d comp=%d", hdr.maxStreamLen, hdr.maxStreamCompression); - //LOGI(" idx: len=%d comp=%d", hdr.idxStreamLen, hdr.idxStreamCompression); - - /* - * Uncompress or endian-swap the pulse streams. - */ - hdr.avgStream = new uint32_t[hdr.numPulses]; - if (hdr.avgStream == NULL) - goto bail; - if (!UncompressPulseStream(inputBuf, hdr.avgStreamLen, hdr.avgStream, - hdr.numPulses, hdr.avgStreamCompression, 4)) - { - goto bail; - } - inputBuf += hdr.avgStreamLen; - - if (hdr.minStreamLen > 0) { - hdr.minStream = new uint32_t[hdr.numPulses]; - if (hdr.minStream == NULL) - goto bail; - if (!UncompressPulseStream(inputBuf, hdr.minStreamLen, hdr.minStream, - hdr.numPulses, hdr.minStreamCompression, 4)) - { - goto bail; - } - inputBuf += hdr.minStreamLen; - } - if (hdr.maxStreamLen > 0) { - hdr.maxStream = new uint32_t[hdr.numPulses]; - if (!UncompressPulseStream(inputBuf, hdr.maxStreamLen, hdr.maxStream, - hdr.numPulses, hdr.maxStreamCompression, 4)) - { - goto bail; - } - inputBuf += hdr.maxStreamLen; - } - if (hdr.idxStreamLen > 0) { - hdr.idxStream = new uint32_t[hdr.numPulses]; - if (!UncompressPulseStream(inputBuf, hdr.idxStreamLen, hdr.idxStream, - hdr.numPulses, hdr.idxStreamCompression, 2)) - { - goto bail; - } - inputBuf += hdr.idxStreamLen; - } - - /* - * Convert the pulse streams to a nibble stream. - */ - result = ConvertPulseStreamsToNibbles(&hdr, bitRate, nibbleBuf, pNibbleLen); - // fall through with result - -bail: - /* clean up */ - if (hdr.avgStream != NULL) - delete[] hdr.avgStream; - if (hdr.minStream != NULL) - delete[] hdr.minStream; - if (hdr.maxStream != NULL) - delete[] hdr.maxStream; - if (hdr.idxStream != NULL) - delete[] hdr.idxStream; - return result; -} - -/* - * Uncompress, or at least endian-swap, the input data. - * - * "inputLen" is the length in bytes of the input stream. For an uncompressed - * stream this should be equal to numPulses*bytesPerPulse, for a compressed - * stream it's the length of the compressed data. - * - * "bytesPerPulse" indicates the width of the input data. This will usually - * be 4, but is 2 for the index stream. The output is always 4 bytes/pulse. - * For Huffman-compressed data, it appears that the input is always 4 bytes. - * - * Returns "true" if all went well, "false" if we hit something that we - * couldn't handle. - */ -bool WrapperFDI::UncompressPulseStream(const uint8_t* inputBuf, long inputLen, - uint32_t* outputBuf, long numPulses, int format, int bytesPerPulse) -{ - assert(bytesPerPulse == 2 || bytesPerPulse == 4); - - /* - * Sample code has a snippet that says: if the format is "uncompressed" - * but inputLen < (numPulses*2), treat it as compressed. This may be - * for handling some badly-formed images. Not currently doing it here. - */ - - if (format == kCompUncompressed) { - int i; - - LOGE("NOT TESTED"); // remove this when we've tested it - - if (inputLen != numPulses * bytesPerPulse) { - LOGI(" FDI: got unc inputLen=%ld, outputLen=%ld", - inputLen, numPulses * bytesPerPulse); - return false; - } - if (bytesPerPulse == 2) { - for (i = 0; i < numPulses; i++) { - *outputBuf++ = GetShortBE(inputBuf); - inputBuf += 2; - } - } else { - for (i = 0; i < numPulses; i++) { - *outputBuf++ = GetLongBE(inputBuf); - inputBuf += 4; - } - } - } else if (format == kCompHuffman) { - if (!ExpandHuffman(inputBuf, inputLen, outputBuf, numPulses)) - return false; - //LOGI(" FDI: Huffman expansion succeeded"); - } else { - LOGI(" FDI: got weird compression format %d", format); - return false; - } - - return true; -} - -/* - * Expand a Huffman-compressed stream. - * - * The code takes bit-slices across the entire input and compresses them - * separately with a static Huffman variant. - * - * "outputBuf" is expected to hold "numPulses" entries. - * - * This implementation is based on the fdi2raw code. - */ -bool WrapperFDI::ExpandHuffman(const uint8_t* inputBuf, long inputLen, - uint32_t* outputBuf, long numPulses) -{ - HuffNode root; - const uint8_t* origInputBuf = inputBuf; - bool signExtend, sixteenBits; - int i, subStreamShift; - uint8_t bits; - uint8_t bitMask; - - memset(outputBuf, 0, numPulses * sizeof(uint32_t)); - subStreamShift = 1; - - while (subStreamShift != 0) { - if (inputBuf - origInputBuf >= inputLen) { - LOGI(" FDI: overran input(1)"); - return false; - } - - /* decode the sub-stream header */ - bits = *inputBuf++; - subStreamShift = bits & 0x7f; // low-order bit number - signExtend = (bits & 0x80) != 0; - bits = *inputBuf++; - sixteenBits = (bits & 0x80) != 0; // ignore redundant high-order - - //LOGI(" FDI: shift=%d ext=%d sixt=%d", - // subStreamShift, signExtend, sixteenBits); - - /* decode the Huffman tree structure */ - root.left = NULL; - root.right = NULL; - bitMask = 0; - inputBuf = HuffExtractTree(inputBuf, &root, &bits, &bitMask); - - //LOGI(" after tree: off=%d", inputBuf - origInputBuf); - - /* extract the Huffman node values */ - if (sixteenBits) - inputBuf = HuffExtractValues16(inputBuf, &root); - else - inputBuf = HuffExtractValues8(inputBuf, &root); - - if (inputBuf - origInputBuf >= inputLen) { - LOGI(" FDI: overran input(2)"); - return false; - } - //LOGI(" after values: off=%d", inputBuf - origInputBuf); - - /* decode the data over all pulses */ - bitMask = 0; - for (i = 0; i < numPulses; i++) { - uint32_t outVal; - const HuffNode* pCurrent = &root; - - /* chase down the tree until we hit a leaf */ - /* (note: nodes have two kids or none) */ - while (true) { - if (pCurrent->left == NULL) { - break; - } else { - bitMask >>= 1; - if (bitMask == 0) { - bitMask = 0x80; - bits = *inputBuf++; - } - if (bits & bitMask) - pCurrent = pCurrent->right; - else - pCurrent = pCurrent->left; - } - } - - outVal = outputBuf[i]; - if (signExtend) { - if (sixteenBits) - outVal |= HuffSignExtend16(pCurrent->val) << subStreamShift; - else - outVal |= HuffSignExtend8(pCurrent->val) << subStreamShift; - } else { - outVal |= pCurrent->val << subStreamShift; - } - outputBuf[i] = outVal; - } - HuffFreeNodes(root.left); - HuffFreeNodes(root.right); - } - - if (inputBuf - origInputBuf != inputLen) { - LOGI(" FDI: warning: Huffman input %ld vs. %ld", - inputBuf - origInputBuf, inputLen); - return false; - } - - return true; -} - - -/* - * Recursively extract the Huffman tree structure for this sub-stream. - */ -const uint8_t* WrapperFDI::HuffExtractTree(const uint8_t* inputBuf, - HuffNode* pNode, uint8_t* pBits, uint8_t* pBitMask) -{ - uint8_t val; - - if (*pBitMask == 0) { - *pBits = *inputBuf++; - *pBitMask = 0x80; - } - val = *pBits & *pBitMask; - (*pBitMask) >>= 1; - - //LOGI(" val=%d", val); - - if (val != 0) { - assert(pNode->left == NULL); - assert(pNode->right == NULL); - return inputBuf; - } else { - pNode->left = new HuffNode; - memset(pNode->left, 0, sizeof(HuffNode)); - inputBuf = HuffExtractTree(inputBuf, pNode->left, pBits, pBitMask); - pNode->right = new HuffNode; - memset(pNode->right, 0, sizeof(HuffNode)); - return HuffExtractTree(inputBuf, pNode->right, pBits, pBitMask); - } -} - -/* - * Recursively get the 16-bit values for our Huffman tree from the stream. - */ -const uint8_t* WrapperFDI::HuffExtractValues16(const uint8_t* inputBuf, - HuffNode* pNode) -{ - if (pNode->left == NULL) { - pNode->val = (*inputBuf++) << 8; - pNode->val |= *inputBuf++; - return inputBuf; - } else { - inputBuf = HuffExtractValues16(inputBuf, pNode->left); - return HuffExtractValues16(inputBuf, pNode->right); - } -} - -/* - * Recursively get the 8-bit values for our Huffman tree from the stream. - */ -const uint8_t* WrapperFDI::HuffExtractValues8(const uint8_t* inputBuf, - HuffNode* pNode) -{ - if (pNode->left == NULL) { - pNode->val = *inputBuf++; - return inputBuf; - } else { - inputBuf = HuffExtractValues8(inputBuf, pNode->left); - return HuffExtractValues8(inputBuf, pNode->right); - } -} - -/* - * Recursively free up the current node and all nodes beneath it. - */ -void WrapperFDI::HuffFreeNodes(HuffNode* pNode) -{ - if (pNode != NULL) { - HuffFreeNodes(pNode->left); - HuffFreeNodes(pNode->right); - delete pNode; - } - -} - -/* - * Sign-extend a 16-bit value to 32 bits. - */ -uint32_t WrapperFDI::HuffSignExtend16(uint32_t val) -{ - if (val & 0x8000) - val |= 0xffff0000; - return val; -} - -/* - * Sign-extend an 8-bit value to 32 bits. - */ -uint32_t WrapperFDI::HuffSignExtend8(uint32_t val) -{ - if (val & 0x80) - val |= 0xffffff00; - return val; -} - - -/* use these to extract values from the index stream */ -#define ZeroStateCount(_val) (((_val) >> 8) & 0xff) -#define OneStateCount(_val) ((_val) & 0xff) - -/* - * Convert our collection of pulse streams into (what we hope will be) - * Apple II nibble form. - * - * This modifies the contents of the minStream, maxStream, and idxStream - * arrays. - * - * "*pNibbleLen" should hold the maximum size of the buffer. On success, - * it will hold the actual number of bytes used. - */ -bool WrapperFDI::ConvertPulseStreamsToNibbles(PulseIndexHeader* pHdr, int bitRate, - uint8_t* nibbleBuf, long* pNibbleLen) -{ - uint32_t* fakeIdxStream = NULL; - bool result = false; - int i; - - /* - * Stream pointers. If we don't have a stream, fake it. - */ - uint32_t* avgStream; - uint32_t* minStream; - uint32_t* maxStream; - uint32_t* idxStream; - - avgStream = pHdr->avgStream; - if (pHdr->minStream != NULL && pHdr->maxStream != NULL) { - minStream = pHdr->minStream; - maxStream = pHdr->maxStream; - - /* adjust the values in the min/max streams */ - for (i = 0; i < pHdr->numPulses; i++) { - maxStream[i] = avgStream[i] + minStream[i] - maxStream[i]; - minStream[i] = avgStream[i] - minStream[i]; - } - } else { - minStream = pHdr->avgStream; - maxStream = pHdr->avgStream; - } - - if (pHdr->idxStream != NULL) - idxStream = pHdr->idxStream; - else { - /* - * The UAE sample code has some stuff to fake it. The code there - * is broken, so I'm guessing it has never been used, but I'm going - * to replicate it here (and probably never test it either). This - * assumes that the original was written for a big-endian machine. - */ - LOGI(" FDI: HEY: using fake index stream"); - DebugBreak(); - fakeIdxStream = new uint32_t[pHdr->numPulses]; - if (fakeIdxStream == NULL) { - LOGI(" FDI: unable to alloc fake idx stream"); - goto bail; - } - for (i = 1; i < pHdr->numPulses; i++) - fakeIdxStream[i] = 0x0200; // '1' for two, '0' for zero - fakeIdxStream[0] = 0x0101; // '1' for one, '0' for one - - idxStream = fakeIdxStream; - } - - /* - * Compute a value for maxIndex. - */ - uint32_t maxIndex; - - maxIndex = 0; - for (i = 0; i < pHdr->numPulses; i++) { - uint32_t sum; - - /* add up the two single-byte values in the index stream */ - sum = ZeroStateCount(idxStream[i]) + OneStateCount(idxStream[i]); - if (sum > maxIndex) - maxIndex = sum; - } - - /* - * Compute a value for indexOffset. - */ - int indexOffset; - - indexOffset = 0; - for (i = 0; i < pHdr->numPulses && OneStateCount(idxStream[i]) != 0; i++) { - /* "falling edge, replace with ZeroStateCount for rising edge" */ - } - if (i < pHdr->numPulses) { - int start = i; - do { - i++; - if (i >= pHdr->numPulses) - i = 0; // wrapped around - } while (i != start && ZeroStateCount(idxStream[i]) == 0); - if (i != start) { - /* index pulse detected */ - while (i != start && - ZeroStateCount(idxStream[i]) > OneStateCount(idxStream[i])) - { - i++; - if (i >= pHdr->numPulses) - i = 0; - } - if (i != start) - indexOffset = i; /* index position detected */ - } - } - - /* - * Compute totalAvg and weakBits, and rewrite idxStream. - * (We don't actually use weakBits.) - */ - uint32_t totalAvg; - int weakBits; - - totalAvg = weakBits = 0; - for (i = 0; i < pHdr->numPulses; i++) { - unsigned int sum; - sum = ZeroStateCount(idxStream[i]) + OneStateCount(idxStream[i]); - if (sum >= maxIndex) - totalAvg += avgStream[i]; // could this overflow...? - else - weakBits++; - - idxStream[i] = sum; - } - - LOGI(" FDI: maxIndex=%u indexOffset=%d totalAvg=%d weakBits=%d", - maxIndex, indexOffset, totalAvg, weakBits); - - /* - * Take our altered stream values and the stuff we've calculated, - * and convert the pulse values into bits. - */ - uint8_t bitBuffer[kBitBufferSize]; - int bitCount; - - bitCount = kBitBufferSize; - - if (!ConvertPulsesToBits(avgStream, minStream, maxStream, idxStream, - pHdr->numPulses, maxIndex, indexOffset, totalAvg, bitRate, - bitBuffer, &bitCount)) - { - LOGI(" FDI: ConvertPulsesToBits() failed"); - goto bail; - } - - //LOGI(" Got %d bits", bitCount); - if (bitCount < 0) { - LOGI(" FDI: overran output bit buffer"); - goto bail; - } - - /* - * We have a bit stream with the GCR bits as they appear coming out of - * the IWM. Convert it to 8-bit nibble form. - * - * We currently discard self-sync byte information. - */ - if (!ConvertBitsToNibbles(bitBuffer, bitCount, nibbleBuf, pNibbleLen)) - { - LOGI(" FDI: ConvertBitsToNibbles() failed"); - goto bail; - } - - result = true; - -bail: - delete[] fakeIdxStream; - return result; -} - - -/* - * Local data structures. Not worth putting in the header file. - */ -const int kPulseLimitVal = 15; /* "tolerance of 15%" */ - -typedef struct PulseSamples { - uint32_t size; - int numBits; -} PulseSamples; - -class PulseSampleCollection { -public: - PulseSampleCollection(void) { - fArrayIndex = fTotalDiv = -1; - fTotal = 0; - } - ~PulseSampleCollection(void) {} - - void Create(int stdMFM2BitCellSize, int numBits) { - int i; - - fArrayIndex = 0; - fTotal = 0; - fTotalDiv = 0; - for (i = 0; i < kSampleArrayMax; i++) { - // "That is (total track length / 50000) for Amiga double density" - fArray[i].size = stdMFM2BitCellSize; - fTotal += fArray[i].size; - fArray[i].numBits = numBits; - fTotalDiv += fArray[i].numBits; - } - assert(fTotalDiv != 0); - } - - uint32_t GetTotal(void) const { return fTotal; } - int GetTotalDiv(void) const { return fTotalDiv; } - - void AdjustTotal(long val) { fTotal += val; } - void AdjustTotalDiv(int val) { fTotalDiv += val; } - void IncrIndex(void) { - fArrayIndex++; - if (fArrayIndex >= kSampleArrayMax) - fArrayIndex = 0; - } - - PulseSamples* GetCurrentArrayEntry(void) { - return &fArray[fArrayIndex]; - } - - enum { - kSampleArrayMax = 10, - }; - -private: - PulseSamples fArray[kSampleArrayMax]; - int fArrayIndex; - uint32_t fTotal; - int fTotalDiv; -}; - -#define MY_RANDOM -#ifdef MY_RANDOM -/* replace rand() with my function */ -#define rand() MyRand() - -/* - * My psuedo-random number generator, which is even less random than - * rand(). It is, however, consistent across all platforms, and the - * value for RAND_MAX is small enough to avoid some integer overflow - * problems that the code has with (2^31-1) implementations. - */ -#undef RAND_MAX -#define RAND_MAX 32767 -int WrapperFDI::MyRand(void) -{ - const int kNumStates = 31; - const int kQuantum = RAND_MAX / (kNumStates+1); - static int state = 0; - int retVal; - - state++; - if (state == kNumStates) - state = 0; - - retVal = (kQuantum * state) + (kQuantum / 2); - assert(retVal >= 0 && retVal <= RAND_MAX); - return retVal; -} -#endif - -/* - * Convert the pulses we've read to a bit stream. This is a tad complex - * because the FDI scanner was reading a GCR disk with an MFM drive. - * - * Pass the output buffer size in bytes in "*pOutputLen". The actual number - * of *bits* output is returned in it. - * - * This is a fairly direct conversion from the sample code. There's a lot - * here that I haven't taken the time to figure out. - */ -bool WrapperFDI::ConvertPulsesToBits(const uint32_t* avgStream, - const uint32_t* minStream, const uint32_t* maxStream, - const uint32_t* idxStream, int numPulses, int maxIndex, - int indexOffset, uint32_t totalAvg, int bitRate, - uint8_t* outputBuf, int* pOutputLen) -{ - PulseSampleCollection samples; - BitOutputBuffer bitOutput(outputBuf, *pOutputLen); - /* magic numbers, from somewhere */ - const uint32_t kStdMFM2BitCellSize = (totalAvg * 5) / bitRate; - const uint32_t kStdMFM8BitCellSize = (totalAvg * 20) / bitRate; - int mfmMagic = 0; // if set to 1, decode as MFM rather than GCR - bool result = false; - int i; - //int debugCounter = 0; - - /* sample code doesn't do this, but I want consistent results */ - srand(0); - - /* - * "detects a long-enough stable pulse coming just after another - * stable pulse" - */ - i = 1; - while (i < numPulses && - (idxStream[i] < (uint32_t) maxIndex || - idxStream[i-1] < (uint32_t) maxIndex || - minStream[i] < (kStdMFM2BitCellSize - (kStdMFM2BitCellSize / 4)) - )) - { - i++; - } - if (i == numPulses) { - LOGW(" FDI: no stable and long-enough pulse in track"); - goto bail; - } - - /* - * Set up some variables. - */ - int nextI, endOfData, adjust, /*bitOffset,*/ step; - uint32_t refPulse; - long jitter; - - samples.Create(kStdMFM2BitCellSize, 1 + mfmMagic); - nextI = i; - endOfData = i; - i--; - adjust = 0; - //bitOffset = 0; - refPulse = 0; - jitter = 0; - step = -1; - - /* - * Run through the data three times: - * (-1) do stuff - * (0) do more stuff - * (1) output bits - */ - while (step < 2) { - /* - * Calculates the current average bit rate from previously - * decoded data. - */ - uint32_t avgSize; - int kCell8Limit = (kPulseLimitVal * kStdMFM8BitCellSize) / 100; - - /* this is the new average size for one MFM bit */ - avgSize = (samples.GetTotal() << (2 + mfmMagic)) / samples.GetTotalDiv(); - - /* - * Prevent avgSize from getting too far out of whack. - * - * "you can try tighter ranges than 25%, or wider ranges. I would - * probably go for tighter..." - */ - if ((avgSize < kStdMFM8BitCellSize - kCell8Limit) || - (avgSize > kStdMFM8BitCellSize + kCell8Limit)) - { - avgSize = kStdMFM8BitCellSize; - } - - /* - * Get the next long-enough pulse (may require more than one pulse). - */ - uint32_t pulse; - - pulse = 0; - while (pulse < ((avgSize / 4) - (avgSize / 16))) { - uint32_t avgPulse, minPulse, maxPulse; - - /* advance i */ - i++; - if (i >= numPulses) - i = 0; // wrapped around - - /* advance nextI */ - if (i == nextI) { - do { - nextI++; - if (nextI >= numPulses) - nextI = 0; - } while (idxStream[nextI] < (uint32_t) maxIndex); - } - - if (idxStream[i] >= (uint32_t) maxIndex) { - /* stable pulse */ - avgPulse = avgStream[i] - jitter; - minPulse = minStream[i]; - maxPulse = maxStream[i]; - if (jitter >= 0) - maxPulse -= jitter; - else - minPulse -= jitter; - - if (maxStream[nextI] - avgStream[nextI] < avgPulse - minPulse) - minPulse = avgPulse - (maxStream[nextI] - avgStream[nextI]); - if (avgStream[nextI] - minStream[nextI] < maxPulse - avgPulse) - maxPulse = avgPulse + (avgStream[nextI] - minStream[nextI]); - if (minPulse < refPulse) - minPulse = refPulse; - - /* - * This appears to use a pseudo-random number generator - * to dither the signal. This strikes me as highly - * questionable, but I'm trying to recreate what the sample - * code does, and I don't fully understand this stuff. - */ - int randVal; - - randVal = rand(); - if (randVal < (RAND_MAX / 2)) { - if (randVal > (RAND_MAX / 4)) { - if (randVal <= (3 * (RAND_MAX / 8))) - randVal = (2 * randVal) - (RAND_MAX / 4); - else - randVal = (4 * randVal) - RAND_MAX; - } - jitter = 0 - (randVal * (avgPulse - minPulse)) / RAND_MAX; - } else { - randVal -= RAND_MAX / 2; - if (randVal > (RAND_MAX / 4)) { - if (randVal <= (3 * (RAND_MAX / 8))) - randVal = (2 * randVal) - (RAND_MAX / 4); - else - randVal = (4 * randVal) - RAND_MAX; - } - jitter = (randVal * (maxPulse - avgPulse)) / RAND_MAX; - } - avgPulse += jitter; - - if (avgPulse < minPulse || avgPulse > maxPulse) { - /* this is bad -- we're out of bounds */ - LOGI(" FDI: avgPulse out of bounds: avg=%u min=%u max=%u", - avgPulse, minPulse, maxPulse); - } - if (avgPulse < refPulse) { - /* I guess this is also bad */ - LOGI(" FDI: avgPulse < refPulse (%u %u)", - avgPulse, refPulse); - } - pulse += avgPulse - refPulse; - refPulse = 0; - - /* - * If we've reached the end, advance to the next step. - */ - if (i == endOfData) - step++; - } else if ((uint32_t) rand() <= (idxStream[i] * RAND_MAX) / maxIndex) { - /* futz with it */ - int randVal; - - avgPulse = avgStream[i]; - minPulse = minStream[i]; - maxPulse = maxStream[i]; - - randVal = rand(); - if (randVal < (RAND_MAX / 2)) { - if (randVal > (RAND_MAX / 4)) { - if (randVal <= (3 * (RAND_MAX / 8))) - randVal = (2 * randVal) - (RAND_MAX / 4); - else - randVal = (4 * randVal) - RAND_MAX; - } - avgPulse -= (randVal * (avgPulse - minPulse)) / RAND_MAX; - } else { - randVal -= RAND_MAX / 2; - if (randVal > (RAND_MAX / 4)) { - if (randVal <= (3 * (RAND_MAX / 8))) - randVal = (2 * randVal) - (RAND_MAX / 4); - else - randVal = (4 * randVal) - RAND_MAX; - } - avgPulse += (randVal * (maxPulse - avgPulse)) / RAND_MAX; - } - if (avgPulse > refPulse && - avgPulse < (avgStream[nextI] - jitter)) - { - pulse += avgPulse - refPulse; - refPulse = avgPulse; - } - } else { - // do nothing - } - } - - /* - * "gets the size in bits from the pulse width, considering the current - * average bitrate" - * - * "realSize" will end up holding the number of bits we're going - * to output for this pulse. - */ - uint32_t adjustedPulse; - int realSize; - - adjustedPulse = pulse; - realSize = 0; - if (mfmMagic != 0) { - while (adjustedPulse >= avgSize) { - realSize += 4; - adjustedPulse -= avgSize / 2; - } - adjustedPulse <<= 3; - while (adjustedPulse >= ((avgSize * 4) + (avgSize / 4))) { - realSize += 2; - adjustedPulse -= avgSize * 2; - } - if (adjustedPulse >= ((avgSize * 3) + (avgSize / 4))) { - if (adjustedPulse <= ((avgSize * 4) - (avgSize / 4))) { - if ((2* ((adjustedPulse >> 2) - adjust)) <= - ((2 * avgSize) - (avgSize / 4))) - { - realSize += 3; - } else { - realSize += 4; - } - } else { - realSize += 4; - } - } else { - if (adjustedPulse > ((avgSize * 3) - (avgSize / 4))) { - realSize += 3; - } else { - if (adjustedPulse >= ((avgSize * 2) + (avgSize / 4))) { - if ((2 * ((adjustedPulse >> 2) - adjust)) < - (avgSize + (avgSize / 4))) - { - realSize += 2; - } else { - realSize += 3; - } - } else { - realSize += 2; - } - } - } - } else { - /* mfmMagic == 0, whatever that means */ - while (adjustedPulse >= (2 * avgSize)) { - realSize += 4; - adjustedPulse -= avgSize; - } - adjustedPulse <<= 2; - - while (adjustedPulse >= ((avgSize * 3) + (avgSize / 4))) { - realSize += 2; - adjustedPulse -= avgSize * 2; - } - if (adjustedPulse >= ((avgSize * 2) + (avgSize / 4))) { - if (adjustedPulse <= ((avgSize * 3) - (avgSize / 4))) { - if (((adjustedPulse >> 1) - adjust) < - (avgSize + (avgSize / 4))) - { - realSize += 2; - } else { - realSize += 3; - } - } else { - realSize += 3; - } - } else { - if (adjustedPulse > ((avgSize * 2) - (avgSize / 4))) - realSize += 2; - else { - if (adjustedPulse >= (avgSize + (avgSize / 4))) { - if (((adjustedPulse >> 1) - adjust) <= - (avgSize - (avgSize / 4))) - { - realSize++; - } else { - realSize += 2; - } - } else { - realSize++; - } - } - } - } - - /* - * "after one pass to correctly initialize the average bitrate, - * outputs the bits" - */ - if (step == 1) { - int j; - - for (j = realSize; j > 1; j--) - bitOutput.WriteBit(0); - bitOutput.WriteBit(1); - } - - /* - * Prepare for next pulse. - */ - adjust = ((realSize * avgSize) / (4 << mfmMagic)) - pulse; - - PulseSamples* pSamples; - pSamples = samples.GetCurrentArrayEntry(); - samples.AdjustTotal(-(long)pSamples->size); - samples.AdjustTotalDiv(-pSamples->numBits); - pSamples->size = pulse; - pSamples->numBits = realSize; - samples.AdjustTotal(pulse); - samples.AdjustTotalDiv(realSize); - samples.IncrIndex(); - } - - *pOutputLen = bitOutput.Finish(); - LOGI(" FDI: converted pulses to %d bits", *pOutputLen); - result = true; - -bail: - return result; -} - - -/* - * Convert a stream of GCR bits into nibbles. - * - * The stream includes 9-bit and 10-bit self-sync bytes. We need to process - * the bits as if we were an Apple II, shifting bits into a register until - * we get a 1 in the msb. - * - * There is a (roughly) 7 in 8 chance that we will not start out reading - * the stream on a byte boundary. We have to read for a bit to let the - * self-sync bytes do their job. - * - * "*pNibbleLen" should hold the maximum size of the buffer. On success, - * it will hold the actual number of bytes used. - */ -bool WrapperFDI::ConvertBitsToNibbles(const uint8_t* bitBuffer, int bitCount, - uint8_t* nibbleBuf, long* pNibbleLen) -{ - BitInputBuffer inputBuffer(bitBuffer, bitCount); - const uint8_t* nibbleBufStart = nibbleBuf; - long outputBufSize = *pNibbleLen; - bool result = false; - uint8_t val; - bool wrap; - - /* - * Start 3/4 of the way through the buffer. That should give us a - * couple of self-sync zones before we hit the end of the buffer. - */ - inputBuffer.SetStartPosition(3 * (bitCount / 4)); - - /* - * Run until we wrap. We should be in sync by that point. - */ - wrap = false; - while (!wrap) { - val = inputBuffer.GetByte(&wrap); - if ((val & 0x80) == 0) - val = (val << 1) | inputBuffer.GetBit(&wrap); - if ((val & 0x80) == 0) - val = (val << 1) | inputBuffer.GetBit(&wrap); - if ((val & 0x80) == 0) { - // not allowed by GCR encoding, probably garbage between sectors - LOGI(" FDI: WARNING: more than 2 consecutive zeroes (sync)"); - } - } - - /* - * Extract the nibbles. - */ - inputBuffer.ResetBitsConsumed(); - wrap = false; - while (true) { - val = inputBuffer.GetByte(&wrap); - if ((val & 0x80) == 0) - val = (val << 1) | inputBuffer.GetBit(&wrap); - if ((val & 0x80) == 0) - val = (val << 1) | inputBuffer.GetBit(&wrap); - if ((val & 0x80) == 0) { - LOGW(" FDI: WARNING: more than 2 consecutive zeroes (read)"); - } - - if (nibbleBuf - nibbleBufStart >= outputBufSize) { - LOGW(" FDI: bits overflowed nibble buffer"); - goto bail; - } - *nibbleBuf++ = val; - - /* if we wrapped around on this one, we've reached the start point */ - if (wrap) - break; - } - - if (inputBuffer.GetBitsConsumed() != bitCount) { - /* we dropped some or double-counted some */ - LOGW(" FDI: WARNING: consumed %d of %d bits", - inputBuffer.GetBitsConsumed(), bitCount); - } - - LOGI(" FDI: consumed %d of %d (first=0x%02x last=0x%02x)", - inputBuffer.GetBitsConsumed(), bitCount, - *nibbleBufStart, *(nibbleBuf-1)); - - *pNibbleLen = nibbleBuf - nibbleBufStart; - result = true; - -bail: - return result; -} diff --git a/ciderpress/diskimg/FocusDrive.cpp b/ciderpress/diskimg/FocusDrive.cpp deleted file mode 100644 index 43b75ce..0000000 --- a/ciderpress/diskimg/FocusDrive.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * This is a container for the Parsons Engineering FocusDrive. - * - * The format was reverse-engineered by Ranger Harke. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -const int kBlkSize = 512; -const int kPartMapBlock = 0; // partition map lives here -const int kMaxPartitions = 30; // max allowed partitions on a drive -const int kPartNameStart = 1; // partition names start here (2 blocks) -const int kPartNameLen = 32; // max length of partition name - -static const char* kSignature = "Parsons Engin."; -const int kSignatureLen = 14; - -/* - * Format of partition map. It resides in the first 256 bytes of block 0. - * All values are in little-endian order. - * - * We also make space here for the partition names, which live on blocks 1+2. - */ -typedef struct DiskFSFocusDrive::PartitionMap { - uint8_t signature[kSignatureLen]; - uint8_t unknown1; - uint8_t partCount; // could be ushort, combined w/unknown1 - uint8_t unknown2[16]; - struct Entry { - uint32_t startBlock; - uint32_t blockCount; - uint32_t unknown1; - uint32_t unknown2; - uint8_t name[kPartNameLen+1]; - } entry[kMaxPartitions]; -} PartitionMap; - - -/* - * Figure out if this is a FocusDrive partition. - * - * The "imageOrder" parameter has no use here, because (in the current - * version) embedded parent volumes are implicitly ProDOS-ordered. - */ -/*static*/ DIError DiskFSFocusDrive::TestImage(DiskImg* pImg, - DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - int partCount; - - /* - * See if block 0 is a FocusDrive partition map. - */ - dierr = pImg->ReadBlockSwapped(kPartMapBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - if (memcmp(blkBuf, kSignature, kSignatureLen) != 0) { - LOGI(" FocusDrive partition signature not found in first part block"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - partCount = blkBuf[0x0f]; - if (partCount == 0 || partCount > kMaxPartitions) { - LOGI(" FocusDrive partition count looks bad (%d)", partCount); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - // success! - LOGI(" Looks like FocusDrive with %d partitions", partCount); - -bail: - return dierr; -} - -/* - * Unpack a partition map block into a partition map data structure. - */ -/*static*/ void DiskFSFocusDrive::UnpackPartitionMap(const uint8_t* buf, - const uint8_t* nameBuf, PartitionMap* pMap) -{ - const uint8_t* ptr; - const uint8_t* namePtr; - int i; - - memcpy(pMap->signature, &buf[0x00], kSignatureLen); - pMap->unknown1 = buf[0x0e]; - pMap->partCount = buf[0x0f]; - memcpy(pMap->unknown2, &buf[0x10], 16); - - ptr = &buf[0x20]; - namePtr = &nameBuf[kPartNameLen]; // not sure what first 32 bytes are - for (i = 0; i < kMaxPartitions; i++) { - pMap->entry[i].startBlock = GetLongLE(ptr); - pMap->entry[i].blockCount = GetLongLE(ptr+4); - pMap->entry[i].unknown1 = GetLongLE(ptr+8); - pMap->entry[i].unknown2 = GetLongLE(ptr+12); - - memcpy(pMap->entry[i].name, namePtr, kPartNameLen); - pMap->entry[i].name[kPartNameLen] = '\0'; - - ptr += 0x10; - namePtr += kPartNameLen; - } - - assert(ptr == buf + kBlkSize); -} - -/* - * Debug: dump the contents of the partition map. - */ -/*static*/ void DiskFSFocusDrive::DumpPartitionMap(const PartitionMap* pMap) -{ - int i; - - LOGI(" FocusDrive partition map (%d partitions):", pMap->partCount); - for (i = 0; i < pMap->partCount; i++) { - LOGI(" %2d: %8d %8d '%s'", i, pMap->entry[i].startBlock, - pMap->entry[i].blockCount, pMap->entry[i].name); - } -} - -/* - * Open up a sub-volume. - */ -DIError DiskFSFocusDrive::OpenSubVolume(long startBlock, long numBlocks, - const char* name) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - //bool tweaked = false; - - LOGI("Adding %ld +%ld", startBlock, numBlocks); - - if (startBlock > fpImg->GetNumBlocks()) { - LOGI("FocusDrive start block out of range (%ld vs %ld)", - startBlock, fpImg->GetNumBlocks()); - return kDIErrBadPartition; - } - if (startBlock + numBlocks > fpImg->GetNumBlocks()) { - LOGI("FocusDrive partition too large (%ld vs %ld avail)", - numBlocks, fpImg->GetNumBlocks() - startBlock); - fpImg->AddNote(DiskImg::kNoteInfo, - "Reduced partition from %ld blocks to %ld.\n", - numBlocks, fpImg->GetNumBlocks() - startBlock); - numBlocks = fpImg->GetNumBlocks() - startBlock; - //tweaked = true; - } - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = pNewImg->OpenImage(fpImg, startBlock, numBlocks); - if (dierr != kDIErrNone) { - LOGI(" FocusDriveSub: OpenImage(%ld,%ld) failed (err=%d)", - startBlock, numBlocks, dierr); - goto bail; - } - - //LOGI(" +++ CFFASub: new image has ro=%d (parent=%d)", - // pNewImg->GetReadOnly(), pImg->GetReadOnly()); - - /* figure out what the format is */ - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" FocusDriveSub: analysis failed (err=%d)", dierr); - goto bail; - } - - /* we allow unrecognized partitions */ - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" FocusDriveSub (%ld,%ld): unable to identify filesystem", - startBlock, numBlocks); - DiskFSUnknown* pUnknownFS = new DiskFSUnknown; - if (pUnknownFS == NULL) { - dierr = kDIErrInternal; - goto bail; - } - //pUnknownFS->SetVolumeInfo((const char*)pMap->pmParType); - pNewFS = pUnknownFS; - } else { - /* open a DiskFS for the sub-image */ - LOGI(" FocusDriveSub (%ld,%ld) analyze succeeded!", startBlock, numBlocks); - pNewFS = pNewImg->OpenAppropriateDiskFS(true); - if (pNewFS == NULL) { - LOGI(" FocusDriveSub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - } - pNewImg->AddNote(DiskImg::kNoteInfo, "Partition name='%s'.", name); - - /* we encapsulate arbitrary stuff, so encourage child to scan */ - pNewFS->SetScanForSubVolumes(kScanSubEnabled); - - /* - * Load the files from the sub-image. When doing our initial tests, - * or when loading data for the volume copier, we don't want to dig - * into our sub-volumes, just figure out what they are and where. - * - * If "initialize" fails, the sub-volume won't get added to the list. - * It's important that a failure at this stage doesn't cause the whole - * thing to fall over. - */ - InitMode initMode; - if (GetScanForSubVolumes() == kScanSubContainerOnly) - initMode = kInitHeaderOnly; - else - initMode = kInitFull; - dierr = pNewFS->Initialize(pNewImg, initMode); - if (dierr != kDIErrNone) { - LOGE(" FocusDriveSub: error %d reading list of files from disk", dierr); - goto bail; - } - - /* add it to the list */ - AddSubVolumeToList(pNewImg, pNewFS); - pNewImg = NULL; - pNewFS = NULL; - -bail: - delete pNewFS; - delete pNewImg; - return dierr; -} - -/* - * Check to see if this is a FocusDrive volume. - */ -/*static*/ DIError DiskFSFocusDrive::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (pImg->GetNumBlocks() < kMinInterestingBlocks) - return kDIErrFilesystemNotFound; - if (pImg->GetIsEmbedded()) // don't look for partitions inside - return kDIErrFilesystemNotFound; - - /* assume ProDOS -- shouldn't matter, since it's embedded */ - if (TestImage(pImg, DiskImg::kSectorOrderProDOS) == kDIErrNone) { - *pFormat = DiskImg::kFormatFocusDrive; - *pOrder = DiskImg::kSectorOrderProDOS; - return kDIErrNone; - } - - LOGI(" FS didn't find valid FocusDrive"); - return kDIErrFilesystemNotFound; -} - - -/* - * Prep the FocusDrive "container" for use. - */ -DIError DiskFSFocusDrive::Initialize(void) -{ - DIError dierr = kDIErrNone; - - LOGI("FocusDrive initializing (scanForSub=%d)", fScanForSubVolumes); - - /* seems pointless *not* to, but we just do what we're told */ - if (fScanForSubVolumes != kScanSubDisabled) { - dierr = FindSubVolumes(); - if (dierr != kDIErrNone) - return dierr; - } - - /* blank out the volume usage map */ - SetVolumeUsageMap(); - - return dierr; -} - - -/* - * Find the various sub-volumes and open them. - */ -DIError DiskFSFocusDrive::FindSubVolumes(void) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kBlkSize]; - uint8_t nameBuf[kBlkSize*2]; - PartitionMap map; - int i; - - dierr = fpImg->ReadBlock(kPartMapBlock, buf); - if (dierr != kDIErrNone) - goto bail; - dierr = fpImg->ReadBlock(kPartNameStart, nameBuf); - if (dierr != kDIErrNone) - goto bail; - dierr = fpImg->ReadBlock(kPartNameStart+1, nameBuf+kBlkSize); - if (dierr != kDIErrNone) - goto bail; - UnpackPartitionMap(buf, nameBuf, &map); - DumpPartitionMap(&map); - - for (i = 0; i < map.partCount; i++) { - dierr = OpenVol(i, map.entry[i].startBlock, map.entry[i].blockCount, - (const char*)map.entry[i].name); - if (dierr != kDIErrNone) - goto bail; - } - -bail: - return dierr; -} - -/* - * Open the volume. If it fails, open a placeholder instead. (If *that* - * fails, return with an error.) - */ -DIError DiskFSFocusDrive::OpenVol(int idx, long startBlock, long numBlocks, - const char* name) -{ - DIError dierr; - - dierr = OpenSubVolume(startBlock, numBlocks, name); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - LOGI(" FocusDrive failed opening sub-volume %d", idx); - dierr = CreatePlaceholder(startBlock, numBlocks, name, NULL, - &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - AddSubVolumeToList(pNewImg, pNewFS); - } else { - LOGI(" FocusDrive unable to create placeholder (err=%d)", - dierr); - // fall out with error - } - } - -bail: - return dierr; -} diff --git a/ciderpress/diskimg/GenericFD.cpp b/ciderpress/diskimg/GenericFD.cpp deleted file mode 100644 index a0adccc..0000000 --- a/ciderpress/diskimg/GenericFD.cpp +++ /dev/null @@ -1,866 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Generic file descriptor class. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -/* - * =========================================================================== - * GenericFD utility functions - * =========================================================================== - */ - -/* - * Copy "length" bytes from "pSrc" to "pDst". Both GenericFDs should be - * seeked to their initial positions. - * - * If "pCRC" is non-NULL, this computes a CRC32 as it goes, using the zlib - * library function. - */ -/*static*/ DIError GenericFD::CopyFile(GenericFD* pDst, GenericFD* pSrc, - di_off_t length, uint32_t* pCRC) -{ - DIError dierr = kDIErrNone; - const int kCopyBufSize = 32768; - uint8_t* copyBuf = NULL; - int copySize; - - LOGD("+++ CopyFile: %ld bytes", (long) length); - - if (pDst == NULL || pSrc == NULL || length < 0) - return kDIErrInvalidArg; - if (length == 0) - return kDIErrNone; - - copyBuf = new uint8_t[kCopyBufSize]; - if (copyBuf == NULL) - return kDIErrMalloc; - - if (pCRC != NULL) - *pCRC = crc32(0L, Z_NULL, 0); - - while (length != 0) { - copySize = kCopyBufSize; - if (copySize > length) - copySize = (int) length; - - dierr = pSrc->Read(copyBuf, copySize); - if (dierr != kDIErrNone) - goto bail; - - if (pCRC != NULL) - *pCRC = crc32(*pCRC, copyBuf, copySize); - - dierr = pDst->Write(copyBuf, copySize); - if (dierr != kDIErrNone) - goto bail; - - length -= copySize; - } - -bail: - delete[] copyBuf; - return dierr; -} - - -/* - * =========================================================================== - * GFDFile - * =========================================================================== - */ - -/* - * The stdio functions (fopen/fread/fwrite/fseek/ftell) are buffered and, - * therefore, faster for small operations. Unfortunately we need 64-bit - * file offsets, and it doesn't look like the Windows stdio stuff will - * support it cleanly (e.g. even the _FPOSOFF macro returns a "long"). - * - * Recent versions of Linux have "fseeko", which is like fseek but takes - * an off_t, so we can continue to use the FILE* functions there. Under - * Windows "lseek" takes a long, so we have to use their specific 64-bit - * variant. - * - * TODO: Visual Studio 2005 added _fseeki64. We should be able to merge - * the bulk of the implementation now. - */ -#ifdef HAVE_FSEEKO - -DIError GFDFile::Open(const char* filename, bool readOnly) -{ - DIError dierr = kDIErrNone; - - if (fFp != NULL) - return kDIErrAlreadyOpen; - if (filename == NULL) - return kDIErrInvalidArg; - if (filename[0] == '\0') - return kDIErrInvalidArg; - - delete[] fPathName; - fPathName = new char[strlen(filename) +1]; - strcpy(fPathName, filename); - - fFp = fopen(filename, readOnly ? "rb" : "r+b"); - if (fFp == NULL) { - if (errno == EACCES) - dierr = kDIErrAccessDenied; - else - dierr = ErrnoOrGeneric(); - LOGI(" GDFile Open failed opening '%s', ro=%d (err=%d)", - filename, readOnly, dierr); - return dierr; - } - fReadOnly = readOnly; - return dierr; -} - -DIError GFDFile::Read(void* buf, size_t length, size_t* pActual) -{ - DIError dierr = kDIErrNone; - size_t actual; - - if (fFp == NULL) - return kDIErrNotReady; - actual = ::fread(buf, 1, length, fFp); - if (actual == 0) { - if (feof(fFp)) - return kDIErrEOF; - if (ferror(fFp)) { - dierr = ErrnoOrGeneric(); - return dierr; - } - LOGI("MYSTERY FREAD RESULT"); - return kDIErrInternal; - } - - if (pActual == NULL) { - if (actual != length) { - dierr = ErrnoOrGeneric(); - LOGW(" GDFile Read failed on %lu bytes (actual=%lu, err=%d)", - (unsigned long) length, (unsigned long) actual, dierr); - return dierr; - } - } else { - *pActual = actual; - } - return dierr; -} - -DIError GFDFile::Write(const void* buf, size_t length, size_t* pActual) -{ - DIError dierr = kDIErrNone; - - if (fFp == NULL) - return kDIErrNotReady; - if (fReadOnly) - return kDIErrAccessDenied; - assert(pActual == NULL); // not handling this yet - if (::fwrite(buf, length, 1, fFp) != 1) { - dierr = ErrnoOrGeneric(); - LOGW(" GDFile Write failed on %lu bytes (err=%d)", - (unsigned long) length, dierr); - return dierr; - } - return dierr; -} - -DIError GFDFile::Seek(di_off_t offset, DIWhence whence) -{ - DIError dierr = kDIErrNone; - //static const long kOneGB = 1024*1024*1024; - //static const long kAlmostTwoGB = kOneGB + (kOneGB -1); - - if (fFp == NULL) - return kDIErrNotReady; - //assert(offset <= kAlmostTwoGB); - //if (::fseek(fFp, (long) offset, whence) != 0) { - if (::fseeko(fFp, offset, whence) != 0) { - dierr = ErrnoOrGeneric(); - LOGI(" GDFile Seek failed (err=%d)", dierr); - return dierr; - } - return dierr; -} - -di_off_t GFDFile::Tell(void) -{ - DIError dierr = kDIErrNone; - di_off_t result; - - if (fFp == NULL) - return kDIErrNotReady; - //result = ::ftell(fFp); - result = ::ftello(fFp); - if (result == -1) { - dierr = ErrnoOrGeneric(); - LOGI(" GDFile Tell failed (err=%d)", dierr); - return result; - } - return result; -} - -DIError GFDFile::Truncate(void) -{ -#if defined(HAVE_FTRUNCATE) - int cc; - cc = ::ftruncate(fileno(fFp), (long) Tell()); - if (cc != 0) - return kDIErrWriteFailed; -#elif defined(HAVE_CHSIZE) - assert(false); // not tested - int cc; - cc = ::chsize(fFd, (long) Tell()); - if (cc != 0) - return kDIErrWriteFailed; -#else -# error "missing truncate" -#endif - return kDIErrNone; -} - -DIError GFDFile::Close(void) -{ - if (fFp == NULL) - return kDIErrNotReady; - - LOGI(" GFDFile closing '%s'", fPathName); - fclose(fFp); - fFp = NULL; - return kDIErrNone; -} - -#else /*HAVE_FSEEKO*/ - -DIError GFDFile::Open(const char* filename, bool readOnly) -{ - DIError dierr = kDIErrNone; - - if (fFd >= 0) - return kDIErrAlreadyOpen; - if (filename == NULL) - return kDIErrInvalidArg; - if (filename[0] == '\0') - return kDIErrInvalidArg; - - delete[] fPathName; - fPathName = new char[strlen(filename) +1]; - strcpy(fPathName, filename); - - fFd = open(filename, readOnly ? O_RDONLY|O_BINARY : O_RDWR|O_BINARY, 0); - if (fFd < 0) { - if (errno == EACCES) { - dierr = kDIErrAccessDenied; - } else if (errno == EINVAL) { - // Happens on Win32 if a Unicode filename conversion failed, - // because non-converting chars turn into '?', which is illegal. - dierr = kDIErrInvalidArg; - } else { - dierr = ErrnoOrGeneric(); - } - LOGW(" GDFile Open failed opening '%s', ro=%d (err=%d)", - filename, readOnly, dierr); - return dierr; - } - fReadOnly = readOnly; - return dierr; -} - -DIError GFDFile::Read(void* buf, size_t length, size_t* pActual) -{ - DIError dierr; - ssize_t actual; - - if (fFd < 0) - return kDIErrNotReady; - actual = ::read(fFd, buf, length); - if (actual == 0) - return kDIErrEOF; - if (actual < 0) { - dierr = ErrnoOrGeneric(); - LOGW(" GDFile Read failed on %d bytes (actual=%d, err=%d)", - length, actual, dierr); - return dierr; - } - - if (pActual == NULL) { - if (actual != (ssize_t) length) { - LOGI(" GDFile Read partial (wanted=%d actual=%d)", - length, actual); - return kDIErrReadFailed; - } - } else { - *pActual = actual; - } - return kDIErrNone; -} - -DIError GFDFile::Write(const void* buf, size_t length, size_t* pActual) -{ - DIError dierr; - ssize_t actual; - - if (fFd < 0) - return kDIErrNotReady; - if (fReadOnly) - return kDIErrAccessDenied; - assert(pActual == NULL); // not handling partial writes yet - actual = ::write(fFd, buf, length); - if (actual != (ssize_t) length) { - dierr = ErrnoOrGeneric(); - LOGI(" GDFile Write failed on %d bytes (actual=%d err=%d)", - length, actual, dierr); - return dierr; - } - return kDIErrNone; -} - -DIError GFDFile::Seek(di_off_t offset, DIWhence whence) -{ - DIError dierr = kDIErrNone; - if (fFd < 0) - return kDIErrNotReady; - -#ifdef WIN32 - __int64 newPosn; - const __int64 kFailure = (__int64) -1; - newPosn = ::_lseeki64(fFd, (__int64) offset, whence); -#else - di_off_t newPosn; - const di_off_t kFailure = (di_off_t) -1; - newPosn = lseek(fFd, offset, whence); -#endif - - if (newPosn == kFailure) { - assert((uint32_t) offset != 0xccccccccUL); // uninitialized data! - dierr = ErrnoOrGeneric(); - LOGI(" GDFile Seek %ld-%lu failed (err=%d)", - (long) (offset >> 32), (uint32_t) offset, dierr); - } - return dierr; -} - -di_off_t GFDFile::Tell(void) -{ - DIError dierr = kDIErrNone; - di_off_t result; - - if (fFd < 0) - return kDIErrNotReady; - -#ifdef WIN32 - result = ::_lseeki64(fFd, 0, SEEK_CUR); -#else - result = lseek(fFd, 0, SEEK_CUR); -#endif - - if (result == -1) { - dierr = ErrnoOrGeneric(); - LOGI(" GDFile Tell failed (err=%d)", dierr); - return result; - } - return result; -} - -DIError GFDFile::Truncate(void) -{ -#if defined(HAVE_FTRUNCATE) - int cc; - cc = ::ftruncate(fFd, (long) Tell()); - if (cc != 0) - return kDIErrWriteFailed; -#elif defined(HAVE_CHSIZE) - int cc; - cc = ::chsize(fFd, (long) Tell()); - if (cc != 0) - return kDIErrWriteFailed; -#else -# error "missing truncate" -#endif - return kDIErrNone; -} - -DIError GFDFile::Close(void) -{ - if (fFd < 0) - return kDIErrNotReady; - - LOGI(" GFDFile closing '%s'", fPathName); - ::close(fFd); - fFd = -1; - return kDIErrNone; -} -#endif /*HAVE_FSEEKO else*/ - - -/* - * =========================================================================== - * GFDBuffer - * =========================================================================== - */ - -DIError GFDBuffer::Open(void* buffer, di_off_t length, bool doDelete, - bool doExpand, bool readOnly) -{ - if (fBuffer != NULL) - return kDIErrAlreadyOpen; - if (length <= 0) - return kDIErrInvalidArg; - if (length > kMaxReasonableSize) { - // be reasonable - LOGI(" GFDBuffer refusing to allocate buffer size(long)=%ld bytes", - (long) length); - return kDIErrInvalidArg; - } - - /* if buffer is NULL, allocate it ourselves */ - if (buffer == NULL) { - fBuffer = (void*) new uint8_t[(int) length]; - if (fBuffer == NULL) - return kDIErrMalloc; - } else - fBuffer = buffer; - - fLength = (long) length; - fAllocLength = (long) length; - fDoDelete = doDelete; - fDoExpand = doExpand; - fReadOnly = readOnly; - - fCurrentOffset = 0; - - return kDIErrNone; -} - -DIError GFDBuffer::Read(void* buf, size_t length, size_t* pActual) -{ - if (fBuffer == NULL) - return kDIErrNotReady; - if (length == 0) - return kDIErrInvalidArg; - - if (fCurrentOffset + (long)length > fLength) { - if (pActual == NULL) { - LOGW(" GFDBuffer underrrun off=%ld len=%lu flen=%ld", - (long) fCurrentOffset, (unsigned long) length, (long) fLength); - return kDIErrDataUnderrun; - } else { - /* set *pActual and adjust "length" */ - assert(fLength >= fCurrentOffset); - length = (size_t) (fLength - fCurrentOffset); - *pActual = length; - - if (length == 0) - return kDIErrEOF; - } - } - if (pActual != NULL) - *pActual = length; - - memcpy(buf, (const char*)fBuffer + fCurrentOffset, length); - fCurrentOffset += length; - - return kDIErrNone; -} - -DIError GFDBuffer::Write(const void* buf, size_t length, size_t* pActual) -{ - if (fBuffer == NULL) - return kDIErrNotReady; - assert(pActual == NULL); // not handling this yet - if (fCurrentOffset + (long)length > fLength) { - if (!fDoExpand) { - LOGI(" GFDBuffer overrun off=%ld len=%lu flen=%ld", - (long) fCurrentOffset, (unsigned long) length, (long) fLength); - return kDIErrDataOverrun; - } - - /* - * Expand the buffer as needed. - * - * We delete the old buffer unless "doDelete" is not set, in - * which case we just drop the pointer. Anything we allocate - * here can and will be deleted; "doDelete" only applies to the - * pointer initially passed in. - */ - if (fCurrentOffset + (long)length <= fAllocLength) { - /* fits inside allocated space, so just extend length */ - fLength = (long) fCurrentOffset + (long)length; - } else { - /* does not fit, realloc buffer */ - fAllocLength = (long) fCurrentOffset + (long)length + 8*1024; - LOGI("Reallocating buffer (new size = %ld)", fAllocLength); - assert(fAllocLength < kMaxReasonableSize); - char* newBuf = new char[(int) fAllocLength]; - if (newBuf == NULL) - return kDIErrMalloc; - - memcpy(newBuf, fBuffer, fLength); - - if (fDoDelete) - delete[] (char*)fBuffer; - else - fDoDelete = true; // future deletions are okay - - fBuffer = newBuf; - fLength = (long) fCurrentOffset + (long)length; - } - } - - memcpy((char*)fBuffer + fCurrentOffset, buf, length); - fCurrentOffset += length; - - return kDIErrNone; -} - -DIError GFDBuffer::Seek(di_off_t offset, DIWhence whence) -{ - if (fBuffer == NULL) - return kDIErrNotReady; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset >= fLength) - return kDIErrInvalidArg; - fCurrentOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fLength) - return kDIErrInvalidArg; - fCurrentOffset = fLength + offset; - break; - case kSeekCur: - if (offset < -fCurrentOffset || - offset >= (fLength - fCurrentOffset)) - { - return kDIErrInvalidArg; - } - fCurrentOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fCurrentOffset >= 0 && fCurrentOffset <= fLength); - return kDIErrNone; -} - -di_off_t GFDBuffer::Tell(void) -{ - if (fBuffer == NULL) - return (di_off_t) -1; - return fCurrentOffset; -} - -DIError GFDBuffer::Close(void) -{ - if (fBuffer == NULL) - return kDIErrNone; - - if (fDoDelete) { - LOGI(" GFDBuffer closing and deleting"); - delete[] (char*) fBuffer; - } else { - LOGI(" GFDBuffer closing"); - } - fBuffer = NULL; - - return kDIErrNone; -} - - -#ifdef _WIN32 -/* - * =========================================================================== - * GFDWinVolume - * =========================================================================== - */ - -/* - * This class is intended for use with logical volumes under Win32. Such - * devices must be accessed on 512-byte boundaries, which means no arbitrary - * seeks or reads. The device driver doesn't seem too adept at figuring - * out how large the device is, either, so we need to work that out for - * ourselves. (The standard approach appears to involve examining the - * partition map for the logical or physical volume, but we don't have a - * partition map to look at.) - */ - -/* - * Prepare a logical volume device for reading or writing. "deviceName" - * must have the form "N:\" for a logical volume or "80:\" for a physical - * volume. - */ -DIError GFDWinVolume::Open(const char* deviceName, bool readOnly) -{ - DIError dierr = kDIErrNone; - HANDLE handle = NULL; - //uint32_t kTwoGBBlocks; - - if (fVolAccess.Ready()) - return kDIErrAlreadyOpen; - if (deviceName == NULL) - return kDIErrInvalidArg; - if (deviceName[0] == '\0') - return kDIErrInvalidArg; - - delete[] fPathName; - fPathName = new char[strlen(deviceName) +1]; - strcpy(fPathName, deviceName); - - // Create a UNICODE representation of the device name. We may want - // to make the argument UNICODE instead, but most of diskimg is 8-bit - // character oriented. - size_t srcLen = strlen(deviceName) + 1; - WCHAR* wdeviceName = new WCHAR[srcLen]; - size_t convertedChars; - mbstowcs_s(&convertedChars, wdeviceName, srcLen, deviceName, _TRUNCATE); - dierr = fVolAccess.Open(wdeviceName, readOnly); - delete[] wdeviceName; - if (dierr != kDIErrNone) - goto bail; - - fBlockSize = fVolAccess.GetBlockSize(); // must be power of 2 - assert(fBlockSize > 0); - //kTwoGBBlocks = kTwoGB / fBlockSize; - - long totalBlocks; - totalBlocks = fVolAccess.GetTotalBlocks(); - fVolumeEOF = (di_off_t)totalBlocks * fBlockSize; - - assert(fVolumeEOF > 0); - - fReadOnly = readOnly; - -bail: - return dierr; -} - -DIError GFDWinVolume::Read(void* buf, size_t length, size_t* pActual) -{ - DIError dierr = kDIErrNone; - uint8_t* blkBuf = NULL; - - //LOGI(" GFDWinVolume: reading %ld bytes from offset %ld", length, - // fCurrentOffset); - - if (!fVolAccess.Ready()) - return kDIErrNotReady; - - // don't allow reading past the end of file - if (fCurrentOffset + (long) length > fVolumeEOF) { - if (pActual == NULL) - return kDIErrDataUnderrun; - length = (size_t) (fVolumeEOF - fCurrentOffset); - } - if (pActual != NULL) - *pActual = length; - if (length == 0) - return kDIErrNone; - - long advanceLen = length; - - blkBuf = new uint8_t[fBlockSize]; // get this off the heap?? - long blockIndex = (long) (fCurrentOffset / fBlockSize); - int bufOffset = (int) (fCurrentOffset % fBlockSize); // req power of 2 - assert(blockIndex >= 0); - - /* - * When possible, do multi-block reads directly into "buf". The first - * and last block may require special handling. - */ - while (length) { - assert(length > 0); - - if (bufOffset != 0 || length < (size_t) fBlockSize) { - assert(bufOffset >= 0 && bufOffset < fBlockSize); - - size_t thisCount; - - dierr = fVolAccess.ReadBlocks(blockIndex, 1, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - thisCount = fBlockSize - bufOffset; - if (thisCount > length) - thisCount = length; - - //LOGI(" Copying %d bytes from block %d", - // thisCount, blockIndex); - - memcpy(buf, blkBuf + bufOffset, thisCount); - length -= thisCount; - buf = (char*) buf + thisCount; - - bufOffset = 0; - blockIndex++; - } else { - assert(bufOffset == 0); - - long blockCount = length / fBlockSize; - assert(blockCount < 32768); - - dierr = fVolAccess.ReadBlocks(blockIndex, (short) blockCount, buf); - if (dierr != kDIErrNone) - goto bail; - - length -= blockCount * fBlockSize; - buf = (char*) buf + blockCount * fBlockSize; - - blockIndex += blockCount; - } - - } - - fCurrentOffset += advanceLen; - -bail: - delete[] blkBuf; - return dierr; -} - -DIError GFDWinVolume::Write(const void* buf, size_t length, size_t* pActual) -{ - DIError dierr = kDIErrNone; - uint8_t* blkBuf = NULL; - - //LOGI(" GFDWinVolume: writing %ld bytes at offset %ld", length, - // fCurrentOffset); - - if (!fVolAccess.Ready()) - return kDIErrNotReady; - if (fReadOnly) - return kDIErrAccessDenied; - - // don't allow writing past the end of the volume - if (fCurrentOffset + (long) length > fVolumeEOF) { - if (pActual == NULL) - return kDIErrDataOverrun; - length = (size_t) (fVolumeEOF - fCurrentOffset); - } - if (pActual != NULL) - *pActual = length; - if (length == 0) - return kDIErrNone; - - long advanceLen = length; - - blkBuf = new uint8_t[fBlockSize]; // get this out of the heap?? - long blockIndex = (long) (fCurrentOffset / fBlockSize); - int bufOffset = (int) (fCurrentOffset % fBlockSize); // req power of 2 - assert(blockIndex >= 0); - - /* - * When possible, do multi-block writes directly from "buf". The first - * and last block may require special handling. - */ - while (length) { - assert(length > 0); - - if (bufOffset != 0 || length < (size_t) fBlockSize) { - assert(bufOffset >= 0 && bufOffset < fBlockSize); - - size_t thisCount; - - dierr = fVolAccess.ReadBlocks(blockIndex, 1, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - thisCount = fBlockSize - bufOffset; - if (thisCount > length) - thisCount = length; - - //LOGI(" Copying %d bytes into block %d (off=%d)", - // thisCount, blockIndex, bufOffset); - - memcpy(blkBuf + bufOffset, buf, thisCount); - length -= thisCount; - buf = (char*) buf + thisCount; - - dierr = fVolAccess.WriteBlocks(blockIndex, 1, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - bufOffset = 0; - blockIndex++; - } else { - assert(bufOffset == 0); - - long blockCount = length / fBlockSize; - assert(blockCount < 32768); - - dierr = fVolAccess.WriteBlocks(blockIndex, (short) blockCount, buf); - if (dierr != kDIErrNone) - goto bail; - - length -= blockCount * fBlockSize; - buf = (char*) buf + blockCount * fBlockSize; - - blockIndex += blockCount; - } - - } - - fCurrentOffset += advanceLen; - -bail: - delete[] blkBuf; - return dierr; -} - -DIError GFDWinVolume::Seek(di_off_t offset, DIWhence whence) -{ - if (!fVolAccess.Ready()) - return kDIErrNotReady; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset >= fVolumeEOF) - return kDIErrInvalidArg; - fCurrentOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fVolumeEOF) - return kDIErrInvalidArg; - fCurrentOffset = fVolumeEOF + offset; - break; - case kSeekCur: - if (offset < -fCurrentOffset || - offset >= (fVolumeEOF - fCurrentOffset)) - { - return kDIErrInvalidArg; - } - fCurrentOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fCurrentOffset >= 0 && fCurrentOffset <= fVolumeEOF); - return kDIErrNone; -} - -di_off_t GFDWinVolume::Tell(void) -{ - if (!fVolAccess.Ready()) - return (di_off_t) -1; - return fCurrentOffset; -} - -DIError GFDWinVolume::Close(void) -{ - if (!fVolAccess.Ready()) - return kDIErrNotReady; - - LOGI(" GFDWinVolume closing"); - fVolAccess.Close(); - return kDIErrNone; -} -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/GenericFD.h b/ciderpress/diskimg/GenericFD.h deleted file mode 100644 index c52d4cc..0000000 --- a/ciderpress/diskimg/GenericFD.h +++ /dev/null @@ -1,317 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Declarations for GenericFD class and sub-classes. - */ -#ifndef DISKIMG_GENERICFD_H -#define DISKIMG_GENERICFD_H - -#include "Win32BlockIO.h" - -namespace DiskImgLib { - -#if 0 -/* - * Embedded file descriptor class, representing an open file on a disk image. - * - * Useful for opening disk images that are stored as files inside of other - * disk images. For stuff like UNIDOS images, which don't have a file - * associated with them, we can either open them as raw blocks, or create - * a "fake" file to access them. The latter is more general, and will work - * for sub-volumes of sub-volumes. - */ -class DISKIMG_API EmbeddedFD { -public: - EmbeddedFD(void) { - fpDiskFS = NULL; - fpA2File = NULL; - } - virtual ~EmbeddedFD(void) {} - - typedef enum Fork { kForkData = 0, kForkRsrc = 1 } Fork; - // bit-flag values for Open call's "access" parameter - enum { - kAccessNone = 0, // somewhat useless - kAccessRead = 0x01, // O_RDONLY - kAccessWrite = 0x02, // O_WRONLY - kAccessCreate = 0x04, // O_CREAT - kAccessMustNotExist = 0x08, // O_EXCL, pointless w/o O_CREAT - - kAccessReadWrite = (kAccessRead | kAccessWrite), - }; - - /* - * Standard set of calls. - */ - DIError Open(DiskFS* pDiskFS, const char* filename, Fork fork = kForkData, - int access = kAccessRead, int fileCreatePerms = 0); - DIError OpenBlocks(DiskFS* pDiskFS, long blockStart, long blockCount, - int access = kAccessRead); - DIError Read(void* buf, size_t length); - DIError Write(const void* buf, size_t length); - DIError Seek(di_off_t offset, DIWhence whence); - DIError Close(void); - -private: - // prevent bitwise copying behavior - EmbeddedFD& operator=(const EmbeddedFD&); - EmbeddedFD(const EmbeddedFD&); - - DiskFS* fpDiskFS; - A2File* fpA2File; -}; -#endif - - -/* - * Generic file source base class. Allows us to treat files on disk, memory - * buffers, and files embedded inside disk images equally. - * - * The file represented by the class is available in its entirety; skipping - * past "wrapper headers" is expected to be done by the caller. - * - * The Read and Write calls take an optional parameter that allows the caller - * to see how much data was actually read or written. If the parameter is - * not specified (or specified as NULL), then failure to return the exact - * amount of data requested results an error. - * - * This is not meant to be the end-all of file wrapper classes; in - * particular, it does not support file creation. - * - * Some libraries, such as NufxLib, require an actual filename to operate - * (bad architecture?). The GetPathName call will return the original - * filename if one exists, or NULL if there isn't one. (At which point the - * caller has the option of creating a temp file, copying the data into - * it, and cranking up NufxLib or zlib on that.) - * - * NOTE to self: see fsopen() to control sharing. - * - * NOTE: the Seek() implementations currently do not consistently allow or - * disallow seeking past the current EOF of a file. When writing a file this - * can be very useful, so someday we should implement it for all classes. - */ -class GenericFD { -public: - GenericFD(void) : fReadOnly(true) {} - virtual ~GenericFD(void) {} /* = 0 */ - - // All sub-classes must provide these, plus a type-specific Open call. - virtual DIError Read(void* buf, size_t length, - size_t* pActual = NULL) = 0; - virtual DIError Write(const void* buf, size_t length, - size_t* pActual = NULL) = 0; - virtual DIError Seek(di_off_t offset, DIWhence whence) = 0; - virtual di_off_t Tell(void) = 0; - virtual DIError Truncate(void) = 0; - virtual DIError Close(void) = 0; - virtual const char* GetPathName(void) const = 0; - - // Flush-data call, only needed for physical devices - virtual DIError Flush(void) { return kDIErrNone; } - - // Utility functions. - virtual DIError Rewind(void) { return Seek(0, kSeekSet); } - - virtual bool GetReadOnly(void) const { return fReadOnly; } - - /* - typedef enum { - kGFDTypeUnknown = 0, - kGFDTypeFile, - kGFDTypeBuffer, - kGFDTypeWinVolume, - kGFDTypeGFD - } GFDType; - virtual GFDType GetGFDType(void) const = 0; - */ - - /* - * Utility function to copy data from one GFD to another. Both GFDs must - * be seeked to their initial positions. "length" bytes will be copied. - */ - static DIError CopyFile(GenericFD* pDst, GenericFD* pSrc, di_off_t length, - uint32_t* pCRC = NULL); - -protected: - GenericFD& operator=(const GenericFD&); - GenericFD(const GenericFD&); - - bool fReadOnly; // set when file is opened -}; - -class GFDFile : public GenericFD { -public: -#ifdef HAVE_FSEEKO - GFDFile(void) : fPathName(NULL), fFp(NULL) {} -#else - GFDFile(void) : fPathName(NULL), fFd(-1) {} -#endif - virtual ~GFDFile(void) { Close(); delete[] fPathName; } - - virtual DIError Open(const char* filename, bool readOnly); - virtual DIError Read(void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Write(const void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Seek(di_off_t offset, DIWhence whence); - virtual di_off_t Tell(void); - virtual DIError Truncate(void); - virtual DIError Close(void); - virtual const char* GetPathName(void) const { return fPathName; } - -private: - char* fPathName; - -#ifdef HAVE_FSEEKO - FILE* fFp; -#else - int fFd; -#endif -}; - -#ifdef _WIN32 -class GFDWinVolume : public GenericFD { -public: - GFDWinVolume(void) : fPathName(NULL), fCurrentOffset(0), fVolumeEOF(-1) - {} - virtual ~GFDWinVolume(void) { delete[] fPathName; } - - virtual DIError Open(const char* deviceName, bool readOnly); - virtual DIError Read(void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Write(const void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Seek(di_off_t offset, DIWhence whence); - virtual di_off_t Tell(void); - virtual DIError Truncate(void) { return kDIErrNotSupported; } - virtual DIError Close(void); - virtual const char* GetPathName(void) const { return fPathName; } - - virtual DIError Flush(void) { return fVolAccess.FlushCache(false); } - -private: - char* fPathName; // for display only - Win32VolumeAccess fVolAccess; - di_off_t fCurrentOffset; - di_off_t fVolumeEOF; - int fBlockSize; // usually 512 -}; -#endif - -class GFDBuffer : public GenericFD { -public: - GFDBuffer(void) : fBuffer(NULL) {} - virtual ~GFDBuffer(void) { Close(); } - - // If "doDelete" is set, the buffer will be freed with delete[] when - // Close is called. This should ONLY be used for storage allocated - // by the DiskImg library, as under Windows it can cause problems - // because DLLs can have their own heap. - // - // "doExpand" will cause writing past the end of the buffer to - // reallocate the buffer. Again, for internally-allocated storage - // only. We expect the initial size to be close to accurate, so we - // don't aggressively expand the buffer. - virtual DIError Open(void* buffer, di_off_t length, bool doDelete, - bool doExpand, bool readOnly); - virtual DIError Read(void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Write(const void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Seek(di_off_t offset, DIWhence whence); - virtual di_off_t Tell(void); - virtual DIError Truncate(void) { - fLength = (long) Tell(); - return kDIErrNone; - } - virtual DIError Close(void); - virtual const char* GetPathName(void) const { return NULL; } - - // Back door; try not to use this. - void* GetBuffer(void) const { return fBuffer; } - -private: - enum { kMaxReasonableSize = 256 * 1024 * 1024 }; - void* fBuffer; - long fLength; // these sit in memory, so there's no - long fAllocLength; // value in using di_off_t here - bool fDoDelete; - bool fDoExpand; - di_off_t fCurrentOffset; // actually limited to (long) -}; - -#if 0 -class GFDEmbedded : public GenericFD { -public: - GFDEmbedded(void) : fEFD(NULL) {} - virtual ~GFDEmbedded(void) { Close(); } - - virtual DIError Open(EmbeddedFD* pEFD, bool readOnly); - virtual DIError Read(void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Write(const void* buf, size_t length, - size_t* pActual = NULL); - virtual DIError Seek(di_off_t offset, DIWhence whence); - virtual di_off_t Tell(void); - virtual DIError Close(void); - virtual const char* GetPathName(void) const { return NULL; } - -private: - EmbeddedFD* fEFD; -}; -#endif - -/* pass all requests straight through to another GFD (with offset bias) */ -class GFDGFD : public GenericFD { -public: - GFDGFD(void) : fpGFD(NULL), fOffset(0) {} - virtual ~GFDGFD(void) { Close(); } - - virtual DIError Open(GenericFD* pGFD, di_off_t offset, bool readOnly) { - if (pGFD == NULL) - return kDIErrInvalidArg; - if (!readOnly && pGFD->GetReadOnly()) - return kDIErrAccessDenied; // can't convert to read-write - fpGFD = pGFD; - fOffset = offset; - fReadOnly = readOnly; - Seek(0, kSeekSet); - return kDIErrNone; - } - virtual DIError Read(void* buf, size_t length, - size_t* pActual = NULL) - { - return fpGFD->Read(buf, length, pActual); - } - virtual DIError Write(const void* buf, size_t length, - size_t* pActual = NULL) - { - return fpGFD->Write(buf, length, pActual); - } - virtual DIError Seek(di_off_t offset, DIWhence whence) { - return fpGFD->Seek(offset + fOffset, whence); - } - virtual di_off_t Tell(void) { - return fpGFD->Tell() -fOffset; - } - virtual DIError Truncate(void) { - return fpGFD->Truncate(); - } - virtual DIError Close(void) { - /* do NOT close underlying descriptor */ - fpGFD = NULL; - return kDIErrNone; - } - virtual const char* GetPathName(void) const { return fpGFD->GetPathName(); } - -private: - GenericFD* fpGFD; - di_off_t fOffset; -}; - -}; // namespace DiskImgLib - -#endif /*__GENERIC_FD__*/ diff --git a/ciderpress/diskimg/Global.cpp b/ciderpress/diskimg/Global.cpp deleted file mode 100644 index f949e60..0000000 --- a/ciderpress/diskimg/Global.cpp +++ /dev/null @@ -1,191 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskImgLib globals. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" -#include "ASPI.h" - -/*static*/ bool Global::fAppInitCalled = false; - -/*static*/ ASPI* Global::fpASPI = NULL; - -/* global constant */ -const char* DiskImgLib::kASPIDev = "ASPI:"; - - -/* - * Perform one-time DLL initialization. - */ -/*static*/ DIError Global::AppInit(void) -{ - NuError nerr; - int32_t major, minor, bug; - - if (fAppInitCalled) { - LOGW("DiskImg AppInit already called"); - return kDIErrNone; - } - - LOGI("Initializing DiskImg library v%d.%d.%d", - kDiskImgVersionMajor, kDiskImgVersionMinor, kDiskImgVersionBug); - -#ifdef _WIN32 - HMODULE hModule; - WCHAR fileNameBuf[256]; - hModule = ::GetModuleHandle(L"DiskImg4.dll"); - if (hModule != NULL && - ::GetModuleFileName(hModule, fileNameBuf, - sizeof(fileNameBuf) / sizeof(WCHAR)) != 0) - { - // GetModuleHandle does not increase ref count, so no need to release - LOGD("DiskImg DLL loaded from '%ls'", fileNameBuf); - } else { - LOGW("Unable to get DiskImg DLL filename"); - } -#endif - - /* - * Make sure we're linked against a compatible version of NufxLib. - */ - nerr = NuGetVersion(&major, &minor, &bug, NULL, NULL); - if (nerr != kNuErrNone) { - LOGE("Unable to get version number from NufxLib."); - return kDIErrNufxLibInitFailed; - } - - if (major != kNuVersionMajor || minor < kNuVersionMinor) { - LOGE("Unexpected NufxLib version %d.%d.%d", - major, minor, bug); - return kDIErrNufxLibInitFailed; - } - - /* - * Do one-time init over in the DiskImg class. - */ - DiskImg::CalcNibbleInvTables(); - -#if defined(HAVE_WINDOWS_CDROM) && defined(WANT_ASPI) - if (kAlwaysTryASPI || IsWin9x()) { - fpASPI = new ASPI; - if (fpASPI->Init() != kDIErrNone) { - delete fpASPI; - fpASPI = NULL; - } - } -#endif - LOGD("DiskImg HasSPTI=%d HasASPI=%d", GetHasSPTI(), GetHasASPI()); - - fAppInitCalled = true; - - return kDIErrNone; -} - -/* - * Perform cleanup at application shutdown time. - */ -/*static*/ DIError Global::AppCleanup(void) -{ - LOGI("DiskImgLib cleanup"); - delete fpASPI; - return kDIErrNone; -} - -/* - * Simple getters. - * - * SPTI is enabled if we're in Win2K *and* ASPI isn't loaded. If ASPI is - * loaded, it can interfere with SPTI, so we want to stick with one or - * the other. - */ -#ifdef _WIN32 -/*static*/ bool Global::GetHasSPTI(void) { return !IsWin9x() && fpASPI == NULL; } -/*static*/ bool Global::GetHasASPI(void) { return fpASPI != NULL; } -/*static*/ unsigned long Global::GetASPIVersion(void) { - assert(fpASPI != NULL); -#ifdef WANT_ASPI - return fpASPI->GetVersion(); -#else - return 123456789; -#endif -} -#else -/*static*/ bool Global::GetHasSPTI(void) { return false; } -/*static*/ bool Global::GetHasASPI(void) { return false; } -/*static*/ unsigned long Global::GetASPIVersion(void) { assert(false); return 0; } -#endif - - -/* - * Return current library versions. - */ -/*static*/ void Global::GetVersion(int32_t* pMajor, int32_t* pMinor, - int32_t* pBug) -{ - if (pMajor != NULL) - *pMajor = kDiskImgVersionMajor; - if (pMinor != NULL) - *pMinor = kDiskImgVersionMinor; - if (pBug != NULL) - *pBug = kDiskImgVersionBug; -} - - -/* - * Pointer to debug message handler function. - */ -/*static*/ Global::DebugMsgHandler Global::gDebugMsgHandler = NULL; - -/* - * Change the debug message handler. The previous handler is returned. - */ -Global::DebugMsgHandler Global::SetDebugMsgHandler(DebugMsgHandler handler) -{ - DebugMsgHandler oldHandler; - - oldHandler = gDebugMsgHandler; - gDebugMsgHandler = handler; - return oldHandler; -} - -/* - * Send a debug message to the debug message handler. - * - * Even if _DEBUG_MSGS is disabled we can still get here from the NuFX error - * handler. - */ -/*static*/ void Global::PrintDebugMsg(const char* file, int line, const char* fmt, ...) -{ - if (gDebugMsgHandler == NULL) { - /* - * This can happen if the app decides to bail with an exit() - * call. I'm not sure what's zapping the pointer. - * - * We get here on "-install" or "-uninstall", which really - * should be using a more Windows-friendly exit strategy. - */ - DebugBreak(); - return; - } - - char buf[512]; - va_list args; - - va_start(args, fmt); -#if defined(HAVE_VSNPRINTF) - (void) vsnprintf(buf, sizeof(buf), fmt, args); -#elif defined(HAVE__VSNPRINTF) - (void) _vsnprintf(buf, sizeof(buf), fmt, args); -#else -# error "hosed" -#endif - va_end(args); - - buf[sizeof(buf)-1] = '\0'; - - (*gDebugMsgHandler)(file, line, buf); -} diff --git a/ciderpress/diskimg/Gutenberg.cpp b/ciderpress/diskimg/Gutenberg.cpp deleted file mode 100644 index 7225e66..0000000 --- a/ciderpress/diskimg/Gutenberg.cpp +++ /dev/null @@ -1,692 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2009 by CiderPress authors. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of Gutenberg disk format (used by the Gutenberg and - * Gutenberg Jr. word processors). - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSGutenberg - * =========================================================================== - */ - -/* -The Gutenberg disk format embeds the file structure meta-data into the disk -sectors themselves, rather than having a separate track/sector list. The -first six bytes in every sector are: - -+00 previous track in this file (+$80 indicates link not valid?) -+01 previous sector -+02 current track (+$80 indicates start of file) -+03 current sector -+04 next track (+$80 indicates end of file) -+05 next sector - -The files are circular -- the "next" and "previous" links will wrap around -- -so you have to test the high bit to see if you've reached an end. - -The disk catalog works the same way, and is present as the first file on -the disk (called "DIR"). (It's not quite the same -- the "previous" pointer -in the first sector just points to the first sector.) The catalog begins -at track 17 sector 7, and skips around the disk. - -The boot area is not represented by a file, and does not include the -embedded T/S links. - -Each directory entry is 16 bytes, and lays out rather nicely in the sector -editor: - - 00: 11 07 11 87 15 0e c7 c2 af cd c1 d3 d4 c5 d2 8d ......GB/MASTER. - 10: c4 c9 d2 a0 a0 a0 a0 a0 a0 a0 a0 a0 11 07 cc 8d DIR ..L. - 20: c3 cf d0 d9 a0 a0 a0 a0 a0 a0 a0 a0 10 40 d0 8d COPY .@P. - 30: c3 cf d0 d9 c1 cc cc a0 a0 a0 a0 a0 10 04 d0 8d COPYALL ..P. - -This shows the six T/S bytes, followed by a nine-character volume name. -The fact that each entry ends in 0x8d is likely a deliberate attempt to -make the file readable as high-ASCII text, with one entry per line. - -The regular directory entries start at 0x10. The file name is 12 bytes, -followed by the track and sector of the start of the file. Note "DIR" -starts at track 17 sector 7, as expected. The next entry, COPY, has 0x40 for -its sector number, indicating that it has been deleted. (Some entries on -Gutenberg Jr. disks use 0x40 for the track number instead?) - -The next value is one of 'L', 'P', 'M', or ' ' (0xcc, 0xd0, 0xcd, 0xa0). -Some files have text, some have fonts, some have executable code (a short -header followed by 6502 instructions). There's no apparent link between -the value and the type of data in the file. -*/ - -//const int kMaxSectors = 32; -const int kMaxVolNameLen = 9; -const int kSctSize = 256; -const int kVTOCTrack = 17; -const int kVTOCSector = 7; -const int kCatalogEntryOffset = 0x10; // first entry in cat sect starts here -const int kCatalogEntrySize = 16; // length in bytes of catalog entries -const int kCatalogEntriesPerSect = 15; // #of entries per catalog sector -const int kEntryDeleted = 0x40; // this is used to designate deleted files -//const int kEntryUnused = 0x00; // this is track# in never-used entries -//const int kMaxTSPairs = 0x7a; // 122 entries for 256-byte sectors -//const int kTSOffset = 0x0c; // first T/S entry in a T/S list - -//const int kMaxTSIterations = 32; - -/* - * Get a pointer to the Nth entry in a catalog sector. - */ -#if 0 -static inline uint8_t* GetCatalogEntryPtr(uint8_t* basePtr, int entryNum) -{ - assert(entryNum >= 0 && entryNum < kCatalogEntriesPerSect); - return basePtr + kCatalogEntryOffset + entryNum * kCatalogEntrySize; -} -#endif - - -/* - * Test this image for Gutenberg-ness. - * - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, - int* pGoodCount) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - int catTrack = kVTOCTrack; - int catSect = kVTOCSector; - int foundGood = 0; - int iterations = 0; - - *pGoodCount = 0; - - /* - * Walk through the catalog track to try to figure out ordering. - */ - while (iterations < DiskFSGutenberg::kMaxCatalogSectors) - { - dierr = pImg->ReadTrackSectorSwapped(catTrack, catSect, sctBuf, - imageOrder, DiskImg::kSectorOrderDOS); - if (dierr != kDIErrNone) { - dierr = kDIErrNone; - break; /* allow it if earlier stuff was okay */ - } - if (catTrack == (sctBuf[2] & 0x7f) && catSect == (sctBuf[3] & 0x7f)) { - // current-sector values matched, check for the end-of-entry bits - foundGood++; - if (sctBuf[0x0f] == 0x8d && sctBuf[0x1f] == 0x8d && - sctBuf[0x2f] == 0x8d && sctBuf[0x3f] == 0x8d && - sctBuf[0x4f] == 0x8d && sctBuf[0x5f] == 0x8d && - sctBuf[0x6f] == 0x8d && sctBuf[0x7f] == 0x8d && - sctBuf[0x8f] == 0x8d && sctBuf[0x9f] == 0x8d) - { - foundGood++; - } - } - catTrack = sctBuf[0x04]; - catSect = sctBuf[0x05]; - if ((catTrack & 0x80) != 0) { - // full circle - break; - } - iterations++; // watch for infinite loops - } - if (iterations >= DiskFSGutenberg::kMaxCatalogSectors) { - /* possible cause: LF->CR conversion screws up link to sector $0a */ - dierr = kDIErrDirectoryLoop; - LOGI(" Gutenberg directory links cause a loop (order=%d)", imageOrder); - goto bail; - } - - LOGI(" Gutenberg foundGood=%d order=%d", foundGood, imageOrder); - *pGoodCount = foundGood; - -bail: - return dierr; -} - -/* - * Test to see if the image is a Gutenberg word processor data disk. - */ -/*static*/ DIError DiskFSGutenberg::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (pImg->GetNumTracks() > kMaxInterestingTracks) - return kDIErrFilesystemNotFound; - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown; - int bestCount = 0; - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - int goodCount = 0; - - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i], &goodCount) == kDIErrNone) { - if (goodCount > bestCount) { - bestCount = goodCount; - bestOrder = ordering[i]; - } - } - } - - if (bestCount >= 2 || - (leniency == kLeniencyVery && bestCount >= 1)) - { - LOGI(" Gutenberg test: bestCount=%d for order=%d", bestCount, bestOrder); - assert(bestOrder != DiskImg::kSectorOrderUnknown); - *pOrder = bestOrder; - *pFormat = DiskImg::kFormatGutenberg; - return kDIErrNone; - } - - LOGI(" Gutenberg didn't find a valid filesystem."); - return kDIErrFilesystemNotFound; -} - - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSGutenberg::Initialize(InitMode initMode) -{ - DIError dierr = kDIErrNone; - - fVolumeUsage.Create(fpImg->GetNumTracks(), fpImg->GetNumSectPerTrack()); - - /* read the contents of the catalog, creating our A2File list */ - dierr = ReadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - /* run through and get file lengths and data offsets */ - dierr = GetFileLengths(); - if (dierr != kDIErrNone) - goto bail; - - sprintf(fDiskVolumeID, "Gutenberg: %s", fDiskVolumeName); - - fDiskIsGood = CheckDiskIsGood(); - - fVolumeUsage.Dump(); - -bail: - return dierr; -} - -/* - * Get the amount of free space remaining. - */ -DIError DiskFSGutenberg::GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const -{ - *pTotalUnits = fpImg->GetNumTracks() * fpImg->GetNumSectPerTrack(); - *pFreeUnits = 0; - *pUnitSize = kSectorSize; - return kDIErrNone; -} - -/* - * Read the disk's catalog. - */ -DIError DiskFSGutenberg::ReadCatalog(void) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - int catTrack, catSect; - int iterations; - - catTrack = kVTOCTrack; - catSect = kVTOCSector; - iterations = 0; - - memset(fCatalogSectors, 0, sizeof(fCatalogSectors)); - - while (catTrack < 35 && catSect < 16 && iterations < kMaxCatalogSectors) - { - LOGD(" Gutenberg reading catalog sector T=%d S=%d", catTrack, catSect); - dierr = fpImg->ReadTrackSector(catTrack, catSect, sctBuf); - if (dierr != kDIErrNone) - goto bail; - memcpy(fDiskVolumeName, &sctBuf[6], kMaxVolNameLen); // Copy out the volume name; it should be the same on all catalog sectors. - fDiskVolumeName[kMaxVolNameLen] = 0x00; - DiskFSGutenberg::LowerASCII((uint8_t*)fDiskVolumeName, kMaxVolNameLen); - A2FileGutenberg::TrimTrailingSpaces(fDiskVolumeName); - - dierr = ProcessCatalogSector(catTrack, catSect, sctBuf); - if (dierr != kDIErrNone) - goto bail; - - fCatalogSectors[iterations].track = catTrack; - fCatalogSectors[iterations].sector = catSect; - - catTrack = sctBuf[0x04]; - catSect = sctBuf[0x05]; - - iterations++; // watch for infinite loops - - } - if (iterations >= kMaxCatalogSectors) { - dierr = kDIErrDirectoryLoop; - goto bail; - } - -bail: - return dierr; -} - -/* - * Process the list of files in one sector of the catalog. - * - * Pass in the track, sector, and the contents of that track and sector. - * (We only use "catTrack" and "catSect" to fill out some fields.) - */ -DIError DiskFSGutenberg::ProcessCatalogSector(int catTrack, int catSect, - const uint8_t* sctBuf) -{ - A2FileGutenberg* pFile; - const uint8_t* pEntry; - int i; - - pEntry = &sctBuf[kCatalogEntryOffset]; - - for (i = 0; i < kCatalogEntriesPerSect; i++) { - if (pEntry[0x0c] != kEntryDeleted && pEntry[0x0d] != kEntryDeleted && - pEntry[0x00] != 0xa0 && pEntry[0x00] != 0x00) - { - pFile = new A2FileGutenberg(this); - - pFile->SetQuality(A2File::kQualityGood); - - pFile->fTrack = pEntry[0x0c]; - pFile->fSector = pEntry[0x0d]; - - memcpy(pFile->fFileName, &pEntry[0x00], A2FileGutenberg::kMaxFileName); - pFile->fFileName[A2FileGutenberg::kMaxFileName] = '\0'; - pFile->FixFilename(); - - //pFile->fCatTS.track = catTrack; - //pFile->fCatTS.sector = catSect; - pFile->fCatEntryNum = i; - - /* can't do these yet, so just set to defaults */ - pFile->fLength = 0; - pFile->fSparseLength = 0; - pFile->fDataOffset = 0; - pFile->fLengthInSectors = 0; - pFile->fLengthInSectors = 0; - - AddFileToList(pFile); - } - //if (pEntry[0x00] == 0xa0) - // break; - pEntry += kCatalogEntrySize; - } - - return kDIErrNone; -} - -/* - * Perform consistency checks on the filesystem. - * - * Returns "true" if disk appears to be perfect, "false" otherwise. - */ -bool DiskFSGutenberg::CheckDiskIsGood(void) -{ - bool result = true; - return result; -} - -/* - * Run through our list of files, computing the lengths and marking file - * usage in the VolumeUsage object. - */ -DIError DiskFSGutenberg::GetFileLengths(void) -{ - A2FileGutenberg* pFile; - uint8_t sctBuf[kSctSize]; - int tsCount = 0; - uint16_t currentTrack, currentSector; - - pFile = (A2FileGutenberg*) GetNextFile(NULL); - while (pFile != NULL) { - DIError dierr; - tsCount = 0; - currentTrack = pFile->fTrack; - currentSector = pFile->fSector; - - while (currentTrack < 0x80) { - tsCount ++; - dierr = fpImg->ReadTrackSector(currentTrack, currentSector, sctBuf); - if (dierr != kDIErrNone) { - LOGI("Gutenberg failed loading track/sector for '%s'", - pFile->GetPathName()); - goto bail; - } - currentTrack = sctBuf[0x04]; - currentSector = sctBuf[0x05]; - } - pFile->fLengthInSectors = tsCount; - pFile->fLength = tsCount * 250; // First six bytes of sector are t/s pointers - - pFile = (A2FileGutenberg*) GetNextFile(pFile); - } - -bail: - return kDIErrNone; -} - -/* - * Convert high ASCII to low ASCII. - * - * Some people put inverse and flashing text into filenames, not to mention - * control characters, so we have to cope with those too. - * - * We modify the first "len" bytes of "buf" in place. - */ -/*static*/ void DiskFSGutenberg::LowerASCII(uint8_t* buf, long len) -{ - while (len--) { - if (*buf & 0x80) { - if (*buf >= 0xa0) - *buf &= 0x7f; - else - *buf = (*buf & 0x7f) + 0x20; - } else - *buf = ((*buf & 0x3f) ^ 0x20) + 0x20; - - buf++; - } -} - - -/* - * =========================================================================== - * A2FileGutenberg - * =========================================================================== - */ - -/* - * Constructor. - */ -A2FileGutenberg::A2FileGutenberg(DiskFS* pDiskFS) : A2File(pDiskFS) -{ - fTrack = -1; - fSector = -1; - fLengthInSectors = 0; - fLocked = true; - fFileName[0] = '\0'; - fFileType = kTypeText; - - fCatTS.track = fCatTS.sector = 0; - fCatEntryNum = -1; - - fAuxType = 0; - fDataOffset = 0; - fLength = -1; - fSparseLength = -1; - - fpOpenFile = NULL; -} - -/* - * Destructor. Make sure an "open" file gets "closed". - */ -A2FileGutenberg::~A2FileGutenberg(void) -{ - delete fpOpenFile; -} - -/* - * Convert the filetype enum to a ProDOS type. - * - */ -uint32_t A2FileGutenberg::GetFileType(void) const -{ - return 0x04; // TXT; -} - -/* - * "Fix" a filename. Convert DOS-ASCII to normal ASCII, and strip - * trailing spaces. - */ -void A2FileGutenberg::FixFilename(void) -{ - DiskFSGutenberg::LowerASCII((uint8_t*)fFileName, kMaxFileName); - TrimTrailingSpaces(fFileName); -} - -/* - * Trim the spaces off the end of a filename. - * - * Assumes the filename has already been converted to low ASCII. - */ -/*static*/ void A2FileGutenberg::TrimTrailingSpaces(char* filename) -{ - char* lastspc = filename + strlen(filename); - - assert(*lastspc == '\0'); - - while (--lastspc) { - if (*lastspc != ' ') - break; - } - - *(lastspc+1) = '\0'; -} - -/* - * Encode a filename into high ASCII, padded out with spaces to - * kMaxFileName chars. Lower case is converted to upper case. This - * does not filter out control characters or other chunk. - * - * "buf" must be able to hold kMaxFileName+1 chars. - */ -/*static*/ void A2FileGutenberg::MakeDOSName(char* buf, const char* name) -{ - for (int i = 0; i < kMaxFileName; i++) { - if (*name == '\0') - *buf++ = (char) 0xa0; - else - *buf++ = toupper(*name++) | 0x80; - } - *buf = '\0'; -} - - -/* - * Set up state for this file. - */ -DIError A2FileGutenberg::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - DIError dierr = kDIErrNone; - A2FDGutenberg* pOpenFile = NULL; - - if (!readOnly) { - if (fpDiskFS->GetDiskImg()->GetReadOnly()) - return kDIErrAccessDenied; - if (fpDiskFS->GetFSDamaged()) - return kDIErrBadDiskImage; - } - - if (fpOpenFile != NULL) { - dierr = kDIErrAlreadyOpen; - goto bail; - } - - if (rsrcFork) - return kDIErrForkNotFound; - - pOpenFile = new A2FDGutenberg(this); - - pOpenFile->fOffset = 0; - pOpenFile->fOpenEOF = fLength; - pOpenFile->fOpenSectorsUsed = fLengthInSectors; - - fpOpenFile = pOpenFile; // add it to our single-member "open file set" - *ppOpenFile = pOpenFile; - pOpenFile = NULL; - -bail: - delete pOpenFile; - return dierr; -} - -/* - * Dump the contents of an A2FileGutenberg. - */ -void A2FileGutenberg::Dump(void) const -{ - LOGI("A2FileGutenberg '%s'", fFileName); - LOGI(" TS T=%-2d S=%-2d", fTrack, fSector); - LOGI(" Cat T=%-2d S=%-2d", fCatTS.track, fCatTS.sector); - LOGI(" type=%d lck=%d slen=%d", fFileType, fLocked, fLengthInSectors); - LOGI(" auxtype=0x%04x length=%ld", - fAuxType, (long) fLength); -} - - -/* - * =========================================================================== - * A2FDGutenberg - * =========================================================================== - */ - -/* - * Read data from the current offset. - * - */ -DIError A2FDGutenberg::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" Gutenberg reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - - A2FileGutenberg* pFile = (A2FileGutenberg*) fpFile; - - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - short currentTrack, currentSector; - //di_off_t actualOffset = fOffset + pFile->fDataOffset; // adjust for embedded len - int bufOffset = 6; - size_t thisCount; - - if (len == 0) - return kDIErrNone; - assert(fOpenEOF != 0); - currentTrack = pFile->fTrack; - currentSector = pFile->fSector; - /* could be more clever in here and avoid double-buffering */ - while (len) { - dierr = pFile->GetDiskFS()->GetDiskImg()->ReadTrackSector( - currentTrack, - currentSector, - sctBuf); - if (dierr != kDIErrNone) { - LOGI(" Gutenberg error reading file '%s'", pFile->GetPathName()); - return dierr; - } - thisCount = kSctSize - bufOffset; - if (thisCount > len) - thisCount = len; - memcpy(buf, sctBuf + bufOffset, thisCount); - len -= thisCount; - buf = (char*)buf + thisCount; - currentTrack = sctBuf[0x04]; - currentSector = sctBuf[0x05]; - } - - return dierr; -} - -/* - * Writing Gutenberg files isn't supported. - */ -DIError A2FDGutenberg::Write(const void* buf, size_t len, size_t* pActual) -{ - return kDIErrNotSupported; -} - -/* - * Seek to the specified offset. - */ -DIError A2FDGutenberg::Seek(di_off_t offset, DIWhence whence) -{ - return kDIErrNotSupported; -} - -/* - * Return current offset. - */ -di_off_t A2FDGutenberg::Tell(void) -{ - return kDIErrNotSupported; -} - -/* - * Release file state. - * - * If the file was modified, we need to update the sector usage count in - * the catalog track, and possibly a length word in the first sector of - * the file (for A/I/B). - * - * Given the current "write all at once" implementation of Write, we could - * have handled the length word back when initially writing the data, but - * someday we may fix that and I don't want to have to rewrite this part. - * - * Most applications don't check the value of "Close", or call it from a - * destructor, so we call CloseDescr whether we succeed or not. - */ -DIError A2FDGutenberg::Close(void) -{ - DIError dierr = kDIErrNone; - - fpFile->CloseDescr(this); - return dierr; -} - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDGutenberg::GetSectorCount(void) const -{ - return fTSCount; -} - -long A2FDGutenberg::GetBlockCount(void) const -{ - return (fTSCount+1)/2; -} - -/* - * Return the Nth track/sector in this file. - * - * Returns (0,0) for a sparse sector. - */ -DIError A2FDGutenberg::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - return kDIErrInvalidIndex; -} - -/* - * Unimplemented - */ -DIError A2FDGutenberg::GetStorage(long blockIdx, long* pBlock) const -{ - return kDIErrInvalidIndex; -} diff --git a/ciderpress/diskimg/HFS.cpp b/ciderpress/diskimg/HFS.cpp deleted file mode 100644 index dfd1536..0000000 --- a/ciderpress/diskimg/HFS.cpp +++ /dev/null @@ -1,2298 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of the Macintosh HFS filesystem. - * - * Most of the stuff lives in libhfs. To avoid problems that could arise - * from people ejecting floppies or trying to use a disk image while - * CiderPress is still open, we call hfs_flush() to force updates to be - * written. (Even with the "no caching" flag set, the master dir block and - * volume bitmap aren't written until flush is called.) - * - * The libhfs code is licensed under the full GPL, making it awkward to - * use in a commercial product. Support for libhfs can be removed with - * the EXCISE_GPL_CODE ifdefs. A stub will remain that can recognize HFS - * volumes, which is useful when dealing with Apple II hard drive and CFFA - * images. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSHFS - * =========================================================================== - */ - -const int kBlkSize = 512; -const int kMasterDirBlock = 2; // also a copy in next-to-last block -const uint16_t kSignature = 0x4244; // or 0xd2d7 for MFS -const int kMaxDirectoryDepth = 128; // not sure what HFS limit is - -//namespace DiskImgLib { - -/* extent descriptor */ -typedef struct ExtDescriptor { - uint16_t xdrStABN; // first allocation block - uint16_t xdrNumABlks; // #of allocation blocks -} ExtDescriptor; -/* extent data record */ -typedef struct ExtDataRec { - ExtDescriptor extDescriptor[3]; -} ExtDataRec; - -/* - * Contents of the HFS MDB. Information comes from "Inside Macintosh: Files", - * chapter 2 ("Data Organization on Volumes"), pages 2-60 to 2-62. - */ -typedef struct DiskFSHFS::MasterDirBlock { - uint16_t drSigWord; // volume signature - uint32_t drCrDate; // date/time of volume creation - uint32_t drLsMod; // date/time of last modification - uint16_t drAtrb; // volume attributes - uint16_t drNmPls; // #of files in root directory - uint16_t drVBMSt; // first block of volume bitmap - uint16_t drAllocPtr; // start of next allocation search - uint16_t drNmAlBlks; // number of allocation blocks in volume - uint32_t drAlBlkSiz; // size (bytes) of allocation blocks - uint32_t drClpSiz; // default clump size - uint16_t drAlBlSt; // first allocation block in volume - uint32_t drNxtCNID; // next unused catalog node ID - uint16_t drFreeBks; // number of unused allocation blocks - uint8_t drVN[28]; // volume name (pascal string) - uint32_t drVolBkUp; // date/time of last backup - uint16_t drVSeqNum; // volume backup sequence number - uint32_t drWrCnt; // volume write count - uint32_t drXTClpSiz; // clump size for extents overflow file - uint32_t drCTClpSiz; // clump size for catalog file - uint16_t drNmRtDirs; // #of directories in root directory - uint32_t drFilCnt; // #of files in volume - uint32_t drDirCnt; // #of directories in volume - uint32_t drFndrInfo[8]; // information used by the Finder - uint16_t drVCSize; // size (blocks) of volume cache - uint16_t drVBMCSize; // size (blocks) of volume bitmap cache - uint16_t drCtlCSize; // size (blocks) of common volume cache - uint32_t drXTFlSize; // size (bytes) of extents overflow file - ExtDataRec drXTExtRec; // extent record for extents overflow file - uint32_t drCTFlSize; // size (bytes) of catalog file - ExtDataRec drCTExtRec; // extent record for catalog file -} MasterDirBlock; - -//}; // namespace DiskImgLib - -/* - * Extract fields from a Master Directory Block. - */ -/*static*/ void DiskFSHFS::UnpackMDB(const uint8_t* buf, MasterDirBlock* pMDB) -{ - pMDB->drSigWord = GetShortBE(&buf[0x00]); - pMDB->drCrDate = GetLongBE(&buf[0x02]); - pMDB->drLsMod = GetLongBE(&buf[0x06]); - pMDB->drAtrb = GetShortBE(&buf[0x0a]); - pMDB->drNmPls = GetShortBE(&buf[0x0c]); - pMDB->drVBMSt = GetShortBE(&buf[0x0e]); - pMDB->drAllocPtr = GetShortBE(&buf[0x10]); - pMDB->drNmAlBlks = GetShortBE(&buf[0x12]); - pMDB->drAlBlkSiz = GetLongBE(&buf[0x14]); - pMDB->drClpSiz = GetLongBE(&buf[0x18]); - pMDB->drAlBlSt = GetShortBE(&buf[0x1c]); - pMDB->drNxtCNID = GetLongBE(&buf[0x1e]); - pMDB->drFreeBks = GetShortBE(&buf[0x22]); - memcpy(pMDB->drVN, &buf[0x24], sizeof(pMDB->drVN)); - pMDB->drVolBkUp = GetLongBE(&buf[0x40]); - pMDB->drVSeqNum = GetShortBE(&buf[0x44]); - pMDB->drWrCnt = GetLongBE(&buf[0x46]); - pMDB->drXTClpSiz = GetLongBE(&buf[0x4a]); - pMDB->drCTClpSiz = GetLongBE(&buf[0x4e]); - pMDB->drNmRtDirs = GetShortBE(&buf[0x52]); - pMDB->drFilCnt = GetLongBE(&buf[0x54]); - pMDB->drDirCnt = GetLongBE(&buf[0x58]); - for (int i = 0; i < (int) NELEM(pMDB->drFndrInfo); i++) - pMDB->drFndrInfo[i] = GetLongBE(&buf[0x5c + i * 4]); - pMDB->drVCSize = GetShortBE(&buf[0x7c]); - pMDB->drVBMCSize = GetShortBE(&buf[0x7e]); - pMDB->drCtlCSize = GetShortBE(&buf[0x80]); - pMDB->drXTFlSize = GetLongBE(&buf[0x82]); - //UnpackExtDataRec(&pMDB->drXTExtRec, &buf[0x86]); // 12 bytes - pMDB->drCTFlSize = GetLongBE(&buf[0x92]); - //UnpackExtDataRec(&pMDB->drXTExtRec, &buf[0x96]); - // next field at 0xa2 -} - -/* - * See if this looks like an HFS volume. - * - * We test a few fields in the master directory block for validity. - */ -/*static*/ DIError DiskFSHFS::TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - MasterDirBlock mdb; - uint8_t blkBuf[kBlkSize]; - - dierr = pImg->ReadBlockSwapped(kMasterDirBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - UnpackMDB(blkBuf, &mdb); - - if (mdb.drSigWord != kSignature) { - dierr = kDIErrFilesystemNotFound; - goto bail; - } - if ((mdb.drAlBlkSiz & 0x1ff) != 0) { - // allocation block size must be a multiple of 512 - LOGI(" HFS: found allocation block size = %u, rejecting", - mdb.drAlBlkSiz); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - if (mdb.drVN[0] == 0 || mdb.drVN[0] > kMaxVolumeName) { - LOGI(" HFS: volume name has len = %d, rejecting", mdb.drVN[0]); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - long minBlocks; - minBlocks = mdb.drNmAlBlks * (mdb.drAlBlkSiz / kBlkSize) + mdb.drAlBlSt + 2; - if (minBlocks > pImg->GetNumBlocks()) { - // We're probably trying to open a 1GB volume as if it were only - // 32MB. Maybe this is a full HFS partition and we're trying to - // see if it's a CFFA image. Whatever the case, we can't do this. - LOGI("HFS: volume exceeds disk image size (%ld vs %ld)", - minBlocks, pImg->GetNumBlocks()); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - // looks good! - -bail: - return dierr; -} - -/* - * Test to see if the image is an HFS disk. - */ -/*static*/ DIError DiskFSHFS::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - //return kDIErrFilesystemNotFound; // DEBUG DEBUG DEBUG - - /* must be block format, should be at least 720K */ - if (!pImg->GetHasBlocks() || pImg->GetNumBlocks() < kExpectedMinBlocks) - return kDIErrFilesystemNotFound; - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i]) == kDIErrNone) { - *pOrder = ordering[i]; - *pFormat = DiskImg::kFormatMacHFS; - return kDIErrNone; - } - } - - LOGI(" HFS didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -/* - * Load some stuff from the volume header. - */ -DIError DiskFSHFS::LoadVolHeader(void) -{ - DIError dierr = kDIErrNone; - MasterDirBlock mdb; - uint8_t blkBuf[kBlkSize]; - - if (fLocalTimeOffset == -1) { - struct tm* ptm; - struct tm tmWhen; - time_t when; - int isDst; - - when = time(NULL); - isDst = localtime(&when)->tm_isdst; - - ptm = gmtime(&when); - if (ptm != NULL) { - tmWhen = *ptm; // make a copy -- static buffers in time functions - tmWhen.tm_isdst = isDst; - - fLocalTimeOffset = (long) (when - mktime(&tmWhen)); - } else - fLocalTimeOffset = 0; - - LOGI(" HFS computed local time offset = %.3f hours", - fLocalTimeOffset / 3600.0); - } - - dierr = fpImg->ReadBlock(kMasterDirBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - UnpackMDB(blkBuf, &mdb); - - /* - * The minimum size of the volume is "number of allocation blocks" plus - * "first allocation block" (to avoid the OS overhead) plus 2 (because - * there's a backup copy of the MDB in the next-to-last block, and - * nothing at all in the very last block). - * - * This isn't the total size, because on larger volumes there can be - * some padding between the last usable block and the backup MDB. The - * only way to find the MDB is to take the DiskImg's block size and - * subtract 2. - */ - assert((mdb.drAlBlkSiz % kBlkSize) == 0); - fNumAllocationBlocks = mdb.drNmAlBlks; - fAllocationBlockSize = mdb.drAlBlkSiz; - fTotalBlocks = fpImg->GetNumBlocks(); - - uint32_t minBlocks; - minBlocks = mdb.drNmAlBlks * (mdb.drAlBlkSiz / kBlkSize) + mdb.drAlBlSt + 2; - assert(fTotalBlocks >= minBlocks); // verified during fs tests - - int volNameLen; - volNameLen = mdb.drVN[0]; - if (volNameLen > kMaxVolumeName) { - assert(false); // should've been trapped earlier - volNameLen = kMaxVolumeName; - } - memcpy(fVolumeName, &mdb.drVN[1], volNameLen); - fVolumeName[volNameLen] = '\0'; - SetVolumeID(); - - fNumFiles = mdb.drFilCnt; - fNumDirectories = mdb.drDirCnt; - fCreatedDateTime = mdb.drCrDate; - fModifiedDateTime = mdb.drLsMod; - - /* - * Create a "magic" directory entry for the volume directory. This - * must come first in the file list. - */ - A2FileHFS* pFile; - pFile = new A2FileHFS(this); - if (pFile == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - pFile->fIsDir = true; - pFile->fIsVolumeDir = true; - pFile->fType = 0; - pFile->fCreator = 0; - strcpy(pFile->fFileName, fVolumeName); // vol names are shorter than - pFile->SetPathName(":", fVolumeName); // filenames, so it fits - pFile->fDataLength = 0; - pFile->fRsrcLength = -1; - pFile->fCreateWhen = - (time_t) (fCreatedDateTime - kDateTimeOffset) - fLocalTimeOffset; - pFile->fModWhen = - (time_t) (fModifiedDateTime - kDateTimeOffset) - fLocalTimeOffset; - pFile->fAccess = DiskFS::kFileAccessUnlocked; - - //LOGI("GOT *** '%s' '%s'", pFile->fFileName, pFile->fPathName); - - AddFileToList(pFile); - -bail: - return dierr; -} - -/* - * Set the volume ID based on fVolumeName. - */ -void DiskFSHFS::SetVolumeID(void) -{ - strcpy(fVolumeID, "HFS "); - strcat(fVolumeID, fVolumeName); -} - -/* - * Blank out the volume usage map. The HFS volume bitmap is not yet supported. - */ -void DiskFSHFS::SetVolumeUsageMap(void) -{ - VolumeUsage::ChunkState cstate; - long block; - - fVolumeUsage.Create(fpImg->GetNumBlocks()); - - cstate.isUsed = true; - cstate.isMarkedUsed = true; - cstate.purpose = VolumeUsage::kChunkPurposeUnknown; - - for (block = fTotalBlocks-1; block >= 0; block--) - fVolumeUsage.SetChunkState(block, &cstate); -} - -/* - * Print some interesting fields to the debug log. - */ -void DiskFSHFS::DumpVolHeader(void) -{ - LOGI("HFS volume header read:"); - LOGI(" volume name = '%s'", fVolumeName); - LOGI(" total blocks = %d (allocSize=%d [x%u], numAllocs=%u)", - fTotalBlocks, fAllocationBlockSize, fAllocationBlockSize / kBlkSize, - fNumAllocationBlocks); - LOGI(" num directories=%d, num files=%d", - fNumDirectories, fNumFiles); - time_t when; - when = (time_t) (fCreatedDateTime - kDateTimeOffset - fLocalTimeOffset); - LOGI(" cre date=0x%08x %.24s", fCreatedDateTime, ctime(&when)); - when = (time_t) (fModifiedDateTime - kDateTimeOffset - fLocalTimeOffset); - LOGI(" mod date=0x%08x %.24s", fModifiedDateTime, ctime(&when)); -} - - -#ifndef EXCISE_GPL_CODE - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSHFS::Initialize(InitMode initMode) -{ - DIError dierr = kDIErrNone; - char msg[kMaxVolumeName + 32]; - - dierr = LoadVolHeader(); - if (dierr != kDIErrNone) - goto bail; - DumpVolHeader(); - - if (initMode == kInitHeaderOnly) { - LOGI(" HFS - headerOnly set, skipping file load"); - goto bail; - } - - sprintf(msg, "Scanning %s", fVolumeName); - if (!fpImg->UpdateScanProgress(msg)) { - LOGI(" HFS cancelled by user"); - dierr = kDIErrCancelled; - goto bail; - } - - /* - * Open the volume with libhfs. We used to set HFS_OPT_NOCACHE to avoid - * consistency problems and reduce the risk of disk corruption should - * CiderPress fail, but it turns out libhfs doesn't write the volume - * bitmap or master dir block unless explicitly flushed anyway. Since - * the caching helps us a lot when just reading -- 4 seconds vs. 9 for - * a CD-ROM over gigabit Ethernet -- we leave it on, and explicitly - * flush every time we make a change. - */ - fHfsVol = hfs_callback_open(LibHFSCB, this, /*HFS_OPT_NOCACHE |*/ - (fpImg->GetReadOnly() ? HFS_MODE_RDONLY : HFS_MODE_RDWR)); - if (fHfsVol == NULL) { - LOGI("ERROR: hfs_opencallback failed: %s", hfs_error); - return kDIErrGeneric; - } - - /* volume dir is guaranteed to come first; if not, we need a lookup func */ - A2FileHFS* pVolumeDir; - pVolumeDir = (A2FileHFS*) GetNextFile(NULL); - - dierr = RecursiveDirAdd(pVolumeDir, ":", 0); - if (dierr != kDIErrNone) - goto bail; - - SetVolumeUsageMap(); - - /* - * Make sure there's nothing lingering. libhfs will fiddle around with - * the MDB if it looks like the volume wasn't unmounted cleanly last time. - */ - hfs_flush(fHfsVol); - -bail: - return dierr; -} - -/* - * Callback function from libhfs. Can read/write/seek. - * - * This is a little clumsy, but it allows us to maintain a separation from - * the libhfs code (which is GPLed). - * - * Returns -1 on failure. - */ -unsigned long DiskFSHFS::LibHFSCB(void* vThis, int op, unsigned long arg1, void* arg2) -{ - DiskFSHFS* pThis = (DiskFSHFS*) vThis; - unsigned long result = (unsigned long) -1; - - assert(pThis != NULL); - - switch (op) { - case HFS_CB_VOLSIZE: - //LOGI(" HFSCB vol size = %ld blocks", pThis->fTotalBlocks); - result = pThis->fTotalBlocks; - break; - case HFS_CB_READ: // arg1=block, arg2=buffer - //LOGI(" HFSCB read block %lu", arg1); - if (arg1 < pThis->fTotalBlocks && arg2 != NULL) { - DIError err = pThis->fpImg->ReadBlock(arg1, arg2); - if (err == kDIErrNone) - result = 0; - else { - LOGI(" HFSCB read %lu failed", arg1); - } - } - break; - case HFS_CB_WRITE: - LOGI(" HFSCB write block %lu", arg1); - if (arg1 < pThis->fTotalBlocks && arg2 != NULL) { - DIError err = pThis->fpImg->WriteBlock(arg1, arg2); - if (err == kDIErrNone) - result = 0; - else { - LOGI(" HFSCB write %lu failed", arg1); - } - } - break; - case HFS_CB_SEEK: // arg1=block, arg2=unused - /* just verify that the seek is legal */ - //LOGI(" HFSCB seek block %lu", arg1); - if (arg1 < pThis->fTotalBlocks) - result = arg1; - break; - default: - assert(false); - } - - //LOGI("--- HFSCB returning %lu", result); - return result; -} - -/* - * Determine the amount of free space on the disk. - */ -DIError DiskFSHFS::GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const -{ - assert(fHfsVol != NULL); - - hfsvolent volEnt; - if (hfs_vstat(fHfsVol, &volEnt) != 0) - return kDIErrGeneric; - - *pTotalUnits = volEnt.totbytes / 512; - *pFreeUnits = volEnt.freebytes / 512; - *pUnitSize = 512; - - return kDIErrNone; -} - -/* - * Recursively traverse the filesystem. - */ -DIError DiskFSHFS::RecursiveDirAdd(A2File* pParent, const char* basePath, int depth) -{ - DIError dierr = kDIErrNone; - hfsdir* dir; - hfsdirent dirEntry; - char* pathBuf = NULL; - int nameOffset; - - /* if we get too deep, assume it's a loop */ - if (depth > kMaxDirectoryDepth) { - dierr = kDIErrDirectoryLoop; - goto bail; - } - - //LOGI(" HFS RecursiveDirAdd '%s'", basePath); - dir = hfs_opendir(fHfsVol, basePath); - if (dir == NULL) { - printf(" HFS unable to open dir '%s'\n", basePath); - LOGI(" HFS unable to open dir '%s'", basePath); - dierr = kDIErrGeneric; - goto bail; - } - - if (strcmp(basePath, ":") == 0) - basePath = ""; - - nameOffset = strlen(basePath) +1; - pathBuf = new char[nameOffset + A2FileHFS::kMaxFileName +1]; - if (pathBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - strcpy(pathBuf, basePath); - pathBuf[nameOffset-1] = A2FileHFS::kFssep; - pathBuf[nameOffset] = '\0'; // not needed - - while (hfs_readdir(dir, &dirEntry) != -1) { - A2FileHFS* pFile; - - pFile = new A2FileHFS(this); - - pFile->InitEntry(&dirEntry); - - pFile->SetPathName(basePath, pFile->fFileName); - pFile->SetParent(pParent); - AddFileToList(pFile); - - if (!fpImg->UpdateScanProgress(NULL)) { - LOGI(" HFS cancelled by user"); - dierr = kDIErrCancelled; - goto bail; - } - - if (dirEntry.flags & HFS_ISDIR) { - strcpy(pathBuf + nameOffset, dirEntry.name); - dierr = RecursiveDirAdd(pFile, pathBuf, depth+1); - if (dierr != kDIErrNone) - goto bail; - } - } - -bail: - delete[] pathBuf; - return dierr; -} - -/* - * Initialize an A2FileHFS structure from the stuff in an hfsdirent. - */ -void A2FileHFS::InitEntry(const hfsdirent* dirEntry) -{ - //printf("--- File '%s' flags=0x%08x fdflags=0x%08x type='%s'\n", - // dirEntry.name, dirEntry.flags, dirEntry.fdflags, - // dirEntry.u.file.type); - - fIsVolumeDir = false; - memcpy(fFileName, dirEntry->name, A2FileHFS::kMaxFileName+1); - fFileName[A2FileHFS::kMaxFileName] = '\0'; // make sure - - if (dirEntry->flags & HFS_ISLOCKED) - fAccess = DiskFS::kFileAccessLocked; - else - fAccess = DiskFS::kFileAccessUnlocked; - if (dirEntry->fdflags & HFS_FNDR_ISINVISIBLE) - fAccess |= A2FileProDOS::kAccessInvisible; - - if (dirEntry->flags & HFS_ISDIR) { - fIsDir = true; - fType = fCreator = 0; - fDataLength = 0; - fRsrcLength = -1; - } else { - uint8_t* pType; - - fIsDir = false; - - pType = (uint8_t*) dirEntry->u.file.type; - fType = - pType[0] << 24 | pType[1] << 16 | pType[2] << 8 | pType[3]; - pType = (uint8_t*) dirEntry->u.file.creator; - fCreator = - pType[0] << 24 | pType[1] << 16 | pType[2] << 8 | pType[3]; - fDataLength = dirEntry->u.file.dsize; - fRsrcLength = dirEntry->u.file.rsize; - - /* - * Resource fork must be at least 512 bytes for Finder, so if - * it has zero length then the file must not have one. - */ - if (fRsrcLength == 0) - fRsrcLength = -1; - } - - /* - * Create/modified dates (we ignore the "last backup" date). The - * hfslib functions convert to time_t for us. - */ - fCreateWhen = dirEntry->crdate; - fModWhen = dirEntry->mddate; -} - -/* - * Return "true" if "name" is valid for use as an HFS volume name. - */ -/*static*/ bool DiskFSHFS::IsValidVolumeName(const char* name) -{ - if (name == NULL) - return false; - - int len = strlen(name); - if (len < 1 || len > kMaxVolumeName) - return false; - - while (*name != '\0') { - if (*name == A2FileHFS::kFssep) - return false; - name++; - } - - return true; -} - -/* - * Return "true" if "name" is valid for use as an HFS file name. - */ -/*static*/ bool DiskFSHFS::IsValidFileName(const char* name) -{ - if (name == NULL) - return false; - - int len = strlen(name); - if (len < 1 || len > A2FileHFS::kMaxFileName) - return false; - - while (*name != '\0') { - if (*name == A2FileHFS::kFssep) - return false; - name++; - } - - return true; -} - -/* - * Format the current volume with HFS. - */ -DIError DiskFSHFS::Format(DiskImg* pDiskImg, const char* volName) -{ - assert(strlen(volName) > 0 && strlen(volName) <= kMaxVolumeName); - - if (!IsValidVolumeName(volName)) - return kDIErrInvalidArg; - - /* set fpImg so calls that rely on it will work; we un-set it later */ - assert(fpImg == NULL); - SetDiskImg(pDiskImg); - - /* need this for callback function */ - fTotalBlocks = fpImg->GetNumBlocks(); - - // need HFS_OPT_2048 for CD-ROM? - if (hfs_callback_format(LibHFSCB, this, 0, volName) != 0) { - LOGI("hfs_callback_format failed (%s)", hfs_error); - return kDIErrGeneric; - } - - // no need to flush; HFS volume is closed - - SetDiskImg(NULL); // shouldn't really be set by us - return kDIErrNone; -} - -/* - * Normalize an HFS path. Invokes DoNormalizePath and handles the buffer - * management (if the normalized path doesn't fit in "*pNormalizedBufLen" - * bytes, we set "*pNormalizedBufLen to the required length). - * - * This is invoked from the generalized "add" function in CiderPress, which - * doesn't want to understand the ins and outs of pathnames. - */ -DIError DiskFSHFS::NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) -{ - DIError dierr = kDIErrNone; - char* normalizedPath = NULL; - int len; - - assert(pNormalizedBufLen != NULL); - assert(normalizedBuf != NULL || *pNormalizedBufLen == 0); - - dierr = DoNormalizePath(path, fssep, &normalizedPath); - if (dierr != kDIErrNone) - goto bail; - - assert(normalizedPath != NULL); - len = strlen(normalizedPath); - if (normalizedBuf == NULL || *pNormalizedBufLen <= len) { - /* too short */ - dierr = kDIErrDataOverrun; - } else { - /* fits */ - strcpy(normalizedBuf, normalizedPath); - } - - *pNormalizedBufLen = len+1; // alloc room for the '\0' - -bail: - delete[] normalizedPath; - return dierr; -} - -/* - * Normalize an HFS path. This requires separating each path component - * out, making it HFS-compliant, and then putting it back in. - * The fssep could be anything, so we need to change it to kFssep. - * - * The caller must delete[] "*pNormalizedPath". - */ -DIError DiskFSHFS::DoNormalizePath(const char* path, char fssep, - char** pNormalizedPath) -{ - DIError dierr = kDIErrNone; - char* workBuf = NULL; - char* partBuf = NULL; - char* outputBuf = NULL; - char* start; - char* end; - char* outPtr; - - assert(path != NULL); - workBuf = new char[strlen(path)+1]; - partBuf = new char[strlen(path)+1 +1]; // need +1 for prepending letter - outputBuf = new char[strlen(path) * 2]; - if (workBuf == NULL || partBuf == NULL || outputBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - strcpy(workBuf, path); - outputBuf[0] = '\0'; - - outPtr = outputBuf; - start = workBuf; - while (*start != '\0') { - //char* origStart = start; // need for debug msg - int partIdx; - - if (fssep == '\0') { - end = NULL; - } else { - end = strchr(start, fssep); - if (end != NULL) - *end = '\0'; - } - partIdx = 0; - - /* - * Copy, converting colons to underscores. We should strip out any - * illegal characters here, but there's not much in HFS that's - * considered illegal. - */ - while (*start != '\0') { - if (*start == A2FileHFS::kFssep) - partBuf[partIdx++] = '_'; - else - partBuf[partIdx++] = *start; - start++; - } - - /* - * Truncate at 31 chars, preserving anything that looks like a - * filename extension. "partIdx" represents the length of the - * string at this point. "partBuf" holds the string, which we - * want to null-terminate before proceeding. - * - * Try to keep the filename extension, if any. - */ - partBuf[partIdx] = '\0'; - if (partIdx > A2FileHFS::kMaxFileName) { - const char* pDot = strrchr(partBuf, '.'); - //int DEBUGDOTLEN = pDot - partBuf; - if (pDot != NULL && partIdx - (pDot-partBuf) <= kMaxExtensionLen) { - int dotLen = partIdx - (pDot-partBuf); - memmove(partBuf + (A2FileProDOS::kMaxFileName - dotLen), - pDot, dotLen); // don't use memcpy, move might overlap - } - partIdx = A2FileProDOS::kMaxFileName; - } - partBuf[partIdx] = '\0'; - - //LOGI(" HFS Converted component '%s' to '%s'", - // origStart, partBuf); - - if (outPtr != outputBuf) - *outPtr++ = A2FileHFS::kFssep; - strcpy(outPtr, partBuf); - outPtr += partIdx; - - /* - * Continue with next segment. - */ - if (end == NULL) - break; - start = end+1; - } - - *outPtr = '\0'; - - LOGI(" HFS Converted path '%s' to '%s' (fssep='%c')", - path, outputBuf, fssep); - assert(*outputBuf != '\0'); - - *pNormalizedPath = outputBuf; - outputBuf = NULL; - -bail: - delete[] workBuf; - delete[] partBuf; - delete[] outputBuf; - return dierr; -} - -/* - * Compare two Macintosh filename strings. - * - * This requires some effort because the Macintosh Roman character set - * doesn't sort the same way that ASCII does. HFS is case-insensitive but - * case-preserving, so we need to deal with that too. The hfs_charorder - * table takes care of it. - * - * Returns <0, ==0, or >0 depending on whether sstr1 is lexically less than, - * equal to, or greater than sstr2. - */ -/*static*/ int DiskFSHFS::CompareMacFileNames(const char* sstr1, const char* sstr2) -{ - const uint8_t* str1 = (const uint8_t*) sstr1; - const uint8_t* str2 = (const uint8_t*) sstr2; - int diff; - - while (*str1 && *str2) { - diff = hfs_charorder[*str1] - hfs_charorder[*str2]; - - if (diff != 0) - return diff; - - str1++; - str2++; - } - - return *str1 - *str2; -} - -/* - * Keep tweaking the filename until it no longer matches an existing file. - * The first time this is called we don't know if the name is unique or not, - * so we need to start by checking that. - * - * We have our choice between the DiskFS GetFileByName(), which traverses - * a linear list, and hfs_stat(), which uses more efficient data structures - * but may require disk reads. We use the DiskFS interface, on the assumption - * that someday we'll switch the linear list to a tree structure. - */ -DIError DiskFSHFS::MakeFileNameUnique(const char* pathName, char** pUniqueName) -{ - A2File* pFile; - const int kMaxExtra = 3; - const int kMaxDigits = 999; - char* uniqueName; - char* fileName; // points inside uniqueName - - assert(pathName != NULL); - assert(pathName[0] == A2FileHFS::kFssep); - - /* see if it exists */ - pFile = GetFileByName(pathName+1); - if (pFile == NULL) { - *pUniqueName = NULL; - return kDIErrNone; - } - - /* make a copy we can chew on */ - uniqueName = new char[strlen(pathName) + kMaxExtra +1]; - strcpy(uniqueName, pathName); - - fileName = strrchr(uniqueName, A2FileHFS::kFssep); - assert(fileName != NULL); - fileName++; - - int nameLen = strlen(fileName); - int dotOffset=0, dotLen=0; - char dotBuf[kMaxExtensionLen+1]; - - /* ensure the result will be null-terminated */ - memset(fileName + nameLen, 0, kMaxExtra+1); - - /* - * If this has what looks like a filename extension, grab it. We want - * to preserve ".gif", ".c", etc., since the filetypes don't necessarily - * do everything we need. - */ - const char* cp = strrchr(fileName, '.'); - if (cp != NULL) { - int tmpOffset = cp - fileName; - if (tmpOffset > 0 && nameLen - tmpOffset <= kMaxExtensionLen) { - LOGI(" HFS (keeping extension '%s')", cp); - assert(strlen(cp) <= kMaxExtensionLen); - strcpy(dotBuf, cp); - dotOffset = tmpOffset; - dotLen = nameLen - dotOffset; - } - } - - int digits = 0; - int digitLen; - int copyOffset; - char digitBuf[kMaxExtra+1]; - do { - if (digits == kMaxDigits) - return kDIErrFileExists; - digits++; - - /* not the most efficient way to do this, but it'll do */ - sprintf(digitBuf, "%d", digits); - digitLen = strlen(digitBuf); - if (nameLen + digitLen > A2FileHFS::kMaxFileName) - copyOffset = A2FileHFS::kMaxFileName - dotLen - digitLen; - else - copyOffset = nameLen - dotLen; - memcpy(fileName + copyOffset, digitBuf, digitLen); - if (dotLen != 0) - memcpy(fileName + copyOffset + digitLen, dotBuf, dotLen); - } while (GetFileByName(uniqueName+1) != NULL); - - LOGI(" HFS converted to unique name: %s", uniqueName); - - *pUniqueName = uniqueName; - return kDIErrNone; -} - -/* - * Create a new file or directory. Automatically creates the base path - * if necessary. - * - * NOTE: much of this was cloned out of the ProDOS code. We probably want - * a stronger set of utility functions in the parent class now that we have - * more than one hierarchical file system. - */ -DIError DiskFSHFS::CreateFile(const CreateParms* pParms, A2File** ppNewFile) -{ - DIError dierr = kDIErrNone; - char typeStr[5], creatorStr[5]; - char* normalizedPath = NULL; - char* basePath = NULL; - char* fileName = NULL; - char* fullPath = NULL; - A2FileHFS* pSubdir = NULL; - A2FileHFS* pNewFile = NULL; - hfsfile* pHfsFile = NULL; - const bool createUnique = (GetParameter(kParm_CreateUnique) != 0); - - assert(fHfsVol != NULL); - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - - assert(pParms != NULL); - assert(pParms->pathName != NULL); - assert(pParms->storageType == A2FileProDOS::kStorageSeedling || - pParms->storageType == A2FileProDOS::kStorageExtended || - pParms->storageType == A2FileProDOS::kStorageDirectory); - // kStorageVolumeDirHeader not allowed -- that's created by Format - LOGI(" HFS ---v--- CreateFile '%s'", pParms->pathName); - - /* - * Normalize the pathname so that all components are HFS-safe - * and separated by ':'. - * - * This must not "sanitize" the path. We need to be working with the - * original characters, not the sanitized-for-display versions. - */ - assert(pParms->pathName != NULL); - dierr = DoNormalizePath(pParms->pathName, pParms->fssep, - &normalizedPath); - if (dierr != kDIErrNone) - goto bail; - assert(normalizedPath != NULL); - - /* - * The normalized path lacks a leading ':', and might need to - * have some digits added to make the name unique. - */ - fullPath = new char[strlen(normalizedPath)+2]; - fullPath[0] = A2FileHFS::kFssep; - strcpy(fullPath+1, normalizedPath); - delete[] normalizedPath; - normalizedPath = NULL; - - /* - * Make the name unique within the current directory. This requires - * appending digits until the name doesn't match any others. - */ - if (createUnique && - pParms->storageType != A2FileProDOS::kStorageDirectory) - { - char* uniquePath; - - dierr = MakeFileNameUnique(fullPath, &uniquePath); - if (dierr != kDIErrNone) - goto bail; - if (uniquePath != NULL) { - delete[] fullPath; - fullPath = uniquePath; - } - } else { - /* can't make unique; check to see if it already exists */ - hfsdirent dirEnt; - if (hfs_stat(fHfsVol, fullPath, &dirEnt) == 0) { - if (pParms->storageType == A2FileProDOS::kStorageDirectory) - dierr = kDIErrDirectoryExists; - else - dierr = kDIErrFileExists; - goto bail; - } - } - - /* - * Split the base path and filename apart. - */ - char* cp; - cp = strrchr(fullPath, A2FileHFS::kFssep); - assert(cp != NULL); - if (cp == fullPath) { - assert(basePath == NULL); - fileName = new char[strlen(fullPath) +1]; - strcpy(fileName, fullPath); - } else { - int dirNameLen = cp - fullPath; - - fileName = new char[strlen(cp+1) +1]; - strcpy(fileName, cp+1); - basePath = new char[dirNameLen+1]; - strncpy(basePath, fullPath, dirNameLen); - basePath[dirNameLen] = '\0'; - } - - LOGI("SPLIT: '%s' '%s'", basePath, fileName); - - assert(fileName != NULL); - - /* - * Open the base path. If it doesn't exist, create it recursively. - */ - if (basePath != NULL) { - LOGI(" HFS Creating '%s' in '%s'", fileName, basePath); - /* - * Open the named subdir, creating it if it doesn't exist. We need - * to check basePath+1 because we're comparing against what's in our - * linear file list, and that doesn't include the leading ':'. - */ - pSubdir = (A2FileHFS*)GetFileByName(basePath+1, CompareMacFileNames); - if (pSubdir == NULL) { - LOGI(" HFS Creating subdir '%s'", basePath); - A2File* pNewSub; - CreateParms newDirParms; - newDirParms.pathName = basePath; - newDirParms.fssep = A2FileHFS::kFssep; - newDirParms.storageType = A2FileProDOS::kStorageDirectory; - newDirParms.fileType = 0; - newDirParms.auxType = 0; - newDirParms.access = 0; - newDirParms.createWhen = newDirParms.modWhen = time(NULL); - dierr = this->CreateFile(&newDirParms, &pNewSub); - if (dierr != kDIErrNone) - goto bail; - assert(pNewSub != NULL); - - pSubdir = (A2FileHFS*) pNewSub; - } - - /* - * And now the annoying part. We need to reconstruct basePath out - * of the filenames actually present, rather than relying on the - * argument passed in. That's because HFS is case-insensitive but - * case-preserving. It's not crucial for our inner workings, but the - * linear file list in the DiskFS should have accurate strings. - * (It'll work just fine, but the display might show the wrong values - * for parent directories until they reload the disk.) - * - * On the bright side, we know exactly how long the string needs - * to be, so we can just stomp on it in place. Assuming, of course, - * that the filename created matches up with what the filename - * normalizer came up with, which we can guarantee since (a) everybody - * uses the same normalizer and (b) the "uniqueify" stuff doesn't - * kick in for subdirs because we wouldn't be creating a new subdir - * if it didn't already exist. - * - * This is essentially the same as RegeneratePathName(), but that's - * meant for a situation where the filename already exists. - */ - A2FileHFS* pBaseDir = pSubdir; - int basePathLen = strlen(basePath); - while (!pBaseDir->IsVolumeDirectory()) { - const char* fixedName = pBaseDir->GetFileName(); - int fixedLen = strlen(fixedName); - if (fixedLen > basePathLen) { - assert(false); - break; - } - assert(basePathLen == fixedLen || - *(basePath + (basePathLen-fixedLen-1)) == kDIFssep); - memcpy(basePath + (basePathLen-fixedLen), fixedName, fixedLen); - basePathLen -= fixedLen+1; - - pBaseDir = (A2FileHFS*) pBaseDir->GetParent(); - assert(pBaseDir != NULL); - } - // check the math; we should be left with the leading ':' - if (pSubdir->IsVolumeDirectory()) - assert(basePathLen == 1); - else - assert(basePathLen == 0); - } else { - /* open the volume directory */ - LOGI(" HFS Creating '%s' in volume dir", fileName); - /* volume dir must be first in the list */ - pSubdir = (A2FileHFS*) GetNextFile(NULL); - assert(pSubdir != NULL); - assert(pSubdir->IsVolumeDirectory()); - } - if (pSubdir == NULL) { - LOGI(" HFS Unable to open subdir '%s'", basePath); - dierr = kDIErrFileNotFound; - goto bail; - } - - /* - * Figure out file type. - */ - A2FileHFS::ConvertTypeToHFS(pParms->fileType, pParms->auxType, - typeStr, creatorStr); - - /* - * Create the file or directory. Populate "dirEnt" with the details. - */ - hfsdirent dirEnt; - if (pParms->storageType == A2FileProDOS::kStorageDirectory) { - /* create the directory */ - if (hfs_mkdir(fHfsVol, fullPath) != 0) { - LOGI(" HFS mkdir '%s' failed: %s", fullPath, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - if (hfs_stat(fHfsVol, fullPath, &dirEnt) != 0) { - LOGI(" HFS stat on new dir failed: %s", hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - /* create date *might* be useful, but probably not worth adjusting */ - } else { - /* create, and open, the file */ - pHfsFile = hfs_create(fHfsVol, fullPath, typeStr, creatorStr); - if (pHfsFile == NULL) { - LOGI(" HFS create failed: %s", hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - if (hfs_fstat(pHfsFile, &dirEnt) != 0) { - LOGI(" HFS fstat on new file failed: %s", hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - - /* set the attributes according to pParms, and update the file */ - dirEnt.crdate = pParms->createWhen; - dirEnt.mddate = pParms->modWhen; - if (pParms->access & A2FileProDOS::kAccessInvisible) - dirEnt.fdflags |= HFS_FNDR_ISINVISIBLE; - else - dirEnt.fdflags &= ~HFS_FNDR_ISINVISIBLE; - if ((pParms->access & ~A2FileProDOS::kAccessInvisible) == kFileAccessLocked) - dirEnt.flags |= HFS_ISLOCKED; - else - dirEnt.flags &= ~HFS_ISLOCKED; - - (void) hfs_fsetattr(pHfsFile, &dirEnt); - (void) hfs_close(pHfsFile); - pHfsFile = NULL; - } - - /* - * Success! - * - * Create a new entry and set the structure fields. - */ - pNewFile = new A2FileHFS(this); - pNewFile->InitEntry(&dirEnt); - pNewFile->SetPathName(basePath == NULL ? "" : basePath, pNewFile->fFileName); - pNewFile->SetParent(pSubdir); - - /* - * Because we're hierarchical, and we guarantee that the contents of - * subdirectories are grouped together, we must insert the file into an - * appropriate place in the list rather than just throwing it onto the - * end. - * - * The proper location for the new file in the linear list is in sorted - * order with the files in the current directory. We have to be careful - * here because libhfs is going to use Macintosh Roman sort ordering, - * which may be different from ASCII ordering. Worst case: we end up - * putting it in the wrong place and it jumps around when the disk image - * is reopened. - * - * All files in a subdir appear in the list after that subdir, but there - * might be intervening entries from deeper directories. So we have to - * chase through some or all of the file list to find the right place. - * Not great, but we don't have enough files or do adds often enough to - * make this worth optimizing. - */ - A2File* pLastSubdirFile; - A2File* pPrevFile; - A2File* pNextFile; - - pPrevFile = pLastSubdirFile = pSubdir; - pNextFile = GetNextFile(pPrevFile); - while (pNextFile != NULL) { - if (pNextFile->GetParent() == pNewFile->GetParent()) { - /* in same subdir, compare names */ - if (CompareMacFileNames(pNextFile->GetPathName(), - pNewFile->GetPathName()) > 0) - { - /* passed it; insert new after previous file */ - pLastSubdirFile = pPrevFile; - LOGI(" HFS Found '%s' > cur(%s)", pNextFile->GetPathName(), - pNewFile->GetPathName()); - break; - } - - /* still too early; save in case it's last one in dir */ - pLastSubdirFile = pNextFile; - } - pPrevFile = pNextFile; - pNextFile = GetNextFile(pNextFile); - } - - /* insert us after last file we saw that was part of the same subdir */ - LOGI(" HFS inserting '%s' after '%s'", pNewFile->GetPathName(), - pLastSubdirFile->GetPathName()); - InsertFileInList(pNewFile, pLastSubdirFile); - //LOGI("LIST NOW:"); - //DumpFileList(); - - *ppNewFile = pNewFile; - pNewFile = NULL; - -bail: - delete pNewFile; - delete[] normalizedPath; - delete[] basePath; - delete[] fileName; - delete[] fullPath; - hfs_flush(fHfsVol); - LOGI(" HFS ---^--- CreateFile '%s' DONE", pParms->pathName); - return dierr; -} - -/* - * Delete the named file. - * - * We need to use a different call for file vs. directory. - */ -DIError DiskFSHFS::DeleteFile(A2File* pGenericFile) -{ - DIError dierr = kDIErrNone; - char* pathName = NULL; - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - if (pGenericFile->IsFileOpen()) - return kDIErrFileOpen; - - A2FileHFS* pFile = (A2FileHFS*) pGenericFile; - pathName = pFile->GetLibHFSPathName(); - LOGI(" Deleting '%s'", pathName); - - if (pFile->IsDirectory()) { - if (hfs_rmdir(fHfsVol, pathName) != 0) { - LOGI(" HFS rmdir failed '%s': '%s'", pathName, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - } else { - if (hfs_delete(fHfsVol, pathName) != 0) { - LOGI(" HFS delete failed '%s': '%s'", pathName, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - } - - /* - * Remove the A2File* from the list. - */ - DeleteFileFromList(pFile); - -bail: - hfs_flush(fHfsVol); - delete[] pathName; - return dierr; -} - -/* - * Rename a file. - * - * Pass in a pointer to the file and a string with the new filename (just - * the filename, not a pathname -- this function doesn't move files - * between directories). The new name must already be normalized. - * - * Renaming the magic volume directory "file" is not allowed. - * - * We don't try to keep AppleWorks aux type flags consistent (they're used - * to determine which characters are lower case on ProDOS disks). They'll - * get fixed up when we copy them to a ProDOS disk, which is the only way - * 8-bit AppleWorks can get at them. - */ -DIError DiskFSHFS::RenameFile(A2File* pGenericFile, const char* newName) -{ - DIError dierr = kDIErrNone; - A2FileHFS* pFile = (A2FileHFS*) pGenericFile; - char* colonOldName = NULL; - char* colonNewName = NULL; - - if (pFile == NULL || newName == NULL) - return kDIErrInvalidArg; - if (!IsValidFileName(newName)) - return kDIErrInvalidArg; - if (pFile->IsVolumeDirectory()) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - char* lastColon; - - colonOldName = pFile->GetLibHFSPathName(); // adds ':' to start of string - lastColon = strrchr(colonOldName, A2FileHFS::kFssep); - assert(lastColon != NULL); - if (lastColon == colonOldName) { - /* in root dir */ - colonNewName = new char[1 + strlen(newName) +1]; - colonNewName[0] = A2FileHFS::kFssep; - strcpy(colonNewName+1, newName); - } else { - /* prepend subdir */ - int len = lastColon - colonOldName +1; // e.g. ":path1:path2:" - colonNewName = new char[len + strlen(newName) +1]; - strncpy(colonNewName, colonOldName, len); - strcpy(colonNewName+len, newName); - } - - LOGI(" HFS renaming '%s' to '%s'", colonOldName, colonNewName); - - if (hfs_rename(fHfsVol, colonOldName, colonNewName) != 0) { - LOGI(" HFS rename('%s','%s') failed: %s", - colonOldName, colonNewName, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - - /* - * Success! Update the file name. - */ - strcpy(pFile->fFileName, newName); - - /* - * Now the fun part. If we simply renamed a file, we can just update the - * one entry. If we renamed a directory, life gets interesting because - * we store the full pathname in every A2FileHFS entry. (It's an - * efficiency win most of the time, but it's really annoying here.) - * - * HFS makes this especially unpleasant because it keeps the files - * arranged in sorted order. If we change a file's name, we may have to - * move it to a new position in the linear file list. If we don't, the - * list no longer reflects the order in which the files actually appear - * on the disk, and they'll shift around when we reload. - * - * There are two approaches: re-sort the list (awkward, since it's stored - * in a linked list -- we'd probably want to sort tags in a parallel - * structure), or find the affected block of files, find the new start - * position, and shift the entire range in one shot. - * - * This doesn't seem like something that anybody but me will ever care - * about, so I'm going to skip it for now. - */ - A2File* pCur; - if (pFile->IsDirectory()) { - /* do all files that come after us */ - pCur = pFile; - while (pCur != NULL) { - RegeneratePathName((A2FileHFS*) pCur); - pCur = GetNextFile(pCur); - } - } else { - RegeneratePathName(pFile); - } - -bail: - delete[] colonOldName; - delete[] colonNewName; - hfs_flush(fHfsVol); - return dierr; -} - -/* - * Regenerate fPathName for the specified file. - * - * Has no effect on the magic volume dir entry. - * - * This could be implemented more efficiently, but it's only used when - * renaming files, so there's not much point. - * - * [This was lifted straight out of the ProDOS sources. It should probably - * be moved into generic DiskFS.] - */ -DIError DiskFSHFS::RegeneratePathName(A2FileHFS* pFile) -{ - A2FileHFS* pParent; - char* buf = NULL; - int len; - - /* nothing to do here */ - if (pFile->IsVolumeDirectory()) - return kDIErrNone; - - /* compute the length of the path name */ - len = strlen(pFile->GetFileName()); - pParent = (A2FileHFS*) pFile->GetParent(); - while (!pParent->IsVolumeDirectory()) { - len++; // leave space for the ':' - len += strlen(pParent->GetFileName()); - - pParent = (A2FileHFS*) pParent->GetParent(); - } - - buf = new char[len+1]; - if (buf == NULL) - return kDIErrMalloc; - - /* generate the new path name */ - int partLen; - partLen = strlen(pFile->GetFileName()); - strcpy(buf + len - partLen, pFile->GetFileName()); - len -= partLen; - - pParent = (A2FileHFS*) pFile->GetParent(); - while (!pParent->IsVolumeDirectory()) { - assert(len > 0); - buf[--len] = A2FileHFS::kFssep; - - partLen = strlen(pParent->GetFileName()); - strncpy(buf + len - partLen, pParent->GetFileName(), partLen); - len -= partLen; - assert(len >= 0); - - pParent = (A2FileHFS*) pParent->GetParent(); - } - - LOGI("Replacing '%s' with '%s'", pFile->GetPathName(), buf); - pFile->SetPathName("", buf); - delete[] buf; - - return kDIErrNone; -} - -/* - * Change the HFS volume name. - * - * This uses the same libhfs interface that we use for renaming files. The - * Mac convention is to *not* start the volume name with a colon. In fact, - * the libhfs convention is to *end* the volume names with a colon. - */ -DIError DiskFSHFS::RenameVolume(const char* newName) -{ - DIError dierr = kDIErrNone; - A2FileHFS* pFile; - char* oldNameColon = NULL; - char* newNameColon = NULL; - - if (!IsValidVolumeName(newName)) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - - /* get file list entry for volume name */ - pFile = (A2FileHFS*) GetNextFile(NULL); - assert(strcmp(pFile->GetFileName(), fVolumeName) == 0); - - oldNameColon = new char[strlen(fVolumeName)+2]; - strcpy(oldNameColon, fVolumeName); - strcat(oldNameColon, ":"); - newNameColon = new char[strlen(newName)+2]; - strcpy(newNameColon, newName); - strcat(newNameColon, ":"); - - if (hfs_rename(fHfsVol, oldNameColon, newNameColon) != 0) { - LOGI(" HFS rename '%s' -> '%s' failed: %s", - oldNameColon, newNameColon, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - - /* update stuff */ - strcpy(fVolumeName, newName); - SetVolumeID(); - strcpy(pFile->fFileName, newName); - pFile->SetPathName("", newName); - -bail: - delete[] oldNameColon; - delete[] newNameColon; - hfs_flush(fHfsVol); - return dierr; -} - -/* - * Set file attributes. - */ -DIError DiskFSHFS::SetFileInfo(A2File* pGenericFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) -{ - DIError dierr = kDIErrNone; - A2FileHFS* pFile = (A2FileHFS*) pGenericFile; - hfsdirent dirEnt; - char* colonPath; - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (pFile == NULL) - return kDIErrInvalidArg; - if (pFile->IsDirectory() || pFile->IsVolumeDirectory()) - return kDIErrNone; // impossible; just ignore it - - colonPath = pFile->GetLibHFSPathName(); - - if (hfs_stat(fHfsVol, colonPath, &dirEnt) != 0) { - LOGI(" HFS unable to stat '%s': %s", colonPath, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - - A2FileHFS::ConvertTypeToHFS(fileType, auxType, - dirEnt.u.file.type, dirEnt.u.file.creator); - - if (accessFlags & A2FileProDOS::kAccessInvisible) - dirEnt.fdflags |= HFS_FNDR_ISINVISIBLE; - else - dirEnt.fdflags &= ~HFS_FNDR_ISINVISIBLE; - - if ((accessFlags & ~A2FileProDOS::kAccessInvisible) == kFileAccessLocked) - dirEnt.flags |= HFS_ISLOCKED; - else - dirEnt.flags &= ~HFS_ISLOCKED; - - LOGD(" HFS setting '%s' to fdflags=0x%04x flags=0x%04x", - colonPath, dirEnt.fdflags, dirEnt.flags); - LOGD(" type=0x%08x creator=0x%08x", fileType, auxType); - - if (hfs_setattr(fHfsVol, colonPath, &dirEnt) != 0) { - LOGW(" HFS setattr '%s' failed: %s", colonPath, hfs_error); - dierr = kDIErrGeneric; - goto bail; - } - - /* update our local copy */ - pFile->fType = fileType; - pFile->fCreator = auxType; - pFile->fAccess = accessFlags; // should actually base them on HFS vals - -bail: - delete[] colonPath; - hfs_flush(fHfsVol); - return dierr; -} - -#endif // !EXCISE_GPL_CODE - - -/* - * =========================================================================== - * A2FileHFS - * =========================================================================== - */ - -/* - * Dump the contents of the A2File structure. - */ -void A2FileHFS::Dump(void) const -{ - LOGI("A2FileHFS '%s'", fFileName); -} - -/* convert hex to decimal */ -inline int FromHex(char hexVal) -{ - if (hexVal >= '0' && hexVal <= '9') - return hexVal - '0'; - else if (hexVal >= 'a' && hexVal <= 'f') - return hexVal -'a' + 10; - else if (hexVal >= 'A' && hexVal <= 'F') - return hexVal - 'A' + 10; - else - return -1; -} - -/* - * If this has a ProDOS filetype, convert it. - * - * This stuff is defined in Technical Note PT515, "Apple File Exchange Q&As". - * In theory we should convert type=BINA and type=TEXT regardless of the - * creator, but since those just go to generic text/binary types I don't - * think we need to handle it here (and I'm more comfortable leaving them - * with their Macintosh creators). - * - * In some respects, converting to ProDOS types is a bad idea, because we - * don't have a 1:1 mapping. If we copy a pdos/p\0\0\0 file we will store it - * as pdos/BINA instead. In practice, for the Apple II world they are - * equivalent, and CiderPress really doesn't need the "raw" file type. If - * it becomes annoying, we can add a DiskFSParameter to control it. - */ -uint32_t A2FileHFS::GetFileType(void) const -{ - if (fCreator != kPdosType) - return fType; - - if ((fType & 0xffff) == 0x2020) { - // 'XY ', where XY are hex digits for ProDOS file type - int digit1, digit2; - - digit1 = FromHex((char) (fType >> 24)); - digit2 = FromHex((char) (fType >> 16)); - if (digit1 < 0 || digit2 < 0) { - LOGI(" Unexpected: pdos + %08x", fType); - return 0x00; - } - return digit1 << 4 | digit2; - } - - uint8_t flag = (uint8_t)(fType >> 24); - if (flag == 0x70) { // 'p' - /* type and aux embedded within */ - return (fType >> 16) & 0xff; - } else { - /* type stored as a string */ - if (fType == 0x42494e41) // 'BINA' - return 0x00; // NON - else if (fType == 0x54455854) // 'TEXT' - return 0x04; - else if (fType == 0x50535953) // 'PSYS' - return 0xff; - else if (fType == 0x50533136) // 'PS16' - return 0xb3; - else - return 0x00; - } -} - -/* - * If this has a ProDOS aux type, convert it. - */ -uint32_t A2FileHFS::GetAuxType(void) const -{ - if (fCreator != kPdosType) - return fCreator; - - uint8_t flag = (uint8_t)(fType >> 24); - if (flag == 0x70) { // 'p' - /* type and aux embedded within */ - return fType & 0xffff; - } else { - return 0x0000; - } -} - -/* - * Set the full pathname to a combination of the base path and the - * current file's name. - * - * If we're in the volume directory, pass in "" for the base path (not NULL). - */ -void A2FileHFS::SetPathName(const char* basePath, const char* fileName) -{ - assert(basePath != NULL && fileName != NULL); - if (fPathName != NULL) - delete[] fPathName; - - // strip leading ':' (but treat ":" specially for volume dir entry) - if (basePath[0] == ':' && basePath[1] != '\0') - basePath++; - - int baseLen = strlen(basePath); - fPathName = new char[baseLen + 1 + strlen(fileName)+1]; - strcpy(fPathName, basePath); - if (baseLen != 0 && - !(baseLen == 1 && basePath[0] == ':')) - { - *(fPathName + baseLen) = kFssep; - baseLen++; - } - strcpy(fPathName + baseLen, fileName); -} - - -#ifndef EXCISE_GPL_CODE - -/* - * Return a copy of the pathname that libhfs will like. - * - * The caller must delete[] the return value. - */ -char* A2FileHFS::GetLibHFSPathName(void) const -{ - char* nameBuf; - - nameBuf = new char[strlen(fPathName)+2]; - nameBuf[0] = kFssep; - strcpy(nameBuf+1, fPathName); - - return nameBuf; -} - -/* - * Convert numeric file/aux type to HFS strings. "pType" and "pCreator" must - * be able to hold 5 bytes each (4-byte type + nul). - * - * Follows the PT515 recommendations, mostly. The "PSYS" and "PS16" - * conversions discard the file's aux type and therefore are unsuitable, - * and the conversion of SRC throws away its identity. - */ -/*static*/ void A2FileHFS::ConvertTypeToHFS(uint32_t fileType, uint32_t auxType, - char* pType, char* pCreator) -{ - if (fileType == 0x00 && auxType == 0x0000) { - strcpy(pCreator, "pdos"); - strcpy(pType, "BINA"); - } else if (fileType == 0x04 && auxType == 0x0000) { - strcpy(pCreator, "pdos"); - strcpy(pType, "TEXT"); - } else if (fileType <= 0xff && - auxType <= 0xffff) - { - pType[0] = 'p'; - pType[1] = (uint8_t) fileType; - pType[2] = (uint8_t) (auxType >> 8); - pType[3] = (uint8_t) auxType; - pType[4] = '\0'; - pCreator[0] = 'p'; - pCreator[1] = 'd'; - pCreator[2] = 'o'; - pCreator[3] = 's'; - pCreator[4] = '\0'; - } else { - pType[0] = (uint8_t)(fileType >> 24); - pType[1] = (uint8_t)(fileType >> 16); - pType[2] = (uint8_t)(fileType >> 8); - pType[3] = (uint8_t) fileType; - pType[4] = '\0'; - pCreator[0] = (uint8_t)(auxType >> 24); - pCreator[1] = (uint8_t)(auxType >> 16); - pCreator[2] = (uint8_t)(auxType >> 8); - pCreator[3] = (uint8_t) auxType; - pCreator[4] = '\0'; - } -} - - -/* - * Open a file through libhfs. - * - * libhfs wants filenames to begin with ':' unless they start with the - * name of the volume. This is the opposite of the convention followed - * by the rest of CiderPress (and most of the civilized world), so instead - * of storing the pathname that way we just tack it on here. - */ -DIError A2FileHFS::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - DIError dierr = kDIErrNone; - A2FDHFS* pOpenFile = NULL; - hfsfile* pHfsFile; - char* nameBuf = NULL; - - if (fpOpenFile != NULL) - return kDIErrAlreadyOpen; - //if (rsrcFork && fRsrcLength < 0) - // return kDIErrForkNotFound; - - nameBuf = GetLibHFSPathName(); - - DiskFSHFS* pDiskFS = (DiskFSHFS*) GetDiskFS(); - pHfsFile = hfs_open(pDiskFS->GetHfsVol(), nameBuf); - if (pHfsFile == NULL) { - LOGI(" HFS hfs_open(%s) failed: %s", nameBuf, hfs_error); - dierr = kDIErrGeneric; // better value might be in errno - goto bail; - } - hfs_setfork(pHfsFile, rsrcFork ? 1 : 0); - - pOpenFile = new A2FDHFS(this, pHfsFile); - - fpOpenFile = pOpenFile; - *ppOpenFile = pOpenFile; - -bail: - delete[] nameBuf; - return dierr; -} - - -/* - * =========================================================================== - * A2FDHFS - * =========================================================================== - */ - -/* - * Read a chunk of data from the fake file. - */ -DIError A2FDHFS::Read(void* buf, size_t len, size_t* pActual) -{ - long result; - - LOGD(" HFS reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), - hfs_seek(fHfsFile, 0, HFS_SEEK_CUR)); - - //A2FileHFS* pFile = (A2FileHFS*) fpFile; - - result = hfs_read(fHfsFile, buf, len); - if (result < 0) - return kDIErrReadFailed; - - if (pActual != NULL) { - *pActual = (size_t) result; - } else if (result != (long) len) { - // short read, can't report it, return error - return kDIErrDataUnderrun; - } - - /* - * To do this right we need to break the hfs_read() into smaller - * pieces. However, it only really affects us for files that are - * getting reformatted, because that's the only time we grab the - * entire thing in one big piece. - */ - long offset = hfs_seek(fHfsFile, 0, HFS_SEEK_CUR); - if (!UpdateProgress(offset)) { - return kDIErrCancelled; - } - - return kDIErrNone; -} - -/* - * Write data at the current offset. - * - * (In the current implementation, the entire file is always written in - * one piece. This function does work correctly with multiple smaller - * pieces though, because it lets libhfs do all the work.) - */ -DIError A2FDHFS::Write(const void* buf, size_t len, size_t* pActual) -{ - long result; - - LOGD(" HFS writing %lu bytes to '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), - hfs_seek(fHfsFile, 0, HFS_SEEK_CUR)); - - fModified = true; // assume something gets changed - - //A2FileHFS* pFile = (A2FileHFS*) fpFile; - - result = hfs_write(fHfsFile, buf, len); - if (result < 0) - return kDIErrWriteFailed; - - if (pActual != NULL) { - *pActual = (size_t) result; - } else if (result != (long) len) { - // short write, can't report it, return error - return kDIErrDataUnderrun; - } - - /* to make this work right, we need to break hfs_write into pieces */ - long offset = hfs_seek(fHfsFile, 0, HFS_SEEK_CUR); - if (!UpdateProgress(offset)) { - return kDIErrCancelled; - } - - /* - * We don't hfs_flush here, because we don't expect the application to - * hold the file open, and we flush in Close(). - */ - - return kDIErrNone; -} - -/* - * Seek to a new offset. - */ -DIError A2FDHFS::Seek(di_off_t offset, DIWhence whence) -{ - int hfsWhence; - unsigned long result; - - switch (whence) { - case kSeekSet: hfsWhence = HFS_SEEK_SET; break; - case kSeekEnd: hfsWhence = HFS_SEEK_END; break; - case kSeekCur: hfsWhence = HFS_SEEK_CUR; break; - default: - assert(false); - return kDIErrInvalidArg; - } - - result = hfs_seek(fHfsFile, (long) offset, hfsWhence); - if (result == (unsigned long) -1) { - DebugBreak(); - return kDIErrGeneric; - } - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDHFS::Tell(void) -{ - di_off_t offset; - - /* get current position without moving pointer */ - offset = hfs_seek(fHfsFile, 0, HFS_SEEK_CUR); - return offset; -} - -/* - * Release file state, and tell our parent to destroy us. - */ -DIError A2FDHFS::Close(void) -{ - hfsdirent dirEnt; - - /* - * If the file was written to, update our info. - */ - if (fModified) { - if (hfs_fstat(fHfsFile, &dirEnt) == 0) { - A2FileHFS* pFile = (A2FileHFS*) fpFile; - pFile->fDataLength = dirEnt.u.file.dsize; - pFile->fRsrcLength = dirEnt.u.file.rsize; - if (pFile->fRsrcLength == 0) - pFile->fRsrcLength = -1; - LOGI(" HFS close set dataLen=%ld rsrcLen=%ld", - (long) pFile->fDataLength, (long) pFile->fRsrcLength); - } else { - LOGI(" HFS Close fstat failed: %s", hfs_error); - // close it anyway - } - } - - hfs_close(fHfsFile); - fHfsFile = NULL; - - /* flush changes */ - if (fModified) { - DiskFSHFS* pDiskFS = (DiskFSHFS*) fpFile->GetDiskFS(); - - if (hfs_flush(pDiskFS->GetHfsVol()) != 0) { - LOGI("HEY: Close flush failed!"); - DebugBreak(); - } - } - - fpFile->CloseDescr(this); - return kDIErrNone; -} - -/* - * Return the #of sectors/blocks in the file. Not supported, but since HFS - * doesn't support "sparse" files we can fake it. - */ -long A2FDHFS::GetSectorCount(void) const -{ - A2FileHFS* pFile = (A2FileHFS*) fpFile; - return (long) ((pFile->fDataLength+255) / 256 + - (pFile->fRsrcLength+255) / 256); -} - -long A2FDHFS::GetBlockCount(void) const -{ - A2FileHFS* pFile = (A2FileHFS*) fpFile; - return (long) ((pFile->fDataLength+511) / 512 + - (pFile->fRsrcLength+511) / 512); -} - -/* - * Return the Nth track/sector in this file. Not supported. - */ -DIError A2FDHFS::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - return kDIErrNotSupported; -} - -/* - * Return the Nth 512-byte block in this file. Not supported. - */ -DIError A2FDHFS::GetStorage(long blockIdx, long* pBlock) const -{ - return kDIErrNotSupported; -} - - - - -#else // EXCISE_GPL_CODE ----------------------------------------------------- - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSHFS::Initialize(InitMode initMode) -{ - DIError dierr = kDIErrNone; - - dierr = LoadVolHeader(); - if (dierr != kDIErrNone) - goto bail; - DumpVolHeader(); - - CreateFakeFile(); - - SetVolumeUsageMap(); - -bail: - return dierr; -} - -/* - * Fill a buffer with some interesting stuff, and add it to the file list. - */ -void DiskFSHFS::CreateFakeFile(void) -{ - A2FileHFS* pFile; - char buf[768]; // currently running about 475 - static const char* kFormatMsg = -"The Macintosh HFS filesystem is not supported. CiderPress knows how to\r" -"recognize HFS volumes so that it can identify partitions on CFFA-formatted\r" -"CompactFlash cards and Apple II CD-ROMs, but the current version does not\r" -"know how to view or extract files.\r" -"\r" -"Some information about this HFS volume:\r" -"\r" -" Volume name : '%s'\r" -" Storage capacity : %ld blocks (%.2fMB)\r" -" Number of files : %ld\r" -" Number of folders : %ld\r" -" Last modified : %s\r" -"\r" -; - char dateBuf[32]; - long capacity; - const char* timeStr; - - capacity = (fAllocationBlockSize / kBlkSize) * fNumAllocationBlocks; - - /* get the mod time, format it, and remove the trailing '\n' */ - time_t when = - (time_t) (fModifiedDateTime - kDateTimeOffset - fLocalTimeOffset); - timeStr = ctime(&when); - if (timeStr == NULL) { - LOGI("Invalid date %ld (orig=%ld)", when, fModifiedDateTime); - strcpy(dateBuf, ""); - } else - strncpy(dateBuf, timeStr, sizeof(dateBuf)); - int len = strlen(dateBuf); - if (len > 0) - dateBuf[len-1] = '\0'; - - memset(buf, 0, sizeof(buf)); - snprintf(buf, NELEM(buf), kFormatMsg, - fVolumeName, - capacity, - (double) capacity / 2048.0, - fNumFiles, - fNumDirectories, - dateBuf); - - pFile = new A2FileHFS(this); - pFile->fIsDir = false; - pFile->fIsVolumeDir = false; - pFile->fType = 0; - pFile->fCreator = 0; - pFile->SetFakeFile(buf, strlen(buf)); - strcpy(pFile->fFileName, "(not supported)"); - pFile->SetPathName("", pFile->fFileName); - pFile->fDataLength = 0; - pFile->fRsrcLength = -1; - pFile->fCreateWhen = 0; - pFile->fModWhen = 0; - - pFile->SetFakeFile(buf, strlen(buf)); - - AddFileToList(pFile); -} - -/* - * We could do this, but there's not much point. - */ -DIError GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const -{ - return kDIErrNotSupported; -} - -/* - * Not a whole lot to do. - */ -DIError A2FileHFS::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - A2FDHFS* pOpenFile = NULL; - - if (fpOpenFile != NULL) - return kDIErrAlreadyOpen; - if (rsrcFork && fRsrcLength < 0) - return kDIErrForkNotFound; - assert(readOnly == true); - - pOpenFile = new A2FDHFS(this, NULL); - - fpOpenFile = pOpenFile; - *ppOpenFile = pOpenFile; - - return kDIErrNone; -} - - -/* - * =========================================================================== - * A2FDHFS - * =========================================================================== - */ - -/* - * Read a chunk of data from the fake file. - */ -DIError A2FDHFS::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" HFS reading %d bytes from '%s' (offset=%ld)", - len, fpFile->GetPathName(), (long) fOffset); - - A2FileHFS* pFile = (A2FileHFS*) fpFile; - - /* don't allow them to read past the end of the file */ - if (fOffset + (long)len > pFile->fDataLength) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (size_t) (pFile->fDataLength - fOffset); - } - if (pActual != NULL) - *pActual = len; - - memcpy(buf, pFile->GetFakeFileBuf(), len); - - fOffset += len; - - return kDIErrNone; -} - -/* - * Write data at the current offset. - */ -DIError A2FDHFS::Write(const void* buf, size_t len, size_t* pActual) -{ - return kDIErrNotSupported; -} - -/* - * Seek to a new offset. - */ -DIError A2FDHFS::Seek(di_off_t offset, DIWhence whence) -{ - di_off_t fileLen = ((A2FileHFS*) fpFile)->fDataLength; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fileLen) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fileLen) - return kDIErrInvalidArg; - fOffset = fileLen + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fileLen - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fileLen); - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDHFS::Tell(void) -{ - return fOffset; -} - -/* - * Release file state, and tell our parent to destroy us. - */ -DIError A2FDHFS::Close(void) -{ - fpFile->CloseDescr(this); - return kDIErrNone; -} - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDHFS::GetSectorCount(void) const -{ - A2FileHFS* pFile = (A2FileHFS*) fpFile; - return (long) ((pFile->fDataLength+255) / 256); -} - -long A2FDHFS::GetBlockCount(void) const -{ - A2FileHFS* pFile = (A2FileHFS*) fpFile; - return (long) ((pFile->fDataLength+511) / 512); -} - -/* - * Return the Nth track/sector in this file. - */ -DIError A2FDHFS::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - return kDIErrNotSupported; -} - -/* - * Return the Nth 512-byte block in this file. - */ -DIError A2FDHFS::GetStorage(long blockIdx, long* pBlock) const -{ - return kDIErrNotSupported; -} - -#endif // EXCISE_GPL_CODE --------------------------------------------------- diff --git a/ciderpress/diskimg/ImageWrapper.cpp b/ciderpress/diskimg/ImageWrapper.cpp deleted file mode 100644 index db2b203..0000000 --- a/ciderpress/diskimg/ImageWrapper.cpp +++ /dev/null @@ -1,2476 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Code for handling disk image "wrappers", things like DiskCopy, 2MG, and - * SHK that surround a disk image. - * - * Returning with kDIErrBadChecksum from Test or Prep is taken as a sign - * that, while we have correctly identified the wrapper format, the contents - * of the file are corrupt, and the user needs to be told. - * - * Some formats, such as 2MG, include a DOS volume number. This is useful - * because DOS actually embeds the volume number in sector headers; the value - * stored in the VTOC is ignored by certain things (notably some games with - * trivial copy-protection). This value needs to be preserved. It's - * unclear how useful this will actually be; mostly we just want to preserve - * it when translating from one format to another. - * - * If a library (such as NufxLib) needs to read an actual file, it can - * (usually) pry the name out of the GFD. - * - * In general, it should be possible to write to any "wrapped" file that we - * can read from. For things like NuFX and DDD, this means we need to be - * able to re-compress the image file when we're done with it. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" -#include "TwoImg.h" - - -/* - * =========================================================================== - * 2MG (a/k/a 2IMG) - * =========================================================================== - */ - -/* - * Test to see if this is a 2MG file. - * - * The easiest way to do that is to open up the header and see if - * it looks valid. - */ -/*static*/ DIError Wrapper2MG::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - TwoImgHeader header; - - LOGI("Testing for 2MG"); - - // HEY: should test for wrappedLength > 2GB; if so, skip - - pGFD->Rewind(); - if (header.ReadHeader(pGFD, (long) wrappedLength) != 0) - return kDIErrGeneric; - - LOGI("Looks like valid 2MG"); - return kDIErrNone; -} - -/* - * Get the header (again) and use it to locate the data. - */ -DIError Wrapper2MG::Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - TwoImgHeader header; - long offset; - - LOGI("Prepping for 2MG"); - pGFD->Rewind(); - if (header.ReadHeader(pGFD, (long) wrappedLength) != 0) - return kDIErrGeneric; - - offset = header.fDataOffset; - - if (header.fFlags & TwoImgHeader::kDOSVolumeSet) - *pDiskVolNum = header.GetDOSVolumeNum(); - - *pLength = header.fDataLen; - *pPhysical = DiskImg::kPhysicalFormatSectors; - if (header.fImageFormat == TwoImgHeader::kImageFormatDOS) - *pOrder = DiskImg::kSectorOrderDOS; - else if (header.fImageFormat == TwoImgHeader::kImageFormatProDOS) - *pOrder = DiskImg::kSectorOrderProDOS; - else if (header.fImageFormat == TwoImgHeader::kImageFormatNibble) { - *pOrder = DiskImg::kSectorOrderPhysical; - if (*pLength == kTrackCount525 * kTrackLenNib525) { - LOGI(" Prepping for 6656-byte 2MG-NIB"); - *pPhysical = DiskImg::kPhysicalFormatNib525_6656; - } else if (*pLength == kTrackCount525 * kTrackLenNb2525) { - LOGI(" Prepping for 6384-byte 2MG-NB2"); - *pPhysical = DiskImg::kPhysicalFormatNib525_6384; - } else { - LOGI(" NIB 2MG with length=%ld rejected", (long) *pLength); - return kDIErrOddLength; - } - } - - *ppNewGFD = new GFDGFD; - return ((GFDGFD*)*ppNewGFD)->Open(pGFD, offset, readOnly); -} - -/* - * Initialize fields for a new file. - */ -DIError Wrapper2MG::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - TwoImgHeader header; - int cc; - - switch (physical) { - case DiskImg::kPhysicalFormatNib525_6656: - if (length != kTrackLenNib525 * kTrackCount525) { - LOGI("Invalid 2MG nibble length %ld", (long) length); - return kDIErrInvalidArg; - } - header.InitHeader(TwoImgHeader::kImageFormatNibble, (long) length, - 8 * kTrackCount525); // 8 blocks per track - break; - case DiskImg::kPhysicalFormatSectors: - if ((length % 512) != 0) { - LOGI("Invalid 2MG length %ld", (long) length); - return kDIErrInvalidArg; - } - if (order == DiskImg::kSectorOrderProDOS) - cc = header.InitHeader(TwoImgHeader::kImageFormatProDOS, - (long) length, (long) length / 512); - else if (order == DiskImg::kSectorOrderDOS) - cc = header.InitHeader(TwoImgHeader::kImageFormatDOS, - (long) length, (long) length / 512); - else { - LOGI("Invalid 2MG sector order %d", order); - return kDIErrInvalidArg; - } - if (cc != 0) { - LOGI("TwoImg InitHeader failed (len=%ld)", (long) length); - return kDIErrInvalidArg; - } - break; - default: - LOGI("Invalid 2MG physical %d", physical); - return kDIErrInvalidArg; - } - - if (dosVolumeNum != DiskImg::kVolumeNumNotSet) - header.SetDOSVolumeNum(dosVolumeNum); - - cc = header.WriteHeader(pWrapperGFD); - if (cc != 0) { - LOGI("ERROR: 2MG header write failed (cc=%d)", cc); - return kDIErrGeneric; - } - - long footerLen = header.fCmtLen + header.fCreatorLen; - if (footerLen > 0) { - // This is currently impossible, which is good because the Seek call - // will fail if pWrapperGFD is a buffer. - assert(false); - pWrapperGFD->Seek(header.fDataOffset + length, kSeekSet); - header.WriteFooter(pWrapperGFD); - } - - long offset = header.fDataOffset; - - - *pWrappedLength = length + offset + footerLen; - *pDataFD = new GFDGFD; - return ((GFDGFD*)*pDataFD)->Open(pWrapperGFD, offset, false); -} - -/* - * We only use GFDGFD, so there's nothing to do here. - * - * If we want to support changing the comment field in an open image, we'd - * need to handle making the file longer or shorter here. Right now we - * just ignore everything that comes before or after the start of the data. - * Since there's no checksum, none of the header fields change, so we - * don't even deal with that. - */ -DIError Wrapper2MG::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - return kDIErrNone; -} - - -/* - * =========================================================================== - * SHK (ShrinkIt NuFX), also .SDK and .BXY - * =========================================================================== - */ - -/* - * NOTE: this doesn't override the global error message callback because - * we expect it to be set by the application. - */ - -/* - * Display error messages... or not. - */ -/*static*/ NuResult WrapperNuFX::ErrMsgHandler(NuArchive* /*pArchive*/, - void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - if (pErrorMessage->isDebug) { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - "[D] %s\n", pErrorMessage->message); - } else { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - "%s\n", pErrorMessage->message); - } - - return kNuOK; -} - -/* - * Open a NuFX archive, and verify that it holds exactly one disk archive. - * - * On success, the NuArchive pointer and thread idx are set, and 0 is - * returned. Returns -1 on failure. - */ -/*static*/ DIError WrapperNuFX::OpenNuFX(const char* pathName, NuArchive** ppArchive, - NuThreadIdx* pThreadIdx, long* pLength, bool readOnly) -{ - NuError nerr = kNuErrNone; - NuArchive* pArchive = NULL; - NuRecordIdx recordIdx; - NuAttr attr; - const NuRecord* pRecord; - const NuThread* pThread = NULL; - int idx; - - LOGI("Opening file '%s' to test for NuFX", pathName); - - /* - * Open the archive. - */ - if (readOnly) { - nerr = NuOpenRO(pathName, &pArchive); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to open archive (err=%d)", nerr); - goto bail; - } - } else { - char* tmpPath; - - tmpPath = GenTempPath(pathName); - if (tmpPath == NULL) { - nerr = kNuErrInternal; - goto bail; - } - - nerr = NuOpenRW(pathName, tmpPath, 0, &pArchive); - if (nerr != kNuErrNone) { - LOGI(" NuFX OpenRW failed (nerr=%d)", nerr); - nerr = kNuErrGeneric; - delete[] tmpPath; - goto bail; - } - delete[] tmpPath; - } - - NuSetErrorMessageHandler(pArchive, ErrMsgHandler); - - nerr = NuGetAttr(pArchive, kNuAttrNumRecords, &attr); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to get record count (err=%d)", nerr); - goto bail; - } - if (attr != 1) { - LOGI(" NuFX archive has %d entries, not disk-only", attr); - nerr = kNuErrGeneric; - if (attr > 1) - goto file_archive; - else - goto bail; // shouldn't get zero-count archives, but... - } - - /* get the first record */ - nerr = NuGetRecordIdxByPosition(pArchive, 0, &recordIdx); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to get first recordIdx (err=%d)", nerr); - goto bail; - } - nerr = NuGetRecord(pArchive, recordIdx, &pRecord); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to get first record (err=%d)", nerr); - goto bail; - } - - /* find a disk image thread */ - for (idx = 0; idx < (int)NuRecordGetNumThreads(pRecord); idx++) { - pThread = NuGetThread(pRecord, idx); - - if (NuGetThreadID(pThread) == kNuThreadIDDiskImage) - break; - } - if (idx == (int)NuRecordGetNumThreads(pRecord)) { - LOGI(" NuFX no disk image found in first record"); - nerr = kNuErrGeneric; - goto file_archive; - } - assert(pThread != NULL); - *pThreadIdx = pThread->threadIdx; - - /* - * Don't allow zero-length disks. - */ - *pLength = pThread->actualThreadEOF; - if (!*pLength) { - LOGI(" NuFX length of disk image is bad (%ld)", *pLength); - nerr = kNuErrGeneric; - goto bail; - } - - /* - * Success! - */ - assert(nerr == kNuErrNone); - *ppArchive = pArchive; - pArchive = NULL; - -bail: - if (pArchive != NULL) - NuClose(pArchive); - if (nerr == kNuErrNone) - return kDIErrNone; - else if (nerr == kNuErrBadMHCRC || nerr == kNuErrBadRHCRC) - return kDIErrBadChecksum; - else - return kDIErrGeneric; - -file_archive: - if (pArchive != NULL) - NuClose(pArchive); - return kDIErrFileArchive; -} - -/* - * Load a disk image into memory. - * - * Allocates a buffer with the specified length and loads the desired - * thread into it. - * - * In an LZW-I compressed thread, the third byte of the compressed thread - * data is the disk volume number that P8 ShrinkIt would use when formatting - * the disk. In an LZW-II compressed thread, it's the first byte of the - * compressed data. Uncompressed disk images simply don't have the disk - * volume number in them. Until NufxLib provides a simple way to access - * this bit of loveliness, we're going to pretend it's not there. - * - * Returns 0 on success, -1 on error. - */ -DIError WrapperNuFX::GetNuFXDiskImage(NuArchive* pArchive, NuThreadIdx threadIdx, - long length, char** ppData) -{ - NuError err; - NuDataSink* pDataSink = NULL; - uint8_t* buf = NULL; - - assert(length > 0); - buf = new uint8_t[length]; - if (buf == NULL) - return kDIErrMalloc; - - /* - * Create a buffer and expand the disk image into it. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, length, - &pDataSink); - if (err != kNuErrNone) { - LOGI(" NuFX: unable to create data sink (err=%d)", err); - goto bail; - } - - err = NuExtractThread(pArchive, threadIdx, pDataSink); - if (err != kNuErrNone) { - LOGI(" NuFX: unable to extract thread (err=%d)", err); - goto bail; - } - - //err = kNuErrBadThreadCRC; goto bail; // debug test only - - *ppData = (char*)buf; - -bail: - NuFreeDataSink(pDataSink); - if (err != kNuErrNone) { - LOGI(" NuFX GetNuFXDiskImage returning after nuerr=%d", err); - delete[] buf; - } - if (err == kNuErrNone) - return kDIErrNone; - else if (err == kNuErrBadDataCRC || err == kNuErrBadThreadCRC) - return kDIErrBadChecksum; - else if (err == kNuErrBadData) - return kDIErrBadCompressedData; - else if (err == kNuErrBadFormat) - return kDIErrUnsupportedCompression; - else - return kDIErrGeneric; -} - -/* - * Test to see if this is a single-record NuFX archive with a disk archive - * in it. - */ -/*static*/ DIError WrapperNuFX::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - DIError dierr; - NuArchive* pArchive = NULL; - NuThreadIdx threadIdx; - long length; - const char* imagePath; - - imagePath = pGFD->GetPathName(); - if (imagePath == NULL) { - LOGI("Can't test NuFX on non-file"); - return kDIErrNotSupported; - } - LOGI("Testing for NuFX"); - dierr = OpenNuFX(imagePath, &pArchive, &threadIdx, &length, true); - if (dierr != kDIErrNone) - return dierr; - - /* success; throw away state in case they don't like us anyway */ - assert(pArchive != NULL); - NuClose(pArchive); - - return kDIErrNone; -} - -/* - * Open the archive, extract the disk image into a memory buffer. - */ -DIError WrapperNuFX::Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - DIError dierr = kDIErrNone; - NuThreadIdx threadIdx; - GFDBuffer* pNewGFD = NULL; - char* buf = NULL; - long length = -1; - const char* imagePath; - - imagePath = pGFD->GetPathName(); - if (imagePath == NULL) { - assert(false); // should've been caught in Test - return kDIErrNotSupported; - } - pGFD->Close(); // don't hold the file open - dierr = OpenNuFX(imagePath, &fpArchive, &threadIdx, &length, readOnly); - if (dierr != kDIErrNone) - goto bail; - - dierr = GetNuFXDiskImage(fpArchive, threadIdx, length, &buf); - if (dierr != kDIErrNone) - goto bail; - - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, readOnly); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - /* - * Success! - */ - assert(dierr == kDIErrNone); - *ppNewGFD = pNewGFD; - *pLength = length; - *pPhysical = DiskImg::kPhysicalFormatSectors; - *pOrder = DiskImg::kSectorOrderProDOS; - - LOGI(" NuFX is ready, threadIdx=%d", threadIdx); - fThreadIdx = threadIdx; - -bail: - if (dierr != kDIErrNone) { - NuClose(fpArchive); - fpArchive = NULL; - delete pNewGFD; - delete buf; - } - return dierr; -} - -/* - * Given a filename, create a suitable temp pathname. - * - * This is really the wrong place to be doing this -- the application - * should get to deal with this -- but it's not the end of the world - * if we handle it here. Add to wish list: fix NufxLib so that the - * temp file can be a memory buffer. - * - * Returns a string allocated with new[]. - */ -/*static*/ char* WrapperNuFX::GenTempPath(const char* path) -{ - static const char* kTmpTemplate = "DItmp_XXXXXX"; - char* tmpPath; - - assert(path != NULL); - assert(strlen(path) > 0); - - tmpPath = new char[strlen(path) + 32]; - if (tmpPath == NULL) - return NULL; - - strcpy(tmpPath, path); - - /* back up to the first thing that looks like it's an fssep */ - char* cp; - cp = tmpPath + strlen(tmpPath); - while (--cp >= tmpPath) { - if (*cp == '/' || *cp == '\\' || *cp == ':') - break; - } - - /* we either fell off the back end or found an fssep; advance */ - cp++; - - strcpy(cp, kTmpTemplate); - - LOGI(" NuFX GenTempPath '%s' -> '%s'", path, tmpPath); - - return tmpPath; -} - -/* - * Initialize fields for a new file. - * - * "pWrapperGFD" will be fairly useless after this, because we're - * recreating the underlying file. (If it doesn't have an underlying - * file, then we're hosed.) - */ -DIError WrapperNuFX::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - assert(physical == DiskImg::kPhysicalFormatSectors); - assert(order == DiskImg::kSectorOrderProDOS); - - DIError dierr = kDIErrNone; - NuArchive* pArchive; - const char* imagePath; - char* tmpPath = NULL; - uint8_t* buf = NULL; - NuError nerr; - - /* - * Create the NuFX archive, stomping on the existing file. (This - * makes pWrapperGFD invalid, but such is life with NufxLib.) - */ - imagePath = pWrapperGFD->GetPathName(); - if (imagePath == NULL) { - assert(false); // must not have an outer wrapper - dierr = kDIErrNotSupported; - goto bail; - } - pWrapperGFD->Close(); // don't hold the file open - tmpPath = GenTempPath(imagePath); - if (tmpPath == NULL) { - dierr = kDIErrInternal; - goto bail; - } - - nerr = NuOpenRW(imagePath, tmpPath, kNuOpenCreat, &pArchive); - if (nerr != kNuErrNone) { - LOGI(" NuFX OpenRW failed (nerr=%d)", nerr); - dierr = kDIErrGeneric; - goto bail; - } - - /* - * Create a blank chunk of memory for the image. - */ - assert(length > 0); - buf = new uint8_t[(int) length]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - GFDBuffer* pNewGFD; - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, false); - if (dierr != kDIErrNone) { - delete pNewGFD; - goto bail; - } - *pDataFD = pNewGFD; - buf = NULL; // now owned by pNewGFD; - - /* - * Success! Set misc stuff. - */ - fThreadIdx = 0; // don't have one to overwrite - fpArchive = pArchive; - -bail: - delete[] tmpPath; - delete[] buf; - return dierr; -} - -/* - * Close the NuFX archive. - */ -DIError WrapperNuFX::CloseNuFX(void) -{ - NuError nerr; - - /* throw away any un-flushed changes so that "close" can't fail */ - (void) NuAbort(fpArchive); - - nerr = NuClose(fpArchive); - if (nerr != kNuErrNone) { - LOGI("WARNING: NuClose failed"); - return kDIErrGeneric; - } - return kDIErrNone; -} - -/* - * Write the data using the default compression method. - * - * Doesn't touch "pWrapperGFD" or "pWrappedLen". Could probably update - * "pWrappedLen", but that's really only useful if we have a gzip Outer - * that wants to know how much data we have. Because we don't write to - * pWrapperGFD, we can't have a gzip wrapper, so there's no point in - * updating it. - */ -DIError WrapperNuFX::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - NuError nerr = kNuErrNone; - NuFileDetails fileDetails; - NuRecordIdx recordIdx; - NuThreadIdx threadIdx; - NuDataSource* pDataSource = NULL; - - if (fThreadIdx != 0) { - /* - * Mark the old record for deletion. - */ - nerr = NuGetRecordIdxByPosition(fpArchive, 0, &recordIdx); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to get first recordIdx (err=%d)", nerr); - goto bail; - } - nerr = NuDeleteRecord(fpArchive, recordIdx); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to delete first record (err=%d)", nerr); - goto bail; - } - } - - assert((dataLen % 512) == 0); - - nerr = NuSetValue(fpArchive, kNuValueDataCompression, - fCompressType + kNuCompressNone); - if (nerr != kNuErrNone) { - LOGI("WARNING: unable to set compression to format %d", - fCompressType); - nerr = kNuErrNone; - } else { - LOGI(" NuFX set compression to %d/%d", fCompressType, - fCompressType + kNuCompressNone); - } - - /* - * Fill out the fileDetails record appropriately. - */ - memset(&fileDetails, 0, sizeof(fileDetails)); - fileDetails.threadID = kNuThreadIDDiskImage; - if (fStorageName != NULL) - fileDetails.storageNameMOR = fStorageName; // TODO - else - fileDetails.storageNameMOR = "NEW.DISK"; - fileDetails.fileSysID = kNuFileSysUnknown; - fileDetails.fileSysInfo = kDefaultStorageFssep; - fileDetails.storageType = 512; - fileDetails.extraType = (long) (dataLen / 512); - fileDetails.access = kNuAccessUnlocked; - - time_t now; - now = time(NULL); - UNIXTimeToDateTime(&now, &fileDetails.archiveWhen); - UNIXTimeToDateTime(&now, &fileDetails.modWhen); - UNIXTimeToDateTime(&now, &fileDetails.createWhen); - - /* - * Create the new record. - */ - nerr = NuAddRecord(fpArchive, &fileDetails, &recordIdx); - if (nerr != kNuErrNone) { - LOGI(" NuFX AddRecord failed (nerr=%d)", nerr); - goto bail; - } - - /* - * Create a data source for the thread. - * - * We need to get the memory buffer from pDataGFD, which we do in - * a somewhat unwholesome manner. However, there's no other way to - * feed the data into NufxLib. - */ - nerr = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, - (const uint8_t*) ((GFDBuffer*) pDataGFD)->GetBuffer(), - 0, (long) dataLen, NULL, &pDataSource); - if (nerr != kNuErrNone) { - LOGI(" NuFX unable to create NufxLib data source (nerr=%d)", nerr); - goto bail; - } - - /* - * Add the thread. - */ - nerr = NuAddThread(fpArchive, recordIdx, kNuThreadIDDiskImage, - pDataSource, &threadIdx); - if (nerr != kNuErrNone) { - LOGI(" NuFX AddThread failed (nerr=%d)", nerr); - goto bail; - } - pDataSource = NULL; // now owned by NufxLib - LOGI(" NuFX added thread %d in record %d, flushing changes", - threadIdx, recordIdx); - - /* - * Flush changes (does the actual compression). - */ - uint32_t status; - nerr = NuFlush(fpArchive, &status); - if (nerr != kNuErrNone) { - LOGI(" NuFX flush failed (nerr=%d, status=%u)", nerr, status); - goto bail; - } - - /* update the threadID */ - fThreadIdx = threadIdx; - -bail: - NuFreeDataSource(pDataSource); - if (nerr != kNuErrNone) - return kDIErrGeneric; - return kDIErrNone; -} - -/* - * Common NuFX utility function. This ought to be in NufxLib. - */ -void WrapperNuFX::UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime) -{ - struct tm* ptm; - - assert(pWhen != NULL); - assert(pDateTime != NULL); - - ptm = localtime(pWhen); - pDateTime->second = ptm->tm_sec; - pDateTime->minute = ptm->tm_min; - pDateTime->hour = ptm->tm_hour; - pDateTime->day = ptm->tm_mday -1; - pDateTime->month = ptm->tm_mon; - pDateTime->year = ptm->tm_year; - pDateTime->extra = 0; - pDateTime->weekDay = ptm->tm_wday +1; -} - - -/* - * =========================================================================== - * DDD (DDD 2.1, DDD Pro) - * =========================================================================== - */ - -/* - * There really isn't a way to test if the file is a DDD archive, except - * to try to unpack it. One thing we can do fairly quickly is look for - * runs of repeated bytes, which will be impossible in a DDD file because - * we compress runs of repeated bytes with RLE. - */ -/*static*/ DIError WrapperDDD::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - DIError dierr; - GenericFD* pNewGFD = NULL; - LOGI("Testing for DDD"); - - pGFD->Rewind(); - - dierr = CheckForRuns(pGFD); - if (dierr != kDIErrNone) - return dierr; - - dierr = Unpack(pGFD, &pNewGFD, NULL); - delete pNewGFD; - return dierr; -} - -/* - * Load a bunch of data and check it for repeated byte sequences that - * would be removed by RLE. Runs of 4 bytes or longer should have been - * stripped out. DDD adds a couple of zeroes onto the end, so to avoid - * special cases we assume that a run of 5 is okay, and only flunk the - * data when it gets to 6. - * - * One big exception: the "favorites" table isn't run-length encoded, - * and if the track is nothing but zeroes the entire thing will be - * filled with 0xff. So we allow runs of 0xff bytes. - * - * PROBLEM: some sequences, such as repeated d5aa, can turn into what looks - * like a run of bytes in the output. We can't assume that arbitrary - * sequences of bytes won't be repeated. It does appear that we can assume - * that 00 bytes won't be repeated, so we can still scan for a series of - * zeroes and reject the image if found (which should clear us for all - * uncompressed formats and any compressed format with a padded file header). - * - * The goal is to detect uncompressed data sources. The test for DDD - * should come after other compressed data formats. - * - * For speed we crank the data in 8K at a time and don't correctly handle - * the boundaries. We do, however, need to avoid scanning the last 256 - * bytes of the file, because DOS DDD just fills it with junk, and it's - * possible that junk might have runs in it. - */ -/*static*/ DIError WrapperDDD::CheckForRuns(GenericFD* pGFD) -{ - DIError dierr = kDIErrNone; - int kRunThreshold = 5; - uint8_t buf[8192]; - size_t bufCount; - int runLen; - di_off_t fileLen; - int i; - - dierr = pGFD->Seek(0, kSeekEnd); - if (dierr != kDIErrNone) - goto bail; - fileLen = pGFD->Tell(); - pGFD->Rewind(); - - fileLen -= 256; // could be extra data from DOS DDD - - while (fileLen) { - bufCount = (size_t) fileLen; - if (bufCount > sizeof(buf)) - bufCount = sizeof(buf); - fileLen -= bufCount; - - dierr = pGFD->Read(buf, bufCount); - if (dierr != kDIErrNone) - goto bail; - //LOGI(" DDD READ %d bytes", bufCount); - if (dierr != kDIErrNone) { - LOGI(" DDD CheckForRuns read failed (err=%d)", dierr); - return dierr; - } - - runLen = 0; - for (i = 1; i < (int) bufCount; i++) { - if (buf[i] == 0 && buf[i] == buf[i-1]) { - runLen++; - if (runLen == kRunThreshold && buf[i] != 0xff) { - LOGI(" DDD found run of >= %d of 0x%02x, bailing", - runLen+1, buf[i]); - return kDIErrGeneric; - } - } else { - runLen = 0; - } - } - } - - LOGI(" DDD CheckForRuns scan complete, no long runs found"); - -bail: - return dierr; -} - -/* - * Prepping is much the same as testing, but we fill in a few more details. - */ -DIError WrapperDDD::Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - DIError dierr; - LOGI("Prepping for DDD"); - - assert(*ppNewGFD == NULL); - - dierr = Unpack(pGFD, ppNewGFD, pDiskVolNum); - if (dierr != kDIErrNone) - return dierr; - - *pLength = kNumTracks * kTrackLen; - *pPhysical = DiskImg::kPhysicalFormatSectors; - *pOrder = DiskImg::kSectorOrderDOS; - - return dierr; -} - -/* - * Unpack a compressed disk image from "pGFD" to a new memory buffer - * created in "*ppNewGFD". - */ -/*static*/ DIError WrapperDDD::Unpack(GenericFD* pGFD, GenericFD** ppNewGFD, - short* pDiskVolNum) -{ - DIError dierr; - GFDBuffer* pNewGFD = NULL; - uint8_t* buf = NULL; - short diskVolNum; - - pGFD->Rewind(); - - buf = new uint8_t[kNumTracks * kTrackLen]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - pNewGFD = new GFDBuffer; - if (pNewGFD == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = pNewGFD->Open(buf, kNumTracks * kTrackLen, true, false, false); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - dierr = UnpackDisk(pGFD, pNewGFD, &diskVolNum); - if (dierr != kDIErrNone) - goto bail; - - if (pDiskVolNum != NULL) - *pDiskVolNum = diskVolNum; - *ppNewGFD = pNewGFD; - pNewGFD = NULL; // now owned by caller - -bail: - delete[] buf; - delete pNewGFD; - return dierr; -} - -/* - * Initialize stuff for a new file. There's no file header or other - * goodies, so we leave "pWrapperGFD" and "pWrappedLength" alone. - */ -DIError WrapperDDD::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - assert(length == kNumTracks * kTrackLen); - assert(physical == DiskImg::kPhysicalFormatSectors); - assert(order == DiskImg::kSectorOrderDOS); - - DIError dierr; - uint8_t* buf = NULL; - - /* - * Create a blank chunk of memory for the image. - */ - buf = new uint8_t[(int) length]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - GFDBuffer* pNewGFD; - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, false); - if (dierr != kDIErrNone) { - delete pNewGFD; - goto bail; - } - *pDataFD = pNewGFD; - buf = NULL; // now owned by pNewGFD; - - // can't set *pWrappedLength yet - - if (dosVolumeNum != DiskImg::kVolumeNumNotSet) - fDiskVolumeNum = dosVolumeNum; - else - fDiskVolumeNum = kDefaultNibbleVolumeNum; - -bail: - delete[] buf; - return dierr; -} - -/* - * Compress the disk image. - */ -DIError WrapperDDD::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - DIError dierr; - - assert(dataLen == kNumTracks * kTrackLen); - - pDataGFD->Rewind(); - - dierr = PackDisk(pDataGFD, pWrapperGFD, fDiskVolumeNum); - if (dierr != kDIErrNone) - return dierr; - - *pWrappedLen = pWrapperGFD->Tell(); - LOGI(" DDD compressed from %d to %ld", - kNumTracks * kTrackLen, (long) *pWrappedLen); - - return kDIErrNone; -} - - -/* - * =========================================================================== - * DiskCopy (primarily a Mac format) - * =========================================================================== - */ - -/* - * DiskCopy 4.2 header, from FTN $e0/0005. - * - * All values are BIG-endian. - */ -const int kDC42NameLen = 64; -const int kDC42ChecksumOffset = 72; // where the checksum lives -const int kDC42DataOffset = 84; // header is always this long -const int kDC42PrivateMagic = 0x100; -const int kDC42FakeTagLen = 19200; // add a "fake" tag to match Mac - -typedef struct DiskImgLib::DC42Header { - char diskName[kDC42NameLen+1]; // from pascal string - uint32_t dataSize; // in bytes - uint32_t tagSize; - uint32_t dataChecksum; - uint32_t tagChecksum; - uint8_t diskFormat; // should be 1 for 800K - uint8_t formatByte; // should be $24, sometimes $22 - uint16_t privateWord; // must be 0x0100 - // userData begins at +84 - // tagData follows user data -} DC42Header; - -/* - * Dump the contents of a DC42Header. - */ -/*static*/ void WrapperDiskCopy42::DumpHeader(const DC42Header* pHeader) -{ - LOGI("--- header contents:"); - LOGI("\tdiskName = '%s'", pHeader->diskName); - LOGI("\tdataSize = %d (%dK)", pHeader->dataSize, - pHeader->dataSize / 1024); - LOGI("\ttagSize = %d", pHeader->tagSize); - LOGI("\tdataChecksum = 0x%08x", pHeader->dataChecksum); - LOGI("\ttagChecksum = 0x%08x", pHeader->tagChecksum); - LOGI("\tdiskFormat = %d", pHeader->diskFormat); - LOGI("\tformatByte = 0x%02x", pHeader->formatByte); - LOGI("\tprivateWord = 0x%04x", pHeader->privateWord); -} - -/* - * Init a DC42 header for an 800K ProDOS disk. - */ -void WrapperDiskCopy42::InitHeader(DC42Header* pHeader) -{ - memset(pHeader, 0, sizeof(*pHeader)); - if (fStorageName == NULL || strlen(fStorageName) == 0) - strcpy(pHeader->diskName, "-not a Macintosh disk"); - else - strcpy(pHeader->diskName, fStorageName); - pHeader->dataSize = 819200; - pHeader->tagSize = kDC42FakeTagLen; // emulate Mac behavior - pHeader->dataChecksum = 0xffffffff; // fixed during Flush - pHeader->tagChecksum = 0x00000000; // 19200 zeroes - pHeader->diskFormat = 1; - pHeader->formatByte = 0x24; - pHeader->privateWord = kDC42PrivateMagic; -} - -/* - * Read the header from a DC42 file and verify it. - * - * Returns 0 on success, -1 on error or invalid header. - */ -/*static*/ int WrapperDiskCopy42::ReadHeader(GenericFD* pGFD, DC42Header* pHeader) -{ - uint8_t hdrBuf[kDC42DataOffset]; - - if (pGFD->Read(hdrBuf, kDC42DataOffset) != kDIErrNone) - return -1; - - // test the Pascal length byte - if (hdrBuf[0] >= kDC42NameLen) - return -1; - - memcpy(pHeader->diskName, &hdrBuf[1], hdrBuf[0]); - pHeader->diskName[hdrBuf[0]] = '\0'; - - pHeader->dataSize = GetLongBE(&hdrBuf[64]); - pHeader->tagSize = GetLongBE(&hdrBuf[68]); - pHeader->dataChecksum = GetLongBE(&hdrBuf[72]); - pHeader->tagChecksum = GetLongBE(&hdrBuf[76]); - pHeader->diskFormat = hdrBuf[80]; - pHeader->formatByte = hdrBuf[81]; - pHeader->privateWord = GetShortBE(&hdrBuf[82]); - - if (pHeader->dataSize != 800 * 1024 || - pHeader->diskFormat != 1 || - (pHeader->formatByte != 0x22 && pHeader->formatByte != 0x24) || - pHeader->privateWord != kDC42PrivateMagic) - { - return -1; - } - - return 0; -} - -/* - * Write the header for a DC42 file. - */ -DIError WrapperDiskCopy42::WriteHeader(GenericFD* pGFD, const DC42Header* pHeader) -{ - uint8_t hdrBuf[kDC42DataOffset]; - - pGFD->Rewind(); - - memset(hdrBuf, 0, sizeof(hdrBuf)); - /* - * Disks created on a Mac include the null byte in the count; not sure - * if this applies to volume labels or just the "not a Macintosh disk" - * magic string. To be safe, we only increment it if it starts with '-'. - * (Need access to a Macintosh to test this.) - */ - hdrBuf[0] = strlen(pHeader->diskName); - if (pHeader->diskName[0] == '-' && hdrBuf[0] < (kDC42NameLen-1)) - hdrBuf[0]++; - memcpy(&hdrBuf[1], pHeader->diskName, hdrBuf[0]); - - PutLongBE(&hdrBuf[64], pHeader->dataSize); - PutLongBE(&hdrBuf[68], pHeader->tagSize); - PutLongBE(&hdrBuf[72], pHeader->dataChecksum); - PutLongBE(&hdrBuf[76], pHeader->tagChecksum); - hdrBuf[80] = pHeader->diskFormat; - hdrBuf[81] = pHeader->formatByte; - PutShortBE(&hdrBuf[82], pHeader->privateWord); - - return pGFD->Write(hdrBuf, kDC42DataOffset); -} - -/* - * Check to see if this is a DiskCopy 4.2 image. - * - * The format doesn't really have a magic number, but if we're stringent - * about our interpretation of some of the header fields (e.g. we only - * recognize 800K disks) we should be okay. - */ -/*static*/ DIError WrapperDiskCopy42::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - DC42Header header; - - LOGI("Testing for DiskCopy"); - - if (wrappedLength < 800 * 1024 + kDC42DataOffset) - return kDIErrGeneric; - - pGFD->Rewind(); - if (ReadHeader(pGFD, &header) != 0) - return kDIErrGeneric; - - DumpHeader(&header); - - return kDIErrNone; -} - -/* - * Compute the funky DiskCopy checksum. - * - * Position "pGFD" at the start of data. - */ -/*static*/ DIError WrapperDiskCopy42::ComputeChecksum(GenericFD* pGFD, uint32_t* pChecksum) -{ - DIError dierr = kDIErrNone; - uint8_t buf[512]; - long dataRem = 800 * 1024 /*pHeader->dataSize*/; - uint32_t checksum; - - assert(dataRem % sizeof(buf) == 0); - assert((sizeof(buf) & 0x01) == 0); // we take it two bytes at a time - - checksum = 0; - while (dataRem) { - int i; - - dierr = pGFD->Read(buf, sizeof(buf)); - if (dierr != kDIErrNone) { - LOGI(" DC42 read failed, dataRem=%ld (err=%d)", dataRem, dierr); - return dierr; - } - - for (i = 0; i < (int) sizeof(buf); i += 2) { - uint16_t val = GetShortBE(buf+i); - - checksum += val; - if (checksum & 0x01) - checksum = checksum >> 1 | 0x80000000; - else - checksum = checksum >> 1; - } - - dataRem -= sizeof(buf); - } - - *pChecksum = checksum; - - return dierr; -} - -/* - * Prepare a DiskCopy image for use. - */ -DIError WrapperDiskCopy42::Prep(GenericFD* pGFD, di_off_t wrappedLength, - bool readOnly, di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - DIError dierr; - DC42Header header; - - LOGI("Prepping for DiskCopy 4.2"); - pGFD->Rewind(); - if (ReadHeader(pGFD, &header) != 0) - return kDIErrGeneric; - - /* - * Verify checksum. File should already be seeked to appropriate place. - */ - uint32_t checksum; - dierr = ComputeChecksum(pGFD, &checksum); - if (dierr != kDIErrNone) - return dierr; - - if (checksum != header.dataChecksum) { - LOGW(" DC42 checksum mismatch (got 0x%08x, expected 0x%08x)", - checksum, header.dataChecksum); - fBadChecksum = true; - //return kDIErrBadChecksum; - } else { - LOGD(" DC42 checksum matches!"); - } - - - /* looks good! */ - *pLength = header.dataSize; - *pPhysical = DiskImg::kPhysicalFormatSectors; - *pOrder = DiskImg::kSectorOrderProDOS; - - *ppNewGFD = new GFDGFD; - return ((GFDGFD*)*ppNewGFD)->Open(pGFD, kDC42DataOffset, readOnly); - -} - -/* - * Initialize fields for a new file. - */ -DIError WrapperDiskCopy42::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - DIError dierr; - DC42Header header; - - assert(length == 800 * 1024); - assert(physical == DiskImg::kPhysicalFormatSectors); - //assert(order == DiskImg::kSectorOrderProDOS); - - InitHeader(&header); // set all but checksum - - dierr = WriteHeader(pWrapperGFD, &header); - if (dierr != kDIErrNone) { - LOGI("ERROR: 2MG header write failed (err=%d)", dierr); - return dierr; - } - - *pWrappedLength = length + kDC42DataOffset; - *pDataFD = new GFDGFD; - return ((GFDGFD*)*pDataFD)->Open(pWrapperGFD, kDC42DataOffset, false); -} - -/* - * We only use GFDGFD, so there's no data to write. However, we do need - * to update the checksum, and append our "fake" tag section. - */ -DIError WrapperDiskCopy42::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - DIError dierr; - uint32_t checksum; - - /* compute the data checksum */ - dierr = pWrapperGFD->Seek(kDC42DataOffset, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - - dierr = ComputeChecksum(pWrapperGFD, &checksum); - if (dierr != kDIErrNone) { - LOGI(" DC42 failed while computing checksum (err=%d)", dierr); - goto bail; - } - - /* write it into the wrapper */ - dierr = pWrapperGFD->Seek(kDC42ChecksumOffset, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - - dierr = WriteLongBE(pWrapperGFD, checksum); - if (dierr != kDIErrNone) - goto bail; - - /* add the tag bytes */ - dierr = pWrapperGFD->Seek(kDC42DataOffset + 800*1024, kSeekSet); - char* tmpBuf; - tmpBuf = new char[kDC42FakeTagLen]; - if (tmpBuf == NULL) - return kDIErrMalloc; - memset(tmpBuf, 0, kDC42FakeTagLen); - dierr = pWrapperGFD->Write(tmpBuf, kDC42FakeTagLen, NULL); - delete[] tmpBuf; - if (dierr != kDIErrNone) - goto bail; - -bail: - return dierr; -} - - -/* - * =========================================================================== - * Sim2eHDV (Sim2e virtual hard-drive images) - * =========================================================================== - */ - -/* -// mkhdv.c -// -// Create a Hard Disk Volume File (.HDV) for simIIe -static int mkhdv(FILE *op, uint blocks) -{ - byte sector[512]; - byte data[15]; - uint i; - - memset(data, 0, sizeof(data)); - memcpy(data, "SIMSYSTEM_HDV", 13); - data[13] = (blocks & 0xff); - data[14] = (blocks & 0xff00) >> 8; - fwrite(data, 1, sizeof(data), op); - - memset(sector, 0, sizeof(sector)); - for (i = 0; i < blocks; i++) - fwrite(sector, 1, sizeof(sector), op); - return 0; -} -*/ - -const int kSim2eHeaderLen = 15; -static const char* kSim2eID = "SIMSYSTEM_HDV"; - -/* - * Test for a virtual hard-drive image. This is either a "raw" unadorned - * image, or one with a 15-byte "SimIIe" header on it. - */ -DIError WrapperSim2eHDV::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - char buf[kSim2eHeaderLen]; - - LOGI("Testing for Sim2e HDV"); - - if (wrappedLength < 512 || - ((wrappedLength - kSim2eHeaderLen) % 4096) != 0) - { - return kDIErrGeneric; - } - - pGFD->Rewind(); - - if (pGFD->Read(buf, sizeof(buf)) != kDIErrNone) - return kDIErrGeneric; - - if (strncmp(buf, kSim2eID, strlen(kSim2eID)) == 0) - return kDIErrNone; - else - return kDIErrGeneric; -} - -/* - * These are always ProDOS volumes. - */ -DIError WrapperSim2eHDV::Prep(GenericFD* pGFD, di_off_t wrappedLength, - bool readOnly, di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - *pLength = wrappedLength - kSim2eHeaderLen; - *pPhysical = DiskImg::kPhysicalFormatSectors; - *pOrder = DiskImg::kSectorOrderProDOS; - - *ppNewGFD = new GFDGFD; - return ((GFDGFD*)*ppNewGFD)->Open(pGFD, kSim2eHeaderLen, readOnly); -} - -/* - * Initialize fields for a new file. - */ -DIError WrapperSim2eHDV::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - uint8_t header[kSim2eHeaderLen]; - long blocks = (long) (length / 512); - - assert(physical == DiskImg::kPhysicalFormatSectors); - assert(order == DiskImg::kSectorOrderProDOS); - - if (blocks < 4 || blocks > 65536) { - LOGI(" Sim2e invalid blocks %ld", blocks); - return kDIErrInvalidArg; - } - if (blocks == 65536) // 32MB volumes are actually 31.9 - blocks = 65535; - - memcpy(header, kSim2eID, strlen(kSim2eID)); - header[13] = (uint8_t) blocks; - header[14] = (uint8_t) ((blocks & 0xff00) >> 8); - DIError dierr = pWrapperGFD->Write(header, kSim2eHeaderLen); - if (dierr != kDIErrNone) { - LOGI(" Sim2eHDV header write failed (err=%d)", dierr); - return dierr; - } - - *pWrappedLength = length + kSim2eHeaderLen; - - *pDataFD = new GFDGFD; - return ((GFDGFD*)*pDataFD)->Open(pWrapperGFD, kSim2eHeaderLen, false); -} - -/* - * We only use GFDGFD, so there's nothing to do here. - */ -DIError WrapperSim2eHDV::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - return kDIErrNone; -} - - -/* - * =========================================================================== - * TrackStar .app images - * =========================================================================== - */ - -/* - * File format: - * $0000 track 0 data - * $1a00 track 1 data - * $3400 track 2 data - * ... - * $3f600 track 39 data - * - * Each track consists of: - * $0000 Text description of disk contents (same on every track), in low - * ASCII, padded out with spaces ($20) - * $002e Start of zeroed-out header field - * $0080 $00 (indicates end of data when reading from end??) - * $0081 Raw nibble data (hi bit set), written backwards - * $19fe Start offset of track data - * - * Take the start offset, add 128, and walk backward until you find a - * value with the high bit clear. If the start offset is zero, start - * scanning from $19fd backward. (This approach courtesty Gerald Ryckman.) - * - * My take: the "offset" actually indicates the length of data, and the - * $00 is there to simplify somebody's algorithm. If the offset is zero - * it means the track couldn't be analyzed successfully, so a raw dump has - * been provided. Tracks 35-39 on most Apple II disks have zero length, - * but occasionally one analyzes "successfully" with some horribly truncated - * length. - * - * I'm going to assert that byte $81 be zero and that nothing else has the - * high bit clear until you hit the end of valid data. - * - * Because the nibbles are stored in reverse order, it's easiest to unpack - * the tracks to local buffers, then re-pack them when saving the file. - */ - -/* - * Test to see if this is a TrackStar 5.25" disk image. - * - * While the image format supports variable-length nibble tracks, it uses - * fixed-length fields to store them. Each track is stored in 6656 bytes, - * but has a 129-byte header and a 2-byte footer (max of 6525). - * - * Images may be 40-track (5.25") or 80-track (5.25" disk with half-track - * stepping). The latter is useful in some circumstances for handling - * copy-protected disks. We don't have a half-track interface, so we just - * ignore the odd-numbered tracks. - * - * There is currently no way for the API to set the number of tracks. - */ -/*static*/ DIError WrapperTrackStar::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - DIError dierr = kDIErrNone; - LOGI("Testing for TrackStar"); - int numTracks; - - /* check the length */ - if (wrappedLength == 6656*40) - numTracks = 40; - else if (wrappedLength == 6656*80) - numTracks = 80; - else - return kDIErrGeneric; - - LOGI(" Checking for %d-track image", numTracks); - - /* verify each track */ - uint8_t trackBuf[kFileTrackStorageLen]; - pGFD->Rewind(); - for (int trk = 0; trk < numTracks; trk++) { - dierr = pGFD->Read(trackBuf, sizeof(trackBuf)); - if (dierr != kDIErrNone) - goto bail; - dierr = VerifyTrack(trk, trackBuf); - if (dierr != kDIErrNone) - goto bail; - } - LOGI(" TrackStar tracks verified"); - -bail: - return dierr; -} - -/* - * Check the format. - */ -/*static*/ DIError WrapperTrackStar::VerifyTrack(int track, const uint8_t* trackBuf) -{ - unsigned int dataLen; - - if (trackBuf[0x80] != 0) { - LOGI(" TrackStar track=%d found nonzero at 129", track); - return kDIErrGeneric; - } - - dataLen = GetShortLE(trackBuf + 0x19fe); - if (dataLen > kMaxTrackLen) { - LOGI(" TrackStar track=%d len=%d exceeds max (%d)", - track, dataLen, kMaxTrackLen); - return kDIErrGeneric; - } - if (dataLen == 0) - dataLen = kMaxTrackLen; - - unsigned int i; - for (i = 0; i < dataLen; i++) { - if ((trackBuf[0x81 + i] & 0x80) == 0) { - LOGI(" TrackStar track=%d found invalid data 0x%02x at %d", - track, trackBuf[0x81+i], i); - return kDIErrGeneric; - } - } - - if (track == 0) { - LOGI(" TrackStar msg='%s'", trackBuf); - } - - return kDIErrNone; -} - -/* - * Fill in some details. - */ -DIError WrapperTrackStar::Prep(GenericFD* pGFD, di_off_t wrappedLength, - bool readOnly, di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - LOGI("Prepping for TrackStar"); - DIError dierr = kDIErrNone; - - if (wrappedLength == kFileTrackStorageLen * 40) - fImageTracks = 40; - else if (wrappedLength == kFileTrackStorageLen * 80) - fImageTracks = 80; - else - return kDIErrInternal; - - dierr = Unpack(pGFD, ppNewGFD); - if (dierr != kDIErrNone) - return dierr; - - *pLength = kTrackStarNumTracks * kTrackAllocSize; - *pPhysical = DiskImg::kPhysicalFormatNib525_Var; - *pOrder = DiskImg::kSectorOrderPhysical; - - return dierr; -} - -/* - * Unpack reverse-order nibbles from "pGFD" to a new memory buffer - * created in "*ppNewGFD". - */ -DIError WrapperTrackStar::Unpack(GenericFD* pGFD, GenericFD** ppNewGFD) -{ - DIError dierr; - GFDBuffer* pNewGFD = NULL; - uint8_t* buf = NULL; - - pGFD->Rewind(); - - buf = new uint8_t[kTrackStarNumTracks * kTrackAllocSize]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - pNewGFD = new GFDBuffer; - if (pNewGFD == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = pNewGFD->Open(buf, kTrackStarNumTracks * kTrackAllocSize, - true, false, false); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - dierr = UnpackDisk(pGFD, pNewGFD); - if (dierr != kDIErrNone) - goto bail; - - *ppNewGFD = pNewGFD; - pNewGFD = NULL; // now owned by caller - -bail: - delete[] buf; - delete pNewGFD; - return dierr; -} - -/* - * Unpack a TrackStar image. This is mainly just copying bytes around. The - * nibble code is perfectly happy with odd-sized tracks. However, we want - * to be able to find a particular track without having to do a lookup. So, - * we just block out 40 sets of 6656-byte tracks. - * - * The resultant image will always have 40 tracks. On an 80-track image - * we skip the odd ones. - * - * The bytes are stored in reverse order, so we need to unpack them to a - * separate buffer. - * - * This fills out "fNibbleTrackInfo". - */ -DIError WrapperTrackStar::UnpackDisk(GenericFD* pGFD, GenericFD* pNewGFD) -{ - DIError dierr = kDIErrNone; - uint8_t inBuf[kFileTrackStorageLen]; - uint8_t outBuf[kTrackAllocSize]; - int i, trk; - - assert(kTrackStarNumTracks <= kMaxNibbleTracks525); - - pGFD->Rewind(); - pNewGFD->Rewind(); - - /* we don't currently support half-tracks */ - fNibbleTrackInfo.numTracks = kTrackStarNumTracks; - for (trk = 0; trk < kTrackStarNumTracks; trk++) { - unsigned int dataLen; - - fNibbleTrackInfo.offset[trk] = trk * kTrackAllocSize; - - /* these were verified earlier, so assume data is okay */ - dierr = pGFD->Read(inBuf, sizeof(inBuf)); - if (dierr != kDIErrNone) - goto bail; - - dataLen = GetShortLE(inBuf + 0x19fe); - if (dataLen == 0) - dataLen = kMaxTrackLen; - assert(dataLen <= kMaxTrackLen); - assert(dataLen <= sizeof(outBuf)); - - fNibbleTrackInfo.length[trk] = dataLen; - - memset(outBuf, 0x11, sizeof(outBuf)); - for (i = 0; i < (int) dataLen; i++) - outBuf[i] = inBuf[128+dataLen-i]; - - pNewGFD->Write(outBuf, sizeof(outBuf)); - - if (fImageTracks == 2*kTrackStarNumTracks) { - /* skip the odd-numbered tracks */ - dierr = pGFD->Read(inBuf, sizeof(inBuf)); - if (dierr != kDIErrNone) - goto bail; - } - } - -bail: - return dierr; -} - - -/* - * Initialize stuff for a new file. There's no file header or other - * goodies, so we leave "pWrapperGFD" and "pWrappedLength" alone. - */ -DIError WrapperTrackStar::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - assert(length == kTrackLenTrackStar525 * kTrackCount525 || - length == kTrackLenTrackStar525 * kTrackStarNumTracks); - assert(physical == DiskImg::kPhysicalFormatNib525_Var); - assert(order == DiskImg::kSectorOrderPhysical); - - DIError dierr; - uint8_t* buf = NULL; - int numTracks = (int) (length / kTrackLenTrackStar525); - int i; - - /* - * Set up the track offset and length table. We use the maximum - * data length (kTrackLenTrackStar525) for each. The nibble write - * routine will alter the length field as appropriate. - */ - fNibbleTrackInfo.numTracks = numTracks; - assert(fNibbleTrackInfo.numTracks <= kMaxNibbleTracks525); - for (i = 0; i < numTracks; i++) { - fNibbleTrackInfo.offset[i] = kTrackLenTrackStar525 * i; - fNibbleTrackInfo.length[i] = kTrackLenTrackStar525; - } - - /* - * Create a blank chunk of memory for the image. - */ - buf = new uint8_t[(int) length]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - GFDBuffer* pNewGFD; - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, false); - if (dierr != kDIErrNone) { - delete pNewGFD; - goto bail; - } - *pDataFD = pNewGFD; - buf = NULL; // now owned by pNewGFD; - - // can't set *pWrappedLength yet - -bail: - delete[] buf; - return dierr; -} - -/* - * Write the stored data into TrackStar format. - * - * The source data is in "pDataGFD" in a layout described by fNibbleTrackInfo. - * We need to create the new file in "pWrapperGFD". - */ -DIError WrapperTrackStar::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - DIError dierr = kDIErrNone; - - assert(dataLen == kTrackLenTrackStar525 * kTrackCount525 || - dataLen == kTrackLenTrackStar525 * kTrackStarNumTracks); - assert(kTrackLenTrackStar525 <= kMaxTrackLen); - - pDataGFD->Rewind(); - - uint8_t writeBuf[kFileTrackStorageLen]; - uint8_t dataBuf[kTrackLenTrackStar525]; - int track, trackLen; - - for (track = 0; track < kTrackStarNumTracks; track++) { - if (track < fNibbleTrackInfo.numTracks) { - dierr = pDataGFD->Read(dataBuf, kTrackLenTrackStar525); - if (dierr != kDIErrNone) - goto bail; - trackLen = fNibbleTrackInfo.length[track]; - assert(fNibbleTrackInfo.offset[track] == kTrackLenTrackStar525 * track); - } else { - LOGI(" TrackStar faking track %d", track); - memset(dataBuf, 0xff, sizeof(dataBuf)); - trackLen = kMaxTrackLen; - } - - memset(writeBuf, 0x80, sizeof(writeBuf)); // not strictly necessary - memset(writeBuf, 0x20, kCommentFieldLen); - memset(writeBuf+kCommentFieldLen, 0x00, 0x81-kCommentFieldLen); - - const char* comment; - if (fStorageName != NULL && *fStorageName != '\0') - comment = fStorageName; - else - comment = "(created by CiderPress)"; - if (strlen(comment) > kCommentFieldLen) - memcpy(writeBuf, comment, kCommentFieldLen); - else - memcpy(writeBuf, comment, strlen(comment)); - - int i; - for (i = 0; i < trackLen; i++) { - // If we write a value here with the high bit clear, we will - // reject the file when we try to open it. So, we force the - // high bit on here, on the assumption that the nibble data - // we've been handled is otherwise good. - //writeBuf[0x81+i] = dataBuf[trackLen - i -1]; - writeBuf[0x81+i] = dataBuf[trackLen - i -1] | 0x80; - } - - if (trackLen == kMaxTrackLen) - PutShortLE(writeBuf + 0x19fe, 0); - else - PutShortLE(writeBuf + 0x19fe, (uint16_t) trackLen); - - dierr = pWrapperGFD->Write(writeBuf, sizeof(writeBuf)); - if (dierr != kDIErrNone) - goto bail; - } - - *pWrappedLen = pWrapperGFD->Tell(); - assert(*pWrappedLen == kFileTrackStorageLen * kTrackStarNumTracks); - -bail: - return dierr; -} - -void WrapperTrackStar::SetNibbleTrackLength(int track, int length) -{ - assert(track >= 0); - assert(length > 0 && length <= kMaxTrackLen); - assert(track < fNibbleTrackInfo.numTracks); - - LOGI(" TrackStar: set length of track %d to %d", track, length); - fNibbleTrackInfo.length[track] = length; -} - - -/* - * =========================================================================== - * FDI (Formatted Disk Image) format - * =========================================================================== - */ - -/* - * The format is described in detail in documents on the "disk2fdi" web site. - * - * FDI is currently unique in that it can (and often does) store nibble - * images of 3.5" disks. Rather than add an understanding of nibblized - * 3.5" disks to DiskImg, I've chosen to present it as a simple 800K - * ProDOS disk image. The only flaw in the scheme is that we have to - * keep track of the bad blocks in a parallel data structure. - */ - -/*static*/ const char* WrapperFDI::kFDIMagic = "Formatted Disk Image file\r\n"; - -/* - * Test to see if this is an FDI disk image. - */ -/*static*/ DIError WrapperFDI::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - DIError dierr = kDIErrNone; - uint8_t headerBuf[kMinHeaderLen]; - FDIHeader hdr; - - LOGI("Testing for FDI"); - - pGFD->Rewind(); - dierr = pGFD->Read(headerBuf, sizeof(headerBuf)); - if (dierr != kDIErrNone) - goto bail; - - UnpackHeader(headerBuf, &hdr); - if (strcmp(hdr.signature, kFDIMagic) != 0) { - LOGI("FDI: FDI signature not found"); - return kDIErrGeneric; - } - if (hdr.version < kMinVersion) { - LOGI("FDI: bad version 0x%.04x", hdr.version); - return kDIErrGeneric; - } - -bail: - return dierr; -} - -/* - * Unpack a 512-byte buffer with the FDI header into its components. - */ -/*static*/ void WrapperFDI::UnpackHeader(const uint8_t* headerBuf, FDIHeader* pHdr) -{ - memcpy(pHdr->signature, &headerBuf[0], kSignatureLen); - pHdr->signature[kSignatureLen] = '\0'; - memcpy(pHdr->creator, &headerBuf[27], kCreatorLen); - pHdr->creator[kCreatorLen] = '\0'; - memcpy(pHdr->comment, &headerBuf[59], kCommentLen); - pHdr->comment[kCommentLen] = '\0'; - - pHdr->version = GetShortBE(&headerBuf[140]); - pHdr->lastTrack = GetShortBE(&headerBuf[142]); - pHdr->lastHead = headerBuf[144]; - pHdr->type = headerBuf[145]; - pHdr->rotSpeed = headerBuf[146]; - pHdr->flags = headerBuf[147]; - pHdr->tpi = headerBuf[148]; - pHdr->headWidth = headerBuf[149]; - pHdr->reserved = GetShortBE(&headerBuf[150]); -} - -/* - * Dump the contents of an FDI header. - */ -/*static*/ void WrapperFDI::DumpHeader(const FDIHeader* pHdr) -{ - static const char* kTypes[] = { - "8\"", "5.25\"", "3.5\"", "3\"" - }; - static const char* kTPI[] = { - "48", "67", "96", "100", "135", "192" - }; - - LOGI(" FDI header contents:"); - LOGI(" signature: '%s'", pHdr->signature); - LOGI(" creator : '%s'", pHdr->creator); - LOGI(" comment : '%s'", pHdr->comment); - LOGI(" version : %d.%d", pHdr->version >> 8, pHdr->version & 0xff); - LOGI(" lastTrack: %d", pHdr->lastTrack); - LOGI(" lastHead : %d", pHdr->lastHead); - LOGI(" type : %d (%s)", pHdr->type, - (/*pHdr->type >= 0 &&*/ pHdr->type < NELEM(kTypes)) ? - kTypes[pHdr->type] : "???"); - LOGI(" rotSpeed : %drpm", pHdr->rotSpeed + 128); - LOGI(" flags : 0x%02x", pHdr->flags); - LOGI(" tpi : %d (%s)", pHdr->tpi, - (/*pHdr->tpi >= 0 &&*/ pHdr->tpi < NELEM(kTPI)) ? - kTPI[pHdr->tpi] : "???"); - LOGI(" headWidth: %d (%s)", pHdr->headWidth, - (/*pHdr->headWidth >= 0 &&*/ pHdr->headWidth < NELEM(kTPI)) ? - kTPI[pHdr->headWidth] : "???"); - LOGI(" reserved : %d", pHdr->reserved); -} - -/* - * Unpack the disk to heap storage. - */ -DIError WrapperFDI::Prep(GenericFD* pGFD, di_off_t wrappedLength, bool readOnly, - di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - LOGI("Prepping for FDI"); - DIError dierr = kDIErrNone; - FDIHeader hdr; - - pGFD->Rewind(); - dierr = pGFD->Read(fHeaderBuf, sizeof(fHeaderBuf)); - if (dierr != kDIErrNone) - goto bail; - - UnpackHeader(fHeaderBuf, &hdr); - if (strcmp(hdr.signature, kFDIMagic) != 0) - return kDIErrGeneric; - DumpHeader(&hdr); - - /* - * There are two formats that we're interested in, 3.5" and 5.25". They - * are handled differently within CiderPress, so we split here. - * - * Sometimes disk2fdi finds extra tracks. No Apple II hardware ever - * went past 40 on 5.25" disks, but we'll humor the software and allow - * images with up to 50. Ditto for 3.5" disks, which should always - * have 80 double-sided tracks. - */ - if (hdr.type == kDiskType525) { - LOGI("FDI: decoding 5.25\" disk"); - if (hdr.lastHead != 0 || hdr.lastTrack >= kMaxNibbleTracks525 + 10) { - LOGI("FDI: bad params head=%d ltrack=%d", - hdr.lastHead, hdr.lastTrack); - dierr = kDIErrUnsupportedImageFeature; - goto bail; - } - if (hdr.lastTrack >= kMaxNibbleTracks525) { - LOGI("FDI: reducing hdr.lastTrack from %d to %d", - hdr.lastTrack, kMaxNibbleTracks525-1); - hdr.lastTrack = kMaxNibbleTracks525-1; - } - - /* - * Unpack to a series of variable-length nibble tracks. The data - * goes into ppNewGFD, and a table of track info goes into - * fNibbleTrackInfo. - */ - dierr = Unpack525(pGFD, ppNewGFD, hdr.lastTrack+1, hdr.lastHead+1); - if (dierr != kDIErrNone) - return dierr; - - *pLength = kMaxNibbleTracks525 * kTrackAllocSize; - *pPhysical = DiskImg::kPhysicalFormatNib525_Var; - *pOrder = DiskImg::kSectorOrderPhysical; - } else if (hdr.type == kDiskType35) { - LOGI("FDI: decoding 3.5\" disk"); - if (hdr.lastHead != 1 || hdr.lastTrack >= kMaxNibbleTracks35 + 10) { - LOGI("FDI: bad params head=%d ltrack=%d", - hdr.lastHead, hdr.lastTrack); - dierr = kDIErrUnsupportedImageFeature; - goto bail; - } - if (hdr.lastTrack >= kMaxNibbleTracks35) { - LOGI("FDI: reducing hdr.lastTrack from %d to %d", - hdr.lastTrack, kMaxNibbleTracks35-1); - hdr.lastTrack = kMaxNibbleTracks35-1; - } - - /* - * Unpack to 800K of 512-byte ProDOS-order blocks, with a - * "bad block" map. - */ - dierr = Unpack35(pGFD, ppNewGFD, hdr.lastTrack+1, hdr.lastHead+1, - ppBadBlockMap); - if (dierr != kDIErrNone) - return dierr; - - *pLength = 800 * 1024; - *pPhysical = DiskImg::kPhysicalFormatSectors; - *pOrder = DiskImg::kSectorOrderProDOS; - } else { - LOGI("FDI: unsupported disk type"); - dierr = kDIErrUnsupportedImageFeature; - goto bail; - } - -bail: - return dierr; -} - -/* - * Unpack pulse timing values to nibbles. - */ -DIError WrapperFDI::Unpack525(GenericFD* pGFD, GenericFD** ppNewGFD, int numCyls, - int numHeads) -{ - DIError dierr = kDIErrNone; - GFDBuffer* pNewGFD = NULL; - uint8_t* buf = NULL; - int numTracks; - - numTracks = numCyls * numHeads; - if (numTracks < kMaxNibbleTracks525) - numTracks = kMaxNibbleTracks525; - - pGFD->Rewind(); - - buf = new uint8_t[numTracks * kTrackAllocSize]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - pNewGFD = new GFDBuffer; - if (pNewGFD == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = pNewGFD->Open(buf, numTracks * kTrackAllocSize, - true, false, false); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - dierr = UnpackDisk525(pGFD, pNewGFD, numCyls, numHeads); - if (dierr != kDIErrNone) - goto bail; - - *ppNewGFD = pNewGFD; - pNewGFD = NULL; // now owned by caller - -bail: - delete[] buf; - delete pNewGFD; - return dierr; -} - -/* - * Unpack pulse timing values to fully-decoded blocks. - */ -DIError WrapperFDI::Unpack35(GenericFD* pGFD, GenericFD** ppNewGFD, int numCyls, - int numHeads, LinearBitmap** ppBadBlockMap) -{ - DIError dierr = kDIErrNone; - GFDBuffer* pNewGFD = NULL; - uint8_t* buf = NULL; - - pGFD->Rewind(); - - buf = new uint8_t[800 * 1024]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - pNewGFD = new GFDBuffer; - if (pNewGFD == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = pNewGFD->Open(buf, 800 * 1024, true, false, false); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - *ppBadBlockMap = new LinearBitmap(1600); - if (*ppBadBlockMap == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = UnpackDisk35(pGFD, pNewGFD, numCyls, numHeads, *ppBadBlockMap); - if (dierr != kDIErrNone) - goto bail; - - *ppNewGFD = pNewGFD; - pNewGFD = NULL; // now owned by caller - -bail: - delete[] buf; - delete pNewGFD; - return dierr; -} - -/* - * Initialize stuff for a new file. There's no file header or other - * goodies, so we leave "pWrapperGFD" and "pWrappedLength" alone. - */ -DIError WrapperFDI::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - DIError dierr = kDIErrGeneric; // not yet -#if 0 - uint8_t* buf = NULL; - int numTracks = (int) (length / kTrackLenTrackStar525); - int i; - - assert(length == kTrackLenTrackStar525 * kTrackCount525 || - length == kTrackLenTrackStar525 * kTrackStarNumTracks); - assert(physical == DiskImg::kPhysicalFormatNib525_Var); - assert(order == DiskImg::kSectorOrderPhysical); - - /* - * Set up the track offset and length table. We use the maximum - * data length (kTrackLenTrackStar525) for each. The nibble write - * routine will alter the length field as appropriate. - */ - fNibbleTrackInfo.numTracks = numTracks; - assert(fNibbleTrackInfo.numTracks <= kMaxNibbleTracks); - for (i = 0; i < numTracks; i++) { - fNibbleTrackInfo.offset[i] = kTrackLenTrackStar525 * i; - fNibbleTrackInfo.length[i] = kTrackLenTrackStar525; - } - - /* - * Create a blank chunk of memory for the image. - */ - buf = new uint8_t[(int) length]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - GFDBuffer* pNewGFD; - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, false); - if (dierr != kDIErrNone) { - delete pNewGFD; - goto bail; - } - *pDataFD = pNewGFD; - buf = NULL; // now owned by pNewGFD; - - // can't set *pWrappedLength yet - -bail: - delete[] buf; -#endif - return dierr; -} - -/* - * Write the stored data into FDI format. - * - * The source data is in "pDataGFD" in a layout described by fNibbleTrackInfo. - * We need to create the new file in "pWrapperGFD". - */ -DIError WrapperFDI::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - DIError dierr = kDIErrGeneric; // not yet - -#if 0 - assert(dataLen == kTrackLenTrackStar525 * kTrackCount525 || - dataLen == kTrackLenTrackStar525 * kTrackStarNumTracks); - assert(kTrackLenTrackStar525 <= kMaxTrackLen); - - pDataGFD->Rewind(); - - uint8_t writeBuf[kFileTrackStorageLen]; - uint8_t dataBuf[kTrackLenTrackStar525]; - int track, trackLen; - - for (track = 0; track < kTrackStarNumTracks; track++) { - if (track < fNibbleTrackInfo.numTracks) { - dierr = pDataGFD->Read(dataBuf, kTrackLenTrackStar525); - if (dierr != kDIErrNone) - goto bail; - trackLen = fNibbleTrackInfo.length[track]; - assert(fNibbleTrackInfo.offset[track] == kTrackLenTrackStar525 * track); - } else { - LOGI(" TrackStar faking track %d", track); - memset(dataBuf, 0xff, sizeof(dataBuf)); - trackLen = kMaxTrackLen; - } - - memset(writeBuf, 0x80, sizeof(writeBuf)); // not strictly necessary - memset(writeBuf, 0x20, kCommentFieldLen); - memset(writeBuf+kCommentFieldLen, 0x00, 0x81-kCommentFieldLen); - - const char* comment; - if (fStorageName != NULL && *fStorageName != '\0') - comment = fStorageName; - else - comment = "(created by CiderPress)"; - if (strlen(comment) > kCommentFieldLen) - memcpy(writeBuf, comment, kCommentFieldLen); - else - memcpy(writeBuf, comment, strlen(comment)); - - int i; - for (i = 0; i < trackLen; i++) - writeBuf[0x81+i] = dataBuf[trackLen - i -1]; - - if (trackLen == kMaxTrackLen) - PutShortLE(writeBuf + 0x19fe, 0); - else - PutShortLE(writeBuf + 0x19fe, (uint16_t) trackLen); - - dierr = pWrapperGFD->Write(writeBuf, sizeof(writeBuf)); - if (dierr != kDIErrNone) - goto bail; - } - - *pWrappedLen = pWrapperGFD->Tell(); - assert(*pWrappedLen == kFileTrackStorageLen * kTrackStarNumTracks); - -bail: -#endif - return dierr; -} - -void WrapperFDI::SetNibbleTrackLength(int track, int length) -{ - assert(false); // not yet -#if 0 - assert(track >= 0); - assert(length > 0 && length <= kMaxTrackLen); - assert(track < fNibbleTrackInfo.numTracks); - - LOGI(" FDI: set length of track %d to %d", track, length); - fNibbleTrackInfo.length[track] = length; -#endif -} - - -/* - * =========================================================================== - * Unadorned nibble format - * =========================================================================== - */ - -/* - * See if this is unadorned nibble format. - */ -/*static*/ DIError WrapperUnadornedNibble::Test(GenericFD* pGFD, - di_off_t wrappedLength) -{ - LOGI("Testing for unadorned nibble"); - - /* test length */ - if (wrappedLength != kTrackCount525 * kTrackLenNib525 && - wrappedLength != kTrackCount525 * kTrackLenNb2525) - { - return kDIErrGeneric; - } - - /* quick scan for invalid data */ - const int kScanSize = 512; - uint8_t buf[kScanSize]; - - pGFD->Rewind(); - if (pGFD->Read(buf, sizeof(buf)) != kDIErrNone) - return kDIErrGeneric; - - /* - * Make sure this is a nibble image and not just a ProDOS volume that - * happened to get the right number of blocks. The primary test is - * for < 0x80 since there's no way that can be valid, even on a track - * full of junk. - */ - for (int i = 0; i < kScanSize; i++) { - if (buf[i] < 0x80) { - LOGD(" Disqualifying len=%ld from nibble, byte=0x%02x", - (long) wrappedLength, buf[i]); - return kDIErrGeneric; - } else if (buf[i] < 0x96) { - LOGD(" Warning: funky byte 0x%02x in file", buf[i]); - } - } - - return kDIErrNone; -} - -/* - * Prepare unadorned nibble for use. Not much to do here. - */ -DIError WrapperUnadornedNibble::Prep(GenericFD* pGFD, di_off_t wrappedLength, - bool readOnly, di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - LOGI("Prep for unadorned nibble"); - - if (wrappedLength == kTrackCount525 * kTrackLenNib525) { - LOGI(" Prepping for 6656-byte NIB"); - *pPhysical = DiskImg::kPhysicalFormatNib525_6656; - } else if (wrappedLength == kTrackCount525 * kTrackLenNb2525) { - LOGI(" Prepping for 6384-byte NB2"); - *pPhysical = DiskImg::kPhysicalFormatNib525_6384; - } else { - LOGI(" Unexpected wrappedLength %ld for unadorned nibble", - (long) wrappedLength); - assert(false); - } - - *pLength = wrappedLength; - *pOrder = DiskImg::kSectorOrderPhysical; - - *ppNewGFD = new GFDGFD; - return ((GFDGFD*)*ppNewGFD)->Open(pGFD, 0, readOnly); -} - -/* - * Initialize fields for a new file. - */ -DIError WrapperUnadornedNibble::Create(di_off_t length, - DiskImg::PhysicalFormat physical, DiskImg::SectorOrder order, - short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - LOGI("Create unadorned nibble"); - - *pWrappedLength = length; - *pDataFD = new GFDGFD; - return ((GFDGFD*)*pDataFD)->Open(pWrapperGFD, 0, false); -} - -/* - * We only use GFDGFD, so there's nothing to do here. - */ -DIError WrapperUnadornedNibble::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - return kDIErrNone; -} - - -/* - * =========================================================================== - * Unadorned sectors - * =========================================================================== - */ - -/* - * See if this is unadorned sector format. The only way we can really tell - * is by looking at the file size. - * - * The only requirement is that it be a multiple of 512 bytes. This holds - * for all ProDOS volumes and all floppy disk images. We also need to test - * for 13-sector ".d13" images. - * - * It also holds for 35-track 6656-byte unadorned nibble images, so we need - * to test for them first. - */ -/*static*/ DIError WrapperUnadornedSector::Test(GenericFD* pGFD, di_off_t wrappedLength) -{ - LOGI("Testing for unadorned sector (wrappedLength=%ld/%u)", - (long) (wrappedLength >> 32), (uint32_t) wrappedLength); - if (wrappedLength >= 1536 && (wrappedLength % 512) == 0) - return kDIErrNone; - if (wrappedLength == kD13Length) // 13-sector image? - return kDIErrNone; - - return kDIErrGeneric; -} - -/* - * Prepare unadorned sector for use. Not much to do here. - */ -DIError WrapperUnadornedSector::Prep(GenericFD* pGFD, di_off_t wrappedLength, - bool readOnly, di_off_t* pLength, DiskImg::PhysicalFormat* pPhysical, - DiskImg::SectorOrder* pOrder, short* pDiskVolNum, - LinearBitmap** ppBadBlockMap, GenericFD** ppNewGFD) -{ - LOGI("Prepping for unadorned sector"); - assert(wrappedLength > 0); - *pLength = wrappedLength; - *pPhysical = DiskImg::kPhysicalFormatSectors; - //*pOrder = undetermined - - *ppNewGFD = new GFDGFD; - return ((GFDGFD*)*ppNewGFD)->Open(pGFD, 0, readOnly); -} - -/* - * Initialize fields for a new file. - */ -DIError WrapperUnadornedSector::Create(di_off_t length, DiskImg::PhysicalFormat physical, - DiskImg::SectorOrder order, short dosVolumeNum, GenericFD* pWrapperGFD, - di_off_t* pWrappedLength, GenericFD** pDataFD) -{ - LOGI("Create unadorned sector"); - - *pWrappedLength = length; - *pDataFD = new GFDGFD; - return ((GFDGFD*)*pDataFD)->Open(pWrapperGFD, 0, false); -} - -/* - * We only use GFDGFD, so there's nothing to do here. - */ -DIError WrapperUnadornedSector::Flush(GenericFD* pWrapperGFD, GenericFD* pDataGFD, - di_off_t dataLen, di_off_t* pWrappedLen) -{ - return kDIErrNone; -} diff --git a/ciderpress/diskimg/MacPart.cpp b/ciderpress/diskimg/MacPart.cpp deleted file mode 100644 index e94f37a..0000000 --- a/ciderpress/diskimg/MacPart.cpp +++ /dev/null @@ -1,470 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * The "MacPart" DiskFS is a container class for multiple ProDOS and HFS - * volumes. It represents a partitioned disk device, such as a hard - * drive or CD-ROM. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -const int kBlkSize = 512; -const int kDDRBlock = 0; // Driver Descriptor Record block -const int kPartMapStart = 1; // start of partition map - - -/* - * Format of DDR (block 0). - */ -typedef struct DiskFSMacPart::DriverDescriptorRecord { - uint16_t sbSig; // {device signature} - uint16_t sbBlkSize; // {block size of the device} - uint32_t sbBlkCount; // {number of blocks on the device} - uint16_t sbDevType; // {reserved} - uint16_t sbDevId; // {reserved} - uint32_t sbData; // {reserved} - uint16_t sbDrvrCount; // {number of driver descriptor entries} - uint16_t hiddenPad; // implicit in specification - uint32_t ddBlock; // {first driver's starting block} - uint16_t ddSize; // {size of the driver, in 512-byte blocks} - uint16_t ddType; // {operating system type (MacOS = 1)} - uint16_t ddPad[242]; // {additional drivers, if any} -} DriverDescriptorRecord; - -/* - * Format of partition map blocks. The partition map is an array of these. - */ -typedef struct DiskFSMacPart::PartitionMap { - uint16_t pmSig; // {partition signature} - uint16_t pmSigPad; // {reserved} - uint32_t pmMapBlkCnt; // {number of blocks in partition map} - uint32_t pmPyPartStart; // {first physical block of partition} - uint32_t pmPartBlkCnt; // {number of blocks in partition} - uint8_t pmPartName[32]; // {partition name} - uint8_t pmParType[32]; // {partition type} - uint32_t pmLgDataStart; // {first logical block of data area} - uint32_t pmDataCnt; // {number of blocks in data area} - uint32_t pmPartStatus; // {partition status information} - uint32_t pmLgBootStart; // {first logical block of boot code} - uint32_t pmBootSize; // {size of boot code, in bytes} - uint32_t pmBootAddr; // {boot code load address} - uint32_t pmBootAddr2; // {reserved} - uint32_t pmBootEntry; // {boot code entry point} - uint32_t pmBootEntry2; // {reserved} - uint32_t pmBootCksum; // {boot code checksum} - uint8_t pmProcessor[16]; // {processor type} - uint16_t pmPad[188]; // {reserved} -} PartitionMap; - - -/* - * Figure out if this is a Macintosh-style partition. - * - * The "imageOrder" parameter has no use here, because (in the current - * version) embedded parent volumes are implicitly ProDOS-ordered. - * - * It would be difficult to guess the block order based on the partition - * structure, because the partition map entries can appear in any order. - */ -/*static*/ DIError DiskFSMacPart::TestImage(DiskImg* pImg, - DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - DriverDescriptorRecord ddr; - long pmMapBlkCnt; - - assert(sizeof(PartitionMap) == kBlkSize); - assert(sizeof(DriverDescriptorRecord) == kBlkSize); - - /* check the DDR block */ - dierr = pImg->ReadBlockSwapped(kDDRBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - UnpackDDR(blkBuf, &ddr); - - if (ddr.sbSig != kDDRSignature) { - dierr = kDIErrFilesystemNotFound; - goto bail; - } - if (ddr.sbBlkSize != kBlkSize || ddr.sbBlkCount == 0) { - if (ddr.sbBlkSize == 0 && ddr.sbBlkCount == 0) { - /* - * This is invalid, but it's the way floptical images formatted - * by the C.V.Tech format utilities look. - */ - LOGI(" MacPart NOTE: found zeroed-out DDR, continuing anyway"); - } else if (ddr.sbBlkSize == kBlkSize && ddr.sbBlkCount == 0) { - /* - * This showed up on a disc, so handle it too. - */ - LOGI(" MacPart NOTE: found partially-zeroed-out DDR, continuing"); - } else { - LOGI(" MacPart found 'ER' signature but blkSize=%d blkCount=%d", - ddr.sbBlkSize, ddr.sbBlkCount); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - } - DumpDDR(&ddr); - - /* make sure block 1 is a partition */ - dierr = pImg->ReadBlockSwapped(kPartMapStart, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - if (GetShortBE(&blkBuf[0x00]) != kPartitionSignature) { - LOGI(" MacPart partition signature not found in first part block"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - pmMapBlkCnt = GetLongBE(&blkBuf[0x04]); - if (pmMapBlkCnt <= 0 || pmMapBlkCnt > 256) { - LOGI(" MacPart unreasonable pmMapBlkCnt value %ld", - pmMapBlkCnt); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* could test the rest -- might fix "imageOrder", might not -- but - the format is pretty unambiguous, and we don't care about the order */ - - // success! - LOGI(" MacPart partition map block count = %ld", pmMapBlkCnt); - -bail: - return dierr; -} - -/* - * Unpack a DDR disk block into a DDR data structure. - */ -/*static*/ void DiskFSMacPart::UnpackDDR(const uint8_t* buf, - DriverDescriptorRecord* pDDR) -{ - pDDR->sbSig = GetShortBE(&buf[0x00]); - pDDR->sbBlkSize = GetShortBE(&buf[0x02]); - pDDR->sbBlkCount = GetLongBE(&buf[0x04]); - pDDR->sbDevType = GetShortBE(&buf[0x08]); - pDDR->sbDevId = GetShortBE(&buf[0x0a]); - pDDR->sbData = GetLongBE(&buf[0x0c]); - pDDR->sbDrvrCount = GetShortBE(&buf[0x10]); - pDDR->hiddenPad = GetShortBE(&buf[0x12]); - pDDR->ddBlock = GetLongBE(&buf[0x14]); - pDDR->ddSize = GetShortBE(&buf[0x18]); - pDDR->ddType = GetShortBE(&buf[0x1a]); - - int i; - for (i = 0; i < (int) NELEM(pDDR->ddPad); i++) { - pDDR->ddPad[i] = GetShortBE(&buf[0x1c] + i * sizeof(pDDR->ddPad[0])); - } - assert(0x1c + i * sizeof(pDDR->ddPad[0]) == (unsigned int) kBlkSize); -} - -/* - * Debug: dump the contents of the DDR. - */ -/*static*/ void DiskFSMacPart::DumpDDR(const DriverDescriptorRecord* pDDR) -{ - LOGI(" MacPart driver descriptor record"); - LOGI(" sbSig=0x%04x sbBlkSize=%d sbBlkCount=%d", - pDDR->sbSig, pDDR->sbBlkSize, pDDR->sbBlkCount); - LOGI(" sbDevType=%d sbDevId=%d sbData=%d sbDrvrCount=%d", - pDDR->sbDevType, pDDR->sbDevId, pDDR->sbData, pDDR->sbDrvrCount); - LOGI(" (pad=%d) ddBlock=%d ddSize=%d ddType=%d", - pDDR->hiddenPad, pDDR->ddBlock, pDDR->ddSize, pDDR->ddType); -} - -/* - * Unpack a partition map disk block into a partition map data structure. - */ -/*static*/ void DiskFSMacPart::UnpackPartitionMap(const uint8_t* buf, - PartitionMap* pMap) -{ - pMap->pmSig = GetShortBE(&buf[0x00]); - pMap->pmSigPad = GetShortBE(&buf[0x02]); - pMap->pmMapBlkCnt = GetLongBE(&buf[0x04]); - pMap->pmPyPartStart = GetLongBE(&buf[0x08]); - pMap->pmPartBlkCnt = GetLongBE(&buf[0x0c]); - memcpy(pMap->pmPartName, &buf[0x10], sizeof(pMap->pmPartName)); - pMap->pmPartName[sizeof(pMap->pmPartName)-1] = '\0'; - memcpy(pMap->pmParType, &buf[0x30], sizeof(pMap->pmParType)); - pMap->pmParType[sizeof(pMap->pmParType)-1] = '\0'; - pMap->pmLgDataStart = GetLongBE(&buf[0x50]); - pMap->pmDataCnt = GetLongBE(&buf[0x54]); - pMap->pmPartStatus = GetLongBE(&buf[0x58]); - pMap->pmLgBootStart = GetLongBE(&buf[0x5c]); - pMap->pmBootSize = GetLongBE(&buf[0x60]); - pMap->pmBootAddr = GetLongBE(&buf[0x64]); - pMap->pmBootAddr2 = GetLongBE(&buf[0x68]); - pMap->pmBootEntry = GetLongBE(&buf[0x6c]); - pMap->pmBootEntry2 = GetLongBE(&buf[0x70]); - pMap->pmBootCksum = GetLongBE(&buf[0x74]); - memcpy((char*) pMap->pmProcessor, &buf[0x78], sizeof(pMap->pmProcessor)); - pMap->pmProcessor[sizeof(pMap->pmProcessor)-1] = '\0'; - - int i; - for (i = 0; i < (int) NELEM(pMap->pmPad); i++) { - pMap->pmPad[i] = GetShortBE(&buf[0x88] + i * sizeof(pMap->pmPad[0])); - } - assert(0x88 + i * sizeof(pMap->pmPad[0]) == (unsigned int) kBlkSize); -} - -/* - * Debug: dump the contents of the partition map. - */ -/*static*/ void DiskFSMacPart::DumpPartitionMap(long block, const PartitionMap* pMap) -{ - LOGI(" MacPart partition map: block=%ld", block); - LOGI(" pmSig=0x%04x (pad=0x%04x) pmMapBlkCnt=%d", - pMap->pmSig, pMap->pmSigPad, pMap->pmMapBlkCnt); - LOGI(" pmPartName='%s' pmParType='%s'", - pMap->pmPartName, pMap->pmParType); - LOGI(" pmPyPartStart=%d pmPartBlkCnt=%d", - pMap->pmPyPartStart, pMap->pmPartBlkCnt); - LOGI(" pmLgDataStart=%d pmDataCnt=%d", - pMap->pmLgDataStart, pMap->pmDataCnt); - LOGI(" pmPartStatus=%d", - pMap->pmPartStatus); - LOGI(" pmLgBootStart=%d pmBootSize=%d", - pMap->pmLgBootStart, pMap->pmBootSize); - LOGI(" pmBootAddr=%d pmBootAddr2=%d pmBootEntry=%d pmBootEntry2=%d", - pMap->pmBootAddr, pMap->pmBootAddr2, - pMap->pmBootEntry, pMap->pmBootEntry2); - LOGI(" pmBootCksum=%d pmProcessor='%s'", - pMap->pmBootCksum, pMap->pmProcessor); -} - - -/* - * Open up a sub-volume. - */ -DIError DiskFSMacPart::OpenSubVolume(const PartitionMap* pMap) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - long startBlock, numBlocks; - bool tweaked = false; - - assert(pMap != NULL); - startBlock = pMap->pmPyPartStart; - numBlocks = pMap->pmPartBlkCnt; - - LOGI("Adding '%s' (%s) %ld +%ld", - pMap->pmPartName, pMap->pmParType, startBlock, numBlocks); - - if (startBlock > fpImg->GetNumBlocks()) { - LOGI("MacPart start block out of range (%ld vs %ld)", - startBlock, fpImg->GetNumBlocks()); - return kDIErrBadPartition; - } - if (startBlock + numBlocks > fpImg->GetNumBlocks()) { - LOGI("MacPart partition too large (%ld vs %ld avail)", - numBlocks, fpImg->GetNumBlocks() - startBlock); - fpImg->AddNote(DiskImg::kNoteInfo, - "Reduced partition '%s' (%s) from %ld blocks to %ld.\n", - pMap->pmPartName, pMap->pmParType, numBlocks, - fpImg->GetNumBlocks() - startBlock); - numBlocks = fpImg->GetNumBlocks() - startBlock; - tweaked = true; - } - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - /* - * If "tweaked" is true, we want to make the volume read-only, so that the - * volume copier doesn't stomp on it (on the off chance we've got it - * wrong). However, that won't stop the volume copier from stomping on - * the entire thing, so we really need to change *all* members of the - * diskimg tree to be read-only. This seems counter-productive though. - * - * So far the only actual occurrence of tweakedness was from the first - * Apple "develop" CD-ROM, which had a bad Apple_Extra partition on the - * end. - */ - (void) tweaked; - - dierr = pNewImg->OpenImage(fpImg, startBlock, numBlocks); - if (dierr != kDIErrNone) { - LOGI(" MacPartSub: OpenImage(%ld,%ld) failed (err=%d)", - startBlock, numBlocks, dierr); - goto bail; - } - - //LOGI(" +++ CFFASub: new image has ro=%d (parent=%d)", - // pNewImg->GetReadOnly(), pImg->GetReadOnly()); - - /* the partition is typed; currently no way to give hints to analyzer */ - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" MacPartSub: analysis failed (err=%d)", dierr); - goto bail; - } - - /* we allow unrecognized partitions */ - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" MacPartSub (%ld,%ld): unable to identify filesystem", - startBlock, numBlocks); - DiskFSUnknown* pUnknownFS = new DiskFSUnknown; - if (pUnknownFS == NULL) { - dierr = kDIErrInternal; - goto bail; - } - pUnknownFS->SetVolumeInfo((const char*)pMap->pmParType); - pNewFS = pUnknownFS; - pNewImg->AddNote(DiskImg::kNoteInfo, "Partition name='%s' type='%s'.", - pMap->pmPartName, pMap->pmParType); - } else { - /* open a DiskFS for the sub-image */ - LOGI(" MacPartSub (%ld,%ld) analyze succeeded!", startBlock, numBlocks); - pNewFS = pNewImg->OpenAppropriateDiskFS(true); - if (pNewFS == NULL) { - LOGI(" MacPartSub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - } - - /* we encapsulate arbitrary stuff, so encourage child to scan */ - pNewFS->SetScanForSubVolumes(kScanSubEnabled); - - /* - * Load the files from the sub-image. When doing our initial tests, - * or when loading data for the volume copier, we don't want to dig - * into our sub-volumes, just figure out what they are and where. - * - * If "initialize" fails, the sub-volume won't get added to the list. - * It's important that a failure at this stage doesn't cause the whole - * thing to fall over. - */ - InitMode initMode; - if (GetScanForSubVolumes() == kScanSubContainerOnly) - initMode = kInitHeaderOnly; - else - initMode = kInitFull; - dierr = pNewFS->Initialize(pNewImg, initMode); - if (dierr != kDIErrNone) { - LOGI(" MacPartSub: error %d reading list of files from disk", dierr); - goto bail; - } - - /* add it to the list */ - AddSubVolumeToList(pNewImg, pNewFS); - pNewImg = NULL; - pNewFS = NULL; - -bail: - delete pNewFS; - delete pNewImg; - return dierr; -} - -/* - * Check to see if this is a MacPart volume. - */ -/*static*/ DIError DiskFSMacPart::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (pImg->GetNumBlocks() < kMinInterestingBlocks) - return kDIErrFilesystemNotFound; - if (pImg->GetIsEmbedded()) // don't look for partitions inside - return kDIErrFilesystemNotFound; - - /* assume ProDOS -- shouldn't matter, since it's embedded */ - if (TestImage(pImg, DiskImg::kSectorOrderProDOS) == kDIErrNone) { - *pFormat = DiskImg::kFormatMacPart; - *pOrder = DiskImg::kSectorOrderProDOS; - return kDIErrNone; - } - - LOGI(" FS didn't find valid MacPart"); - return kDIErrFilesystemNotFound; -} - -/* - * Prep the MacPart "container" for use. - */ -DIError DiskFSMacPart::Initialize(void) -{ - DIError dierr = kDIErrNone; - - LOGI("MacPart initializing (scanForSub=%d)", fScanForSubVolumes); - - /* seems pointless *not* to, but we just do what we're told */ - if (fScanForSubVolumes != kScanSubDisabled) { - dierr = FindSubVolumes(); - if (dierr != kDIErrNone) - return dierr; - } - - /* blank out the volume usage map */ - SetVolumeUsageMap(); - - return dierr; -} - -/* - * Find the various sub-volumes and open them. - * - * Because the partitions are explicitly typed, we don't need to probe - * their contents. But we do anyway. - */ -DIError DiskFSMacPart::FindSubVolumes(void) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kBlkSize]; - PartitionMap map; - int i, numMapBlocks; - - dierr = fpImg->ReadBlock(kPartMapStart, buf); - if (dierr != kDIErrNone) - goto bail; - UnpackPartitionMap(buf, &map); - numMapBlocks = map.pmMapBlkCnt; - - for (i = 0; i < numMapBlocks; i++) { - if (i != 0) { - dierr = fpImg->ReadBlock(kPartMapStart+i, buf); - if (dierr != kDIErrNone) - goto bail; - UnpackPartitionMap(buf, &map); - } - DumpPartitionMap(kPartMapStart+i, &map); - - dierr = OpenSubVolume(&map); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - LOGI(" MacPart failed opening sub-volume %d", i); - dierr = CreatePlaceholder(map.pmPyPartStart, map.pmPartBlkCnt, - (const char*)map.pmPartName, (const char*)map.pmParType, - &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - AddSubVolumeToList(pNewImg, pNewFS); - } else { - LOGI(" MacPart unable to create placeholder (err=%d)", - dierr); - break; // something's wrong -- bail out with error - } - } - } - -bail: - return dierr; -} diff --git a/ciderpress/diskimg/Makefile b/ciderpress/diskimg/Makefile deleted file mode 100644 index ca2d0d0..0000000 --- a/ciderpress/diskimg/Makefile +++ /dev/null @@ -1,57 +0,0 @@ -# -# DiskImg makefile for Linux. -# -.SILENT: - -SHELL = /bin/sh -CC = gcc -CXX = g++ -AR = ar -OPT = -g -D_DEBUG -#-DEXCISE_GPL_CODE -#OPT = -g -O2 -GCC_FLAGS = -Wall -Wwrite-strings -Wpointer-arith -Wshadow -# -Wstrict-prototypes -CXXFLAGS = $(OPT) $(GCC_FLAGS) -D_FILE_OFFSET_BITS=64 - -SRCS = ASPI.cpp CFFA.cpp Container.cpp CPM.cpp DDD.cpp DiskFS.cpp \ - DiskImg.cpp DIUtil.cpp DOS33.cpp DOSImage.cpp FAT.cpp FDI.cpp \ - FocusDrive.cpp \GenericFD.cpp Global.cpp Gutenberg.cpp HFS.cpp \ - ImageWrapper.cpp MacPart.cpp MicroDrive.cpp Nibble.cpp \ - Nibble35.cpp OuterWrapper.cpp OzDOS.cpp Pascal.cpp ProDOS.cpp \ - RDOS.cpp TwoImg.cpp UNIDOS.cpp VolumeUsage.cpp Win32BlockIO.cpp -OBJS = ASPI.o CFFA.o Container.o CPM.o DDD.o DiskFS.o \ - DiskImg.o DIUtil.o DOS33.o DOSImage.o FDI.o \ - FocusDrive.o FAT.o GenericFD.o Global.o Gutenberg.o HFS.o \ - ImageWrapper.o MacPart.o MicroDrive.o Nibble.o \ - Nibble35.o OuterWrapper.o OzDOS.o Pascal.o ProDOS.o \ - RDOS.o TwoImg.o UNIDOS.o VolumeUsage.o Win32BlockIO.o - -STATIC_PRODUCT = libdiskimg.a -PRODUCT = $(STATIC_PRODUCT) - - -all: - -mkdir -p ./build - cd ./build && cmake .. - cd ./build && $(MAKE) - -all_orig: $(PRODUCT) - @true - -$(STATIC_PRODUCT): $(OBJS) - -rm -f $(STATIC_PRODUCT) - $(AR) rcv $@ $(OBJS) - -clean: - -rm -f *.o core - -rm -f $(STATIC_PRODUCT) - -rm -f Makefile.bak - -tags:: - @ctags -R --totals * - -depend: - makedepend -- $(CFLAGS) -- $(SRCS) - -# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/ciderpress/diskimg/MicroDrive.cpp b/ciderpress/diskimg/MicroDrive.cpp deleted file mode 100644 index 0201da9..0000000 --- a/ciderpress/diskimg/MicroDrive.cpp +++ /dev/null @@ -1,397 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * The "MicroDrive" DiskFS is a container class for multiple ProDOS and HFS - * volumes. It represents a partitioned disk device, such as a hard - * drive or CF card, that has been formatted for use with ///SHH Systeme's - * MicroDrive card for the Apple II. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -const int kBlkSize = 512; -const int kPartMapBlock = 0; // partition map lives here -const uint32_t kPartSizeMask = 0x00ffffff; - - -/* - * Format of partition map. It resides in the first 256 bytes of block 0. - * All values are in little-endian order. - * - * The layout was discovered through reverse-engineering. Additional notes: - * - From Joachim Lange: - - Below, this is the configuration block as it is used in all - MicroDrive cards. Please verify that my ID shortcut can be - found at offset 0, otherwise the partition info is not - valid. Most of the other parms are not useful, some are - historic and not useful anymore. As a second security - measure, verify that the first partition starts at - absolute block 256. This is also a fixed value used in all - MicroDrive cards. Of course the partition size is not two - bytes long but three (not four), the 4th byte is used for - switching drives in a two-drive configuration. So, for - completeness, when reading partition sizes, perform a - partitionLength[..] & 0x00FFFFFF, or at least issue a - warning that something may be wrong. The offset - (partitionStart) could reach into the 4th byte. - I have attached the config block in a zip file because - the mailer would probably re-format the source text. - */ -const int kMaxNumParts = 8; -typedef struct DiskFSMicroDrive::PartitionMap { - uint16_t magic; // partition signature - uint16_t cylinders; // #of cylinders - uint16_t reserved1; // ?? - uint16_t heads; // #of heads/cylinder - uint16_t sectors; // #of sectors/track - uint16_t reserved2; // ?? - uint8_t numPart1; // #of partitions in first chunk - uint8_t numPart2; // #of partitions in second chunk - uint8_t reserved3[10]; // bytes 0x0e-0x17 - uint16_t romVersion; // IIgs ROM01 or ROM03 - uint8_t reserved4[6]; // bytes 0x1a-0x1f - uint32_t partitionStart1[kMaxNumParts]; // bytes 0x20-0x3f - uint32_t partitionLength1[kMaxNumParts]; // bytes 0x40-0x5f - uint8_t reserved5[32]; // bytes 0x60-0x7f - uint32_t partitionStart2[kMaxNumParts]; // bytes 0x80-0x9f - uint32_t partitionLength2[kMaxNumParts]; // bytes 0xa0-0xbf - - uint8_t padding[320]; -} PartitionMap; - - -/* - * Figure out if this is a MicroDrive partition. - * - * The "imageOrder" parameter has no use here, because (in the current - * version) embedded parent volumes are implicitly ProDOS-ordered. - */ -/*static*/ DIError DiskFSMicroDrive::TestImage(DiskImg* pImg, - DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - int partCount1, partCount2; - - assert(sizeof(PartitionMap) == kBlkSize); - - /* - * See if block 0 is a MicroDrive partition map. - */ - dierr = pImg->ReadBlockSwapped(kPartMapBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - if (GetShortLE(&blkBuf[0x00]) != kPartitionSignature) { - LOGI(" MicroDrive partition signature not found in first part block"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - /* might assert that partCount2 be zero unless partCount1 == 8? */ - partCount1 = blkBuf[0x0c]; - partCount2 = blkBuf[0x0d]; - if (partCount1 == 0 || partCount1 > kMaxNumParts || - partCount2 > kMaxNumParts) - { - LOGI(" MicroDrive unreasonable partCount values %d/%d", - partCount1, partCount2); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* consider testing other fields */ - - // success! - LOGI(" MicroDrive partition map count = %d/%d", partCount1, partCount2); - -bail: - return dierr; -} - - -/* - * Unpack a partition map block into a partition map data structure. - */ -/*static*/ void DiskFSMicroDrive::UnpackPartitionMap(const uint8_t* buf, - PartitionMap* pMap) -{ - pMap->magic = GetShortLE(&buf[0x00]); - pMap->cylinders = GetShortLE(&buf[0x02]); - pMap->reserved1 = GetShortLE(&buf[0x04]); - pMap->heads = GetShortLE(&buf[0x06]); - pMap->sectors = GetShortLE(&buf[0x08]); - pMap->reserved2 = GetShortLE(&buf[0x0a]); - pMap->numPart1 = buf[0x0c]; - pMap->numPart2 = buf[0x0d]; - memcpy(pMap->reserved3, &buf[0x0e], sizeof(pMap->reserved3)); - pMap->romVersion = GetShortLE(&buf[0x18]); - memcpy(pMap->reserved4, &buf[0x1a], sizeof(pMap->reserved4)); - - for (int i = 0; i < kMaxNumParts; i++) { - pMap->partitionStart1[i] = GetLongLE(&buf[0x20] + i * 4); - pMap->partitionLength1[i] = GetLongLE(&buf[0x40] + i * 4) & kPartSizeMask; - pMap->partitionStart2[i] = GetLongLE(&buf[0x80] + i * 4); - pMap->partitionLength2[i] = GetLongLE(&buf[0xa0] + i * 4) & kPartSizeMask; - } - memcpy(pMap->reserved5, &buf[0x60], sizeof(pMap->reserved5)); - memcpy(pMap->padding, &buf[0x80], sizeof(pMap->padding)); -} - -/* - * Debug: dump the contents of the partition map. - */ -/*static*/ void DiskFSMicroDrive::DumpPartitionMap(const PartitionMap* pMap) -{ - LOGI(" MicroDrive partition map:"); - LOGI(" cyls=%d res1=%d heads=%d sects=%d", - pMap->cylinders, pMap->reserved1, pMap->heads, pMap->sectors); - LOGI(" res2=%d numPart1=%d numPart2=%d", - pMap->reserved2, pMap->numPart1, pMap->numPart2); - LOGI(" romVersion=ROM%02d", pMap->romVersion); - - int i, parts; - - parts = pMap->numPart1; - assert(parts <= kMaxNumParts); - for (i = 0; i < parts; i++) { - LOGI(" %2d: startLBA=%8d length=%d", - i, pMap->partitionStart1[i], pMap->partitionLength1[i]); - } - parts = pMap->numPart2; - assert(parts <= kMaxNumParts); - for (i = 0; i < parts; i++) { - LOGI(" %2d: startLBA=%8d length=%d", - i+8, pMap->partitionStart2[i], pMap->partitionLength2[i]); - } -} - - -/* - * Open up a sub-volume. - */ -DIError DiskFSMicroDrive::OpenSubVolume(long startBlock, long numBlocks) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - //bool tweaked = false; - - LOGI("Adding %ld +%ld", startBlock, numBlocks); - - if (startBlock > fpImg->GetNumBlocks()) { - LOGI("MicroDrive start block out of range (%ld vs %ld)", - startBlock, fpImg->GetNumBlocks()); - return kDIErrBadPartition; - } - if (startBlock + numBlocks > fpImg->GetNumBlocks()) { - LOGI("MicroDrive partition too large (%ld vs %ld avail)", - numBlocks, fpImg->GetNumBlocks() - startBlock); - fpImg->AddNote(DiskImg::kNoteInfo, - "Reduced partition from %ld blocks to %ld.\n", - numBlocks, fpImg->GetNumBlocks() - startBlock); - numBlocks = fpImg->GetNumBlocks() - startBlock; - //tweaked = true; - } - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = pNewImg->OpenImage(fpImg, startBlock, numBlocks); - if (dierr != kDIErrNone) { - LOGI(" MicroDriveSub: OpenImage(%ld,%ld) failed (err=%d)", - startBlock, numBlocks, dierr); - goto bail; - } - - //LOGI(" +++ CFFASub: new image has ro=%d (parent=%d)", - // pNewImg->GetReadOnly(), pImg->GetReadOnly()); - - /* figure out what the format is */ - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" MicroDriveSub: analysis failed (err=%d)", dierr); - goto bail; - } - - /* we allow unrecognized partitions */ - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" MicroDriveSub (%ld,%ld): unable to identify filesystem", - startBlock, numBlocks); - DiskFSUnknown* pUnknownFS = new DiskFSUnknown; - if (pUnknownFS == NULL) { - dierr = kDIErrInternal; - goto bail; - } - //pUnknownFS->SetVolumeInfo((const char*)pMap->pmParType); - pNewFS = pUnknownFS; - //pNewImg->AddNote(DiskImg::kNoteInfo, "Partition name='%s' type='%s'.", - // pMap->pmPartName, pMap->pmParType); - } else { - /* open a DiskFS for the sub-image */ - LOGI(" MicroDriveSub (%ld,%ld) analyze succeeded!", startBlock, numBlocks); - pNewFS = pNewImg->OpenAppropriateDiskFS(true); - if (pNewFS == NULL) { - LOGI(" MicroDriveSub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - } - - /* we encapsulate arbitrary stuff, so encourage child to scan */ - pNewFS->SetScanForSubVolumes(kScanSubEnabled); - - /* - * Load the files from the sub-image. When doing our initial tests, - * or when loading data for the volume copier, we don't want to dig - * into our sub-volumes, just figure out what they are and where. - * - * If "initialize" fails, the sub-volume won't get added to the list. - * It's important that a failure at this stage doesn't cause the whole - * thing to fall over. - */ - InitMode initMode; - if (GetScanForSubVolumes() == kScanSubContainerOnly) - initMode = kInitHeaderOnly; - else - initMode = kInitFull; - dierr = pNewFS->Initialize(pNewImg, initMode); - if (dierr != kDIErrNone) { - LOGI(" MicroDriveSub: error %d reading list of files from disk", dierr); - goto bail; - } - - /* add it to the list */ - AddSubVolumeToList(pNewImg, pNewFS); - pNewImg = NULL; - pNewFS = NULL; - -bail: - delete pNewFS; - delete pNewImg; - return dierr; -} - -/* - * Check to see if this is a MicroDrive volume. - */ -/*static*/ DIError DiskFSMicroDrive::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (pImg->GetNumBlocks() < kMinInterestingBlocks) - return kDIErrFilesystemNotFound; - if (pImg->GetIsEmbedded()) // don't look for partitions inside - return kDIErrFilesystemNotFound; - - /* assume ProDOS -- shouldn't matter, since it's embedded */ - if (TestImage(pImg, DiskImg::kSectorOrderProDOS) == kDIErrNone) { - *pFormat = DiskImg::kFormatMicroDrive; - *pOrder = DiskImg::kSectorOrderProDOS; - return kDIErrNone; - } - - LOGI(" FS didn't find valid MicroDrive"); - return kDIErrFilesystemNotFound; -} - - -/* - * Prep the MicroDrive "container" for use. - */ -DIError DiskFSMicroDrive::Initialize(void) -{ - DIError dierr = kDIErrNone; - - LOGI("MicroDrive initializing (scanForSub=%d)", fScanForSubVolumes); - - /* seems pointless *not* to, but we just do what we're told */ - if (fScanForSubVolumes != kScanSubDisabled) { - dierr = FindSubVolumes(); - if (dierr != kDIErrNone) - return dierr; - } - - /* blank out the volume usage map */ - SetVolumeUsageMap(); - - return dierr; -} - - -/* - * Find the various sub-volumes and open them. - */ -DIError DiskFSMicroDrive::FindSubVolumes(void) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kBlkSize]; - PartitionMap map; - int i; - - dierr = fpImg->ReadBlock(kPartMapBlock, buf); - if (dierr != kDIErrNone) - goto bail; - UnpackPartitionMap(buf, &map); - DumpPartitionMap(&map); - - /* first part of the table */ - for (i = 0; i < map.numPart1; i++) { - dierr = OpenVol(i, - map.partitionStart1[i], map.partitionLength1[i]); - if (dierr != kDIErrNone) - goto bail; - } - - /* second part of the table */ - for (i = 0; i < map.numPart2; i++) { - dierr = OpenVol(i + kMaxNumParts, - map.partitionStart2[i], map.partitionLength2[i]); - if (dierr != kDIErrNone) - goto bail; - } - -bail: - return dierr; -} - -/* - * Open the volume. If it fails, open a placeholder instead. (If *that* - * fails, return with an error.) - */ -DIError DiskFSMicroDrive::OpenVol(int idx, long startBlock, long numBlocks) -{ - DIError dierr; - - dierr = OpenSubVolume(startBlock, numBlocks); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - LOGW(" MicroDrive failed opening sub-volume %d", idx); - dierr = CreatePlaceholder(startBlock, numBlocks, NULL, NULL, - &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - AddSubVolumeToList(pNewImg, pNewFS); - } else { - LOGE(" MicroDrive unable to create placeholder (err=%d)", - dierr); - // fall out with error - } - } - -bail: - return dierr; -} diff --git a/ciderpress/diskimg/Nibble.cpp b/ciderpress/diskimg/Nibble.cpp deleted file mode 100644 index 5b53509..0000000 --- a/ciderpress/diskimg/Nibble.cpp +++ /dev/null @@ -1,1021 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * DiskImg nibblized read/write functions. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -/* define this for verbose output */ -//#define NIB_VERBOSE_DEBUG - - -/* - * =========================================================================== - * Nibble encoding and decoding - * =========================================================================== - */ - -/*static*/ uint8_t DiskImg::kDiskBytes53[32] = { - 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, - 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, - 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, - 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff -}; -/*static*/ uint8_t DiskImg::kDiskBytes62[64] = { - 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, - 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, - 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, - 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, - 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, - 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, - 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, - 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff -}; -/*static*/ uint8_t DiskImg::kInvDiskBytes53[256]; // all values are 0-31 -/*static*/ uint8_t DiskImg::kInvDiskBytes62[256]; // all values are 0-63 - -/* - * Compute tables to convert disk bytes back to values. - * - * Should be called once, at DLL initialization time. - */ -/*static*/ void DiskImg::CalcNibbleInvTables(void) -{ - unsigned int i; - - memset(kInvDiskBytes53, kInvInvalidValue, sizeof(kInvDiskBytes53)); - for (i = 0; i < sizeof(kDiskBytes53); i++) { - assert(kDiskBytes53[i] >= 0x96); - kInvDiskBytes53[kDiskBytes53[i]] = i; - } - - memset(kInvDiskBytes62, kInvInvalidValue, sizeof(kInvDiskBytes62)); - for (i = 0; i < sizeof(kDiskBytes62); i++) { - assert(kDiskBytes62[i] >= 0x96); - kInvDiskBytes62[kDiskBytes62[i]] = i; - } -} - -/* - * Find the start of the data field of a sector in nibblized data. - * - * Returns the index start on success or -1 on failure. - */ -int DiskImg::FindNibbleSectorStart(const CircularBufferAccess& buffer, int track, - int sector, const NibbleDescr* pNibbleDescr, int* pVol) -{ - const int kMaxDataReach = 48; // fairly arbitrary - //DIError dierr; - long trackLen = buffer.GetSize(); - - assert(sector >= 0 && sector < 16); - - int i; - - for (i = 0; i < trackLen; i++) { - bool foundAddr = false; - - if (pNibbleDescr->special == kNibbleSpecialSkipFirstAddrByte) { - if (/*buffer[i] == pNibbleDescr->addrProlog[0] &&*/ - buffer[i+1] == pNibbleDescr->addrProlog[1] && - buffer[i+2] == pNibbleDescr->addrProlog[2]) - { - foundAddr = true; - } - } else { - if (buffer[i] == pNibbleDescr->addrProlog[0] && - buffer[i+1] == pNibbleDescr->addrProlog[1] && - buffer[i+2] == pNibbleDescr->addrProlog[2]) - { - foundAddr = true; - } - } - - if (foundAddr) { - //i += 3; - - /* found the address header, decode the address */ - short hdrVol, hdrTrack, hdrSector, hdrChksum; - DecodeAddr(buffer, i+3, &hdrVol, &hdrTrack, &hdrSector, - &hdrChksum); - - if (pNibbleDescr->addrVerifyTrack && track != hdrTrack) { - LOGI(" Track mismatch (T=%d) got T=%d,S=%d", - track, hdrTrack, hdrSector); - continue; - } - - if (pNibbleDescr->addrVerifyChecksum) { - if ((pNibbleDescr->addrChecksumSeed ^ - hdrVol ^ hdrTrack ^ hdrSector ^ hdrChksum) != 0) - { - LOGW(" Addr checksum mismatch (want T=%d,S=%d, got " - "T=%d,S=%d)", - track, sector, hdrTrack, hdrSector); - continue; - } - } - - i += 3; - - int j; - for (j = 0; j < pNibbleDescr->addrEpilogVerifyCount; j++) { - if (buffer[i+8+j] != pNibbleDescr->addrEpilog[j]) { - //LOGI(" Bad epilog byte %d (%02x vs %02x)", - // j, buffer[i+8+j], pNibbleDescr->addrEpilog[j]); - break; - } - } - if (j != pNibbleDescr->addrEpilogVerifyCount) - continue; - -#ifdef NIB_VERBOSE_DEBUG - LOGI(" Good header, T=%d,S=%d (looking for T=%d,S=%d)", - hdrTrack, hdrSector, track, sector); -#endif - - if (pNibbleDescr->special == kNibbleSpecialMuse) { - /* e.g. original Castle Wolfenstein */ - if (track > 2) { - if ((hdrSector & 0x01) != 0) - continue; - hdrSector /= 2; - } - } - - if (sector != hdrSector) - continue; - - /* - * Scan forward and look for data prolog. We want to limit - * the reach of our search so we don't blunder into the data - * field of the next sector. - */ - for (j = 0; j < kMaxDataReach; j++) { - if (buffer[i + j] == pNibbleDescr->dataProlog[0] && - buffer[i + j +1] == pNibbleDescr->dataProlog[1] && - buffer[i + j +2] == pNibbleDescr->dataProlog[2]) - { - *pVol = hdrVol; - return buffer.Normalize(i + j + 3); - } - } - } - } - -#ifdef NIB_VERBOSE_DEBUG - LOGI(" Couldn't find T=%d,S=%d", track, sector); -#endif - return -1; -} - -/* - * Decode the values in the address field. - */ -void DiskImg::DecodeAddr(const CircularBufferAccess& buffer, int offset, - short* pVol, short* pTrack, short* pSector, short* pChksum) -{ - //unsigned int vol, track, sector, chksum; - - *pVol = ConvFrom44(buffer[offset], buffer[offset+1]); - *pTrack = ConvFrom44(buffer[offset+2], buffer[offset+3]); - *pSector = ConvFrom44(buffer[offset+4], buffer[offset+5]); - *pChksum = ConvFrom44(buffer[offset+6], buffer[offset+7]); -} - -/* - * Decode the sector pointed to by "pData" and described by "pNibbleDescr". - * This invokes the appropriate function (e.g. 5&3 or 6&2) to decode the - * data into a 256-byte sector. - */ -DIError DiskImg::DecodeNibbleData(const CircularBufferAccess& buffer, int idx, - uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) -{ - switch (pNibbleDescr->encoding) { - case kNibbleEnc62: - return DecodeNibble62(buffer, idx, sctBuf, pNibbleDescr); - case kNibbleEnc53: - return DecodeNibble53(buffer, idx, sctBuf, pNibbleDescr); - default: - assert(false); - return kDIErrInternal; - } -} - -/* - * Encode the sector pointed to by "pData" and described by "pNibbleDescr". - * This invokes the appropriate function (e.g. 5&3 or 6&2) to encode the - * data from a 256-byte sector. - */ -void DiskImg::EncodeNibbleData(const CircularBufferAccess& buffer, int idx, - const uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) const -{ - switch (pNibbleDescr->encoding) { - case kNibbleEnc62: - EncodeNibble62(buffer, idx, sctBuf, pNibbleDescr); - break; - case kNibbleEnc53: - EncodeNibble53(buffer, idx, sctBuf, pNibbleDescr); - break; - default: - assert(false); - break; - } -} - -/* - * Decode 6&2 encoding. - */ -DIError DiskImg::DecodeNibble62(const CircularBufferAccess& buffer, int idx, - uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) -{ - uint8_t twos[kChunkSize62 * 3]; // 258 - int chksum = pNibbleDescr->dataChecksumSeed; - uint8_t decodedVal; - int i; - - /* - * Pull the 342 bytes out, convert them from disk bytes to 6-bit - * values, and arrange them into a DOS-like pair of buffers. - */ - for (i = 0; i < kChunkSize62; i++) { - decodedVal = kInvDiskBytes62[buffer[idx++]]; - if (decodedVal == kInvInvalidValue) - return kDIErrInvalidDiskByte; - assert(decodedVal < sizeof(kDiskBytes62)); - - chksum ^= decodedVal; - twos[i] = - ((chksum & 0x01) << 1) | ((chksum & 0x02) >> 1); - twos[i + kChunkSize62] = - ((chksum & 0x04) >> 1) | ((chksum & 0x08) >> 3); - twos[i + kChunkSize62*2] = - ((chksum & 0x10) >> 3) | ((chksum & 0x20) >> 5); - } - - for (i = 0; i < 256; i++) { - decodedVal = kInvDiskBytes62[buffer[idx++]]; - if (decodedVal == kInvInvalidValue) - return kDIErrInvalidDiskByte; - assert(decodedVal < sizeof(kDiskBytes62)); - - chksum ^= decodedVal; - sctBuf[i] = (chksum << 2) | twos[i]; - } - - /* - * Grab the 343rd byte (the checksum byte) and see if we did this - * right. - */ - //printf("Dec checksum value is 0x%02x\n", chksum); - decodedVal = kInvDiskBytes62[buffer[idx++]]; - if (decodedVal == kInvInvalidValue) - return kDIErrInvalidDiskByte; - assert(decodedVal < sizeof(kDiskBytes62)); - chksum ^= decodedVal; - - if (pNibbleDescr->dataVerifyChecksum && chksum != 0) { - LOGI(" NIB bad data checksum"); - return kDIErrBadChecksum; - } - return kDIErrNone; -} - -/* - * Encode 6&2 encoding. - */ -void DiskImg::EncodeNibble62(const CircularBufferAccess& buffer, int idx, - const uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) const -{ - uint8_t top[256]; - uint8_t twos[kChunkSize62]; - int twoPosn, twoShift; - int i; - - memset(twos, 0, sizeof(twos)); - - twoShift = 0; - for (i = 0, twoPosn = kChunkSize62-1; i < 256; i++) { - unsigned int val = sctBuf[i]; - top[i] = val >> 2; - twos[twoPosn] |= ((val & 0x01) << 1 | (val & 0x02) >> 1) << twoShift; - - if (twoPosn == 0) { - twoPosn = kChunkSize62; - twoShift += 2; - } - twoPosn--; - } - - int chksum = pNibbleDescr->dataChecksumSeed; - for (i = kChunkSize62-1; i >= 0; i--) { - assert(twos[i] < sizeof(kDiskBytes62)); - buffer[idx++] = kDiskBytes62[twos[i] ^ chksum]; - chksum = twos[i]; - } - - for (i = 0; i < 256; i++) { - assert(top[i] < sizeof(kDiskBytes62)); - buffer[idx++] = kDiskBytes62[top[i] ^ chksum]; - chksum = top[i]; - } - - //printf("Enc checksum value is 0x%02x\n", chksum); - buffer[idx++] = kDiskBytes62[chksum]; -} - -/* - * Decode 5&3 encoding. - */ -DIError DiskImg::DecodeNibble53(const CircularBufferAccess& buffer, int idx, - uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) -{ - uint8_t base[256]; - uint8_t threes[kThreeSize]; - int chksum = pNibbleDescr->dataChecksumSeed; - uint8_t decodedVal; - int i; - - /* - * Pull the 410 bytes out, convert them from disk bytes to 5-bit - * values, and arrange them into a DOS-like pair of buffers. - */ - for (i = kThreeSize-1; i >= 0; i--) { - decodedVal = kInvDiskBytes53[buffer[idx++]]; - if (decodedVal == kInvInvalidValue) - return kDIErrInvalidDiskByte; - assert(decodedVal < sizeof(kDiskBytes53)); - - chksum ^= decodedVal; - threes[i] = chksum; - } - - for (i = 0; i < 256; i++) { - decodedVal = kInvDiskBytes53[buffer[idx++]]; - if (decodedVal == kInvInvalidValue) - return kDIErrInvalidDiskByte; - assert(decodedVal < sizeof(kDiskBytes53)); - - chksum ^= decodedVal; - base[i] = (chksum << 3); - } - - /* - * Grab the 411th byte (the checksum byte) and see if we did this - * right. - */ - //printf("Dec checksum value is 0x%02x\n", chksum); - decodedVal = kInvDiskBytes53[buffer[idx++]]; - if (decodedVal == kInvInvalidValue) - return kDIErrInvalidDiskByte; - assert(decodedVal < sizeof(kDiskBytes53)); - chksum ^= decodedVal; - - if (pNibbleDescr->dataVerifyChecksum && chksum != 0) { - LOGI(" NIB bad data checksum (0x%02x)", chksum); - return kDIErrBadChecksum; - } - - /* - * Convert this pile of stuff into 256 data bytes. - */ - uint8_t* bufPtr; - - bufPtr = sctBuf; - for (i = kChunkSize53-1; i >= 0; i--) { - int three1, three2, three3, three4, three5; - - three1 = threes[i]; - three2 = threes[kChunkSize53 + i]; - three3 = threes[kChunkSize53*2 + i]; - three4 = (three1 & 0x02) << 1 | (three2 & 0x02) | (three3 & 0x02) >> 1; - three5 = (three1 & 0x01) << 2 | (three2 & 0x01) << 1 | (three3 & 0x01); - - *bufPtr++ = base[i] | ((three1 >> 2) & 0x07); - *bufPtr++ = base[kChunkSize53 + i] | ((three2 >> 2) & 0x07); - *bufPtr++ = base[kChunkSize53*2 + i] | ((three3 >> 2) & 0x07); - *bufPtr++ = base[kChunkSize53*3 + i] | (three4 & 0x07); - *bufPtr++ = base[kChunkSize53*4 + i] | (three5 & 0x07); - } - assert(bufPtr == sctBuf + 255); - - /* - * Convert the very last byte, which is handled specially. - */ - *bufPtr = base[255] | (threes[kThreeSize-1] & 0x07); - - return kDIErrNone; -} - -/* - * Encode 5&3 encoding. - */ -void DiskImg::EncodeNibble53(const CircularBufferAccess& buffer, int idx, - const uint8_t* sctBuf, const NibbleDescr* pNibbleDescr) const -{ - uint8_t top[kChunkSize53 * 5 +1]; // (255 / 0xff) +1 - uint8_t threes[kChunkSize53 * 3 +1]; // (153 / 0x99) +1 - int i, chunk; - - /* - * Split the bytes into sections. - */ - chunk = kChunkSize53-1; - for (i = 0; i < (int) sizeof(top)-1; i += 5) { - int three1, three2, three3, three4, three5; - - three1 = *sctBuf++; - three2 = *sctBuf++; - three3 = *sctBuf++; - three4 = *sctBuf++; - three5 = *sctBuf++; - - top[chunk] = three1 >> 3; - top[chunk + kChunkSize53*1] = three2 >> 3; - top[chunk + kChunkSize53*2] = three3 >> 3; - top[chunk + kChunkSize53*3] = three4 >> 3; - top[chunk + kChunkSize53*4] = three5 >> 3; - - threes[chunk] = - (three1 & 0x07) << 2 | (three4 & 0x04) >> 1 | (three5 & 0x04) >> 2; - threes[chunk + kChunkSize53*1] = - (three2 & 0x07) << 2 | (three4 & 0x02) | (three5 & 0x02) >> 1; - threes[chunk + kChunkSize53*2] = - (three3 & 0x07) << 2 | (three4 & 0x01) << 1 | (three5 & 0x01); - - chunk--; - } - assert(chunk == -1); - - /* - * Handle the last byte. - */ - int val; - val = *sctBuf++; - top[255] = val >> 3; - threes[kThreeSize-1] = val & 0x07; - - /* - * Write the bytes. - */ - int chksum = pNibbleDescr->dataChecksumSeed; - for (i = sizeof(threes)-1; i >= 0; i--) { - assert(threes[i] < sizeof(kDiskBytes53)); - buffer[idx++] = kDiskBytes53[threes[i] ^ chksum]; - chksum = threes[i]; - } - - for (i = 0; i < 256; i++) { - assert(top[i] < sizeof(kDiskBytes53)); - buffer[idx++] = kDiskBytes53[top[i] ^ chksum]; - chksum = top[i]; - } - - //printf("Enc checksum value is 0x%02x\n", chksum); - buffer[idx++] = kDiskBytes53[chksum]; -} - - -/* - * =========================================================================== - * Higher-level functions - * =========================================================================== - */ - -/* - * Dump some bytes as hex values into a string. - * - * "buf" must be able to hold (num * 3) characters. - */ -static void DumpBytes(const uint8_t* bytes, unsigned int num, char* buf) -{ - sprintf(buf, "%02x", bytes[0]); - buf += 2; - - for (int i = 1; i < (int) num; i++) { - sprintf(buf, " %02x", bytes[i]); - buf += 3; - } - - *buf = '\0'; -} - -static inline const char* VerifyStr(bool val) -{ - return val ? "verify" : "ignore"; -} - -/* - * Dump the contents of a NibbleDescr struct. - */ -void DiskImg::DumpNibbleDescr(const NibbleDescr* pNibDescr) const -{ - char outBuf1[48]; - char outBuf2[48]; - const char* encodingStr; - - switch (pNibDescr->encoding) { - case kNibbleEnc62: encodingStr = "6&2"; break; - case kNibbleEnc53: encodingStr = "5&3"; break; - case kNibbleEnc44: encodingStr = "4&4"; break; - default: encodingStr = "???"; break; - } - - LOGI("NibbleDescr '%s':", pNibDescr->description); - LOGI(" Nibble encoding is %s", encodingStr); - DumpBytes(pNibDescr->addrProlog, sizeof(pNibDescr->addrProlog), outBuf1); - DumpBytes(pNibDescr->dataProlog, sizeof(pNibDescr->dataProlog), outBuf2); - LOGI(" Addr prolog: %s Data prolog: %s", outBuf1, outBuf2); - DumpBytes(pNibDescr->addrEpilog, sizeof(pNibDescr->addrEpilog), outBuf1); - DumpBytes(pNibDescr->dataEpilog, sizeof(pNibDescr->dataEpilog), outBuf2); - LOGI(" Addr epilog: %s (%d) Data epilog: %s (%d)", - outBuf1, pNibDescr->addrEpilogVerifyCount, - outBuf2, pNibDescr->dataEpilogVerifyCount); - LOGI(" Addr checksum: %s Data checksum: %s", - VerifyStr(pNibDescr->addrVerifyChecksum), - VerifyStr(pNibDescr->dataVerifyChecksum)); - LOGI(" Addr checksum seed: 0x%02x Data checksum seed: 0x%02x", - pNibDescr->addrChecksumSeed, pNibDescr->dataChecksumSeed); - LOGI(" Addr check track: %s", - VerifyStr(pNibDescr->addrVerifyTrack)); -} - - -/* - * Load a nibble track into our track buffer. - */ -DIError DiskImg::LoadNibbleTrack(long track, long* pTrackLen) -{ - DIError dierr = kDIErrNone; - long offset; - assert(track >= 0 && track < kMaxNibbleTracks525); - - *pTrackLen = GetNibbleTrackLength(track); - offset = GetNibbleTrackOffset(track); - assert(*pTrackLen > 0); - assert(offset >= 0); - - if (track == fNibbleTrackLoaded) { -#ifdef NIB_VERBOSE_DEBUG - LOGI(" DI track %d already loaded", track); -#endif - return kDIErrNone; - } else { - LOGI(" DI loading track %ld", track); - } - - /* invalidate in case we fail with partial read */ - fNibbleTrackLoaded = -1; - - /* alloc track buffer if needed */ - if (fNibbleTrackBuf == NULL) { - fNibbleTrackBuf = new uint8_t[kTrackAllocSize]; - if (fNibbleTrackBuf == NULL) - return kDIErrMalloc; - } - - /* - * Read the entire track into memory. - */ - dierr = CopyBytesOut(fNibbleTrackBuf, offset, *pTrackLen); - if (dierr != kDIErrNone) - return dierr; - - fNibbleTrackLoaded = track; - - return dierr; -} - -/* - * Save the track buffer back to disk. - */ -DIError DiskImg::SaveNibbleTrack(void) -{ - if (fNibbleTrackLoaded < 0) { - LOGI("ERROR: tried to save track without loading it first"); - return kDIErrInternal; - } - assert(fNibbleTrackBuf != NULL); - - DIError dierr = kDIErrNone; - long trackLen = GetNibbleTrackLength(fNibbleTrackLoaded); - long offset = GetNibbleTrackOffset(fNibbleTrackLoaded); - - /* write the track to fpDataGFD */ - dierr = CopyBytesIn(fNibbleTrackBuf, offset, trackLen); - return dierr; -} - - -/* - * Count up the number of readable sectors found on this track, and - * return it. If "pVol" is non-NULL, return the volume number from - * one of the readable sectors. - */ -int DiskImg::TestNibbleTrack(int track, const NibbleDescr* pNibbleDescr, - int* pVol) -{ - long trackLen; - int count = 0; - - assert(track >= 0 && track < kTrackCount525); - assert(pNibbleDescr != NULL); - - if (LoadNibbleTrack(track, &trackLen) != kDIErrNone) { - LOGI(" DI FindNibbleSectorStart: LoadNibbleTrack failed"); - return 0; - } - - CircularBufferAccess buffer(fNibbleTrackBuf, trackLen); - - int i, sectorIdx; - for (i = 0; i < pNibbleDescr->numSectors; i++) { - int vol; - sectorIdx = FindNibbleSectorStart(buffer, track, i, pNibbleDescr, &vol); - if (sectorIdx >= 0) { - if (pVol != NULL) - *pVol = vol; - - uint8_t sctBuf[256]; - if (DecodeNibbleData(buffer, sectorIdx, sctBuf, pNibbleDescr) == kDIErrNone) - count++; - } - } - - LOGI(" Tests on track=%d with '%s' returning count=%d", - track, pNibbleDescr->description, count); - - return count; -} - -/* - * Analyze the nibblized track data. - * - * On entry: - * fPhysical indicates the appropriate nibble format - * - * On exit: - * fpNibbleDescr points to the most-likely-to-succeed NibbleDescr - * fDOSVolumeNum holds a volume number from one of the tracks - * fNumTracks holds the number of tracks on the disk - */ -DIError DiskImg::AnalyzeNibbleData(void) -{ - assert(IsNibbleFormat(fPhysical)); - - if (fPhysical == kPhysicalFormatNib525_Var) { - /* TrackStar can have up to 40 */ - fNumTracks = fpImageWrapper->GetNibbleNumTracks(); - assert(fNumTracks > 0); - } else { - /* fixed-length formats (.nib, .nb2) are always 35 tracks */ - fNumTracks = kTrackCount525; - } - - /* - * Try to read sectors from tracks 1, 16, 17, and 26. If we can get - * at least 13 out of 16 (or 10 out of 13) on three out of four tracks, - * we have a winner. - */ - int i, good, goodTracks; - int protoVol = kVolumeNumNotSet; - - for (i = 0; i < fNumNibbleDescrEntries; i++) { - if (fpNibbleDescrTable[i].numSectors == 0) { - /* uninitialized "custom" entry */ - LOGI(" Skipping '%s'", fpNibbleDescrTable[i].description); - continue; - } - LOGI(" Trying '%s'", fpNibbleDescrTable[i].description); - goodTracks = 0; - - good = TestNibbleTrack(1, &fpNibbleDescrTable[i], NULL); - if (good > fpNibbleDescrTable[i].numSectors - 4) - goodTracks++; - good = TestNibbleTrack(16, &fpNibbleDescrTable[i], NULL); - if (good > fpNibbleDescrTable[i].numSectors - 4) - goodTracks++; - good = TestNibbleTrack(17, &fpNibbleDescrTable[i], &protoVol); - if (good > fpNibbleDescrTable[i].numSectors - 4) - goodTracks++; - good = TestNibbleTrack(26, &fpNibbleDescrTable[i], NULL); - if (good > fpNibbleDescrTable[i].numSectors - 4) - goodTracks++; - - if (goodTracks >= 3) { - LOGI(" Looks like '%s' (%d-sector), vol=%d", - fpNibbleDescrTable[i].description, - fpNibbleDescrTable[i].numSectors, protoVol); - fpNibbleDescr = &fpNibbleDescrTable[i]; - fDOSVolumeNum = protoVol; - break; - } - } - if (i == fNumNibbleDescrEntries) { - LOGI("AnalyzeNibbleData did not find matching NibbleDescr"); - return kDIErrBadNibbleSectors; - } - - return kDIErrNone; -} - -/* - * Read a sector from a nibble image. - * - * While fNumTracks is valid, fNumSectPerTrack is a little flaky, because - * in theory each track could be formatted differently. - */ -DIError DiskImg::ReadNibbleSector(long track, int sector, void* buf, - const NibbleDescr* pNibbleDescr) -{ - if (pNibbleDescr == NULL) { - /* disk has no recognizable sectors */ - LOGI(" DI ReadNibbleSector: pNibbleDescr is NULL, returning failure"); - return kDIErrBadNibbleSectors; - } - if (sector >= pNibbleDescr->numSectors) { - /* e.g. trying to read sector 14 on a 13-sector disk */ - LOGI(" DI ReadNibbleSector: bad sector number request"); - return kDIErrInvalidSector; - } - - assert(pNibbleDescr != NULL); - assert(IsNibbleFormat(fPhysical)); - assert(track >= 0 && track < GetNumTracks()); - assert(sector >= 0 && sector < pNibbleDescr->numSectors); - - DIError dierr = kDIErrNone; - long trackLen; - int sectorIdx, vol; - - dierr = LoadNibbleTrack(track, &trackLen); - if (dierr != kDIErrNone) { - LOGI(" DI ReadNibbleSector: LoadNibbleTrack %ld failed", track); - return dierr; - } - - CircularBufferAccess buffer(fNibbleTrackBuf, trackLen); - sectorIdx = FindNibbleSectorStart(buffer, track, sector, pNibbleDescr, - &vol); - if (sectorIdx < 0) - return kDIErrSectorUnreadable; - - dierr = DecodeNibbleData(buffer, sectorIdx, (uint8_t*) buf, - pNibbleDescr); - - return dierr; -} - -/* - * Write a sector to a nibble image. - */ -DIError DiskImg::WriteNibbleSector(long track, int sector, const void* buf, - const NibbleDescr* pNibbleDescr) -{ - assert(pNibbleDescr != NULL); - assert(IsNibbleFormat(fPhysical)); - assert(track >= 0 && track < GetNumTracks()); - assert(sector >= 0 && sector < pNibbleDescr->numSectors); - assert(!fReadOnly); - - DIError dierr = kDIErrNone; - long trackLen; - int sectorIdx, vol; - - dierr = LoadNibbleTrack(track, &trackLen); - if (dierr != kDIErrNone) { - LOGI(" DI ReadNibbleSector: LoadNibbleTrack %ld failed", track); - return dierr; - } - - CircularBufferAccess buffer(fNibbleTrackBuf, trackLen); - sectorIdx = FindNibbleSectorStart(buffer, track, sector, pNibbleDescr, - &vol); - if (sectorIdx < 0) - return kDIErrSectorUnreadable; - - EncodeNibbleData(buffer, sectorIdx, (uint8_t*) buf, pNibbleDescr); - - dierr = SaveNibbleTrack(); - if (dierr != kDIErrNone) { - LOGI(" DI ReadNibbleSector: SaveNibbleTrack %ld failed", track); - return dierr; - } - - return dierr; -} - -/* - * Get the contents of the nibble track. - * - * "buf" must be able to hold kTrackAllocSize bytes. - */ -DIError DiskImg::ReadNibbleTrack(long track, uint8_t* buf, long* pTrackLen) -{ - DIError dierr; - - dierr = LoadNibbleTrack(track, pTrackLen); - if (dierr != kDIErrNone) { - LOGI(" DI ReadNibbleTrack: LoadNibbleTrack %ld failed", track); - return dierr; - } - - memcpy(buf, fNibbleTrackBuf, *pTrackLen); - return kDIErrNone; -} - -/* - * Set the contents of a nibble track. - * - * NOTE: This currently does the wrong thing when converting from .nb2 to - * .nib. Fixed-length formats shouldn't be allowed to interact. Figure - * this out someday. For now, the higher-level code prevents it. - */ -DIError DiskImg::WriteNibbleTrack(long track, const uint8_t* buf, long trackLen) -{ - DIError dierr; - long oldTrackLen; - - /* load the track to set the "current track" stuff */ - dierr = LoadNibbleTrack(track, &oldTrackLen); - if (dierr != kDIErrNone) { - LOGI(" DI WriteNibbleTrack: LoadNibbleTrack %ld failed", track); - return dierr; - } - - if (trackLen > GetNibbleTrackAllocLength()) { - LOGI("ERROR: tried to write too-long track len (%ld vs %d)", - trackLen, GetNibbleTrackAllocLength()); - return kDIErrInvalidArg; - } - - if (trackLen < oldTrackLen) // pad out any extra space - memset(fNibbleTrackBuf, 0xff, oldTrackLen); - memcpy(fNibbleTrackBuf, buf, trackLen); - fpImageWrapper->SetNibbleTrackLength(track, trackLen); - - dierr = SaveNibbleTrack(); - if (dierr != kDIErrNone) { - LOGI(" DI ReadNibbleSector: SaveNibbleTrack %ld failed", track); - return dierr; - } - - return kDIErrNone; -} - -/* - * Create a blank nibble image, using fpNibbleDescr as the template. - * Sets "fLength". - * - * Tracks are written the same way regardless of actual track length (be - * it 6656, 6384, or variable-length). Anything longer than 6384 just has - * more padding at the end of the track. - * - * The format looks like this: - * Gap one (48 self-sync bytes) - * For each sector: - * Address field (14 bytes, e.g. d5aa96 vol track sect chksum deaaeb) - * Gap two (six self-sync bytes) - * Data field (6 header bytes, 1 checksum byte, and 342 or 410 data bytes) - * Gap three (27 self-sync bytes) - * - * 48 + (14 + 6 + (6 + 1 + 342) + 27) * 16 = 6384 - * 48 + (14 + 6 + (6 + 1 + 410) + 27) * 13 = 6080 - */ -DIError DiskImg::FormatNibbles(GenericFD* pGFD) const -{ - assert(fHasNibbles); - assert(GetNumTracks() > 0); - - DIError dierr = kDIErrNone; - uint8_t trackBuf[kTrackAllocSize]; - /* these should be the same except for var-len images */ - long trackAllocLen = GetNibbleTrackAllocLength(); - long trackLen = GetNibbleTrackFormatLength(); - int track; - - assert(trackLen > 0); - pGFD->Rewind(); - - /* - * If we don't have sector access, take a shortcut and just fill the - * entire image with 0xff. - */ - if (!fHasSectors) { - memset(trackBuf, 0xff, trackLen); - for (track = 0; track < GetNumTracks(); track++) { - /* write the track to the GFD */ - dierr = pGFD->Write(trackBuf, trackAllocLen); - if (dierr != kDIErrNone) - return dierr; - fpImageWrapper->SetNibbleTrackLength(track, trackAllocLen); - } - - return kDIErrNone; - } - - - assert(fHasSectors); - assert(fpNibbleDescr != NULL); - assert(fpNibbleDescr->numSectors == GetNumSectPerTrack()); - assert(fpNibbleDescr->encoding == kNibbleEnc53 || - fpNibbleDescr->encoding == kNibbleEnc62); - assert(fDOSVolumeNum != kVolumeNumNotSet); - - /* - * Create a prototype sector. The data for a sector full of zeroes - * is exactly the same; only the address header changes. - */ - uint8_t sampleSource[256]; - uint8_t sampleBuf[512]; // must hold 5&3 and 6&2 - CircularBufferAccess sample(sampleBuf, 512); - long dataLen; - - if (fpNibbleDescr->encoding == kNibbleEnc53) - dataLen = 410 +1; - else - dataLen = 342 +1; - - memset(sampleSource, 0, sizeof(sampleSource)); - EncodeNibbleData(sample, 0, sampleSource, fpNibbleDescr); - - /* - * For each track in the image, "format" the expected number of - * sectors, then write the data to the GFD. - */ - for (track = 0; track < GetNumTracks(); track++) { - //LOGI("Formatting track %d", track); - uint8_t* trackPtr = trackBuf; - - /* - * Fill with "self-sync" bytes. - */ - memset(trackBuf, 0xff, trackAllocLen); - - /* gap one */ - trackPtr += 48; - - for (int sector = 0; sector < fpNibbleDescr->numSectors; sector++) { - /* - * Write address field. - */ - uint16_t hdrTrack, hdrSector, hdrVol, hdrChksum; - hdrTrack = track; - hdrSector = sector; - hdrVol = fDOSVolumeNum; - *trackPtr++ = fpNibbleDescr->addrProlog[0]; - *trackPtr++ = fpNibbleDescr->addrProlog[1]; - *trackPtr++ = fpNibbleDescr->addrProlog[2]; - *trackPtr++ = Conv44(hdrVol, true); - *trackPtr++ = Conv44(hdrVol, false); - *trackPtr++ = Conv44(hdrTrack, true); - *trackPtr++ = Conv44(hdrTrack, false); - *trackPtr++ = Conv44(hdrSector, true); - *trackPtr++ = Conv44(hdrSector, false); - hdrChksum = fpNibbleDescr->addrChecksumSeed ^ - hdrVol ^ hdrTrack ^ hdrSector; - *trackPtr++ = Conv44(hdrChksum, true); - *trackPtr++ = Conv44(hdrChksum, false); - *trackPtr++ = fpNibbleDescr->addrEpilog[0]; - *trackPtr++ = fpNibbleDescr->addrEpilog[1]; - *trackPtr++ = fpNibbleDescr->addrEpilog[2]; - - /* gap two */ - trackPtr += 6; - - /* - * Write data field. - */ - *trackPtr++ = fpNibbleDescr->dataProlog[0]; - *trackPtr++ = fpNibbleDescr->dataProlog[1]; - *trackPtr++ = fpNibbleDescr->dataProlog[2]; - memcpy(trackPtr, sampleBuf, dataLen); - trackPtr += dataLen; - *trackPtr++ = fpNibbleDescr->dataEpilog[0]; - *trackPtr++ = fpNibbleDescr->dataEpilog[1]; - *trackPtr++ = fpNibbleDescr->dataEpilog[2]; - - /* gap three */ - trackPtr += 27; - } - - assert(trackPtr - trackBuf == 6384 || - trackPtr - trackBuf == 6080); - - /* - * Write the track to the GFD. - */ - dierr = pGFD->Write(trackBuf, trackAllocLen); - if (dierr != kDIErrNone) - break; - - /* on a variable-length image, reduce track len to match */ - fpImageWrapper->SetNibbleTrackLength(track, trackLen); - } - - return dierr; -} diff --git a/ciderpress/diskimg/Nibble35.cpp b/ciderpress/diskimg/Nibble35.cpp deleted file mode 100644 index 67e213d..0000000 --- a/ciderpress/diskimg/Nibble35.cpp +++ /dev/null @@ -1,550 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * GCR nibble image support for 3.5" disks. - * - * Each track has between 8 and 12 512-byte sectors. The encoding is similar - * to but different from that used on 5.25" disks. - * - * THOUGHT: this is currently designed for unpacking all blocks from a track. - * We really ought to allow the user to view the track in nibble form, which - * means reworking the interface to be more like the 5.25" nibble stuff. We - * should present it as a block interface rather than track/sector; the code - * here can convert the block # to track/sector, and just provide a raw - * interface for the nibble track viewer. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -/* -Physical sector layout: - -+00 self-sync 0xff pattern (36 10-bit bytes, or 45 8-bit bytes) -+36 addr prolog (0xd5 0xaa 0x96) -+39 6&2enc track number (0-79 mod 63) -+40 6&2enc sector number (0-N) -+41 6&2enc side (0x00 or 0x20, ORed with 0x01 for tracks >= 64) -+42 6&2enc format (0x22, 0x24, others?) -+43 6&2enc checksum (track ^ sector ^ side ^ format) -+44 addr epilog (0xde 0xaa) -+46 self-sync 0xff (6 10-bit bytes) -+52 data prolog (0xd5 0xaa 0xad) -+55 6&2enc sector number (another copy) -+56 6&2enc nibblized data (699 bytes) -+755 checksum, 3 bytes 6&2 encoded as 4 bytes -+759 data epilog (0xde 0xaa) -+761 0xff (pad byte) - -Some sources say it starts with 42 10-bit self-sync bytes instead of 36. -*/ - -/* - * Basic disk geometry. - */ -const int kCylindersPerDisk = 80; -const int kHeadsPerCylinder = 2; -const int kMaxSectorsPerTrack = 12; -const int kSectorSize35 = 524; // 512 data bytes + 12 tag bytes -const int kTagBytesLen = 12; -const int kDataChecksumLen = 3; -const int kChunkSize35 = 175; // ceil(524 / 3) -const int kOffsetToChecksum = 699; -const int kNibblizedOutputLen = (kOffsetToChecksum + 4); -//const int kMaxDataReach = 48; // should only be 6 bytes */ - -enum { - kAddrProlog0 = 0xd5, - kAddrProlog1 = 0xaa, - kAddrProlog2 = 0x96, - kAddrEpilog0 = 0xde, - kAddrEpilog1 = 0xaa, - - kDataProlog0 = 0xd5, - kDataProlog1 = 0xaa, - kDataProlog2 = 0xad, - kDataEpilog0 = 0xde, - kDataEpilog1 = 0xaa, -}; - -/* - * There are 12 sectors per track for the first 16 cylinders, 11 sectors - * per track for the next 16, and so on until we're down to 8 per track. - */ -/*static*/ int DiskImg::SectorsPerTrack35(int cylinder) -{ - return kMaxSectorsPerTrack - (cylinder / 16); -} - -/* - * Convert cylinder/head/sector to a block number on a 3.5" disk. - */ -/*static*/ int DiskImg::CylHeadSect35ToBlock(int cyl, int head, int sect) -{ - int i, block; - - assert(cyl >= 0 && cyl < kCylindersPerDisk); - assert(head >= 0 && head < kHeadsPerCylinder); - assert(sect >= 0 && sect < SectorsPerTrack35(cyl)); - - block = 0; - for (i = 0; i < cyl; i++) - block += SectorsPerTrack35(i) * kHeadsPerCylinder; - if (head) - block += SectorsPerTrack35(i); - block += sect; - - //LOGI("Nib35: c/h/s %d/%d/%d --> block %d", cyl, head, sect, block); - assert(block >= 0 && block < 1600); - return block; -} - -/* - * Unpack a nibble track. - * - * "outputBuf" must be able to hold 512 * 12 sectors of decoded sector data. - */ -/*static*/ DIError DiskImg::UnpackNibbleTrack35(const uint8_t* nibbleBuf, - long nibbleLen, uint8_t* outputBuf, int cyl, int head, - LinearBitmap* pBadBlockMap) -{ - CircularBufferAccess buffer(nibbleBuf, nibbleLen); - bool foundSector[kMaxSectorsPerTrack]; - uint8_t sectorBuf[kSectorSize35]; - uint8_t readSum[kDataChecksumLen]; - uint8_t calcSum[kDataChecksumLen]; - int i; - - memset(&foundSector, 0, sizeof(foundSector)); - - i = 0; - while (i < nibbleLen) { - int sector; - - i = FindNextSector35(buffer, i, cyl, head, §or); - if (i < 0) - break; - - assert(sector >= 0 && sector < SectorsPerTrack35(cyl)); - if (foundSector[sector]) { - LOGI("Nib35: WARNING: found two copies of sect %d on cyl=%d head=%d", - sector, cyl, head); - } else { - memset(sectorBuf, 0xa9, sizeof(sectorBuf)); - if (DecodeNibbleSector35(buffer, i, sectorBuf, readSum, calcSum)) - { - /* successfully decoded sector, copy data & verify checksum */ - foundSector[sector] = true; - memcpy(outputBuf + kBlockSize * sector, - sectorBuf + kTagBytesLen, kBlockSize); - - if (calcSum[0] != readSum[0] || - calcSum[1] != readSum[1] || - calcSum[2] != readSum[2]) - { - LOGI("Nib35: checksum mismatch: 0x%06x vs. 0x%06x", - calcSum[0] << 16 | calcSum[1] << 8 | calcSum[2], - readSum[0] << 16 | readSum[1] << 8 | readSum[2]); - LOGI("Nib35: marking cyl=%d head=%d sect=%d (block=%d)", - cyl, head, sector, - CylHeadSect35ToBlock(cyl, head, sector)); - pBadBlockMap->Set(CylHeadSect35ToBlock(cyl, head, sector)); - } - } - } - } - - /* - * Check to see if we have all our parts. Anything missing sets - * a flag in the "bad block" map. - */ - for (i = SectorsPerTrack35(cyl)-1; i >= 0; i--) { - if (!foundSector[i]) { - LOGI("Nib35: didn't find cyl=%d head=%d sect=%d (block=%d)", - cyl, head, i, CylHeadSect35ToBlock(cyl, head, i)); - pBadBlockMap->Set(CylHeadSect35ToBlock(cyl, head, i)); - } - - /* - // DEBUG test - if ((cyl == 0 || cyl == 12 || cyl == 79) && - (head == (cyl & 0x01)) && - (i == 1 || i == 7)) - { - LOGI("DEBUG: setting bad %d/%d/%d (%d)", - cyl, head, i, CylHeadSect35ToBlock(cyl, head, i)); - pBadBlockMap->Set(CylHeadSect35ToBlock(cyl, head, i)); - } - */ - } - - return kDIErrNone; // maybe return an error if nothing found? -} - -/* - * Returns the offset of the next sector, or -1 if we went off the end. - */ -/*static*/ int DiskImg::FindNextSector35(const CircularBufferAccess& buffer, - int start, int cyl, int head, int* pSector) -{ - int end = buffer.GetSize(); - int i; - - for (i = start; i < end; i++) { - bool foundAddr = false; - - if (buffer[i] == kAddrProlog0 && - buffer[i+1] == kAddrProlog1 && - buffer[i+2] == kAddrProlog2) - { - foundAddr = true; - } - - if (foundAddr) { - /* decode the address field */ - int trackNum, sectNum, side, format, checksum; - - trackNum = kInvDiskBytes62[buffer[i+3]]; - sectNum = kInvDiskBytes62[buffer[i+4]]; - side = kInvDiskBytes62[buffer[i+5]]; - format = kInvDiskBytes62[buffer[i+6]]; - checksum = kInvDiskBytes62[buffer[i+7]]; - if (trackNum == kInvInvalidValue || - sectNum == kInvInvalidValue || - side == kInvInvalidValue || - format == kInvInvalidValue || - checksum == kInvInvalidValue) - { - LOGI("Nib35: garbled address header found"); - continue; - } - //LOGI(" Nib35: got addr: track=%2d sect=%2d side=%d format=%d sum=0x%02x", - // trackNum, sectNum, side, format, checksum); - if (side != ((head * 0x20) | (cyl >> 6))) { - LOGI("Nib35: unexpected value for side: %d on cyl=%d head=%d", - side, cyl, head); - } - if (sectNum >= SectorsPerTrack35(cyl)) { - LOGI("Nib35: invalid value for sector: %d (cyl=%d)", - sectNum, cyl); - continue; - } - /* format seems to be 0x22 or 0x24 */ - if (checksum != (trackNum ^ sectNum ^ side ^ format)) { - LOGI("Nib35: unexpected checksum: 0x%02x vs. 0x%02x", - checksum, trackNum ^ sectNum ^ side ^ format); - continue; - } - - /* check the epilog bytes */ - if (buffer[i+8] != kAddrEpilog0 || - buffer[i+9] != kAddrEpilog1) - { - LOGI("Nib35: invalid address epilog"); - /* maybe we allow this anyway? */ - } - - *pSector = sectNum; - return i+10; // move past address field - } - } - - return -1; -} - -/* - * Unpack a 524-byte sector from a 3.5" disk. Start with "start" pointed - * in the general vicinity of the data prolog bytes. - * - * "sectorBuf" must hold at least kSectorSize35 bytes. It will be filled - * with the decoded data. - * "readChecksum" and "calcChecksum" must each hold at least kDataChecksumLen - * bytes. The former holds the checksum read from the sector, the latter - * holds the checksum computed from the data. - * - * The 4 to 3 conversion is pretty straightforward. The checksum is - * a little crazy. - * - * Returns "true" if all goes well, "false" if there is a problem. Does - * not return false on a checksum mismatch -- it's up to the caller to - * verify the checksum if desired. - */ -/*static*/ bool DiskImg::DecodeNibbleSector35(const CircularBufferAccess& buffer, - int start, uint8_t* sectorBuf, uint8_t* readChecksum, - uint8_t* calcChecksum) -{ - const int kMaxDataReach35 = 48; // fairly arbitrary - uint8_t* sectorBufStart = sectorBuf; - uint8_t part0[kChunkSize35], part1[kChunkSize35], part2[kChunkSize35]; - unsigned int chk0, chk1, chk2; - uint8_t val, nib0, nib1, nib2, twos; - int i, off; - - /* - * Find the start of the actual data. Adjust "start" to point at it. - */ - for (off = start; off < start + kMaxDataReach35; off++) { - if (buffer[off] == kDataProlog0 && - buffer[off+1] == kDataProlog1 && - buffer[off+2] == kDataProlog2) - { - start = off + 4; // 3 prolog bytes + sector number - break; - } - } - if (off == start + kMaxDataReach35) { - LOGI("nib25: could not find start of data field"); - return false; - } - - /* - * Assemble 8-bit bytes from 6&2 encoded values. - */ - off = start; - for (i = 0; i < kChunkSize35; i++) { - twos = kInvDiskBytes62[buffer[off++]]; - nib0 = kInvDiskBytes62[buffer[off++]]; - nib1 = kInvDiskBytes62[buffer[off++]]; - if (i != kChunkSize35-1) - nib2 = kInvDiskBytes62[buffer[off++]]; - else - nib2 = 0; - - if (twos == kInvInvalidValue || - nib0 == kInvInvalidValue || - nib1 == kInvInvalidValue || - nib2 == kInvInvalidValue) - { - // junk found - LOGI("Nib25: found invalid disk byte in sector data at %d", - off - start); - LOGI(" (one of 0x%02x 0x%02x 0x%02x 0x%02x)", - buffer[off-4], buffer[off-3], buffer[off-2], buffer[off-1]); - return false; - //if (twos == kInvInvalidValue) - // twos = 0; - //if (nib0 == kInvInvalidValue) - // nib0 = 0; - //if (nib1 == kInvInvalidValue) - // nib1 = 0; - //if (nib2 == kInvInvalidValue) - // nib2 = 0; - } - - part0[i] = nib0 | ((twos << 2) & 0xc0); - part1[i] = nib1 | ((twos << 4) & 0xc0); - part2[i] = nib2 | ((twos << 6) & 0xc0); - } - assert(off == start + kOffsetToChecksum); - - chk0 = chk1 = chk2 = 0; - i = 0; - while (true) { - chk0 = (chk0 & 0xff) << 1; - if (chk0 & 0x0100) - chk0++; - - val = part0[i] ^ chk0; - chk2 += val; - if (chk0 & 0x0100) { - chk2++; - chk0 &= 0xff; - } - *sectorBuf++ = val; - - val = part1[i] ^ chk2; - chk1 += val; - if (chk2 > 0xff) { - chk1++; - chk2 &= 0xff; - } - *sectorBuf++ = val; - - if (sectorBuf - sectorBufStart == 524) - break; - - val = part2[i] ^ chk1; - chk0 += val; - if (chk1 > 0xff) { - chk0++; - chk1 &= 0xff; - } - *sectorBuf++ = val; - - i++; - assert(i < kChunkSize35); - //LOGI("i = %d, diff=%d", i, sectorBuf - sectorBufStart); - } - - calcChecksum[0] = chk0; - calcChecksum[1] = chk1; - calcChecksum[2] = chk2; - - if (!UnpackChecksum35(buffer, off, readChecksum)) { - LOGI("Nib35: failure reading checksum"); - readChecksum[0] = calcChecksum[0] ^ 0xff; // force a failure - return false; - } - off += 4; // skip past checksum bytes - - if (buffer[off] != kDataEpilog0 || buffer[off+1] != kDataEpilog1) { - LOGI("nib25: WARNING: data epilog not found"); - // allow it, if the checksum matches - } - -//#define TEST_ENC_35 -#ifdef TEST_ENC_35 - { - uint8_t nibBuf[kNibblizedOutputLen]; - memset(nibBuf, 0xcc, sizeof(nibBuf)); - - /* encode what we just decoded */ - EncodeNibbleSector35(sectorBufStart, nibBuf); - /* compare it to the original */ - for (i = 0; i < kNibblizedOutputLen; i++) { - if (buffer[start + i] != nibBuf[i]) { - /* - * The very last "twos" entry may have undefined bits when - * written by a real drive. Peel it apart and ignore the - * two flaky bits. - */ - if (i == 696) { - uint8_t val1, val2; - val1 = kInvDiskBytes62[buffer[start + i]]; - val2 = kInvDiskBytes62[nibBuf[i]]; - if ((val1 & 0xfc) != (val2 & 0xfc)) { - LOGI("Nib35 DEBUG: output differs at byte %d" - " (0x%02x vs 0x%02x / 0x%02x vs 0x%02x)", - i, buffer[start+i], nibBuf[i], val1, val2); - } - } else { - // note: checksum is 699-702 - LOGI("Nib35 DEBUG: output differs at byte %d (0x%02x vs 0x%02x)", - i, buffer[start+i], nibBuf[i]); - } - } - } - } -#endif /*TEST_ENC_35*/ - - return true; -} - -/* - * Unpack the 6&2 encoded 3-byte checksum at the end of a sector. - * - * "offset" should point to the first byte of the checksum. - * - * Returns "true" if all goes well, "false" otherwise. - */ -/*static*/ bool DiskImg::UnpackChecksum35(const CircularBufferAccess& buffer, - int offset, uint8_t* checksumBuf) -{ - uint8_t nib0, nib1, nib2, twos; - - twos = kInvDiskBytes62[buffer[offset++]]; - nib2 = kInvDiskBytes62[buffer[offset++]]; - nib1 = kInvDiskBytes62[buffer[offset++]]; - nib0 = kInvDiskBytes62[buffer[offset++]]; - - if (twos == kInvInvalidValue || - nib0 == kInvInvalidValue || - nib1 == kInvInvalidValue || - nib2 == kInvInvalidValue) - { - LOGI("nib25: found invalid disk byte in checksum"); - return false; - } - - checksumBuf[0] = nib0 | ((twos << 6) & 0xc0); - checksumBuf[1] = nib1 | ((twos << 4) & 0xc0); - checksumBuf[2] = nib2 | ((twos << 2) & 0xc0); - return true; -} - -/* - * Encode 524 bytes of sector data into 699 bytes of 6&2 nibblized data - * plus a 4-byte checksum. - * - * "outBuf" must be able to hold kNibblizedOutputLen bytes. - */ -/*static*/ void DiskImg::EncodeNibbleSector35(const uint8_t* sectorData, - uint8_t* outBuf) -{ - const uint8_t* sectorDataStart = sectorData; - uint8_t* outBufStart = outBuf; - uint8_t part0[kChunkSize35], part1[kChunkSize35], part2[kChunkSize35]; - unsigned int chk0, chk1, chk2; - uint8_t val, twos; - int i; - - /* - * Compute checksum and split the input into 3 pieces. - */ - i = 0; - chk0 = chk1 = chk2 = 0; - while (true) { - chk0 = (chk0 & 0xff) << 1; - if (chk0 & 0x0100) - chk0++; - - val = *sectorData++; - chk2 += val; - if (chk0 & 0x0100) { - chk2++; - chk0 &= 0xff; - } - part0[i] = (val ^ chk0) & 0xff; - - val = *sectorData++; - chk1 += val; - if (chk2 > 0xff) { - chk1++; - chk2 &= 0xff; - } - part1[i] = (val ^ chk2) & 0xff; - - if (sectorData - sectorDataStart == 524) - break; - - val = *sectorData++; - chk0 += val; - if (chk1 > 0xff) { - chk0++; - chk1 &= 0xff; - } - part2[i] = (val ^ chk1) & 0xff; - i++; - } - part2[kChunkSize35-1] = 0; // gets merged into the "twos" - - assert(i == kChunkSize35-1); - - /* - * Output the nibble data. - */ - for (i = 0; i < kChunkSize35; i++) { - twos = ((part0[i] & 0xc0) >> 2) | - ((part1[i] & 0xc0) >> 4) | - ((part2[i] & 0xc0) >> 6); - - *outBuf++ = kDiskBytes62[twos]; - *outBuf++ = kDiskBytes62[part0[i] & 0x3f]; - *outBuf++ = kDiskBytes62[part1[i] & 0x3f]; - if (i != kChunkSize35 -1) - *outBuf++ = kDiskBytes62[part2[i] & 0x3f]; - } - - /* - * Output the checksum. - */ - twos = ((chk0 & 0xc0) >> 6) | ((chk1 & 0xc0) >> 4) | ((chk2 & 0xc0) >> 2); - *outBuf++ = kDiskBytes62[twos]; - *outBuf++ = kDiskBytes62[chk2 & 0x3f]; - *outBuf++ = kDiskBytes62[chk1 & 0x3f]; - *outBuf++ = kDiskBytes62[chk0 & 0x3f]; - - assert(outBuf - outBufStart == kNibblizedOutputLen); -} diff --git a/ciderpress/diskimg/OuterWrapper.cpp b/ciderpress/diskimg/OuterWrapper.cpp deleted file mode 100644 index ce6f3e5..0000000 --- a/ciderpress/diskimg/OuterWrapper.cpp +++ /dev/null @@ -1,1504 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Code for handling "outer wrappers" like ZIP and gzip. - * - * TODO: for safety, these should compress into a temp file and then rename - * the temp file over the original. The current implementation just - * truncates the open file descriptor or reopens the original file. Both - * risk data loss if the program or system crashes while the data is being - * written to disk. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" -#define DEF_MEM_LEVEL 8 // normally in zutil.h - - -/* - * =========================================================================== - * OuterGzip - * =========================================================================== - */ - -/* - * Test to see if this is a gzip file. - * - * This test is pretty weak, so we shouldn't even be looking at this - * unless the file ends in ".gz". A better test would scan the entire - * header. - * - * Would be nice to just gzopen the file, but unfortunately that tries - * to be "helpful" and reads the file whether it's in gz format or not. - * Some days I could do with a little less "help". - */ -/*static*/ DIError OuterGzip::Test(GenericFD* pGFD, di_off_t outerLength) -{ - const int kGzipMagic = 0x8b1f; // 0x1f 0x8b - uint16_t magic, magicBuf; - const char* imagePath; - - LOGI("Testing for gzip"); - - /* don't need this here, but we will later on */ - imagePath = pGFD->GetPathName(); - if (imagePath == NULL) { - LOGI("Can't test gzip on non-file"); - return kDIErrNotSupported; - } - - pGFD->Rewind(); - - if (pGFD->Read(&magicBuf, 2) != kDIErrNone) - return kDIErrGeneric; - magic = GetShortLE((uint8_t*) &magicBuf); - - if (magic == kGzipMagic) - return kDIErrNone; - else - return kDIErrGeneric; -} - -/* - * The gzip file format has a length embedded in the footer, but - * unfortunately there is no interface to access it. So, we have - * to keep reading until we run out of data, extending the buffer - * to accommodate the new data each time. (We could also just read - * the footer directly, but that requires that there are no garbage - * bytes at the end of the file, which is a real concern on some FTP sites.) - * - * Start out by trying sizes that we think will work (140K, 800K), - * then grow quickly. - * - * The largest possible ProDOS image is 32MB, but it's possible to - * have an HFS volume or a partitioned image larger than that. We currently - * cap the limit to avoid nasty behavior when encountering really - * large .gz files. This isn't great -- we ought to support extracting - * to a temp file, or allowing the caller to specify what the largest - * size they can handle is. - */ -DIError OuterGzip::ExtractGzipImage(gzFile gzfp, char** pBuf, di_off_t* pLength) -{ - DIError dierr = kDIErrNone; - const int kMinEmpty = 256 * 1024; - const int kStartSize = 141 * 1024; - const int kNextSize1 = 801 * 1024; - const int kNextSize2 = 1024 * 1024; - const int kMaxIncr = 4096 * 1024; - const int kAbsoluteMax = kMaxUncompressedSize; - char* buf = NULL; - char* newBuf = NULL; - long curSize, maxSize; - - assert(gzfp != NULL); - assert(pBuf != NULL); - assert(pLength != NULL); - - curSize = 0; - maxSize = kStartSize; - - buf = new char[maxSize]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - while (1) { - long len; - - /* - * Try to fill the buffer. - * - * It appears that zlib v1.1.4 was more tolerant of certain kinds - * of broken archives than v1.2.1. Both give you a pile of data - * on the first read, with no error reported, but the next read - * attempt returns with z_err=-3 (Z_DATA_ERROR) and z_eof set. I'm - * not sure exactly what the flaw is, but I'm guessing something - * got lopped off the end of the archives. gzip v1.3.3 won't touch - * them either. - * - * It would be easy enough to access them, if they were accessible. - * Unfortunately the implementation is buried. Instead, we do - * a quick test against known unadorned floppy disk sizes to see - * if we can salvage the contents. (Our read attempts are all - * slightly *over* the standard disk sizes, so if it comes back right - * on one we're *probably* okay.) - */ - len = gzread(gzfp, buf + curSize, maxSize - curSize); - if (len < 0) { - LOGI(" ExGZ Call to gzread failed, errno=%d", errno); - if (curSize == 140*1024 || curSize == 800*1024) { - LOGI("WARNING: accepting damaged gzip file"); - fWrapperDamaged = true; - break; // sleazy, but currently necessary - } - dierr = kDIErrReadFailed; - goto bail; - } else if (len == 0) { - /* EOF reached */ - break; - } else if (len < (maxSize - curSize)) { - /* we've probably reached the end, but we can't be sure, - so let's go around again */ - LOGI(" ExGZ gzread(%ld) returned %ld, letting it ride", - maxSize - curSize, len); - curSize += len; - } else { - /* update buffer, and grow it if it's not big enough */ - curSize += len; - LOGI(" max=%ld cur=%ld", maxSize, curSize); - if (maxSize - curSize < kMinEmpty) { - /* not enough room, grow it */ - - if (maxSize == kStartSize) - maxSize = kNextSize1; - else if (maxSize == kNextSize1) - maxSize = kNextSize2; - else { - if (maxSize < kMaxIncr) - maxSize = maxSize * 2; - else - maxSize += kMaxIncr; - } - - newBuf = new char[maxSize]; - if (newBuf == NULL) { - LOGI(" ExGZ failed buffer alloc (%ld)", - maxSize); - dierr = kDIErrMalloc; - goto bail; - } - - memcpy(newBuf, buf, curSize); - delete[] buf; - buf = newBuf; - newBuf = NULL; - - LOGI(" ExGZ grew buffer to %ld", maxSize); - } else { - /* don't need to grow buffer yet */ - LOGI(" ExGZ read %ld bytes, cur=%ld max=%ld", - len, curSize, maxSize); - } - } - assert(curSize < maxSize); - - if (curSize > kAbsoluteMax) { - LOGI(" ExGZ excessive size, probably not a disk image"); - dierr = kDIErrTooBig; // close enough - goto bail; - } - } - - if (curSize + (1024*1024) < maxSize) { - /* shrink it down so it fits */ - LOGI(" Down-sizing buffer from %ld to %ld", maxSize, curSize); - newBuf = new char[curSize]; - if (newBuf == NULL) - goto bail; - memcpy(newBuf, buf, curSize); - delete[] buf; - buf = newBuf; - newBuf = NULL; - } - - *pBuf = buf; - *pLength = curSize; - LOGI(" ExGZ final size = %ld", curSize); - - buf = NULL; - -bail: - delete[] buf; - delete[] newBuf; - return dierr; -} - -/* - * Open the archive, and extract the disk image into a memory buffer. - */ -DIError OuterGzip::Load(GenericFD* pOuterGFD, di_off_t outerLength, bool readOnly, - di_off_t* pWrapperLength, GenericFD** ppWrapperGFD) -{ - DIError dierr = kDIErrNone; - GFDBuffer* pNewGFD = NULL; - char* buf = NULL; - di_off_t length = -1; - const char* imagePath; - gzFile gzfp = NULL; - - imagePath = pOuterGFD->GetPathName(); - if (imagePath == NULL) { - assert(false); // should've been caught in Test - return kDIErrNotSupported; - } - - gzfp = gzopen(imagePath, "rb"); // use "readOnly" here - if (gzfp == NULL) { // DON'T retry RO -- should be done at higher level? - LOGI("gzopen failed, errno=%d", errno); - dierr = kDIErrGeneric; - goto bail; - } - - dierr = ExtractGzipImage(gzfp, &buf, &length); - if (dierr != kDIErrNone) - goto bail; - - /* - * Everything is going well. Now we substitute a memory-based GenericFD - * for the existing GenericFD. - */ - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, readOnly); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - /* - * Success! - */ - assert(dierr == kDIErrNone); - *ppWrapperGFD = pNewGFD; - pNewGFD = NULL; - - *pWrapperLength = length; - -bail: - if (dierr != kDIErrNone) { - delete pNewGFD; - } - if (gzfp != NULL) - gzclose(gzfp); - return dierr; -} - -/* - * Save the contents of "pWrapperGFD" to the file pointed to by - * "pOuterGFD". - * - * "pOuterGFD" isn't disturbed (same as Load). All we want is to get the - * filename and then do everything through gzio. - */ -DIError OuterGzip::Save(GenericFD* pOuterGFD, GenericFD* pWrapperGFD, - di_off_t wrapperLength) -{ - DIError dierr = kDIErrNone; - const char* imagePath; - gzFile gzfp = NULL; - - LOGI(" GZ save (wrapperLen=%ld)", (long) wrapperLength); - assert(wrapperLength > 0); - - /* - * Reopen the file. - */ - imagePath = pOuterGFD->GetPathName(); - if (imagePath == NULL) { - assert(false); // should've been caught long ago - return kDIErrNotSupported; - } - - gzfp = gzopen(imagePath, "wb"); - if (gzfp == NULL) { - LOGI("gzopen for write failed, errno=%d", errno); - dierr = kDIErrGeneric; - goto bail; - } - - char buf[16384]; - size_t actual; - long written, totalWritten; - - pWrapperGFD->Rewind(); - - totalWritten = 0; - while (wrapperLength > 0) { - dierr = pWrapperGFD->Read(buf, sizeof(buf), &actual); - if (dierr == kDIErrEOF) { - dierr = kDIErrNone; - break; - } - if (dierr != kDIErrNone) { - LOGI("Error reading source GFD during gzip save (err=%d)",dierr); - goto bail; - } - assert(actual > 0); - - written = gzwrite(gzfp, buf, actual); - if (written == 0) { - LOGE("Failed writing %lu bytes to gzio", (unsigned long) actual); - dierr = kDIErrGeneric; - goto bail; - } - - totalWritten += written; - wrapperLength -= actual; - } - assert(wrapperLength == 0); // not expecting any slop - - LOGD(" GZ wrote %ld bytes", totalWritten); - - /* - * Success! - */ - assert(dierr == kDIErrNone); - -bail: - if (gzfp != NULL) - gzclose(gzfp); - return dierr; -} - - -/* - * =========================================================================== - * OuterZip - * =========================================================================== - */ - -/* - * Test to see if this is a ZIP archive. - */ -/*static*/ DIError OuterZip::Test(GenericFD* pGFD, di_off_t outerLength) -{ - DIError dierr = kDIErrNone; - CentralDirEntry cde; - - LOGI("Testing for zip"); - dierr = ReadCentralDir(pGFD, outerLength, &cde); - if (dierr != kDIErrNone) - goto bail; - - /* - * Make sure it's a compression method we support. - */ - if (cde.fCompressionMethod != kCompressStored && - cde.fCompressionMethod != kCompressDeflated) - { - LOGI(" ZIP compression method %d not supported", - cde.fCompressionMethod); - dierr = kDIErrGeneric; - goto bail; - } - - /* - * Limit the size to something reasonable. - */ - if (cde.fUncompressedSize < 512 || - cde.fUncompressedSize > kMaxUncompressedSize) - { - LOGI(" ZIP uncompressed size %u is outside range", - cde.fUncompressedSize); - dierr = kDIErrGeneric; - goto bail; - } - - assert(dierr == kDIErrNone); - -bail: - return dierr; -} - -/* - * Open the archive, and extract the disk image into a memory buffer. - */ -DIError OuterZip::Load(GenericFD* pOuterGFD, di_off_t outerLength, bool readOnly, - di_off_t* pWrapperLength, GenericFD** ppWrapperGFD) -{ - DIError dierr = kDIErrNone; - GFDBuffer* pNewGFD = NULL; - CentralDirEntry cde; - uint8_t* buf = NULL; - di_off_t length = -1; - const char* pExt; - - dierr = ReadCentralDir(pOuterGFD, outerLength, &cde); - if (dierr != kDIErrNone) - goto bail; - - if (cde.fFileNameLength > 0) { - pExt = FindExtension((const char*) cde.fFileName, kZipFssep); - if (pExt != NULL) { - assert(*pExt == '.'); - SetExtension(pExt+1); - - LOGI("OuterZip using extension '%s'", GetExtension()); - } - - SetStoredFileName((const char*) cde.fFileName); - } - - dierr = ExtractZipEntry(pOuterGFD, &cde, &buf, &length); - if (dierr != kDIErrNone) - goto bail; - - /* - * Everything is going well. Now we substitute a memory-based GenericFD - * for the existing GenericFD. - */ - pNewGFD = new GFDBuffer; - dierr = pNewGFD->Open(buf, length, true, false, readOnly); - if (dierr != kDIErrNone) - goto bail; - buf = NULL; // now owned by pNewGFD; - - /* - * Success! - */ - assert(dierr == kDIErrNone); - *ppWrapperGFD = pNewGFD; - pNewGFD = NULL; - - *pWrapperLength = length; - -bail: - if (dierr != kDIErrNone) { - delete pNewGFD; - } - return dierr; -} - -/* - * Save the contents of "pWrapperGFD" to the file pointed to by - * "pOuterGFD". - */ -DIError OuterZip::Save(GenericFD* pOuterGFD, GenericFD* pWrapperGFD, - di_off_t wrapperLength) -{ - DIError dierr = kDIErrNone; - LocalFileHeader lfh; - CentralDirEntry cde; - EndOfCentralDir eocd; - di_off_t lfhOffset; - - LOGI(" ZIP save (wrapperLen=%ld)", (long) wrapperLength); - assert(wrapperLength > 0); - - dierr = pOuterGFD->Rewind(); - if (dierr != kDIErrNone) - goto bail; - dierr = pOuterGFD->Truncate(); - if (dierr != kDIErrNone) - goto bail; - - dierr = pWrapperGFD->Rewind(); - if (dierr != kDIErrNone) - goto bail; - - lfhOffset = pOuterGFD->Tell(); // always 0 with only one file - - /* - * Don't store an empty filename. Some applications, e.g. Info-ZIP's - * "unzip", get confused. Ideally the DiskImg image creation code - * will have set the actual filename, with an extension that matches - * the file contents. - */ - if (fStoredFileName == NULL || fStoredFileName[0] == '\0') - SetStoredFileName("disk"); - - /* - * Write the ZIP local file header. We don't have file lengths or - * CRCs yet, so we have to go back and fill those in later. - */ - lfh.fVersionToExtract = kDefaultVersion; -#if NO_ZIP_COMPRESS - lfh.fGPBitFlag = 0; - lfh.fCompressionMethod = 0; -#else - lfh.fGPBitFlag = 0x0002; // indicates maximum compression used - lfh.fCompressionMethod = 8; // when compressionMethod == deflate -#endif - GetMSDOSTime(&lfh.fLastModFileDate, &lfh.fLastModFileTime); - lfh.SetFileName(fStoredFileName); - dierr = lfh.Write(pOuterGFD); - if (dierr != kDIErrNone) - goto bail; - - /* - * Write the compressed data. - */ - uint32_t crc; - di_off_t compressedLen; - if (lfh.fCompressionMethod == kCompressDeflated) { - dierr = DeflateGFDToGFD(pOuterGFD, pWrapperGFD, wrapperLength, - &compressedLen, &crc); - if (dierr != kDIErrNone) - goto bail; - } else if (lfh.fCompressionMethod == kCompressStored) { - dierr = GenericFD::CopyFile(pOuterGFD, pWrapperGFD, wrapperLength, - &crc); - if (dierr != kDIErrNone) - goto bail; - compressedLen = wrapperLength; - } else { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - /* - * Go back and take care of the local file header stuff. - * - * It's not supposed to be necessary, but some utilities (WinZip, - * Info-ZIP) get bent out of shape if these aren't set and the data - * is compressed. They seem okay with it when the file isn't - * compressed. I don't understand this behavior, but writing the - * local file header is easy enough. - */ - lfh.fCRC32 = crc; - lfh.fCompressedSize = (uint32_t) compressedLen; - lfh.fUncompressedSize = (uint32_t) wrapperLength; - - di_off_t curPos; - curPos = pOuterGFD->Tell(); - dierr = pOuterGFD->Seek(lfhOffset, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - dierr = lfh.Write(pOuterGFD); - if (dierr != kDIErrNone) - goto bail; - dierr = pOuterGFD->Seek(curPos, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - - di_off_t cdeStart, cdeFinish; - cdeStart = pOuterGFD->Tell(); - - /* - * Write the central dir entry. This is largely just a copy of the - * data in the local file header (and in fact some utilities will - * get rather bent out of shape if the two don't match exactly). - */ - cde.fVersionMadeBy = kDefaultVersion; - cde.fVersionToExtract = lfh.fVersionToExtract; - cde.fGPBitFlag = lfh.fGPBitFlag; - cde.fCompressionMethod = lfh.fCompressionMethod; - cde.fLastModFileDate = lfh.fLastModFileDate; - cde.fLastModFileTime = lfh.fLastModFileTime; - cde.fCRC32 = lfh.fCRC32; - cde.fCompressedSize = lfh.fCompressedSize; - cde.fUncompressedSize = lfh.fUncompressedSize; - assert(lfh.fExtraFieldLength == 0 && cde.fExtraFieldLength == 0); - cde.fExternalAttrs = 0x81b60020; // matches what WinZip does - cde.fLocalHeaderRelOffset = (uint32_t) lfhOffset; - cde.SetFileName(fStoredFileName); - dierr = cde.Write(pOuterGFD); - if (dierr != kDIErrNone) - goto bail; - - cdeFinish = pOuterGFD->Tell(); - - /* - * Write the end-of-central-dir stuff. - */ - eocd.fNumEntries = 1; - eocd.fTotalNumEntries = 1; - eocd.fCentralDirSize = (uint32_t) (cdeFinish - cdeStart); - eocd.fCentralDirOffset = (uint32_t) cdeStart; - assert(eocd.fCentralDirSize >= EndOfCentralDir::kEOCDLen); - dierr = eocd.Write(pOuterGFD); - if (dierr != kDIErrNone) - goto bail; - - /* - * Success! - */ - assert(dierr == kDIErrNone); - -bail: - return dierr; -} - -/* - * Track the name of the file stored in the ZIP archive. - */ -void OuterZip::SetStoredFileName(const char* name) -{ - delete[] fStoredFileName; - fStoredFileName = StrcpyNew(name); -} - -/* - * Find the central directory and read the contents. - * - * We currently only support archives with a single entry. - * - * The fun thing about ZIP archives is that they may or may not be - * readable from start to end. In some cases, notably for archives - * that were written to stdout, the only length information is in the - * central directory at the end of the file. - * - * Of course, the central directory can be followed by a variable-length - * comment field, so we have to scan through it backwards. The comment - * is at most 64K, plus we have 18 bytes for the end-of-central-dir stuff - * itself, plus apparently sometimes people throw random junk on the end - * just for the fun of it. - * - * This is all a little wobbly. If the wrong value ends up in the EOCD - * area, we're hosed. This appears to be the way that the Info-ZIP guys - * do it though, so we're in pretty good company if this fails. - */ -/*static*/ DIError OuterZip::ReadCentralDir(GenericFD* pGFD, di_off_t outerLength, - CentralDirEntry* pDirEntry) -{ - DIError dierr = kDIErrNone; - EndOfCentralDir eocd; - uint8_t* buf = NULL; - di_off_t seekStart; - long readAmount; - int i; - - /* too small to be a ZIP archive? */ - if (outerLength < EndOfCentralDir::kEOCDLen + 4) - return kDIErrGeneric; - - buf = new uint8_t[kMaxEOCDSearch]; - if (buf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - if (outerLength > kMaxEOCDSearch) { - seekStart = outerLength - kMaxEOCDSearch; - readAmount = kMaxEOCDSearch; - } else { - seekStart = 0; - readAmount = (long) outerLength; - } - dierr = pGFD->Seek(seekStart, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - - /* read the last part of the file into the buffer */ - dierr = pGFD->Read(buf, readAmount); - if (dierr != kDIErrNone) - goto bail; - - /* find the end-of-central-dir magic */ - for (i = readAmount - 4; i >= 0; i--) { - if (buf[i] == 0x50 && - GetLongLE(&buf[i]) == EndOfCentralDir::kSignature) - { - LOGI("+++ Found EOCD at buf+%d", i); - break; - } - } - if (i < 0) { - LOGI("+++ EOCD not found, not ZIP"); - dierr = kDIErrGeneric; - goto bail; - } - - /* extract eocd values */ - dierr = eocd.ReadBuf(buf + i, readAmount - i); - if (dierr != kDIErrNone) - goto bail; - eocd.Dump(); - - if (eocd.fDiskNumber != 0 || eocd.fDiskWithCentralDir != 0 || - eocd.fNumEntries != 1 || eocd.fTotalNumEntries != 1) - { - LOGI(" Probable ZIP archive has more than one member"); - dierr = kDIErrFileArchive; - goto bail; - } - - /* - * So far so good. "fCentralDirSize" is the size in bytes of the - * central directory, so we can just seek back that far to find it. - * We can also seek forward fCentralDirOffset bytes from the - * start of the file. - * - * We're not guaranteed to have the rest of the central dir in the - * buffer, nor are we guaranteed that the central dir will have any - * sort of convenient size. We need to skip to the start of it and - * read the header, then the other goodies. - * - * The only thing we really need right now is the file comment, which - * we're hoping to preserve. - */ - dierr = pGFD->Seek(eocd.fCentralDirOffset, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - - /* - * Read the central dir entry. - */ - dierr = pDirEntry->Read(pGFD); - if (dierr != kDIErrNone) - goto bail; - - pDirEntry->Dump(); - - { - uint8_t checkBuf[4]; - dierr = pGFD->Read(checkBuf, 4); - if (dierr != kDIErrNone) - goto bail; - if (GetLongLE(checkBuf) != EndOfCentralDir::kSignature) { - LOGI("CDE read check failed"); - assert(false); - dierr = kDIErrGeneric; - goto bail; - } - LOGI("+++ CDE read check passed"); - } - -bail: - delete[] buf; - return dierr; -} - -/* - * The central directory tells us where to find the local header. We - * have to skip over that to get to the start of the data. - */ -DIError OuterZip::ExtractZipEntry(GenericFD* pOuterGFD, CentralDirEntry* pCDE, - uint8_t** pBuf, di_off_t* pLength) -{ - DIError dierr = kDIErrNone; - LocalFileHeader lfh; - uint8_t* buf = NULL; - - /* seek to the start of the local header */ - dierr = pOuterGFD->Seek(pCDE->fLocalHeaderRelOffset, kSeekSet); - if (dierr != kDIErrNone) - goto bail; - - /* - * Read the local file header, mainly as a way to get past it. There - * are legitimate reasons why the size fields and filename might be - * empty, so we really don't want to depend on any data in the LFH. - * We just need to find where the data starts. - */ - dierr = lfh.Read(pOuterGFD); - if (dierr != kDIErrNone) - goto bail; - lfh.Dump(); - - /* we should now be pointing at the data */ - LOGI("File offset is 0x%08lx", (long) pOuterGFD->Tell()); - - buf = new uint8_t[pCDE->fUncompressedSize]; - if (buf == NULL) { - /* a very real possibility */ - LOGI(" ZIP unable to allocate buffer of %u bytes", - pCDE->fUncompressedSize); - dierr = kDIErrMalloc; - goto bail; - } - - /* unpack or copy the data */ - if (pCDE->fCompressionMethod == kCompressDeflated) { - dierr = InflateGFDToBuffer(pOuterGFD, pCDE->fCompressedSize, - pCDE->fUncompressedSize, buf); - if (dierr != kDIErrNone) - goto bail; - } else if (pCDE->fCompressionMethod == kCompressStored) { - dierr = pOuterGFD->Read(buf, pCDE->fUncompressedSize); - if (dierr != kDIErrNone) - goto bail; - } else { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - /* check the CRC32 */ - uint32_t crc; - crc = crc32(0L, Z_NULL, 0); - crc = crc32(crc, buf, pCDE->fUncompressedSize); - - if (crc == pCDE->fCRC32) { - LOGI("+++ ZIP CRCs match"); - } else { - LOGI("ZIP CRC mismatch: inflated crc32=0x%08x, stored=0x%08x", - crc, pCDE->fCRC32); - dierr = kDIErrBadChecksum; - goto bail; - } - - *pBuf = buf; - *pLength = pCDE->fUncompressedSize; - - buf = NULL; - -bail: - delete[] buf; - return dierr; -} - -/* - * Uncompress data from "pOuterGFD" to "buf". - * - * "buf" must be able to hold "uncompSize" bytes. - */ -DIError OuterZip::InflateGFDToBuffer(GenericFD* pGFD, unsigned long compSize, - unsigned long uncompSize, uint8_t* buf) -{ - DIError dierr = kDIErrNone; - const size_t kReadBufSize = 65536; - uint8_t* readBuf = NULL; - z_stream zstream; - int zerr; - unsigned long compRemaining; - - readBuf = new uint8_t[kReadBufSize]; - if (readBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - compRemaining = compSize; - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = buf; - zstream.avail_out = uncompSize; - zstream.data_type = Z_UNKNOWN; - - /* - * Use the undocumented "negative window bits" feature to tell zlib - * that there's no zlib header waiting for it. - */ - zerr = inflateInit2(&zstream, -MAX_WBITS); - if (zerr != Z_OK) { - dierr = kDIErrInternal; - if (zerr == Z_VERSION_ERROR) { - LOGI("Installed zlib is not compatible with linked version (%s)", - ZLIB_VERSION); - } else { - LOGI("Call to inflateInit2 failed (zerr=%d)", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - unsigned long getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kReadBufSize) ? - kReadBufSize : compRemaining; - LOGI("+++ reading %ld bytes (%ld left)", getSize, - compRemaining); - - dierr = pGFD->Read(readBuf, getSize); - if (dierr != kDIErrNone) { - LOGI("inflate read failed"); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = readBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - dierr = kDIErrInternal; - LOGI("zlib inflate call failed (zerr=%d)", zerr); - goto z_bail; - } - - /* output buffer holds all, so no need to write the output */ - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if (zstream.total_out != uncompSize) { - dierr = kDIErrBadCompressedData; - LOGI("Size mismatch on inflated file (%ld vs %ld)", - zstream.total_out, uncompSize); - goto z_bail; - } - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] readBuf; - return dierr; -} - -/* - * Get the current date/time, in MS-DOS format. - */ -void OuterZip::GetMSDOSTime(uint16_t* pDate, uint16_t* pTime) -{ -#if 0 - /* this gets gmtime; we want localtime */ - SYSTEMTIME sysTime; - FILETIME fileTime; - ::GetSystemTime(&sysTime); - ::SystemTimeToFileTime(&sysTime, &fileTime); - ::FileTimeToDosDateTime(&fileTime, pDate, pTime); - //LOGI("+++ Windows date: %04x %04x %d", *pDate, *pTime, - // (*pTime >> 11) & 0x1f); -#endif - - time_t now = time(NULL); - DOSTime(now, pDate, pTime); - //LOGI("+++ Our date : %04x %04x %d", *pDate, *pTime, - // (*pTime >> 11) & 0x1f); -} - -/* - * Convert a time_t to MS-DOS date and time values. - */ -void OuterZip::DOSTime(time_t when, uint16_t* pDate, uint16_t* pTime) -{ - time_t even; - - *pDate = *pTime = 0; - - struct tm* ptm; - - /* round up to an even number of seconds */ - even = (time_t)(((unsigned long)(when) + 1) & (~1)); - - /* expand */ - ptm = localtime(&even); - - int year; - year = ptm->tm_year; - if (year < 80) - year = 80; - - *pDate = (year - 80) << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; - *pTime = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1; -} - -/* - * Compress "length" bytes of data from "pSrc" to "pDst". - */ -DIError OuterZip::DeflateGFDToGFD(GenericFD* pDst, GenericFD* pSrc, - di_off_t srcLen, di_off_t* pCompLength, uint32_t* pCRC) -{ - DIError dierr = kDIErrNone; - const size_t kBufSize = 32768; - uint8_t* inBuf = NULL; - uint8_t* outBuf = NULL; - z_stream zstream; - uint32_t crc; - int zerr; - - /* - * Create an input buffer and an output buffer. - */ - inBuf = new uint8_t[kBufSize]; - outBuf = new uint8_t[kBufSize]; - if (inBuf == NULL || outBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - /* - * Initialize the zlib stream. - */ - memset(&zstream, 0, sizeof(zstream)); - zstream.zalloc = Z_NULL; - zstream.zfree = Z_NULL; - zstream.opaque = Z_NULL; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = deflateInit2(&zstream, Z_BEST_COMPRESSION, - Z_DEFLATED, -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY); - if (zerr != Z_OK) { - dierr = kDIErrInternal; - if (zerr == Z_VERSION_ERROR) { - LOGI("Installed zlib is not compatible with linked version (%s)", - ZLIB_VERSION); - } else { - LOGI("Call to deflateInit2 failed (zerr=%d)", zerr); - } - goto bail; - } - - crc = crc32(0L, Z_NULL, 0); - - /* - * Loop while we have data. - */ - do { - long getSize; - int flush; - - /* only read if the input is empty */ - if (zstream.avail_in == 0 && srcLen) { - getSize = (srcLen > (long) kBufSize) ? kBufSize : (long) srcLen; - LOGI("+++ reading %ld bytes", getSize); - - dierr = pSrc->Read(inBuf, getSize); - if (dierr != kDIErrNone) { - LOGI("deflate read failed"); - goto z_bail; - } - - srcLen -= getSize; - - crc = crc32(crc, inBuf, getSize); - - zstream.next_in = inBuf; - zstream.avail_in = getSize; - } - - if (srcLen == 0) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ - - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - LOGI("zlib deflate call failed (zerr=%d)", zerr); - dierr = kDIErrInternal; - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != kBufSize)) - { - LOGI("+++ writing %ld bytes", zstream.next_out - outBuf); - dierr = pDst->Write(outBuf, zstream.next_out - outBuf); - if (dierr != kDIErrNone) { - LOGI("write failed in deflate"); - goto z_bail; - } - - zstream.next_out = outBuf; - zstream.avail_out = kBufSize; - } - } while (zerr == Z_OK); - - assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - *pCompLength = zstream.total_out; - *pCRC = crc; - -z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ - -bail: - delete[] inBuf; - delete[] outBuf; - - return dierr; -} - -/* - * Set the "fExtension" field. - */ -void OuterZip::SetExtension(const char* ext) -{ - delete[] fExtension; - fExtension = StrcpyNew(ext); -} - - -/* - * =================================== - * OuterZip::LocalFileHeader - * =================================== - */ - -/* - * Read a local file header. - * - * On entry, "pGFD" points to the signature at the start of the header. - * On exit, "pGFD" points to the start of data. - */ -DIError OuterZip::LocalFileHeader::Read(GenericFD* pGFD) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kLFHLen]; - - dierr = pGFD->Read(buf, kLFHLen); - if (dierr != kDIErrNone) - goto bail; - - if (GetLongLE(&buf[0x00]) != kSignature) { - LOGI(" ZIP: whoops: didn't find expected signature"); - assert(false); - return kDIErrGeneric; - } - - fVersionToExtract = GetShortLE(&buf[0x04]); - fGPBitFlag = GetShortLE(&buf[0x06]); - fCompressionMethod = GetShortLE(&buf[0x08]); - fLastModFileTime = GetShortLE(&buf[0x0a]); - fLastModFileDate = GetShortLE(&buf[0x0c]); - fCRC32 = GetLongLE(&buf[0x0e]); - fCompressedSize = GetLongLE(&buf[0x12]); - fUncompressedSize = GetLongLE(&buf[0x16]); - fFileNameLength = GetShortLE(&buf[0x1a]); - fExtraFieldLength = GetShortLE(&buf[0x1c]); - - /* grab filename */ - if (fFileNameLength != 0) { - assert(fFileName == NULL); - fFileName = new uint8_t[fFileNameLength+1]; - if (fFileName == NULL) { - dierr = kDIErrMalloc; - goto bail; - } else { - dierr = pGFD->Read(fFileName, fFileNameLength); - fFileName[fFileNameLength] = '\0'; - } - if (dierr != kDIErrNone) - goto bail; - } - - dierr = pGFD->Seek(fExtraFieldLength, kSeekCur); - if (dierr != kDIErrNone) - goto bail; - -bail: - return dierr; -} - -/* - * Write a local file header. - */ -DIError OuterZip::LocalFileHeader::Write(GenericFD* pGFD) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kLFHLen]; - - PutLongLE(&buf[0x00], kSignature); - PutShortLE(&buf[0x04], fVersionToExtract); - PutShortLE(&buf[0x06], fGPBitFlag); - PutShortLE(&buf[0x08], fCompressionMethod); - PutShortLE(&buf[0x0a], fLastModFileTime); - PutShortLE(&buf[0x0c], fLastModFileDate); - PutLongLE(&buf[0x0e], fCRC32); - PutLongLE(&buf[0x12], fCompressedSize); - PutLongLE(&buf[0x16], fUncompressedSize); - PutShortLE(&buf[0x1a], fFileNameLength); - PutShortLE(&buf[0x1c], fExtraFieldLength); - - dierr = pGFD->Write(buf, kLFHLen); - if (dierr != kDIErrNone) - goto bail; - - /* write filename */ - if (fFileNameLength != 0) { - dierr = pGFD->Write(fFileName, fFileNameLength); - if (dierr != kDIErrNone) - goto bail; - } - assert(fExtraFieldLength == 0); - -bail: - return dierr; -} - -/* - * Change the filename field. - */ -void OuterZip::LocalFileHeader::SetFileName(const char* name) -{ - delete[] fFileName; - fFileName = NULL; - fFileNameLength = 0; - - if (name != NULL) { - fFileNameLength = strlen(name); - fFileName = new uint8_t[fFileNameLength+1]; - if (fFileName == NULL) { - LOGW("Malloc failure in SetFileName %u", fFileNameLength); - fFileName = NULL; - fFileNameLength = 0; - } else { - memcpy(fFileName, name, fFileNameLength); - fFileName[fFileNameLength] = '\0'; - LOGD("+++ OuterZip LFH filename set to '%s'", fFileName); - } - } -} - -/* - * Dump the contents of a LocalFileHeader object. - */ -void OuterZip::LocalFileHeader::Dump(void) const -{ - LOGI(" LocalFileHeader contents:"); - LOGI(" versToExt=%u gpBits=0x%04x compression=%u", - fVersionToExtract, fGPBitFlag, fCompressionMethod); - LOGI(" modTime=0x%04x modDate=0x%04x crc32=0x%08x", - fLastModFileTime, fLastModFileDate, fCRC32); - LOGI(" compressedSize=%u uncompressedSize=%u", - fCompressedSize, fUncompressedSize); - LOGI(" filenameLen=%u extraLen=%u", - fFileNameLength, fExtraFieldLength); -} - - -/* - * =================================== - * OuterZip::CentralDirEntry - * =================================== - */ - -/* - * Read the central dir entry that appears next in the file. - * - * On entry, "pGFD" should be positioned on the signature bytes for the - * entry. On exit, "pGFD" will point at the signature word for the next - * entry or for the EOCD. - */ -DIError OuterZip::CentralDirEntry::Read(GenericFD* pGFD) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kCDELen]; - - dierr = pGFD->Read(buf, kCDELen); - if (dierr != kDIErrNone) - goto bail; - - if (GetLongLE(&buf[0x00]) != kSignature) { - LOGI(" ZIP: whoops: didn't find expected signature"); - assert(false); - return kDIErrGeneric; - } - - fVersionMadeBy = GetShortLE(&buf[0x04]); - fVersionToExtract = GetShortLE(&buf[0x06]); - fGPBitFlag = GetShortLE(&buf[0x08]); - fCompressionMethod = GetShortLE(&buf[0x0a]); - fLastModFileTime = GetShortLE(&buf[0x0c]); - fLastModFileDate = GetShortLE(&buf[0x0e]); - fCRC32 = GetLongLE(&buf[0x10]); - fCompressedSize = GetLongLE(&buf[0x14]); - fUncompressedSize = GetLongLE(&buf[0x18]); - fFileNameLength = GetShortLE(&buf[0x1c]); - fExtraFieldLength = GetShortLE(&buf[0x1e]); - fFileCommentLength = GetShortLE(&buf[0x20]); - fDiskNumberStart = GetShortLE(&buf[0x22]); - fInternalAttrs = GetShortLE(&buf[0x24]); - fExternalAttrs = GetLongLE(&buf[0x26]); - fLocalHeaderRelOffset = GetLongLE(&buf[0x2a]); - - /* grab filename */ - if (fFileNameLength != 0) { - assert(fFileName == NULL); - fFileName = new uint8_t[fFileNameLength+1]; - if (fFileName == NULL) { - dierr = kDIErrMalloc; - goto bail; - } else { - dierr = pGFD->Read(fFileName, fFileNameLength); - fFileName[fFileNameLength] = '\0'; - } - if (dierr != kDIErrNone) - goto bail; - } - - /* skip over "extra field" */ - dierr = pGFD->Seek(fExtraFieldLength, kSeekCur); - if (dierr != kDIErrNone) - goto bail; - - /* grab comment, if any */ - if (fFileCommentLength != 0) { - assert(fFileComment == NULL); - fFileComment = new uint8_t[fFileCommentLength+1]; - if (fFileComment == NULL) { - dierr = kDIErrMalloc; - goto bail; - } else { - dierr = pGFD->Read(fFileComment, fFileCommentLength); - fFileComment[fFileCommentLength] = '\0'; - } - if (dierr != kDIErrNone) - goto bail; - } - -bail: - return dierr; -} - -/* - * Write a central dir entry. - */ -DIError OuterZip::CentralDirEntry::Write(GenericFD* pGFD) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kCDELen]; - - PutLongLE(&buf[0x00], kSignature); - PutShortLE(&buf[0x04], fVersionMadeBy); - PutShortLE(&buf[0x06], fVersionToExtract); - PutShortLE(&buf[0x08], fGPBitFlag); - PutShortLE(&buf[0x0a], fCompressionMethod); - PutShortLE(&buf[0x0c], fLastModFileTime); - PutShortLE(&buf[0x0e], fLastModFileDate); - PutLongLE(&buf[0x10], fCRC32); - PutLongLE(&buf[0x14], fCompressedSize); - PutLongLE(&buf[0x18], fUncompressedSize); - PutShortLE(&buf[0x1c], fFileNameLength); - PutShortLE(&buf[0x1e], fExtraFieldLength); - PutShortLE(&buf[0x20], fFileCommentLength); - PutShortLE(&buf[0x22], fDiskNumberStart); - PutShortLE(&buf[0x24], fInternalAttrs); - PutLongLE(&buf[0x26], fExternalAttrs); - PutLongLE(&buf[0x2a], fLocalHeaderRelOffset); - - dierr = pGFD->Write(buf, kCDELen); - if (dierr != kDIErrNone) - goto bail; - - /* write filename */ - if (fFileNameLength != 0) { - dierr = pGFD->Write(fFileName, fFileNameLength); - if (dierr != kDIErrNone) - goto bail; - } - assert(fExtraFieldLength == 0); - assert(fFileCommentLength == 0); - -bail: - return dierr; -} - -/* - * Change the filename field. - */ -void OuterZip::CentralDirEntry::SetFileName(const char* name) -{ - delete[] fFileName; - fFileName = NULL; - fFileNameLength = 0; - - if (name != NULL) { - fFileNameLength = strlen(name); - fFileName = new uint8_t[fFileNameLength+1]; - if (fFileName == NULL) { - LOGI("Malloc failure in SetFileName %u", fFileNameLength); - fFileName = NULL; - fFileNameLength = 0; - } else { - memcpy(fFileName, name, fFileNameLength); - fFileName[fFileNameLength] = '\0'; - LOGI("+++ OuterZip CDE filename set to '%s'", fFileName); - } - } -} - -/* - * Dump the contents of a CentralDirEntry object. - */ -void OuterZip::CentralDirEntry::Dump(void) const -{ - LOGI(" CentralDirEntry contents:"); - LOGI(" versMadeBy=%u versToExt=%u gpBits=0x%04x compression=%u", - fVersionMadeBy, fVersionToExtract, fGPBitFlag, fCompressionMethod); - LOGI(" modTime=0x%04x modDate=0x%04x crc32=0x%08x", - fLastModFileTime, fLastModFileDate, fCRC32); - LOGI(" compressedSize=%u uncompressedSize=%u", - fCompressedSize, fUncompressedSize); - LOGI(" filenameLen=%u extraLen=%u commentLen=%u", - fFileNameLength, fExtraFieldLength, fFileCommentLength); - LOGI(" diskNumStart=%u intAttr=0x%04x extAttr=0x%08x relOffset=%u", - fDiskNumberStart, fInternalAttrs, fExternalAttrs, - fLocalHeaderRelOffset); - - if (fFileName != NULL) { - LOGI(" filename: '%s'", fFileName); - } - if (fFileComment != NULL) { - LOGI(" comment: '%s'", fFileComment); - } -} - - -/* - * =================================== - * OuterZip::EndOfCentralDir - * =================================== - */ - -/* - * Read the end-of-central-dir fields. - * - * "buf" should be positioned at the EOCD signature. - */ -DIError OuterZip::EndOfCentralDir::ReadBuf(const uint8_t* buf, int len) -{ - if (len < kEOCDLen) { - /* looks like ZIP file got truncated */ - LOGI(" Zip EOCD: expected >= %d bytes, found %d", - kEOCDLen, len); - return kDIErrBadArchiveStruct; - } - - if (GetLongLE(&buf[0x00]) != kSignature) - return kDIErrInternal; - - fDiskNumber = GetShortLE(&buf[0x04]); - fDiskWithCentralDir = GetShortLE(&buf[0x06]); - fNumEntries = GetShortLE(&buf[0x08]); - fTotalNumEntries = GetShortLE(&buf[0x0a]); - fCentralDirSize = GetLongLE(&buf[0x0c]); - fCentralDirOffset = GetLongLE(&buf[0x10]); - fCommentLen = GetShortLE(&buf[0x14]); - - return kDIErrNone; -} - -/* - * Write an end-of-central-directory section. - */ -DIError OuterZip::EndOfCentralDir::Write(GenericFD* pGFD) -{ - DIError dierr = kDIErrNone; - uint8_t buf[kEOCDLen]; - - PutLongLE(&buf[0x00], kSignature); - PutShortLE(&buf[0x04], fDiskNumber); - PutShortLE(&buf[0x06], fDiskWithCentralDir); - PutShortLE(&buf[0x08], fNumEntries); - PutShortLE(&buf[0x0a], fTotalNumEntries); - PutLongLE(&buf[0x0c], fCentralDirSize); - PutLongLE(&buf[0x10], fCentralDirOffset); - PutShortLE(&buf[0x14], fCommentLen); - - dierr = pGFD->Write(buf, kEOCDLen); - if (dierr != kDIErrNone) - goto bail; - - assert(fCommentLen == 0); - -bail: - return dierr; -} - -/* - * Dump the contents of an EndOfCentralDir object. - */ -void -OuterZip::EndOfCentralDir::Dump(void) const -{ - LOGI(" EndOfCentralDir contents:"); - LOGI(" diskNum=%u diskWCD=%u numEnt=%u totalNumEnt=%u", - fDiskNumber, fDiskWithCentralDir, fNumEntries, fTotalNumEntries); - LOGI(" centDirSize=%u centDirOff=%u commentLen=%u", - fCentralDirSize, fCentralDirOffset, fCommentLen); -} diff --git a/ciderpress/diskimg/OzDOS.cpp b/ciderpress/diskimg/OzDOS.cpp deleted file mode 100644 index 0bac55d..0000000 --- a/ciderpress/diskimg/OzDOS.cpp +++ /dev/null @@ -1,320 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskFSOzDOS class. - * - * It would make life MUCH EASIER to have the DiskImg recognize this as - * a file format and just rearrange the blocks into linear order for us, - * but unfortunately that's not going to happen. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSOzDOS - * =========================================================================== - */ - -const int kExpectedNumBlocks = 1600; -const int kExpectedTracks = 50; // 50 tracks of 32 sectors == 400K -const int kExpectedSectors = 32; -const int kVTOCTrack = 17; -const int kVTOCSector = 0; -const int kSctSize = 256; - -//const int kCatalogEntrySize = 0x23; // length in bytes of catalog entries -//const int kCatalogEntriesPerSect = 7; // #of entries per catalog sector -const int kMaxTSPairs = 0x7a; // 122 entries for 256-byte sectors -//const int kTSOffset = 0x0c; // first T/S entry in a T/S list - -//const int kMaxTSIterations = 32; -const int kMaxCatalogIterations = 64; - - -/* - * Read a track/sector, adjusting for 32-sector disks being treated as - * if they were 16-sector. - */ -static DIError ReadTrackSectorAdjusted(DiskImg* pImg, int track, int sector, - int sectorOffset, uint8_t* buf, DiskImg::SectorOrder imageOrder) -{ - track *= 4; - sector = sector * 2 + sectorOffset; - while (sector >= 16) { - track++; - sector -= 16; - } - return pImg->ReadTrackSectorSwapped(track, sector, buf, imageOrder, - DiskImg::kSectorOrderDOS); -} - -/* - * Test for presence of 400K OzDOS 3.3 volumes. - */ -static DIError TestImageHalf(DiskImg* pImg, int sectorOffset, - DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - int numTracks, numSectors; - int catTrack, catSect; - int foundGood = 0; - int iterations = 0; - - assert(sectorOffset == 0 || sectorOffset == 1); - - dierr = ReadTrackSectorAdjusted(pImg, kVTOCTrack, kVTOCSector, - sectorOffset, sctBuf, imageOrder); - if (dierr != kDIErrNone) - goto bail; - - catTrack = sctBuf[0x01]; - catSect = sctBuf[0x02]; - numTracks = sctBuf[0x34]; - numSectors = sctBuf[0x35]; - - if (!(sctBuf[0x27] == kMaxTSPairs) || - /*!(sctBuf[0x36] == 0 && sctBuf[0x37] == 1) ||*/ // bytes per sect - !(numTracks == kExpectedTracks) || - !(numSectors == 32) || - !(catTrack < numTracks && catSect < numSectors) || - 0) - { - LOGI(" OzDOS header test %d failed", sectorOffset); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* - * Walk through the catalog track to try to figure out ordering. - */ - while (catTrack != 0 && catSect != 0 && iterations < kMaxCatalogIterations) - { - dierr = ReadTrackSectorAdjusted(pImg, catTrack, catSect, - sectorOffset, sctBuf, imageOrder); - if (dierr != kDIErrNone) - goto bail_ok; /* allow it if not fully broken */ - - if (sctBuf[1] == catTrack && sctBuf[2] == catSect-1) - foundGood++; - - catTrack = sctBuf[1]; - catSect = sctBuf[2]; - iterations++; // watch for infinite loops - } - if (iterations >= kMaxCatalogIterations) { - dierr = kDIErrDirectoryLoop; - goto bail; - } - -bail_ok: - LOGI(" OzDOS foundGood=%d off=%d swap=%d", foundGood, sectorOffset, - imageOrder); - /* foundGood hits 3 even when swap is wrong */ - if (foundGood > 4) - dierr = kDIErrNone; - else - dierr = kDIErrFilesystemNotFound; - -bail: - return dierr; -} - -/* - * Test both of the DOS partitions. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder) -{ - DIError dierr; - - LOGI(" OzDOS checking first half (swap=%d)", imageOrder); - dierr = TestImageHalf(pImg, 0, imageOrder); - if (dierr != kDIErrNone) - return dierr; - - LOGI(" OzDOS checking second half (swap=%d)", imageOrder); - dierr = TestImageHalf(pImg, 1, imageOrder); - if (dierr != kDIErrNone) - return dierr; - - return kDIErrNone; -} - -/* - * Test to see if the image is a OzDOS volume. - */ -/*static*/ DIError DiskFSOzDOS::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - /* only on 800K disks (at the least, insist on numTracks being even) */ - if (pImg->GetNumBlocks() != kExpectedNumBlocks) - return kDIErrFilesystemNotFound; - - /* if a value is specified, try that first -- useful for OverrideFormat */ - if (*pOrder != DiskImg::kSectorOrderUnknown) { - if (TestImage(pImg, *pOrder) == kDIErrNone) { - LOGI(" OzDOS accepted FirstTry value"); - return kDIErrNone; - } - } - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i]) == kDIErrNone) { - *pOrder = ordering[i]; - *pFormat = DiskImg::kFormatOzDOS; - return kDIErrNone; - } - } - - LOGI(" OzDOS didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -#if 0 -/* - * Test to see if the image is a 'wide' (32-sector) DOS3.3 volume, i.e. - * half of a OzDOS volume. - */ -/*static*/ DIError DiskFS::TestOzWideDOS33(const DiskImg* pImg, - DiskImg::SectorOrder* pOrder) -{ - DIError dierr = kDIErrNone; - - /* only on 400K disks (at the least, insist on numTracks being even) */ - if (pImg->GetNumBlocks() != kExpectedNumBlocks/2) - return kDIErrFilesystemNotFound; - - /* if a value is specified, try that first -- useful for OverrideFormat */ - if (*pOrder != DiskImg::kSectorOrderUnknown) { - if (TestImageHalf(pImg, 0, *pOrder) == kDIErrNone) { - LOGI(" WideDOS accepted FirstTry value"); - return kDIErrNone; - } - } - - if (TestImageHalf(pImg, 0, DiskImg::kSectorOrderDOS) == kDIErrNone) { - *pOrder = DiskImg::kSectorOrderDOS; - } else if (TestImageHalf(pImg, 0, DiskImg::kSectorOrderProDOS) == kDIErrNone) { - *pOrder = DiskImg::kSectorOrderProDOS; - } else if (TestImageHalf(pImg, 0, DiskImg::kSectorOrderPhysical) == kDIErrNone) { - *pOrder = DiskImg::kSectorOrderPhysical; - } else { - LOGI(" FS didn't find valid 'wide' DOS3.3"); - return kDIErrFilesystemNotFound; - } - - return kDIErrNone; -} -#endif - -/* - * Set up our sub-volumes. - */ -DIError DiskFSOzDOS::Initialize(void) -{ - DIError dierr = kDIErrNone; - - if (fScanForSubVolumes != kScanSubDisabled) { - dierr = OpenSubVolume(0); - if (dierr != kDIErrNone) - return dierr; - - dierr = OpenSubVolume(1); - if (dierr != kDIErrNone) - return dierr; - } else { - LOGI(" OzDOS not scanning for sub-volumes"); - } - - SetVolumeUsageMap(); - - return kDIErrNone; -} - -/* - * Open up one of the DOS 3.3 sub-volumes. - */ -DIError DiskFSOzDOS::OpenSubVolume(int idx) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - // open the full 800K; SetPairedSectors cuts it in half - dierr = pNewImg->OpenImage(fpImg, 0, 0, - 2 * kExpectedTracks * kExpectedSectors); - if (dierr != kDIErrNone) { - LOGI(" OzSub: OpenImage(%d,0,%d) failed (err=%d)", - 0, 2 * kExpectedTracks * kExpectedSectors, dierr); - goto bail; - } - - assert(idx == 0 || idx == 1); - pNewImg->SetPairedSectors(true, 1-idx); - - LOGI(" OzSub: testing for recognizable volume in idx=%d", idx); - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" OzSub: analysis failed (err=%d)", dierr); - goto bail; - } - - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" OzSub: unable to identify filesystem"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* open a DiskFS for the sub-image */ - LOGI(" UNISub %d succeeded!", idx); - pNewFS = pNewImg->OpenAppropriateDiskFS(); - if (pNewFS == NULL) { - LOGI(" OzSub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* load the files from the sub-image */ - dierr = pNewFS->Initialize(pNewImg, kInitFull); - if (dierr != kDIErrNone) { - LOGE(" OzSub: error %d reading list of files from disk", dierr); - goto bail; - } - - /* if this really is DOS 3.3, override the "volume name" */ - if (pNewImg->GetFSFormat() == DiskImg::kFormatDOS33) { - DiskFSDOS33* pDOS = (DiskFSDOS33*) pNewFS; /* eek, a downcast */ - pDOS->SetDiskVolumeNum(idx+1); - } - - /* - * Success, add it to the sub-volume list. - */ - AddSubVolumeToList(pNewImg, pNewFS); - -bail: - if (dierr != kDIErrNone) { - delete pNewFS; - delete pNewImg; - } - return dierr; -} diff --git a/ciderpress/diskimg/Pascal.cpp b/ciderpress/diskimg/Pascal.cpp deleted file mode 100644 index 4d615e7..0000000 --- a/ciderpress/diskimg/Pascal.cpp +++ /dev/null @@ -1,1863 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskFSPascal class. - * - * Currently each file may only be open once. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSPascal - * =========================================================================== - */ - -const int kBlkSize = 512; -const int kVolHeaderBlock = 2; // first directory block -//const int kMaxCatalogIterations = 64; // should be short, linear catalog -const int kHugeDir = 32; -static const char* kInvalidNameChars = "$=?,[#:"; - - -/* - * See if this looks like a Pascal volume. - * - * We test a few fields in the volume directory for validity. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[512]; - uint8_t volName[DiskFSPascal::kMaxVolumeName+1]; - - dierr = pImg->ReadBlockSwapped(kVolHeaderBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - - if (!(blkBuf[0x00] == 0 && blkBuf[0x01] == 0) || - !(blkBuf[0x04] == 0 && blkBuf[0x05] == 0) || - !(blkBuf[0x06] > 0 && blkBuf[0x06] <= DiskFSPascal::kMaxVolumeName) || - 0) - { - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* volume name length is good, check the name itself */ - /* (this may be overly restrictive, but it's probably good to be) */ - memset(volName, 0, sizeof(volName)); - memcpy(volName, &blkBuf[0x07], blkBuf[0x06]); - if (!DiskFSPascal::IsValidVolumeName((const char*) volName)) - return kDIErrFilesystemNotFound; - -bail: - return dierr; -} - -/* - * Test to see if the image is a Pascal disk. - */ -/*static*/ DIError DiskFSPascal::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i]) == kDIErrNone) { - *pOrder = ordering[i]; - *pFormat = DiskImg::kFormatPascal; - return kDIErrNone; - } - } - - LOGI(" Pascal didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSPascal::Initialize(void) -{ - DIError dierr = kDIErrNone; - - fDiskIsGood = false; // hosed until proven innocent - fEarlyDamage = false; - - fVolumeUsage.Create(fpImg->GetNumBlocks()); - - dierr = LoadVolHeader(); - if (dierr != kDIErrNone) - goto bail; - DumpVolHeader(); - - dierr = ProcessCatalog(); - if (dierr != kDIErrNone) - goto bail; - - dierr = ScanFileUsage(); - if (dierr != kDIErrNone) { - /* this might not be fatal; just means that *some* files are bad */ - goto bail; - } - - fDiskIsGood = CheckDiskIsGood(); - - fVolumeUsage.Dump(); - - //A2File* pFile; - //pFile = GetNextFile(NULL); - //while (pFile != NULL) { - // pFile->Dump(); - // pFile = GetNextFile(pFile); - //} - -bail: - return dierr; -} - -/* - * Read some interesting fields from the volume header. - */ -DIError DiskFSPascal::LoadVolHeader(void) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - int nameLen, maxFiles; - - dierr = fpImg->ReadBlock(kVolHeaderBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - /* vol header is same size as dir entry, but different layout */ - fStartBlock = GetShortLE(&blkBuf[0x00]); - assert(fStartBlock == 0); // verified in "TestImage" - fNextBlock = GetShortLE(&blkBuf[0x02]); - assert(GetShortLE(&blkBuf[0x04]) == 0); // type - nameLen = blkBuf[0x06] & 0x07; - memcpy(fVolumeName, &blkBuf[0x07], nameLen); - fVolumeName[nameLen] = '\0'; - fTotalBlocks = GetShortLE(&blkBuf[0x0e]); - fNumFiles = GetShortLE(&blkBuf[0x10]); - fAccessWhen = GetShortLE(&blkBuf[0x12]); // time of last access - fDateSetWhen = GetShortLE(&blkBuf[0x14]); // most recent date set - fStuff1 = GetShortLE(&blkBuf[0x16]); // filler - fStuff2 = GetShortLE(&blkBuf[0x18]); // filler - - if (fTotalBlocks != fpImg->GetNumBlocks()) { - // saw this most recently on a 40-track .APP image; not a problem - LOGI(" Pascal WARNING: total (%u) != img (%ld)", - fTotalBlocks, fpImg->GetNumBlocks()); - } - - /* - * Sanity checks. - */ - if (fNextBlock > 34) { - // directory really shouldn't be more than 6; I'm being generous - fpImg->AddNote(DiskImg::kNoteWarning, - "Pascal directory is too big (%d blocks); trimming.", - fNextBlock - fStartBlock); - } - - /* max #of file entries, including the vol dir header */ - maxFiles = ((fNextBlock - kVolHeaderBlock) * kBlkSize) / kDirectoryEntryLen; - if (fNumFiles > maxFiles-1) { - fpImg->AddNote(DiskImg::kNoteWarning, - "Pascal fNumFiles (%d) exceeds max files (%d); trimming.\n", - fNumFiles, maxFiles-1); - fEarlyDamage = true; - } - - SetVolumeID(); - -bail: - return dierr; -} - -/* - * Set the volume ID field. - */ -void DiskFSPascal::SetVolumeID(void) -{ - sprintf(fVolumeID, "Pascal %s:", fVolumeName); -} - -/* - * Dump what we pulled out of the volume header. - */ -void DiskFSPascal::DumpVolHeader(void) -{ - time_t access, dateSet; - - LOGI(" Pascal volume header for '%s'", fVolumeName); - LOGI(" startBlock=%d nextBlock=%d", - fStartBlock, fNextBlock); - LOGI(" totalBlocks=%d numFiles=%d access=0x%04x dateSet=0x%04x", - fTotalBlocks, fNumFiles, fAccessWhen, fDateSetWhen); - - access = A2FilePascal::ConvertPascalDate(fAccessWhen); - dateSet = A2FilePascal::ConvertPascalDate(fDateSetWhen); - LOGI(" -->access %.24s", ctime(&access)); - LOGI(" -->dateSet %.24s", ctime(&dateSet)); - - //LOGI("Unconvert access=0x%04x dateSet=0x%04x", - // A2FilePascal::ConvertPascalDate(access), - // A2FilePascal::ConvertPascalDate(dateSet)); -} - - -/* - * Read the catalog from the disk. - * - * No distinction is made for block boundaries, so we want to slurp the - * entire thing into memory. - * - * Sets "fDirectory". - */ -DIError DiskFSPascal::LoadCatalog(void) -{ - DIError dierr = kDIErrNone; - uint8_t* dirPtr; - int block, numBlocks; - - assert(fDirectory == NULL); - - numBlocks = fNextBlock - kVolHeaderBlock; - if (numBlocks <= 0 || numBlocks > kHugeDir) { - dierr = kDIErrBadDiskImage; - goto bail; - } - - fDirectory = new uint8_t[kBlkSize * numBlocks]; - if (fDirectory == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - block = kVolHeaderBlock; - dirPtr = fDirectory; - while (numBlocks--) { - dierr = fpImg->ReadBlock(block, dirPtr); - if (dierr != kDIErrNone) - goto bail; - - block++; - dirPtr += kBlkSize; - } - -bail: - if (dierr != kDIErrNone) { - delete[] fDirectory; - fDirectory = NULL; - } - return dierr; -} - -/* - * Write our copy of the catalog back out to disk. - */ -DIError DiskFSPascal::SaveCatalog(void) -{ - DIError dierr = kDIErrNone; - uint8_t* dirPtr; - int block, numBlocks; - - assert(fDirectory != NULL); - - numBlocks = fNextBlock - kVolHeaderBlock; - block = kVolHeaderBlock; - dirPtr = fDirectory; - while (numBlocks--) { - dierr = fpImg->WriteBlock(block, dirPtr); - if (dierr != kDIErrNone) - goto bail; - - block++; - dirPtr += kBlkSize; - } - -bail: - return dierr; -} - -/* - * Free the catalog storage. - */ -void DiskFSPascal::FreeCatalog(void) -{ - delete[] fDirectory; - fDirectory = NULL; -} - - -/* - * Process the catalog into A2File structures. - */ -DIError DiskFSPascal::ProcessCatalog(void) -{ - DIError dierr = kDIErrNone; - int i, nameLen; - A2FilePascal* pFile; - const uint8_t* dirPtr; - uint16_t prevNextBlock = fNextBlock; - - dierr = LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - dirPtr = fDirectory + kDirectoryEntryLen; // skip vol dir entry - for (i = 0; i < fNumFiles; i++) { - pFile = new A2FilePascal(this); - - pFile->fStartBlock = GetShortLE(&dirPtr[0x00]); - pFile->fNextBlock = GetShortLE(&dirPtr[0x02]); - pFile->fFileType = (A2FilePascal::FileType) GetShortLE(&dirPtr[0x04]); - nameLen = dirPtr[0x06] & 0x0f; - memcpy(pFile->fFileName, &dirPtr[0x07], nameLen); - pFile->fFileName[nameLen] = '\0'; - pFile->fBytesRemaining = GetShortLE(&dirPtr[0x16]); - pFile->fModWhen = GetShortLE(&dirPtr[0x18]); - - /* check bytesRem before setting length field */ - if (pFile->fBytesRemaining > kBlkSize) { - LOGI(" Pascal found strange bytesRem %u on '%s', trimming", - pFile->fBytesRemaining, pFile->fFileName); - pFile->fBytesRemaining = kBlkSize; - pFile->SetQuality(A2File::kQualitySuspicious); - } - - pFile->fLength = pFile->fBytesRemaining + - (pFile->fNextBlock - pFile->fStartBlock -1) * kBlkSize; - - /* - * Check values. - */ - if (pFile->fStartBlock == pFile->fNextBlock) { - LOGI(" Pascal found zero-block file '%s'", pFile->fFileName); - pFile->SetQuality(A2File::kQualityDamaged); - } - if (pFile->fStartBlock < prevNextBlock) { - LOGI(" Pascal start of '%s' (%d) overlaps previous end (%d)", - pFile->fFileName, pFile->fStartBlock, prevNextBlock); - pFile->SetQuality(A2File::kQualityDamaged); - } - - if (pFile->fNextBlock > fpImg->GetNumBlocks()) { - LOGI(" Pascal invalid 'next' block %d (max %ld) '%s'", - pFile->fNextBlock, fpImg->GetNumBlocks(), pFile->fFileName); - pFile->fStartBlock = pFile->fNextBlock = 0; - pFile->fLength = 0; - pFile->SetQuality(A2File::kQualityDamaged); - } else if (pFile->fNextBlock > fTotalBlocks) { - LOGI(" Pascal 'next' block %d exceeds max (%d) '%s'", - pFile->fNextBlock, fTotalBlocks, pFile->fFileName); - pFile->SetQuality(A2File::kQualitySuspicious); - } - - //pFile->Dump(); - AddFileToList(pFile); - - dirPtr += kDirectoryEntryLen; - prevNextBlock = pFile->fNextBlock; - } - -bail: - FreeCatalog(); - return dierr; -} - - -/* - * Create the volume usage map. Since UCSD Pascal volumes have neither - * in-use maps nor index blocks, this is pretty straightforward. - */ -DIError DiskFSPascal::ScanFileUsage(void) -{ - int block; - - /* start with the boot blocks */ - SetBlockUsage(0, VolumeUsage::kChunkPurposeSystem); - SetBlockUsage(1, VolumeUsage::kChunkPurposeSystem); - - for (block = kVolHeaderBlock; block < fNextBlock; block++) { - SetBlockUsage(block, VolumeUsage::kChunkPurposeVolumeDir); - } - - A2FilePascal* pFile; - pFile = (A2FilePascal*) GetNextFile(NULL); - while (pFile != NULL) { - for (block = pFile->fStartBlock; block < pFile->fNextBlock; block++) - SetBlockUsage(block, VolumeUsage::kChunkPurposeUserData); - - pFile = (A2FilePascal*) GetNextFile(pFile); - } - - return kDIErrNone; -} - -/* - * Update an entry in the volume usage map. - */ -void DiskFSPascal::SetBlockUsage(long block, VolumeUsage::ChunkPurpose purpose) -{ - VolumeUsage::ChunkState cstate; - - fVolumeUsage.GetChunkState(block, &cstate); - if (cstate.isUsed) { - cstate.purpose = VolumeUsage::kChunkPurposeConflict; - LOGI(" Pascal conflicting uses for bl=%ld", block); - } else { - cstate.isUsed = true; - cstate.isMarkedUsed = true; - cstate.purpose = purpose; - } - fVolumeUsage.SetChunkState(block, &cstate); -} - - -/* - * Test a string for validity as a Pascal volume name. - * - * Volume names can only be 7 characters long, but otherwise obey the same - * rules as file names. - */ -/*static*/ bool DiskFSPascal::IsValidVolumeName(const char* name) -{ - if (name == NULL) { - assert(false); - return false; - } - if (strlen(name) > kMaxVolumeName) - return false; - return IsValidFileName(name); -} - -/* - * Test a string for validity as a Pascal file name. - * - * Filenames can be up to 15 characters long. All characters are valid. - * However, the system filer gets bent out of shape if you use spaces, - * control characters, any of the wildcards ($=?), or filer meta-characters - * (,[#:). It also converts all alpha characters to upper case, but we can - * take care of that later. - */ -/*static*/ bool DiskFSPascal::IsValidFileName(const char* name) -{ - assert(name != NULL); - - if (name[0] == '\0') - return false; - if (strlen(name) > A2FilePascal::kMaxFileName) - return false; - - /* must be A-Z 0-9 '.' */ - while (*name != '\0') { - if (*name <= 0x20 || *name >= 0x7f) // no space, del, or ctrl - return false; - //if (*name >= 'a' && *name <= 'z') // no lower case - // return false; - if (strchr(kInvalidNameChars, *name) != NULL) // filer metacharacters - return false; - - name++; - } - - return true; -} - -/* - * Put a Pascal filesystem image on the specified DiskImg. - */ -DIError DiskFSPascal::Format(DiskImg* pDiskImg, const char* volName) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - long formatBlocks; - - if (!IsValidVolumeName(volName)) - return kDIErrInvalidArg; - - /* set fpImg so calls that rely on it will work; we un-set it later */ - assert(fpImg == NULL); - SetDiskImg(pDiskImg); - - LOGI(" Pascal formatting disk image"); - - /* write ProDOS-style blocks */ - dierr = fpImg->OverrideFormat(fpImg->GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, fpImg->GetSectorOrder()); - if (dierr != kDIErrNone) - goto bail; - - formatBlocks = pDiskImg->GetNumBlocks(); - if (formatBlocks != 280 && formatBlocks != 1600) { - LOGI(" Pascal: rejecting format req blocks=%ld", formatBlocks); - assert(false); - return kDIErrInvalidArg; - } - - /* - * We should now zero out the disk blocks, but this is done automatically - * on new disk images, so there's no need to do it here. - */ -// dierr = fpImg->ZeroImage(); - LOGI(" Pascal (not zeroing blocks)"); - - /* - * Start by writing blocks 0 and 1 (the boot blocks). The file - * APPLE3:FORMATTER.DATA holds images for 3.5" and 5.25" disks. - */ - dierr = WriteBootBlocks(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Write the disk volume entry. - */ - memset(blkBuf, 0, sizeof(blkBuf)); - PutShortLE(&blkBuf[0x00], 0); // start block - PutShortLE(&blkBuf[0x02], 6); // next block - PutShortLE(&blkBuf[0x04], 0); // "file" type - blkBuf[0x06] = strlen(volName); - memcpy(&blkBuf[0x07], volName, strlen(volName)); - PutShortLE(&blkBuf[0x0e], (uint16_t) pDiskImg->GetNumBlocks()); - PutShortLE(&blkBuf[0x10], 0); // num files - PutShortLE(&blkBuf[0x12], 0); // last access date - PutShortLE(&blkBuf[0x14], 0xa87b); // last date set (Nov 7 1984) - dierr = fpImg->WriteBlock(kVolHeaderBlock, blkBuf); - if (dierr != kDIErrNone) { - LOGI(" Format: block %d write failed (err=%d)", - kVolHeaderBlock, dierr); - goto bail; - } - - /* check our work, and set some object fields, by reading what we wrote */ - dierr = LoadVolHeader(); - if (dierr != kDIErrNone) { - LOGI(" GLITCH: couldn't read header we just wrote (err=%d)", dierr); - goto bail; - } - - -bail: - SetDiskImg(NULL); // shouldn't really be set by us - return dierr; -} - -/* - * Blocks 0 and 1 of a 5.25" bootable Pascal disk, formatted by - * APPLE3:FORMATTER from Pascal v1.3. - */ -const uint8_t gPascal525Block0[] = { - 0x01, 0xe0, 0x70, 0xb0, 0x04, 0xe0, 0x40, 0xb0, 0x39, 0xbd, 0x88, 0xc0, - 0x20, 0x20, 0x08, 0xa2, 0x00, 0xbd, 0x25, 0x08, 0x09, 0x80, 0x20, 0xfd, - 0xfb, 0xe8, 0xe0, 0x1d, 0xd0, 0xf3, 0xf0, 0xfe, 0xa9, 0x0a, 0x4c, 0x24, - 0xfc, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x42, 0x4f, 0x4f, 0x54, 0x20, 0x46, - 0x52, 0x4f, 0x4d, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x20, 0x34, 0x2c, 0x20, - 0x35, 0x20, 0x4f, 0x52, 0x20, 0x36, 0x8a, 0x85, 0x43, 0x4a, 0x4a, 0x4a, - 0x4a, 0x09, 0xc0, 0x85, 0x0d, 0xa9, 0x5c, 0x85, 0x0c, 0xad, 0x00, 0x08, - 0xc9, 0x06, 0xb0, 0x0a, 0x69, 0x02, 0x8d, 0x00, 0x08, 0xe6, 0x3d, 0x6c, - 0x0c, 0x00, 0xa9, 0x00, 0x8d, 0x78, 0x04, 0xa9, 0x0a, 0x85, 0x0e, 0xa9, - 0x80, 0x85, 0x3f, 0x85, 0x11, 0xa9, 0x00, 0x85, 0x10, 0xa9, 0x08, 0x85, - 0x02, 0xa9, 0x02, 0x85, 0x0f, 0xa9, 0x00, 0x20, 0x4c, 0x09, 0xa2, 0x4e, - 0xa0, 0x06, 0xb1, 0x10, 0xd9, 0x39, 0x09, 0xf0, 0x2b, 0x18, 0xa5, 0x10, - 0x69, 0x1a, 0x85, 0x10, 0x90, 0x02, 0xe6, 0x11, 0xca, 0xd0, 0xe9, 0xc6, - 0x0e, 0xd0, 0xcc, 0x20, 0x20, 0x08, 0xa6, 0x43, 0xbd, 0x88, 0xc0, 0xa2, - 0x00, 0xbd, 0x2a, 0x09, 0x09, 0x80, 0x20, 0xfd, 0xfb, 0xe8, 0xe0, 0x15, - 0xd0, 0xf3, 0xf0, 0xfe, 0xc8, 0xc0, 0x13, 0xd0, 0xc9, 0xad, 0x81, 0xc0, - 0xad, 0x81, 0xc0, 0xa9, 0xd0, 0x85, 0x3f, 0xa9, 0x30, 0x85, 0x02, 0xa0, - 0x00, 0xb1, 0x10, 0x85, 0x0f, 0xc8, 0xb1, 0x10, 0x20, 0x4c, 0x09, 0xad, - 0x89, 0xc0, 0xa9, 0xd0, 0x85, 0x3f, 0xa9, 0x10, 0x85, 0x02, 0xa0, 0x00, - 0xb1, 0x10, 0x18, 0x69, 0x18, 0x85, 0x0f, 0xc8, 0xb1, 0x10, 0x69, 0x00, - 0x20, 0x4c, 0x09, 0xa5, 0x43, 0xc9, 0x50, 0xf0, 0x08, 0x90, 0x1a, 0xad, - 0x80, 0xc0, 0x6c, 0xf8, 0xff, 0xa2, 0x00, 0x8e, 0xc4, 0xfe, 0xe8, 0x8e, - 0xc6, 0xfe, 0xe8, 0x8e, 0xb6, 0xfe, 0xe8, 0x8e, 0xb8, 0xfe, 0x4c, 0xfb, - 0x08, 0xa2, 0x00, 0x8e, 0xc0, 0xfe, 0xe8, 0x8e, 0xc2, 0xfe, 0xa2, 0x04, - 0x8e, 0xb6, 0xfe, 0xe8, 0x8e, 0xb8, 0xfe, 0x4c, 0xfb, 0x08, 0x4e, 0x4f, - 0x20, 0x46, 0x49, 0x4c, 0x45, 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, - 0x2e, 0x41, 0x50, 0x50, 0x4c, 0x45, 0x20, 0x0c, 0x53, 0x59, 0x53, 0x54, - 0x45, 0x4d, 0x2e, 0x41, 0x50, 0x50, 0x4c, 0x45, 0x4a, 0x08, 0xa5, 0x0f, - 0x29, 0x07, 0x0a, 0x85, 0x00, 0xa5, 0x0f, 0x28, 0x6a, 0x4a, 0x4a, 0x85, - 0xf0, 0xa9, 0x00, 0x85, 0x3e, 0x4c, 0x78, 0x09, 0xa6, 0x02, 0xf0, 0x22, - 0xc6, 0x02, 0xe6, 0x3f, 0xe6, 0x00, 0xa5, 0x00, 0x49, 0x10, 0xd0, 0x04, - 0x85, 0x00, 0xe6, 0xf0, 0xa4, 0x00, 0xb9, 0x8b, 0x09, 0x85, 0xf1, 0xa2, - 0x00, 0xe4, 0x02, 0xf0, 0x05, 0x20, 0x9b, 0x09, 0x90, 0xda, 0x60, 0x00, - 0x02, 0x04, 0x06, 0x08, 0x0a, 0x0c, 0x0e, 0x01, 0x03, 0x05, 0x07, 0x09, - 0x0b, 0x0d, 0x0f, 0xa6, 0x43, 0xa5, 0xf0, 0x0a, 0x0e, 0x78, 0x04, 0x20, - 0xa3, 0x0a, 0x4e, 0x78, 0x04, 0x20, 0x47, 0x0a, 0xb0, 0xfb, 0xa4, 0x2e, - 0x8c, 0x78, 0x04, 0xc4, 0xf0, 0xd0, 0xe6, 0xa5, 0x2d, 0xc5, 0xf1, 0xd0, - 0xec, 0x20, 0xdf, 0x09, 0xb0, 0xe7, 0x20, 0xc7, 0x09, 0x18, 0x60, 0xa0, - 0x00, 0xa2, 0x56, 0xca, 0x30, 0xfb, 0xb9, 0x00, 0x02, 0x5e, 0x00, 0x03, - 0x2a, 0x5e, 0x00, 0x03, 0x2a, 0x91, 0x3e, 0xc8, 0xd0, 0xed, 0x60, 0xa0, - 0x20, 0x88, 0xf0, 0x61, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x49, 0xd5, 0xd0, - 0xf4, 0xea, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xaa, 0xd0, 0xf2, 0xa0, - 0x56, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xad -}; -const uint8_t gPascal525Block1[] = { - 0xd0, 0xe7, 0xa9, 0x00, 0x88, 0x84, 0x26, 0xbc, 0x8c, 0xc0, 0x10, 0xfb, - 0x59, 0xd6, 0x02, 0xa4, 0x26, 0x99, 0x00, 0x03, 0xd0, 0xee, 0x84, 0x26, - 0xbc, 0x8c, 0xc0, 0x10, 0xfb, 0x59, 0xd6, 0x02, 0xa4, 0x26, 0x99, 0x00, - 0x02, 0xc8, 0xd0, 0xee, 0xbc, 0x8c, 0xc0, 0x10, 0xfb, 0xd9, 0xd6, 0x02, - 0xd0, 0x13, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xde, 0xd0, 0x0a, 0xea, - 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0xc9, 0xaa, 0xf0, 0x5c, 0x38, 0x60, 0xa0, - 0xfc, 0x84, 0x26, 0xc8, 0xd0, 0x04, 0xe6, 0x26, 0xf0, 0xf3, 0xbd, 0x8c, - 0xc0, 0x10, 0xfb, 0xc9, 0xd5, 0xd0, 0xf0, 0xea, 0xbd, 0x8c, 0xc0, 0x10, - 0xfb, 0xc9, 0xaa, 0xd0, 0xf2, 0xa0, 0x03, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, - 0xc9, 0x96, 0xd0, 0xe7, 0xa9, 0x00, 0x85, 0x27, 0xbd, 0x8c, 0xc0, 0x10, - 0xfb, 0x2a, 0x85, 0x26, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x25, 0x26, 0x99, - 0x2c, 0x00, 0x45, 0x27, 0x88, 0x10, 0xe7, 0xa8, 0xd0, 0xb7, 0xbd, 0x8c, - 0xc0, 0x10, 0xfb, 0xc9, 0xde, 0xd0, 0xae, 0xea, 0xbd, 0x8c, 0xc0, 0x10, - 0xfb, 0xc9, 0xaa, 0xd0, 0xa4, 0x18, 0x60, 0x86, 0x2b, 0x85, 0x2a, 0xcd, - 0x78, 0x04, 0xf0, 0x48, 0xa9, 0x00, 0x85, 0x26, 0xad, 0x78, 0x04, 0x85, - 0x27, 0x38, 0xe5, 0x2a, 0xf0, 0x37, 0xb0, 0x07, 0x49, 0xff, 0xee, 0x78, - 0x04, 0x90, 0x05, 0x69, 0xfe, 0xce, 0x78, 0x04, 0xc5, 0x26, 0x90, 0x02, - 0xa5, 0x26, 0xc9, 0x0c, 0xb0, 0x01, 0xa8, 0x20, 0xf4, 0x0a, 0xb9, 0x15, - 0x0b, 0x20, 0x04, 0x0b, 0xa5, 0x27, 0x29, 0x03, 0x0a, 0x05, 0x2b, 0xaa, - 0xbd, 0x80, 0xc0, 0xb9, 0x21, 0x0b, 0x20, 0x04, 0x0b, 0xe6, 0x26, 0xd0, - 0xbf, 0x20, 0x04, 0x0b, 0xad, 0x78, 0x04, 0x29, 0x03, 0x0a, 0x05, 0x2b, - 0xaa, 0xbd, 0x81, 0xc0, 0xa6, 0x2b, 0x60, 0xea, 0xa2, 0x11, 0xca, 0xd0, - 0xfd, 0xe6, 0x46, 0xd0, 0x02, 0xe6, 0x47, 0x38, 0xe9, 0x01, 0xd0, 0xf0, - 0x60, 0x01, 0x30, 0x28, 0x24, 0x20, 0x1e, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x70, 0x2c, 0x26, 0x22, 0x1f, 0x1e, 0x1d, 0x1c, 0x1c, 0x1c, 0x1c, - 0x1c, 0x20, 0x43, 0x4f, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, - 0x41, 0x50, 0x50, 0x4c, 0x45, 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, - 0x45, 0x52, 0x2c, 0x20, 0x49, 0x4e, 0x43, 0x2e, 0x2c, 0x20, 0x31, 0x39, - 0x38, 0x34, 0x2c, 0x20, 0x31, 0x39, 0x38, 0x35, 0x20, 0x43, 0x2e, 0x4c, - 0x45, 0x55, 0x4e, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x03, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbb -}; - -/* - * Block 0 of a 3.5" bootable Pascal disk, formatted by - * APPLE3:FORMATTER from Pascal v1.3. Block 1 is zeroed out. - */ -const uint8_t gPascal35Block0[] = { - 0x01, 0xe0, 0x70, 0xb0, 0x04, 0xe0, 0x40, 0xb0, 0x39, 0xbd, 0x88, 0xc0, - 0x20, 0x20, 0x08, 0xa2, 0x00, 0xbd, 0x25, 0x08, 0x09, 0x80, 0x20, 0xfd, - 0xfb, 0xe8, 0xe0, 0x1d, 0xd0, 0xf3, 0xf0, 0xfe, 0xa9, 0x0a, 0x4c, 0x24, - 0xfc, 0x4d, 0x55, 0x53, 0x54, 0x20, 0x42, 0x4f, 0x4f, 0x54, 0x20, 0x46, - 0x52, 0x4f, 0x4d, 0x20, 0x53, 0x4c, 0x4f, 0x54, 0x20, 0x34, 0x2c, 0x20, - 0x35, 0x20, 0x4f, 0x52, 0x20, 0x36, 0x8a, 0x85, 0x43, 0x4a, 0x4a, 0x4a, - 0x4a, 0x09, 0xc0, 0x85, 0x15, 0x8d, 0x5d, 0x09, 0xa9, 0x00, 0x8d, 0x78, - 0x04, 0x85, 0x14, 0xa9, 0x0a, 0x85, 0x0e, 0xa9, 0x80, 0x85, 0x13, 0x85, - 0x11, 0xa9, 0x00, 0x85, 0x10, 0x85, 0x0b, 0xa9, 0x02, 0x85, 0x0a, 0xa9, - 0x04, 0x85, 0x02, 0x20, 0x40, 0x09, 0xa2, 0x4e, 0xa0, 0x06, 0xb1, 0x10, - 0xd9, 0x2d, 0x09, 0xf0, 0x2b, 0x18, 0xa5, 0x10, 0x69, 0x1a, 0x85, 0x10, - 0x90, 0x02, 0xe6, 0x11, 0xca, 0xd0, 0xe9, 0xc6, 0x0e, 0xd0, 0xcc, 0x20, - 0x20, 0x08, 0xa6, 0x43, 0xbd, 0x88, 0xc0, 0xa2, 0x00, 0xbd, 0x1e, 0x09, - 0x09, 0x80, 0x20, 0xfd, 0xfb, 0xe8, 0xe0, 0x15, 0xd0, 0xf3, 0xf0, 0xfe, - 0xc8, 0xc0, 0x13, 0xd0, 0xc9, 0xad, 0x83, 0xc0, 0xad, 0x83, 0xc0, 0xa9, - 0xd0, 0x85, 0x13, 0xa0, 0x00, 0xb1, 0x10, 0x85, 0x0a, 0xc8, 0xb1, 0x10, - 0x85, 0x0b, 0xa9, 0x18, 0x85, 0x02, 0x20, 0x40, 0x09, 0xad, 0x8b, 0xc0, - 0xa9, 0xd0, 0x85, 0x13, 0xa0, 0x00, 0xb1, 0x10, 0x18, 0x69, 0x18, 0x85, - 0x0a, 0xc8, 0xb1, 0x10, 0x69, 0x00, 0x85, 0x0b, 0xa9, 0x08, 0x85, 0x02, - 0x20, 0x40, 0x09, 0xa5, 0x43, 0xc9, 0x50, 0xf0, 0x08, 0x90, 0x1a, 0xad, - 0x80, 0xc0, 0x6c, 0xf8, 0xff, 0xa2, 0x00, 0x8e, 0xc4, 0xfe, 0xe8, 0x8e, - 0xc6, 0xfe, 0xe8, 0x8e, 0xb6, 0xfe, 0xe8, 0x8e, 0xb8, 0xfe, 0x4c, 0xef, - 0x08, 0xa2, 0x00, 0x8e, 0xc0, 0xfe, 0xe8, 0x8e, 0xc2, 0xfe, 0xa2, 0x04, - 0x8e, 0xb6, 0xfe, 0xe8, 0x8e, 0xb8, 0xfe, 0x4c, 0xef, 0x08, 0x4e, 0x4f, - 0x20, 0x46, 0x49, 0x4c, 0x45, 0x20, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, - 0x2e, 0x41, 0x50, 0x50, 0x4c, 0x45, 0x20, 0x0c, 0x53, 0x59, 0x53, 0x54, - 0x45, 0x4d, 0x2e, 0x41, 0x50, 0x50, 0x4c, 0x45, 0xa9, 0x01, 0x85, 0x42, - 0xa0, 0xff, 0xb1, 0x14, 0x8d, 0x5c, 0x09, 0xa9, 0x00, 0x85, 0x44, 0xa5, - 0x13, 0x85, 0x45, 0xa5, 0x0a, 0x85, 0x46, 0xa5, 0x0b, 0x85, 0x47, 0x20, - 0x00, 0x00, 0x90, 0x03, 0x4c, 0x5b, 0x08, 0xc6, 0x02, 0xf0, 0x0c, 0xe6, - 0x13, 0xe6, 0x13, 0xe6, 0x0a, 0xd0, 0xdc, 0xe6, 0x0b, 0xd0, 0xd8, 0x60, - 0x20, 0x43, 0x4f, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x41, - 0x50, 0x50, 0x4c, 0x45, 0x20, 0x43, 0x4f, 0x4d, 0x50, 0x55, 0x54, 0x45, - 0x52, 0x2c, 0x20, 0x49, 0x4e, 0x43, 0x2e, 0x2c, 0x20, 0x31, 0x39, 0x38, - 0x34, 0x2c, 0x20, 0x31, 0x39, 0x38, 0x35, 0x20, 0x43, 0x2e, 0x4c, 0x45, - 0x55, 0x4e, 0x47, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb0, 0x01, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* - * Write the Pascal boot blocks onto the disk image. - */ -DIError DiskFSPascal::WriteBootBlocks(void) -{ - DIError dierr; - uint8_t block0[512]; - uint8_t block1[512]; - bool is525 = false; - - assert(fpImg->GetHasBlocks()); - if (fpImg->GetNumBlocks() == 280) - is525 = true; - else if (fpImg->GetNumBlocks() == 1600) - is525 = false; - else { - LOGI(" Pascal boot blocks for blocks=%ld unknown", - fpImg->GetNumBlocks()); - return kDIErrInternal; - } - - if (is525) { - memcpy(block0, gPascal525Block0, sizeof(block0)); - memcpy(block1, gPascal525Block1, sizeof(block1)); - } else { - memcpy(block0, gPascal35Block0, sizeof(block0)); - memset(block1, 0, sizeof(block1)); - } - - dierr = fpImg->WriteBlock(0, block0); - if (dierr != kDIErrNone) { - LOGI(" WriteBootBlocks: block0 write failed (err=%d)", dierr); - return dierr; - } - dierr = fpImg->WriteBlock(1, block1); - if (dierr != kDIErrNone) { - LOGI(" WriteBootBlocks: block1 write failed (err=%d)", dierr); - return dierr; - } - - return kDIErrNone; -} - -/* - * Scan for damaged files and conflicting file allocation entries. - * - * Appends some entries to the DiskImg notes, so this should only be run - * once per DiskFS. - * - * Returns "true" if disk appears to be perfect, "false" otherwise. - */ -bool DiskFSPascal::CheckDiskIsGood(void) -{ - //DIError dierr; - bool result = true; - - if (fEarlyDamage) - result = false; - - /* (don't need to check to see if the boot blocks or disk catalog are - marked in use -- the directory is defined by the set of blocks - in the volume header) */ - - /* - * Scan for "damaged" or "suspicious" files diagnosed earlier. - */ - bool damaged, suspicious; - ScanForDamagedFiles(&damaged, &suspicious); - - if (damaged) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files are damaged."); - result = false; - } else if (suspicious) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files look suspicious."); - result = false; - } - - return result; -} - -/* - * Run through the list of files and count up the free blocks. - */ -DIError DiskFSPascal::GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const -{ - A2FilePascal* pFile; - long freeBlocks = 0; - uint16_t prevNextBlock = fNextBlock; - - pFile = (A2FilePascal*) GetNextFile(NULL); - while (pFile != NULL) { - freeBlocks += pFile->fStartBlock - prevNextBlock; - prevNextBlock = pFile->fNextBlock; - - pFile = (A2FilePascal*) GetNextFile(pFile); - } - freeBlocks += fTotalBlocks - prevNextBlock; - - *pTotalUnits = fTotalBlocks; - *pFreeUnits = freeBlocks; - *pUnitSize = kBlockSize; - - return kDIErrNone; -} - - -/* - * Normalize a Pascal path. Used when adding files from DiskArchive. - * - * "*pNormalizedBufLen" is used to pass in the length of the buffer and - * pass out the length of the string (should the buffer prove inadequate). - */ -DIError DiskFSPascal::NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) -{ - DIError dierr = kDIErrNone; - char tmpBuf[A2FilePascal::kMaxFileName+1]; - int len; - - DoNormalizePath(path, fssep, tmpBuf); - len = strlen(tmpBuf)+1; - - if (*pNormalizedBufLen < len) - dierr = kDIErrDataOverrun; - else - strcpy(normalizedBuf, tmpBuf); - *pNormalizedBufLen = len; - - return dierr; -} - -/* - * Normalize a Pascal pathname. Lower case becomes upper case, invalid - * characters get stripped. - * - * "outBuf" must be able to hold kMaxFileName+1 characters. - */ -void DiskFSPascal::DoNormalizePath(const char* name, char fssep, char* outBuf) -{ - char* outp = outBuf; - const char* cp; - - /* throw out leading pathname, if any */ - if (fssep != '\0') { - cp = strrchr(name, fssep); - if (cp != NULL) - name = cp+1; - } - - while (*name != '\0' && (outp - outBuf) < A2FilePascal::kMaxFileName) { - if (*name > 0x20 && *name < 0x7f && - strchr(kInvalidNameChars, *name) == NULL) - { - *outp++ = toupper(*name); - } - - name++; - } - - *outp = '\0'; - - if (*outBuf == '\0') { - /* nothing left */ - strcpy(outBuf, "BLANK"); - } -} - - -/* - * Create an empty file. It doesn't look like pascal normally allows you - * to create a zero-block file, so we create a 1-block file and set the - * "data in last block" field to zero. - * - * We don't know how big the file will be, so we can't do a "best fit" - * algorithm for placement. Instead, we just put it in the largest - * available free space. - * - * NOTE: the Pascal system will auto-delete zero-byte files. It expects a - * brand-new 1-block file to have a "bytes remaining" of 512. The files - * we create here are expected to be written to, not used as filler, so - * this behavior is actually a *good* thing. - */ -DIError DiskFSPascal::CreateFile(const CreateParms* pParms, A2File** ppNewFile) -{ - DIError dierr = kDIErrNone; - const bool createUnique = (GetParameter(kParm_CreateUnique) != 0); - char normalName[A2FilePascal::kMaxFileName+1]; - A2FilePascal* pNewFile = NULL; - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - assert(pParms != NULL); - assert(pParms->pathName != NULL); - assert(pParms->storageType == A2FileProDOS::kStorageSeedling); - LOGI(" Pascal ---v--- CreateFile '%s'", pParms->pathName); - - /* compute maxFiles, which includes the vol dir header */ - int maxFiles = - ((fNextBlock - kVolHeaderBlock) * kBlkSize) / kDirectoryEntryLen; - if (fNumFiles >= maxFiles-1) { - LOGI("Pascal volume directory full (%d entries)", fNumFiles); - return kDIErrVolumeDirFull; - } - - *ppNewFile = NULL; - - DoNormalizePath(pParms->pathName, pParms->fssep, normalName); - - /* - * See if the file already exists. - * - * If "create unique" is set, we append digits until the name doesn't - * match any others. The name will be modified in place. - */ - if (createUnique) { - MakeFileNameUnique(normalName); - } else { - if (GetFileByName(normalName) != NULL) { - LOGI(" Pascal create: normalized name '%s' already exists", - normalName); - dierr = kDIErrFileExists; - goto bail; - } - } - - /* - * Find the largest gap in the file space. - * - * We get an index pointer and A2File pointer to the previous entry. If - * the blank space is at the head of the list, prevIdx will be zero and - * pPrevFile will be NULL. - */ - A2FilePascal* pPrevFile; - int prevIdx; - - dierr = FindLargestFreeArea(&prevIdx, &pPrevFile); - if (dierr != kDIErrNone) - goto bail; - assert(prevIdx >= 0); - - /* - * Make a new entry. - */ - time_t now; - pNewFile = new A2FilePascal(this); - if (pNewFile == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - if (pPrevFile == NULL) - pNewFile->fStartBlock = fNextBlock; - else - pNewFile->fStartBlock = pPrevFile->fNextBlock; - pNewFile->fNextBlock = pNewFile->fStartBlock +1; // alloc 1 block - pNewFile->fFileType = A2FilePascal::ConvertFileType(pParms->fileType); - memset(pNewFile->fFileName, 0, A2FilePascal::kMaxFileName); - strcpy(pNewFile->fFileName, normalName); - pNewFile->fBytesRemaining = 0; - now = time(NULL); - pNewFile->fModWhen = A2FilePascal::ConvertPascalDate(now); - - pNewFile->fLength = 0; - - /* - * Make a hole. - */ - dierr = LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - if (fNumFiles > prevIdx) { - LOGI(" Pascal sliding last %d entries down a slot", - fNumFiles - prevIdx); - memmove(fDirectory + (prevIdx+2) * kDirectoryEntryLen, - fDirectory + (prevIdx+1) * kDirectoryEntryLen, - (fNumFiles - prevIdx) * kDirectoryEntryLen); - } - - /* - * Fill the hole. - */ - uint8_t* dirPtr; - dirPtr = fDirectory + (prevIdx+1) * kDirectoryEntryLen; - PutShortLE(&dirPtr[0x00], pNewFile->fStartBlock); - PutShortLE(&dirPtr[0x02], pNewFile->fNextBlock); - PutShortLE(&dirPtr[0x04], (uint16_t) pNewFile->fFileType); - dirPtr[0x06] = (uint8_t) strlen(pNewFile->fFileName); - memcpy(&dirPtr[0x07], pNewFile->fFileName, A2FilePascal::kMaxFileName); - PutShortLE(&dirPtr[0x16], pNewFile->fBytesRemaining); - PutShortLE(&dirPtr[0x18], pNewFile->fModWhen); - - /* - * Update the #of files. - */ - fNumFiles++; - PutShortLE(&fDirectory[0x10], fNumFiles); - - /* - * Flush. - */ - dierr = SaveCatalog(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Add to the linear file list. - */ - InsertFileInList(pNewFile, pPrevFile); - - *ppNewFile = pNewFile; - pNewFile = NULL; - -bail: - delete pNewFile; - FreeCatalog(); - return dierr; -} - -/* - * Make the name pointed to by "fileName" unique. The name should already - * be FS-normalized, and be in a buffer that can hold at least kMaxFileName+1 - * bytes. - * - * (This is nearly identical to the code in the ProDOS implementation. I'd - * like to make it a general DiskFS function, but making the loop condition - * work requires setting up callbacks, which isn't hard here but is a little - * annoying in ProDOS because of the subdir buffer. So it's cut & paste - * for now.) - * - * Returns an error on failure, which should be impossible. - */ -DIError DiskFSPascal::MakeFileNameUnique(char* fileName) -{ - assert(fileName != NULL); - assert(strlen(fileName) <= A2FilePascal::kMaxFileName); - - if (GetFileByName(fileName) == NULL) - return kDIErrNone; - - LOGI(" Pascal found duplicate of '%s', making unique", fileName); - - int nameLen = strlen(fileName); - int dotOffset=0, dotLen=0; - char dotBuf[kMaxExtensionLen+1]; - - /* ensure the result will be null-terminated */ - memset(fileName + nameLen, 0, (A2FilePascal::kMaxFileName - nameLen) +1); - - /* - * If this has what looks like a filename extension, grab it. We want - * to preserve ".gif", ".c", etc. - */ - const char* cp = strrchr(fileName, '.'); - if (cp != NULL) { - int tmpOffset = cp - fileName; - if (tmpOffset > 0 && nameLen - tmpOffset <= kMaxExtensionLen) { - LOGI(" Pascal (keeping extension '%s')", cp); - assert(strlen(cp) <= kMaxExtensionLen); - strcpy(dotBuf, cp); - dotOffset = tmpOffset; - dotLen = nameLen - dotOffset; - } - } - - const int kMaxDigits = 999; - int digits = 0; - int digitLen; - int copyOffset; - char digitBuf[4]; - do { - if (digits == kMaxDigits) - return kDIErrFileExists; - digits++; - - /* not the most efficient way to do this, but it'll do */ - sprintf(digitBuf, "%d", digits); - digitLen = strlen(digitBuf); - if (nameLen + digitLen > A2FilePascal::kMaxFileName) - copyOffset = A2FilePascal::kMaxFileName - dotLen - digitLen; - else - copyOffset = nameLen - dotLen; - memcpy(fileName + copyOffset, digitBuf, digitLen); - if (dotLen != 0) - memcpy(fileName + copyOffset + digitLen, dotBuf, dotLen); - } while (GetFileByName(fileName) != NULL); - - LOGI(" Pascal converted to unique name: %s", fileName); - - return kDIErrNone; -} - -/* - * Find the largest chunk of free space on the disk. - * - * Returns the index to the directory entry of the file immediately before - * the chunk (where 0 is the directory header), and the corresponding - * A2File entry. - * - * If there's no free space left, returns kDIErrDiskFull. - */ -DIError DiskFSPascal::FindLargestFreeArea(int *pPrevIdx, A2FilePascal** ppPrevFile) -{ - A2FilePascal* pFile; - A2FilePascal* pPrevFile; - uint16_t prevNextBlock = fNextBlock; - int gapSize, maxGap, maxIndex, idx; - - maxIndex = -1; - maxGap = 0; - idx = 0; - *ppPrevFile = pPrevFile = NULL; - - pFile = (A2FilePascal*) GetNextFile(NULL); - while (pFile != NULL) { - gapSize = pFile->fStartBlock - prevNextBlock; - if (gapSize > maxGap) { - maxGap = gapSize; - maxIndex = idx; - *ppPrevFile = pPrevFile; - } - - idx++; - prevNextBlock = pFile->fNextBlock; - pPrevFile = pFile; - pFile = (A2FilePascal*) GetNextFile(pFile); - } - - gapSize = fTotalBlocks - prevNextBlock; - if (gapSize > maxGap) { - maxGap = gapSize; - maxIndex = idx; - *ppPrevFile = pPrevFile; - } - - LOGI("Pascal largest gap after entry %d '%s' (size=%d)", - maxIndex, - *ppPrevFile != NULL ? (*ppPrevFile)->GetPathName() : "(root)", - maxGap); - *pPrevIdx = maxIndex; - - if (maxIndex < 0) - return kDIErrDiskFull; - return kDIErrNone; -} - -/* - * Delete a file. Because Pascal doesn't have a block allocation map, this - * is a simple matter of crunching the directory entry out. - */ -DIError DiskFSPascal::DeleteFile(A2File* pGenericFile) -{ - DIError dierr = kDIErrNone; - A2FilePascal* pFile = (A2FilePascal*) pGenericFile; - uint8_t* pEntry; - int dirLen, offsetToNextEntry; - - if (pGenericFile == NULL) { - assert(false); - return kDIErrInvalidArg; - } - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - if (pGenericFile->IsFileOpen()) - return kDIErrFileOpen; - - LOGI(" Pascal deleting '%s'", pFile->GetPathName()); - - dierr = LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - pEntry = FindDirEntry(pFile); - if (pEntry == NULL) { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - dirLen = (fNumFiles+1) * kDirectoryEntryLen; - offsetToNextEntry = (pEntry - fDirectory) + kDirectoryEntryLen; - if (dirLen == offsetToNextEntry) { - LOGI("+++ removing last entry"); - } else { - memmove(pEntry, pEntry+kDirectoryEntryLen, dirLen - offsetToNextEntry); - } - - assert(fNumFiles > 0); - fNumFiles--; - PutShortLE(&fDirectory[0x10], fNumFiles); - - dierr = SaveCatalog(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Remove the A2File* from the list. - */ - DeleteFileFromList(pFile); - -bail: - FreeCatalog(); - return dierr; -} - -/* - * Rename a file. - */ -DIError DiskFSPascal::RenameFile(A2File* pGenericFile, const char* newName) -{ - DIError dierr = kDIErrNone; - A2FilePascal* pFile = (A2FilePascal*) pGenericFile; - char normalName[A2FilePascal::kMaxFileName+1]; - uint8_t* pEntry; - - if (pFile == NULL || newName == NULL) - return kDIErrInvalidArg; - if (!IsValidFileName(newName)) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - /* not strictly necessary, but watch sanity check in Close/FindDirEntry */ - if (pGenericFile->IsFileOpen()) - return kDIErrFileOpen; - - DoNormalizePath(newName, '\0', normalName); - - LOGI(" Pascal renaming '%s' to '%s'", pFile->GetPathName(), normalName); - - dierr = LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - pEntry = FindDirEntry(pFile); - if (pEntry == NULL) { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - pEntry[0x06] = strlen(normalName); - memcpy(&pEntry[0x07], normalName, A2FilePascal::kMaxFileName); - strcpy(pFile->fFileName, normalName); - - dierr = SaveCatalog(); - if (dierr != kDIErrNone) - goto bail; - -bail: - FreeCatalog(); - return dierr; -} - -/* - * Set file info. - * - * Pascal does not have an aux type or access flags. It has a file type, - * but we don't allow the full range of ProDOS types. Attempting to change - * to an unsupported type results in "PDA" being used. - */ -DIError DiskFSPascal::SetFileInfo(A2File* pGenericFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) -{ - DIError dierr = kDIErrNone; - A2FilePascal* pFile = (A2FilePascal*) pGenericFile; - uint8_t* pEntry; - - if (pFile == NULL) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - LOGI("Pascal SetFileInfo '%s' fileType=0x%04x", - pFile->GetPathName(), fileType); - - dierr = LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - pEntry = FindDirEntry(pFile); - if (pEntry == NULL) { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - A2FilePascal::FileType newType; - - newType = A2FilePascal::ConvertFileType(fileType); - PutShortLE(&pEntry[0x04], (uint16_t) newType); - - dierr = SaveCatalog(); - if (dierr != kDIErrNone) - goto bail; - - /* update our local copy */ - pFile->fFileType = newType; - -bail: - FreeCatalog(); - return dierr; -} - -/* - * Change the Pascal volume name. - */ -DIError DiskFSPascal::RenameVolume(const char* newName) -{ - DIError dierr = kDIErrNone; - char normalName[A2FilePascal::kMaxFileName+1]; - - if (!IsValidVolumeName(newName)) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - - DoNormalizePath(newName, '\0', normalName); - - dierr = LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - fDirectory[0x06] = strlen(normalName); - memcpy(&fDirectory[0x07], normalName, fDirectory[0x06]); - strcpy(fVolumeName, normalName); - - SetVolumeID(); - - dierr = SaveCatalog(); - if (dierr != kDIErrNone) - goto bail; - -bail: - FreeCatalog(); - return dierr; -} - - -/* - * Find "pFile" in "fDirectory". - */ -uint8_t* DiskFSPascal::FindDirEntry(A2FilePascal* pFile) -{ - uint8_t* ptr; - int i; - - assert(fDirectory != NULL); - - ptr = fDirectory; // volume header; first iteration skips over it - for (i = 0; i < fNumFiles; i++) { - ptr += kDirectoryEntryLen; - - if (GetShortLE(&ptr[0x00]) == pFile->fStartBlock) { - if (memcmp(&ptr[0x07], pFile->fFileName, ptr[0x06]) != 0) { - assert(false); - LOGI("name/block mismatch on '%s' %d", - pFile->GetPathName(), pFile->fStartBlock); - return NULL; - } - return ptr; - } - } - - return NULL; -} - - -/* - * =========================================================================== - * A2FilePascal - * =========================================================================== - */ - -/* - * Convert Pascal file type to ProDOS file type. - */ -uint32_t A2FilePascal::GetFileType(void) const -{ - switch (fFileType) { - case kTypeUntyped: return 0x00; // NON - case kTypeXdsk: return 0x01; // BAD (was 0xf2 in v1.2.2) - case kTypeCode: return 0x02; // PCD - case kTypeText: return 0x03; // PTX - case kTypeInfo: return 0xf3; // no idea - case kTypeData: return 0x05; // PDA - case kTypeGraf: return 0xf4; // no idea - case kTypeFoto: return 0x08; // FOT - case kTypeSecurdir: return 0xf5; // no idea - default: - LOGI("Pascal WARNING: found invalid file type %d", fFileType); - return 0; - } -} - -/* - * Convert a ProDOS file type to a Pascal file type. - */ -/*static*/ A2FilePascal::FileType A2FilePascal::ConvertFileType(long prodosType) -{ - FileType newType; - - switch (prodosType) { - case 0x00: newType = kTypeUntyped; break; // NON - case 0x01: newType = kTypeXdsk; break; // BAD - case 0x02: newType = kTypeCode; break; // PCD - case 0x03: newType = kTypeText; break; // PTX - case 0xf3: newType = kTypeInfo; break; // ? - case 0x05: newType = kTypeData; break; // PDA - case 0xf4: newType = kTypeGraf; break; // ? - case 0x08: newType = kTypeFoto; break; // FOT - case 0xf5: newType = kTypeSecurdir; break; // ? - default: newType = kTypeData; break; // PDA for generic - } - - return newType; -} - - -/* - * Convert from Pascal compact date format to a time_t. - * - * Format yyyyyyydddddmmmm - * Month 0..12 (0 indicates invalid date) - * Day 0..31 - * Year 0..100 (1900-1999; 100 will be rejected) - * - * We follow the ProDOS protocol of "year < 40 == 1900 + year". We could - * probably make that 1970, but the time_t epoch ends before then. - * - * The Pascal Filer uses a special date with the year 100 in it to indicate - * file updates in progress. If the system comes up and sees a file with - * the year 100, it will assume that the file was created shortly before the - * system crashed, and will remove the file. - */ -/*static*/ time_t A2FilePascal::ConvertPascalDate(PascalDate pascalDate) -{ - int year, month, day; - - month = pascalDate & 0x0f; - if (!month) - return 0; - day = (pascalDate >> 4) & 0x1f; - year = (pascalDate >> 9) & 0x7f; - if (year == 100) { - // ought to mark the file as "suspicious"? - LOGI("Pascal WARNING: date with year=100"); - } - if (year < 40) - year += 100; - - struct tm tmbuf; - time_t when; - - tmbuf.tm_sec = 0; - tmbuf.tm_min = 0; - tmbuf.tm_hour = 0; - tmbuf.tm_mday = day; - tmbuf.tm_mon = month-1; - tmbuf.tm_year = year; - tmbuf.tm_wday = 0; - tmbuf.tm_yday = 0; - tmbuf.tm_isdst = -1; // let it figure DST and time zone - when = mktime(&tmbuf); - - if (when == (time_t) -1) - when = 0; - return when; -} - -/* - * Convert a time_t to a Pascal-format date. - * - * CiderPress uses kDateInvalid==-1 and kDateNone==-2. - */ -/*static*/ A2FilePascal::PascalDate A2FilePascal::ConvertPascalDate(time_t unixDate) -{ - uint32_t date, year; - struct tm* ptm; - - if (unixDate == 0 || unixDate == -1 || unixDate == -2) - return 0; - - ptm = localtime(&unixDate); - if (ptm == NULL) - return 0; // must've been invalid or unspecified - - year = ptm->tm_year; // years since 1900 - if (year >= 100) - year -= 100; - if (year >= 100) { - LOGW("WHOOPS: got year %u from %d", year, ptm->tm_year); - year = 70; - } - date = year << 9 | (ptm->tm_mon+1) | ptm->tm_mday << 4; - return (PascalDate) date; -} - - -/* - * Return the file modification date. - */ -time_t A2FilePascal::GetModWhen(void) const -{ - return ConvertPascalDate(fModWhen); -} - -/* - * Dump the contents of the A2File structure. - */ -void A2FilePascal::Dump(void) const -{ - LOGI("A2FilePascal '%s'", fFileName); - LOGI(" start=%d next=%d type=%d", - fStartBlock, fNextBlock, fFileType); - LOGI(" bytesRem=%d modWhen=0x%04x", - fBytesRemaining, fModWhen); -} - -/* - * Not a whole lot to do, since there's no fancy index blocks. - */ -DIError A2FilePascal::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - A2FDPascal* pOpenFile = NULL; - - if (!readOnly) { - if (fpDiskFS->GetDiskImg()->GetReadOnly()) - return kDIErrAccessDenied; - if (fpDiskFS->GetFSDamaged()) - return kDIErrBadDiskImage; - } - if (fpOpenFile != NULL) - return kDIErrAlreadyOpen; - if (rsrcFork) - return kDIErrForkNotFound; - - pOpenFile = new A2FDPascal(this); - - pOpenFile->fOffset = 0; - pOpenFile->fOpenEOF = fLength; - pOpenFile->fOpenBlocksUsed = fNextBlock - fStartBlock; - pOpenFile->fModified = false; - - fpOpenFile = pOpenFile; - *ppOpenFile = pOpenFile; - - return kDIErrNone; -} - - -/* - * =========================================================================== - * A2FDPascal - * =========================================================================== - */ - -/* - * Read a chunk of data from the current offset. - */ -DIError A2FDPascal::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" Pascal reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - - A2FilePascal* pFile = (A2FilePascal*) fpFile; - - /* don't allow them to read past the end of the file */ - if (fOffset + (long)len > fOpenEOF) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (size_t) (fOpenEOF - fOffset); - } - if (pActual != NULL) - *pActual = len; - long incrLen = len; - - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - long block = pFile->fStartBlock + (long) (fOffset / kBlkSize); - int bufOffset = (long) (fOffset % kBlkSize); // (& 0x01ff) - size_t thisCount; - - if (len == 0) - return kDIErrNone; - assert(fOpenEOF != 0); - - while (len) { - assert(block >= pFile->fStartBlock && block < pFile->fNextBlock); - - dierr = pFile->GetDiskFS()->GetDiskImg()->ReadBlock(block, blkBuf); - if (dierr != kDIErrNone) { - LOGI(" Pascal error reading file '%s'", pFile->fFileName); - return dierr; - } - thisCount = kBlkSize - bufOffset; - if (thisCount > len) - thisCount = len; - - memcpy(buf, blkBuf + bufOffset, thisCount); - len -= thisCount; - buf = (char*)buf + thisCount; - - bufOffset = 0; - block++; - } - - fOffset += incrLen; - - return dierr; -} - -/* - * Write data at the current offset. - * - * We make the customary assumptions here: we're writing to a brand-new file, - * and writing all data in one shot. On a Pascal disk, that makes this - * process almost embarrassingly simple. - */ -DIError A2FDPascal::Write(const void* buf, size_t len, size_t* pActual) -{ - DIError dierr = kDIErrNone; - A2FilePascal* pFile = (A2FilePascal*) fpFile; - DiskFSPascal* pDiskFS = (DiskFSPascal*) fpFile->GetDiskFS(); - uint8_t blkBuf[kBlkSize]; - size_t origLen = len; - - LOGD(" DOS Write len=%lu %s", (unsigned long) len, pFile->GetPathName()); - - if (len >= 0x01000000) { // 16MB - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset == 0); // big simplifying assumption - assert(fOpenEOF == 0); // another one - assert(fOpenBlocksUsed == 1); - assert(buf != NULL); - - /* - * Verify that there's enough room between this file and the next to - * hold the contents of the file. - */ - long blocksNeeded, blocksAvail; - A2FilePascal* pNextFile; - pNextFile = (A2FilePascal*) pDiskFS->GetNextFile(pFile); - if (pNextFile == NULL) - blocksAvail = pDiskFS->GetTotalBlocks() - pFile->fStartBlock; - else - blocksAvail = pNextFile->fStartBlock - pFile->fStartBlock; - - blocksNeeded = (len + kBlkSize -1) / kBlkSize; - LOGD("Pascal write '%s' %lu bytes: avail=%ld needed=%ld", - pFile->GetPathName(), (unsigned long) len, blocksAvail, blocksNeeded); - if (blocksAvail < blocksNeeded) - return kDIErrDiskFull; - - /* - * Write the data. - */ - long block; - block = pFile->fStartBlock; - while (len != 0) { - if (len >= (size_t) kBlkSize) { - /* full block write */ - dierr = pDiskFS->GetDiskImg()->WriteBlock(block, buf); - if (dierr != kDIErrNone) - goto bail; - - len -= kBlkSize; - buf = (uint8_t*) buf + kBlkSize; - } else { - /* partial block write */ - memset(blkBuf, 0, sizeof(blkBuf)); - memcpy(blkBuf, buf, len); - dierr = pDiskFS->GetDiskImg()->WriteBlock(block, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - len = 0; - } - - block++; - } - - /* - * Update FD state. - */ - fOpenBlocksUsed = blocksNeeded; - fOpenEOF = origLen; - fOffset = origLen; - fModified = true; - -bail: - return dierr; -} - -/* - * Seek to a new offset. - */ -DIError A2FDPascal::Seek(di_off_t offset, DIWhence whence) -{ - //di_off_t fileLen = ((A2FilePascal*) fpFile)->fLength; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fOpenEOF) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fOpenEOF) - return kDIErrInvalidArg; - fOffset = fOpenEOF + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fOpenEOF - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fOpenEOF); - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDPascal::Tell(void) -{ - return fOffset; -} - -/* - * Release file state, and tell our parent to destroy us. - * - * Most applications don't check the value of "Close", or call it from a - * destructor, so we call CloseDescr whether we succeed or not. - */ -DIError A2FDPascal::Close(void) -{ - DIError dierr = kDIErrNone; - DiskFSPascal* pDiskFS = (DiskFSPascal*) fpFile->GetDiskFS(); - - if (fModified) { - A2FilePascal* pFile = (A2FilePascal*) fpFile; - uint8_t* pEntry; - - dierr = pDiskFS->LoadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Update our internal copies of stuff. - */ - pFile->fLength = fOpenEOF; - pFile->fNextBlock = pFile->fStartBlock + (uint16_t) fOpenBlocksUsed; - pFile->fModWhen = A2FilePascal::ConvertPascalDate(time(NULL)); - - /* - * Update the "next block" value and the length-in-last-block. We - * have to scan through the directory to find our entry, rather - * than remember an offset at "open" time, on the off chance that - * somebody created or deleted a file after we were opened. - */ - pEntry = pDiskFS->FindDirEntry(pFile); - if (pEntry == NULL) { - // we deleted an open file? - assert(false); - dierr = kDIErrInternal; - goto bail; - } - uint16_t bytesInLastBlock; - bytesInLastBlock = (uint16_t)pFile->fLength % kBlkSize; - if (bytesInLastBlock == 0) - bytesInLastBlock = 512; // exactly filled out last block - - PutShortLE(&pEntry[0x02], pFile->fNextBlock); - PutShortLE(&pEntry[0x16], bytesInLastBlock); - PutShortLE(&pEntry[0x18], pFile->fModWhen); - - dierr = pDiskFS->SaveCatalog(); - if (dierr != kDIErrNone) - goto bail; - } - -bail: - pDiskFS->FreeCatalog(); - fpFile->CloseDescr(this); - return dierr; -} - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDPascal::GetSectorCount(void) const -{ - A2FilePascal* pFile = (A2FilePascal*) fpFile; - return (pFile->fNextBlock - pFile->fStartBlock) * 2; -} - -long A2FDPascal::GetBlockCount(void) const -{ - A2FilePascal* pFile = (A2FilePascal*) fpFile; - return pFile->fNextBlock - pFile->fStartBlock; -} - -/* - * Return the Nth track/sector in this file. - */ -DIError A2FDPascal::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - A2FilePascal* pFile = (A2FilePascal*) fpFile; - long pascalIdx = sectorIdx / 2; - long pascalBlock = pFile->fStartBlock + pascalIdx; - if (pascalBlock >= pFile->fNextBlock) - return kDIErrInvalidIndex; - - /* sparse blocks not possible on Pascal volumes */ - BlockToTrackSector(pascalBlock, (sectorIdx & 0x01) != 0, pTrack, pSector); - return kDIErrNone; -} - -/* - * Return the Nth 512-byte block in this file. - */ -DIError A2FDPascal::GetStorage(long blockIdx, long* pBlock) const -{ - A2FilePascal* pFile = (A2FilePascal*) fpFile; - long pascalBlock = pFile->fStartBlock + blockIdx; - if (pascalBlock >= pFile->fNextBlock) - return kDIErrInvalidIndex; - - *pBlock = pascalBlock; - assert(*pBlock < pFile->GetDiskFS()->GetDiskImg()->GetNumBlocks()); - return kDIErrNone; -} diff --git a/ciderpress/diskimg/ProDOS.cpp b/ciderpress/diskimg/ProDOS.cpp deleted file mode 100644 index 02b78d6..0000000 --- a/ciderpress/diskimg/ProDOS.cpp +++ /dev/null @@ -1,5091 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskFSProDOS class. - * - * We currently only allow one fork to be open at a time, and each file may - * only be opened once. - * - * BUG: does not keep VolumeUsage up to date. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - -// disable Y2K+ dates when testing w/ProSel-16 vol rep (newer ProSel is OK) -//#define OLD_PRODOS_DATES - -#if defined(OLD_PRODOS_DATES) && !(defined(_DEBUG)) -# error "don't set OLD_PRODOS_DATES for production" -#endif - - -/* - * =========================================================================== - * DiskFSProDOS - * =========================================================================== - */ - -const int kBlkSize = 512; -const int kVolHeaderBlock = 2; // block where Volume Header resides -const int kVolDirExpectedNumBlocks = 4; // customary #of volume header blocks -const int kMinReasonableBlocks = 16; // min size for ProDOS volume -const int kExpectedBitmapStart = 6; // block# where vol bitmap should start -const int kMaxCatalogIterations = 1024; // theoretical max is 32768? -const int kMaxDirectoryDepth = 64; // not sure what ProDOS limit is -const int kEntriesPerBlock = 0x0d; // expected value for entries per blk -const int kEntryLength = 0x27; // expected value for dir entry len -const int kTypeDIR = 0x0f; - - -/* - * Directory header. All fields not marked as "only for subdirs" also apply - * to the volume directory header. - */ -typedef struct DiskFSProDOS::DirHeader { - uint8_t storageType; - char dirName[A2FileProDOS::kMaxFileName+1]; - DiskFSProDOS::ProDate createWhen; - uint8_t version; - uint8_t minVersion; - uint8_t access; - uint8_t entryLength; - uint8_t entriesPerBlock; - uint16_t fileCount; - /* the rest are only for subdirs */ - uint16_t parentPointer; - uint8_t parentEntry; - uint8_t parentEntryLength; -} DirHeader; - - -/* - * See if this looks like a ProDOS volume. - * - * We test a few fields in the volume directory header for validity. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - int volDirEntryLength; - int volDirEntriesPerBlock; - - dierr = pImg->ReadBlockSwapped(kVolHeaderBlock, blkBuf, imageOrder, - DiskImg::kSectorOrderProDOS); - if (dierr != kDIErrNone) - goto bail; - - volDirEntryLength = blkBuf[0x23]; - volDirEntriesPerBlock = blkBuf[0x24]; - - - if (!(blkBuf[0x00] == 0 && blkBuf[0x01] == 0) || - !((blkBuf[0x04] & 0xf0) == 0xf0) || - !((blkBuf[0x04] & 0x0f) != 0) || - !(volDirEntryLength * volDirEntriesPerBlock <= kBlkSize) || - !(blkBuf[0x05] >= 'A' && blkBuf[0x05] <= 'Z') || - 0) - { - dierr = kDIErrFilesystemNotFound; - goto bail; - } - -bail: - return dierr; -} - -/* - * Test to see if the image is a ProDOS disk. - */ -/*static*/ DIError DiskFSProDOS::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i]) == kDIErrNone) { - *pOrder = ordering[i]; - *pFormat = DiskImg::kFormatProDOS; - return kDIErrNone; - } - } - - LOGI(" ProDOS didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk has - * no files on it. - */ -DIError DiskFSProDOS::Initialize(InitMode initMode) -{ - DIError dierr = kDIErrNone; - char msg[kMaxVolumeName + 32]; - - fDiskIsGood = false; // hosed until proven innocent - fEarlyDamage = false; - - /* - * NOTE: we'd probably be better off with fTotalBlocks, since that's how - * big the disk *thinks* it is, especially on a CFFA or MacPart subvol. - * However, we know that the image block count is the absolute maximum, - * so while it may not be a tight bound it is an upper bound. - */ - fVolumeUsage.Create(fpImg->GetNumBlocks()); - - dierr = LoadVolHeader(); - if (dierr != kDIErrNone) - goto bail; - DumpVolHeader(); - - dierr = ScanVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - if (initMode == kInitHeaderOnly) { - LOGI(" ProDOS - headerOnly set, skipping file load"); - goto bail; - } - - sprintf(msg, "Scanning %s", fVolumeName); - if (!fpImg->UpdateScanProgress(msg)) { - LOGI(" ProDOS cancelled by user"); - dierr = kDIErrCancelled; - goto bail; - } - - /* volume dir is guaranteed to come first; if not, we need a lookup func */ - A2FileProDOS* pVolumeDir; - pVolumeDir = (A2FileProDOS*) GetNextFile(NULL); - - dierr = RecursiveDirAdd(pVolumeDir, kVolHeaderBlock, "", 0); - if (dierr != kDIErrNone) { - LOGI(" ProDOS RecursiveDirAdd failed"); - goto bail; - } - - sprintf(msg, "Processing %s", fVolumeName); - if (!fpImg->UpdateScanProgress(msg)) { - LOGI(" ProDOS cancelled by user"); - dierr = kDIErrCancelled; - goto bail; - } - - dierr = ScanFileUsage(); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - - /* this might not be fatal; just means that *some* files are bad */ - LOGI("WARNING: ScanFileUsage returned err=%d", dierr); - dierr = kDIErrNone; - fpImg->AddNote(DiskImg::kNoteWarning, - "Some errors were encountered while scanning files."); - fEarlyDamage = true; // make sure we know it's damaged - } - - fDiskIsGood = CheckDiskIsGood(); - - if (fScanForSubVolumes != kScanSubDisabled) - (void) ScanForSubVolumes(); - - if (fpImg->GetNumBlocks() <= 1600) - fVolumeUsage.Dump(); - -// A2File* pFile; -// pFile = GetNextFile(NULL); -// while (pFile != NULL) { -// pFile->Dump(); -// pFile = GetNextFile(pFile); -// } - -bail: - return dierr; -} - -/* - * Read some interesting fields from the volume header. - * - * The "test" function verified certain things, e.g. the storage type - * is $f and the volume name length is nonzero. - */ -DIError DiskFSProDOS::LoadVolHeader(void) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - int nameLen; - - dierr = fpImg->ReadBlock(kVolHeaderBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - //fPrevBlock = GetShortLE(&blkBuf[0x00]); - //fNextBlock = GetShortLE(&blkBuf[0x02]); - nameLen = blkBuf[0x04] & 0x0f; - memcpy(fVolumeName, &blkBuf[0x05], nameLen); - fVolumeName[nameLen] = '\0'; - // 0x14-15 reserved - // undocumented: GS/OS writes the modification date to 0x16-19 - fModWhen = GetLongLE(&blkBuf[0x16]); - // undocumented: GS/OS uses 0x1a-1b for lower-case handling (see below) - fCreateWhen = GetLongLE(&blkBuf[0x1c]); - //fVersion = blkBuf[0x20]; - if (blkBuf[0x21] != 0) { - /* - * We don't care about the MIN_VERSION field, but it looks like GS/OS - * rejects anything with a nonzero value here. We want to add a note - * about it. - */ - fpImg->AddNote(DiskImg::kNoteInfo, - "Volume header has nonzero min_version; could confuse GS/OS."); - } - fAccess = blkBuf[0x22]; - //fEntryLength = blkBuf[0x23]; - //fEntriesPerBlock = blkBuf[0x24]; - fVolDirFileCount = GetShortLE(&blkBuf[0x25]); - fBitMapPointer = GetShortLE(&blkBuf[0x27]); - fTotalBlocks = GetShortLE(&blkBuf[0x29]); - - if (blkBuf[0x1b] & 0x80) { - /* - * Handle lower-case conversion; see GS/OS tech note #8. Unlike - * filenames, volume names are not allowed to contain spaces. If - * they try it we just ignore them. - * - * Technote 8 doesn't actually talk about volume names. By - * experimentation the field was discovered at offset 0x1a from - * the start of the block, which is marked as "reserved" in Beneath - * Apple ProDOS. - */ - uint16_t lcFlags = GetShortLE(&blkBuf[0x1a]); - - GenerateLowerCaseName(fVolumeName, fVolumeName, lcFlags, false); - } - - if (fTotalBlocks <= kVolHeaderBlock) { - /* incr to min; don't use max, or bitmap count may be too large */ - LOGI(" ProDOS found tiny fTotalBlocks (%d), increasing to minimum", - fTotalBlocks); - fpImg->AddNote(DiskImg::kNoteWarning, - "ProDOS filesystem blockcount (%d) too small, setting to %d.", - fTotalBlocks, kMinReasonableBlocks); - fTotalBlocks = kMinReasonableBlocks; - fEarlyDamage = true; - } else if (fTotalBlocks != fpImg->GetNumBlocks()) { - if (fTotalBlocks != 65535 || fpImg->GetNumBlocks() != 65536) { - LOGI(" ProDOS WARNING: total (%u) != img (%ld)", - fTotalBlocks, fpImg->GetNumBlocks()); - // could AddNote here, but not really necessary - } - - /* - * For safety (esp. vol bitmap read), constrain fTotalBlocks. We might - * consider not doing this for ".hdv", which can start small and then - * expand as files are added. (Check "fExpanded".) - */ - if (fTotalBlocks > fpImg->GetNumBlocks()) { - fpImg->AddNote(DiskImg::kNoteWarning, - "ProDOS filesystem blockcount (%d) exceeds disk image blocks (%ld).", - fTotalBlocks, fpImg->GetNumBlocks()); - fTotalBlocks = (uint16_t) fpImg->GetNumBlocks(); - fEarlyDamage = true; - } - } - - /* - * Test for funky volume bitmap pointer. Some disks (e.g. /RAM and - * ProSel-16) truncate the volume directory to eke a little more storage - * out of a disk. There's nothing wrong with that, but we don't want to - * try to use a volume bitmap pointer of zero or 0xffff, because it's - * probably garbage. - */ - if (fBitMapPointer != kExpectedBitmapStart) { - if (fBitMapPointer <= kVolHeaderBlock || - fBitMapPointer > kExpectedBitmapStart) - { - fpImg->AddNote(DiskImg::kNoteWarning, - "Volume bitmap pointer (%d) is probably invalid.", - fBitMapPointer); - fBitMapPointer = 6; // just fix it and hope for the best - fEarlyDamage = true; - } else { - fpImg->AddNote(DiskImg::kNoteInfo, - "Unusual volume bitmap start (%d).", fBitMapPointer); - // try it and see - } - } - - SetVolumeID(); - - /* - * Create a "magic" directory entry for the volume directory. - * - * Normally these values are pulled out of the file entry in the parent - * directory. Here, we synthesize them from the volume dir header. - */ - A2FileProDOS* pFile; - pFile = new A2FileProDOS(this); - if (pFile == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - A2FileProDOS::DirEntry* pEntry; - pEntry = &pFile->fDirEntry; - - int foundStorage; - foundStorage = (blkBuf[0x04] & 0xf0) >> 4; - if (foundStorage != A2FileProDOS::kStorageVolumeDirHeader) { - LOGI(" ProDOS WARNING: unexpected vol dir file type %d", - pEntry->storageType); - /* keep going */ - } - pEntry->storageType = A2FileProDOS::kStorageVolumeDirHeader; - strcpy(pEntry->fileName, fVolumeName); - //nameLen = blkBuf[0x04] & 0x0f; - //memcpy(pEntry->fileName, &blkBuf[0x05], nameLen); - //pEntry->fileName[nameLen] = '\0'; - pFile->SetPathName(":", pEntry->fileName); - pEntry->fileName[nameLen] = '\0'; - pEntry->fileType = kTypeDIR; - pEntry->keyPointer = kVolHeaderBlock; - pEntry->blocksUsed = kVolDirExpectedNumBlocks; - pEntry->eof = kVolDirExpectedNumBlocks * 512; - pEntry->createWhen = GetLongLE(&blkBuf[0x1c]); - pEntry->version = blkBuf[0x20]; - pEntry->minVersion = blkBuf[0x21]; - pEntry->access = blkBuf[0x22]; - pEntry->auxType = 0; -// if (blkBuf[0x20] >= 5) - pEntry->modWhen = GetLongLE(&blkBuf[0x16]); - pEntry->headerPointer = 0; - - pFile->fSparseDataEof = pEntry->eof; - pFile->fSparseRsrcEof = -1; - - AddFileToList(pFile); - -bail: - return dierr; -} - -/* - * Set the volume ID field. - */ -void DiskFSProDOS::SetVolumeID(void) -{ - sprintf(fVolumeID, "ProDOS /%s", fVolumeName); -} - -/* - * Dump what we pulled out of the volume header. - */ -void DiskFSProDOS::DumpVolHeader(void) -{ - LOGI(" ProDOS volume header for '%s'", fVolumeName); - LOGI(" CreateWhen=0x%08x access=0x%02x bitmap=%d totalbl=%d", - fCreateWhen, fAccess, fBitMapPointer, fTotalBlocks); - - time_t when; - when = A2FileProDOS::ConvertProDate(fCreateWhen); - LOGI(" CreateWhen is %.24s", ctime(&when)); - - //LOGI(" prev=%d next=%d bitmap=%d total=%d", - // fPrevBlock, fNextBlock, fBitMapPointer, fTotalBlocks); - //LOGI(" create date=0x%08lx access=0x%02x", fCreateWhen, fAccess); - //LOGI(" version=%d minVersion=%d entryLen=%d epb=%d", - // fVersion, fMinVersion, fEntryLength, fEntriesPerBlock); - //LOGI(" volume dir fileCount=%d", fFileCount); -} - -/* - * Load the disk's volume bitmap into the object's "fBlockUseMap" pointer. - * - * Does not attempt to analyze the data. - */ -DIError DiskFSProDOS::LoadVolBitmap(void) -{ - DIError dierr = kDIErrNone; - int bitBlock, numBlocks; - - if (fBitMapPointer <= kVolHeaderBlock) - return kDIErrBadDiskImage; - if (fTotalBlocks <= kVolHeaderBlock) - return kDIErrBadDiskImage; - - /* should not already be allocated */ - assert(fBlockUseMap == NULL); - delete[] fBlockUseMap; // just in case - - bitBlock = fBitMapPointer; - - numBlocks = GetNumBitmapBlocks(); // based on fTotalBlocks - assert(numBlocks > 0); - - fBlockUseMap = new uint8_t[kBlkSize * numBlocks]; - if (fBlockUseMap == NULL) - return kDIErrMalloc; - - while (numBlocks--) { - dierr = fpImg->ReadBlock(bitBlock + numBlocks, - fBlockUseMap + kBlkSize * numBlocks); - if (dierr != kDIErrNone) { - delete[] fBlockUseMap; - fBlockUseMap = NULL; - return dierr; - } - } - - return kDIErrNone; -} - -/* - * Save our copy of the volume bitmap. - */ -DIError DiskFSProDOS::SaveVolBitmap(void) -{ - DIError dierr = kDIErrNone; - int bitBlock, numBlocks; - - if (fBlockUseMap == NULL) { - assert(false); - return kDIErrNotReady; - } - assert(fBitMapPointer > kVolHeaderBlock); - assert(fTotalBlocks > kVolHeaderBlock); - - bitBlock = fBitMapPointer; - - numBlocks = GetNumBitmapBlocks(); - assert(numBlocks > 0); - - while (numBlocks--) { - dierr = fpImg->WriteBlock(bitBlock + numBlocks, - fBlockUseMap + kBlkSize * numBlocks); - if (dierr != kDIErrNone) - return dierr; - } - - return kDIErrNone; -} - -/* - * Throw away the volume bitmap, discarding any unsaved changes. - * - * It's okay to call this if the bitmap isn't loaded. - */ -void DiskFSProDOS::FreeVolBitmap(void) -{ - delete[] fBlockUseMap; - fBlockUseMap = NULL; -} - -/* - * Examine the volume bitmap, setting fields in the VolumeUsage map - * as appropriate. - */ -DIError DiskFSProDOS::ScanVolBitmap(void) -{ - DIError dierr; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) { - LOGI(" ProDOS failed to load volume bitmap (err=%d)", dierr); - return dierr; - } - - assert(fBlockUseMap != NULL); - - /* mark the boot blocks as system */ - SetBlockUsage(0, VolumeUsage::kChunkPurposeSystem); - SetBlockUsage(1, VolumeUsage::kChunkPurposeSystem); - - /* mark the bitmap blocks as system */ - int i; - for (i = GetNumBitmapBlocks(); i > 0; i--) - SetBlockUsage(fBitMapPointer + i -1, VolumeUsage::kChunkPurposeSystem); - - /* - * Set the "isMarkedUsed" flag in VolumeUsage for all used blocks. - */ - VolumeUsage::ChunkState cstate; - - long block = 0; - long numBytes = (fTotalBlocks + 7) / 8; - for (i = 0; i < numBytes; i++) { - uint8_t val = fBlockUseMap[i]; - - for (int j = 0; j < 8; j++) { - if (!(val & 0x80)) { - /* block is in use, mark it */ - if (fVolumeUsage.GetChunkState(block, &cstate) != kDIErrNone) - { - assert(false); - // keep going, I guess - } - cstate.isMarkedUsed = true; - fVolumeUsage.SetChunkState(block, &cstate); - } - val <<= 1; - block++; - - if (block >= fTotalBlocks) - break; - } - if (block >= fTotalBlocks) - break; - } - - FreeVolBitmap(); - return dierr; -} - -/* - * Generate an empty block use map. - */ -DIError DiskFSProDOS::CreateEmptyBlockMap(void) -{ - DIError dierr; - - /* load from disk; this is just to allocate the data structures */ - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - /* - * Set the bits, block by block. Not the most efficient way, but it's - * fast enough, and it exercises the standard set of functions. - */ - long block; - long firstEmpty = - kVolHeaderBlock + kVolDirExpectedNumBlocks + GetNumBitmapBlocks(); - for (block = 0; block < firstEmpty; block++) - SetBlockUseEntry(block, true); - for ( ; block < fTotalBlocks; block++) - SetBlockUseEntry(block, false); - - dierr = SaveVolBitmap(); - FreeVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - return kDIErrNone; -} - -/* - * Get the state of an entry in the block use map. - * - * Returns "true" if it's in use, "false" otherwise. - */ -bool DiskFSProDOS::GetBlockUseEntry(long block) const -{ - assert(block >= 0 && block < fTotalBlocks); - assert(fBlockUseMap != NULL); - - int offset; - uint8_t mask; - - offset = block / 8; - mask = 0x80 >> (block & 0x07); - if (fBlockUseMap[offset] & mask) - return false; - else - return true; -} - -/* - * Change the state of an entry in the block use map. - */ -void DiskFSProDOS::SetBlockUseEntry(long block, bool inUse) -{ - assert(block >= 0 && block < fTotalBlocks); - assert(fBlockUseMap != NULL); - - if (block == 0 && !inUse) { - // shouldn't happen - assert(false); - } - - int offset; - uint8_t mask; - - offset = block / 8; - mask = 0x80 >> (block & 0x07); - if (!inUse) - fBlockUseMap[offset] |= mask; - else - fBlockUseMap[offset] &= ~mask; -} - -/* - * Check for entries in the block use map past the point where they should be. - * - * Returns "true" if bogus entries were found, "false" if all is well. - */ -bool DiskFSProDOS::ScanForExtraEntries(void) const -{ - assert(fBlockUseMap != NULL); - - int offset, endOffset; - - /* sloppy: we're not checking for excess bits within last byte */ - offset = (fTotalBlocks / 8) +1; - endOffset = GetNumBitmapBlocks() * kBlkSize; - - while (offset < endOffset) { - if (fBlockUseMap[offset] != 0) { - LOGI(" ProDOS found bogus bitmap junk 0x%02x at offset=%d", - fBlockUseMap[offset], offset); - return true; - } - offset++; - } - return false; -} - -/* - * Allocate a new block on a ProDOS volume. - * - * Only touches the in-memory copy. - * - * Returns the block number (0-65535) on success or -1 on failure. - */ -long DiskFSProDOS::AllocBlock(void) -{ - assert(fBlockUseMap != NULL); - -#if 0 // whoa... this is REALLY slow - /* - * Run through the entire set of blocks until we find one that's not - * allocated. We could probably make this faster by scanning bytes and - * then shifting bits, but this is easier and fast enough. - * - * We don't scan block 0 because (a) it should never be available and - * (b) it has a special meaning in some circumstances. We could probably - * start at kVolHeaderBlock+kVolHeaderNumBlocks. - */ - long block; - for (block = kVolHeaderBlock; block < fTotalBlocks; block++) { - if (!GetBlockUseEntry(block)) { - SetBlockUseEntry(block, true); - return block; - } - } -#endif - - int offset; - int maxOffset = (fTotalBlocks + 7) / 8; - - for (offset = 0; offset < maxOffset; offset++) { - if (fBlockUseMap[offset] != 0) { - /* got one, figure out which */ - int subBlock = 0; - uint8_t uch = fBlockUseMap[offset]; - while ((uch & 0x80) == 0) { - subBlock++; - uch <<= 1; - } - - long block = offset * 8 + subBlock; - assert(!GetBlockUseEntry(block)); - SetBlockUseEntry(block, true); - if (block == 0 || block == 1) { - LOGI("PRODOS: GLITCH: rejecting alloc of block 0"); - continue; - } - return block; - } - } - - LOGI("ProDOS: NOTE: AllocBlock just failed!"); - return -1; -} - -/* - * Tally up the number of free blocks. - */ -DIError DiskFSProDOS::GetFreeSpaceCount(long* pTotalUnits, long* pFreeUnits, - int* pUnitSize) const -{ - DIError dierr; - long block, freeBlocks; - freeBlocks = 0; - - dierr = const_cast(this)->LoadVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - for (block = 0; block < fTotalBlocks; block++) { - if (!GetBlockUseEntry(block)) - freeBlocks++; - } - - *pTotalUnits = fTotalBlocks; - *pFreeUnits = freeBlocks; - *pUnitSize = kBlockSize; - - const_cast(this)->FreeVolBitmap(); - return kDIErrNone; -} - -/* - * Update an entry in the VolumeUsage map. - * - * The VolumeUsage map spans the range of blocks - */ -void DiskFSProDOS::SetBlockUsage(long block, VolumeUsage::ChunkPurpose purpose) -{ - VolumeUsage::ChunkState cstate; - - fVolumeUsage.GetChunkState(block, &cstate); - if (cstate.isUsed) { - cstate.purpose = VolumeUsage::kChunkPurposeConflict; - LOGI(" ProDOS conflicting uses for bl=%ld", block); - } else { - cstate.isUsed = true; - cstate.purpose = purpose; - } - fVolumeUsage.SetChunkState(block, &cstate); -} - -/* - * Pass in the number of the first block of the directory. - * - * Start with "pParent" set to the magic entry for the volume dir. - */ -DIError DiskFSProDOS::RecursiveDirAdd(A2File* pParent, uint16_t dirBlock, - const char* basePath, int depth) -{ - DIError dierr = kDIErrNone; - DirHeader header; - uint8_t blkBuf[kBlkSize]; - int numEntries, iterations, foundCount; - bool first; - - /* if we get too deep, assume it's a loop */ - if (depth > kMaxDirectoryDepth) { - dierr = kDIErrDirectoryLoop; - goto bail; - } - - - if (dirBlock < kVolHeaderBlock || dirBlock >= fpImg->GetNumBlocks()) { - LOGI(" ProDOS ERROR: directory block %u out of range", dirBlock); - dierr = kDIErrInvalidBlock; - goto bail; - } - - numEntries = 1; - iterations = 0; - foundCount = 0; - first = true; - - while (dirBlock && iterations < kMaxCatalogIterations) { - dierr = fpImg->ReadBlock(dirBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - if (pParent->IsVolumeDirectory()) - SetBlockUsage(dirBlock, VolumeUsage::kChunkPurposeVolumeDir); - else - SetBlockUsage(dirBlock, VolumeUsage::kChunkPurposeSubdir); - - if (first) { - /* this is the directory header entry */ - dierr = GetDirHeader(blkBuf, &header); - if (dierr != kDIErrNone) - goto bail; - numEntries = header.fileCount; - //LOGI(" ProDOS got dir header numEntries = %d", numEntries); - } - - /* slurp the entries out of this block */ - dierr = SlurpEntries(pParent, &header, blkBuf, first, &foundCount, - basePath, dirBlock, depth); - if (dierr != kDIErrNone) - goto bail; - - dirBlock = GetShortLE(&blkBuf[0x02]); - if (dirBlock != 0 && - (dirBlock < 2 || dirBlock >= fpImg->GetNumBlocks())) - { - LOGI(" ProDOS ERROR: invalid dir link block %u in base='%s'", - dirBlock, basePath); - dierr = kDIErrInvalidBlock; - goto bail; - } - first = false; - iterations++; - } - if (iterations == kMaxCatalogIterations) { - LOGI(" ProDOS subdir iteration count exceeded"); - dierr = kDIErrDirectoryLoop; - goto bail; - } - if (foundCount != numEntries) { - /* not significant; just means somebody isn't updating correctly */ - LOGI(" ProDOS WARNING: numEntries=%d foundCount=%d in base='%s'", - numEntries, foundCount, basePath); - } - -bail: - return dierr; -} - -/* - * Slurp the entries out of a single ProDOS directory block. - * - * Recursively calls RecursiveDirAdd for directories. - * - * "*pFound" is increased by the number of valid entries found in this block. - */ -DIError DiskFSProDOS::SlurpEntries(A2File* pParent, const DirHeader* pHeader, - const uint8_t* blkBuf, bool skipFirst, int* pCount, - const char* basePath, uint16_t thisBlock, int depth) -{ - DIError dierr = kDIErrNone; - int entriesThisBlock = pHeader->entriesPerBlock; - const uint8_t* entryBuf; - A2FileProDOS* pFile; - - int idx = 0; - entryBuf = &blkBuf[0x04]; - if (skipFirst) { - entriesThisBlock--; - entryBuf += pHeader->entryLength; - idx++; - } - - for ( ; entriesThisBlock > 0 ; - entriesThisBlock--, idx++, entryBuf += pHeader->entryLength) - { - if (entryBuf >= blkBuf + kBlkSize) { - LOGI(" ProDOS whoops, just walked out of dirent buffer"); - return kDIErrBadDirectory; - } - - if ((entryBuf[0x00] & 0xf0) == A2FileProDOS::kStorageDeleted) { - /* skip deleted entries */ - continue; - } - - pFile = new A2FileProDOS(this); - if (pFile == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - A2FileProDOS::DirEntry* pEntry; - pEntry = &pFile->fDirEntry; - A2FileProDOS::InitDirEntry(pEntry, entryBuf); - - pFile->SetParent(pParent); - pFile->fParentDirBlock = thisBlock; - pFile->fParentDirIdx = idx; - - pFile->SetPathName(basePath, pEntry->fileName); - - if (pEntry->keyPointer <= kVolHeaderBlock) { - LOGI("ProDOS invalid key pointer %d on '%s'", - pEntry->keyPointer, pFile->GetPathName()); - pFile->SetQuality(A2File::kQualityDamaged); - } else - if (pEntry->storageType == A2FileProDOS::kStorageExtended) { - dierr = ReadExtendedInfo(pFile); - if (dierr != kDIErrNone) { - pFile->SetQuality(A2File::kQualityDamaged); - dierr = kDIErrNone; - } - } - - //pFile->Dump(); - AddFileToList(pFile); - (*pCount)++; - - if (!fpImg->UpdateScanProgress(NULL)) { - LOGI(" ProDOS cancelled by user"); - dierr = kDIErrCancelled; - goto bail; - } - - if (pEntry->storageType == A2FileProDOS::kStorageDirectory) { - // don't need to check for kStorageVolumeDirHeader here - dierr = RecursiveDirAdd(pFile, pEntry->keyPointer, - pFile->GetPathName(), depth+1); - if (dierr != kDIErrNone) { - if (dierr == kDIErrCancelled) - goto bail; - - /* mark subdir as damaged and keep going */ - pFile->SetQuality(A2File::kQualityDamaged); - dierr = kDIErrNone; - } - } - } - -bail: - return dierr; -} - -/* - * Pull the directory header out of the first block of a directory. - */ -DIError DiskFSProDOS::GetDirHeader(const uint8_t* blkBuf, DirHeader* pHeader) -{ - int nameLen; - - pHeader->storageType = (blkBuf[0x04] & 0xf0) >> 4; - if (pHeader->storageType != A2FileProDOS::kStorageSubdirHeader && - pHeader->storageType != A2FileProDOS::kStorageVolumeDirHeader) - { - LOGI(" ProDOS WARNING: subdir header has wrong storage type (%d)", - pHeader->storageType); - /* keep going... might be bad idea */ - } - nameLen = blkBuf[0x04] & 0x0f; - memcpy(pHeader->dirName, &blkBuf[0x05], nameLen); - pHeader->dirName[nameLen] = '\0'; - pHeader->createWhen = GetLongLE(&blkBuf[0x1c]); - pHeader->version = blkBuf[0x20]; - pHeader->minVersion = blkBuf[0x21]; - pHeader->access = blkBuf[0x22]; - pHeader->entryLength = blkBuf[0x23]; - pHeader->entriesPerBlock = blkBuf[0x24]; - pHeader->fileCount = GetShortLE(&blkBuf[0x25]); - pHeader->parentPointer = GetShortLE(&blkBuf[0x27]); - pHeader->parentEntry = blkBuf[0x29]; - pHeader->parentEntryLength = blkBuf[0x2a]; - - if (pHeader->entryLength * pHeader->entriesPerBlock > kBlkSize || - pHeader->entryLength * pHeader->entriesPerBlock == 0) - { - LOGI(" ProDOS invalid subdir header: entryLen=%d, entriesPerBlock=%d", - pHeader->entryLength, pHeader->entriesPerBlock); - return kDIErrBadDirectory; - } - - return kDIErrNone; -} - -/* - * Read the information from the key block of an extended file. - * - * There's some "HFS Finder information" stuffed into the key block - * right after the data fork info, but I'm planning to ignore that. - */ -DIError DiskFSProDOS::ReadExtendedInfo(A2FileProDOS* pFile) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - - dierr = fpImg->ReadBlock(pFile->fDirEntry.keyPointer, blkBuf); - if (dierr != kDIErrNone) { - LOGI(" ProDOS ReadExtendedInfo: unable to read key block %d", - pFile->fDirEntry.keyPointer); - goto bail; - } - - pFile->fExtData.storageType = blkBuf[0x0000] & 0x0f; - pFile->fExtData.keyBlock = GetShortLE(&blkBuf[0x0001]); - pFile->fExtData.blocksUsed = GetShortLE(&blkBuf[0x0003]); - pFile->fExtData.eof = GetLongLE(&blkBuf[0x0005]); - pFile->fExtData.eof &= 0x00ffffff; - - pFile->fExtRsrc.storageType = blkBuf[0x0100] & 0x0f; - pFile->fExtRsrc.keyBlock = GetShortLE(&blkBuf[0x0101]); - pFile->fExtRsrc.blocksUsed = GetShortLE(&blkBuf[0x0103]); - pFile->fExtRsrc.eof = GetLongLE(&blkBuf[0x0105]); - pFile->fExtRsrc.eof &= 0x00ffffff; - - if (pFile->fExtData.keyBlock <= kVolHeaderBlock || - pFile->fExtRsrc.keyBlock <= kVolHeaderBlock) - { - LOGI(" ProDOS ReadExtendedInfo: found bad extended key blocks %d/%d", - pFile->fExtData.keyBlock, pFile->fExtRsrc.keyBlock); - return kDIErrBadFile; - } - -bail: - return dierr; -} - -/* - * Scan all of the files on the disk, reading their block usage into the - * volume usage map. This is important for detecting damage, and makes - * later accesses easier. - * - * As a side-effect, we set the "sparse" length for the file. - */ -DIError DiskFSProDOS::ScanFileUsage(void) -{ - DIError dierr = kDIErrNone; - A2FileProDOS* pFile; - long blockCount, indexCount, sparseCount; - uint16_t* blockList = NULL; - uint16_t* indexList = NULL; - - pFile = (A2FileProDOS*) GetNextFile(NULL); - while (pFile != NULL) { - if (!fpImg->UpdateScanProgress(NULL)) { - LOGI(" ProDOS cancelled by user"); - dierr = kDIErrCancelled; - goto bail; - } - - //pFile->Dump(); - if (pFile->GetQuality() == A2File::kQualityDamaged) - goto skip; - - if (pFile->fDirEntry.storageType == A2FileProDOS::kStorageExtended) { - /* resource fork */ - if (!A2FileProDOS::IsRegularFile(pFile->fExtRsrc.storageType)) { - /* not expecting to find a directory here, but it happens */ - dierr = kDIErrBadFile; - } else { - dierr = pFile->LoadBlockList(pFile->fExtRsrc.storageType, - pFile->fExtRsrc.keyBlock, pFile->fExtRsrc.eof, - &blockCount, &blockList, &indexCount, &indexList); - } - if (dierr != kDIErrNone) { - LOGI(" ProDOS skipping scan rsrc '%s'", - pFile->fDirEntry.fileName); - pFile->SetQuality(A2File::kQualityDamaged); - goto skip; - } - ScanBlockList(blockCount, blockList, indexCount, indexList, - &sparseCount); - pFile->fSparseRsrcEof = - (di_off_t) pFile->fExtRsrc.eof - sparseCount * kBlkSize; - //LOGI(" SparseCount %d rsrcEof %d '%s'", - // sparseCount, pFile->fSparseRsrcEof, pFile->fDirEntry.fileName); - delete[] blockList; - blockList = NULL; - delete[] indexList; - indexList = NULL; - - /* data fork */ - if (!A2FileProDOS::IsRegularFile(pFile->fExtRsrc.storageType)) { - dierr = kDIErrBadFile; - } else { - dierr = pFile->LoadBlockList(pFile->fExtData.storageType, - pFile->fExtData.keyBlock, pFile->fExtData.eof, - &blockCount, &blockList, &indexCount, &indexList); - } - if (dierr != kDIErrNone) { - LOGI(" ProDOS skipping scan data '%s'", - pFile->fDirEntry.fileName); - pFile->SetQuality(A2File::kQualityDamaged); - goto skip; - } - ScanBlockList(blockCount, blockList, indexCount, indexList, - &sparseCount); - pFile->fSparseDataEof = - (di_off_t) pFile->fExtData.eof - sparseCount * kBlkSize; - //LOGI(" SparseCount %d dataEof %d '%s'", - // sparseCount, pFile->fSparseDataEof, pFile->fDirEntry.fileName); - delete[] blockList; - blockList = NULL; - delete[] indexList; - indexList = NULL; - - /* mark the extended key block as in-use */ - SetBlockUsage(pFile->fDirEntry.keyPointer, - VolumeUsage::kChunkPurposeFileStruct); - } else if (pFile->fDirEntry.storageType == A2FileProDOS::kStorageDirectory || - pFile->fDirEntry.storageType == A2FileProDOS::kStorageVolumeDirHeader) - { - /* we already got these during the recursive descent */ - /* (could do them here if we used "fake" directory entry - for volume dir to lead off the recursion) */ - goto skip; - } else if (pFile->fDirEntry.storageType == A2FileProDOS::kStorageSeedling || - pFile->fDirEntry.storageType == A2FileProDOS::kStorageSapling || - pFile->fDirEntry.storageType == A2FileProDOS::kStorageTree) - { - /* standard file */ - dierr = pFile->LoadBlockList(pFile->fDirEntry.storageType, - pFile->fDirEntry.keyPointer, pFile->fDirEntry.eof, - &blockCount, &blockList, &indexCount, &indexList); - if (dierr != kDIErrNone) { - LOGI(" ProDOS skipping scan '%s'", - pFile->fDirEntry.fileName); - pFile->SetQuality(A2File::kQualityDamaged); - goto skip; - } - ScanBlockList(blockCount, blockList, indexCount, indexList, - &sparseCount); - pFile->fSparseDataEof = - (di_off_t) pFile->fDirEntry.eof - sparseCount * kBlkSize; - //LOGI(" +++ sparseCount=%ld blockCount=%ld sparseDataEof=%ld '%s'", - // sparseCount, blockCount, (long) pFile->fSparseDataEof, - // pFile->fDirEntry.fileName); - - delete[] blockList; - blockList = NULL; - delete[] indexList; - indexList = NULL; - } else { - LOGI(" ProDOS found weird storage type %d on '%s', ignoring", - pFile->fDirEntry.storageType, pFile->fDirEntry.fileName); - pFile->SetQuality(A2File::kQualityDamaged); - } - - /* - * A completely empty file written as zero blocks (as opposed to simply - * having its EOF extended, e.g. "sparse seedlings") will have zero data - * blocks but possibly an EOF that doesn't land on 512 bytes. This can - * result in a slightly negative "sparse length", which we trim to zero - * here. - */ - //if (stricmp(pFile->fDirEntry.fileName, "EMPTY.SPARSE.R") == 0) - // LOGI("wahoo"); - if (pFile->fSparseDataEof < 0) - pFile->fSparseDataEof = 0; - if (pFile->fSparseRsrcEof < 0) - pFile->fSparseRsrcEof = 0; - -skip: - pFile = (A2FileProDOS*) GetNextFile(pFile); - } - - dierr = kDIErrNone; - -bail: - return dierr; -} - -/* - * Scan a block list into the volume usage map. - */ -void DiskFSProDOS::ScanBlockList(long blockCount, uint16_t* blockList, - long indexCount, uint16_t* indexList, long* pSparseCount) -{ - assert(blockList != NULL); - assert(indexCount == 0 || indexList != NULL); - assert(pSparseCount != NULL); - - *pSparseCount = 0; - - int i; - for (i = 0; i < blockCount; i++) { - if (blockList[i] != 0) { - SetBlockUsage(blockList[i], VolumeUsage::kChunkPurposeUserData); - } else { - (*pSparseCount)++; // sparse data block - } - } - - for (i = 0; i < indexCount; i++) { - if (indexList[i] != 0) { - SetBlockUsage(indexList[i], VolumeUsage::kChunkPurposeFileStruct); - } // else sparse index block - } -} - -/* - * ProDOS disks may contain other filesystems. The typical DOS-in-ProDOS - * strategy involves marking a bunch of blocks at the end of the disc as - * "in use" without creating a file to go along with them. - * - * We look for certain types of embedded volume by looking for disk - * usage patterns and then testing those with the standard disk testing - * facilities. - */ -DIError DiskFSProDOS::ScanForSubVolumes(void) -{ - DIError dierr = kDIErrNone; - VolumeUsage::ChunkState cstate; - int firstBlock, matchCount; - int block; - - /* this is guaranteed by constraint in volume header read */ - assert(fTotalBlocks <= fpImg->GetNumBlocks()); - - if (fTotalBlocks != 1600) { - LOGI(" ProDOS ScanForSub: not 800K disk (%ld)", - fpImg->GetNumBlocks()); - return kDIErrNone; // only scan 800K disks - } - - matchCount = 0; - for (block = fTotalBlocks-1; block >= 0; block--) { - if (fVolumeUsage.GetChunkState(block, &cstate) != kDIErrNone) { - assert(false); - return kDIErrGeneric; - } - - if (!cstate.isMarkedUsed || cstate.isUsed) - break; - - matchCount++; - } - firstBlock = block+1; - - LOGI("MATCH COUNT %d", matchCount); - if (matchCount < 35*8) // 280 blocks on 35-track floppy - return kDIErrNone; - //if (matchCount % 8 != 0) { // must have 4K tracks - // LOGI(" ProDOS ScanForSub: matchCount %d odd number", - // matchCount); - // return kDIErrNone; - //} - - /* - * Try #1: this is a single DOS 3.3 volume (200K or less). - */ - if ((matchCount % 8) == 0 && matchCount <= (50*8)) { // max 50 tracks - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - LOGI(" Sub #1: looking for single DOS volume"); - dierr = FindSubVolume(firstBlock, matchCount, &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - AddSubVolumeToList(pNewImg, pNewFS); - MarkSubVolumeBlocks(firstBlock, matchCount); - return kDIErrNone; - } - } - - - /* - * Try #2: there are multiple 140K DOS 3.3 volumes here. - * - * We may want to override their volume numbers, but it looks like - * DOS Master disks have distinct volume numbers anyway. - */ - const int kBlkCount140 = 140*2; - if ((matchCount % (kBlkCount140)) == 0) { - int i, count; - bool found = false; - - count = matchCount / kBlkCount140; - LOGI(" Sub #2: looking for %d 140K volumes", - matchCount / kBlkCount140); - - for (i = 0; i < count; i++) { - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - LOGI(" Sub #2: looking for DOS volume at (%d)", - firstBlock + i * kBlkCount140); - dierr = FindSubVolume(firstBlock + i * kBlkCount140, - kBlkCount140, &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - AddSubVolumeToList(pNewImg, pNewFS); - MarkSubVolumeBlocks(firstBlock + i * kBlkCount140, - kBlkCount140); - found = true; - } - } - if (found) - return kDIErrNone; - } - - /* - * Try #3: there are five 160K DOS 3.3 volumes here (which works out - * to exactly 800K). The first DOS volume loses early tracks as - * needed to accommodate the ProDOS directory and up to 28K of - * boot files. - * - * Because the first 160K volume starts at the front of the disk, - * we need to restrict this to non-ProDOS sub-volumes, or we'll see - * a "ghost" volume in the first position. This stuff is going to - * fail if we test for ProDOS before we check for DOS 3.3. - */ - const int kBlkCount160 = 160*2; - if (matchCount == 1537 || matchCount == 1593) { - int i, count; - bool found = false; - - count = 1600 / kBlkCount160; - LOGI(" Sub #3: looking for %d 160K volumes", - matchCount / kBlkCount160); - - for (i = 0; i < count; i++) { - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - LOGI(" Sub #3: looking for DOS volume at (%d)", - i * kBlkCount160); - dierr = FindSubVolume(i * kBlkCount160, - kBlkCount160, &pNewImg, &pNewFS); - if (dierr == kDIErrNone) { - if (pNewImg->GetFSFormat() == DiskImg::kFormatDOS33) { - AddSubVolumeToList(pNewImg, pNewFS); - if (i == 0) - MarkSubVolumeBlocks(firstBlock, kBlkCount160 - firstBlock); - else - MarkSubVolumeBlocks(i * kBlkCount160, kBlkCount160); - } else { - delete pNewFS; - delete pNewImg; - pNewFS = NULL; - pNewImg = NULL; - } - } - } - if (found) - return kDIErrNone; - } - - return kDIErrNone; -} - -/* - * Look for a sub-volume at the specified location. - * - * On success, "*ppDiskImg" and "*ppDiskFS" are newly-allocated objects - * of the appropriate kind. - */ -DIError DiskFSProDOS::FindSubVolume(long blockStart, long blockCount, - DiskImg** ppDiskImg, DiskFS** ppDiskFS) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = pNewImg->OpenImage(fpImg, blockStart, blockCount); - if (dierr != kDIErrNone) { - LOGI(" Sub: OpenImage(%ld,%ld) failed (err=%d)", - blockStart, blockCount, dierr); - goto bail; - } - - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" Sub: analysis failed (err=%d)", dierr); - goto bail; - } - - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" Sub: unable to identify filesystem"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* open a DiskFS for the sub-image */ - LOGI(" Sub DiskImg succeeded, opening DiskFS"); - pNewFS = pNewImg->OpenAppropriateDiskFS(); - if (pNewFS == NULL) { - LOGI(" Sub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* load the files from the sub-image */ - dierr = pNewFS->Initialize(pNewImg, kInitFull); - if (dierr != kDIErrNone) { - LOGE(" Sub: error %d reading list of files from disk", dierr); - goto bail; - } - -bail: - if (dierr != kDIErrNone) { - delete pNewFS; - delete pNewImg; - } else { - assert(pNewImg != NULL && pNewFS != NULL); - *ppDiskImg = pNewImg; - *ppDiskFS = pNewFS; - } - return dierr; -} - -/* - * Mark the blocks used by a sub-volume as in-use. - */ -void DiskFSProDOS::MarkSubVolumeBlocks(long block, long count) -{ - VolumeUsage::ChunkState cstate; - - while (count--) { - if (fVolumeUsage.GetChunkState(block, &cstate) != kDIErrNone) { - assert(false); - return; - } - - assert(cstate.isMarkedUsed && !cstate.isUsed); - cstate.isUsed = true; - cstate.purpose = VolumeUsage::kChunkPurposeEmbedded; - if (fVolumeUsage.SetChunkState(block, &cstate) != kDIErrNone) { - assert(false); - return; - } - - block++; - } -} - -/* - * Put a ProDOS filesystem image on the specified DiskImg. - */ -DIError DiskFSProDOS::Format(DiskImg* pDiskImg, const char* volName) -{ - DIError dierr = kDIErrNone; - const bool allowLowerCase = (GetParameter(kParmProDOS_AllowLowerCase) != 0); - uint8_t blkBuf[kBlkSize]; - long formatBlocks; - - if (!IsValidVolumeName(volName)) - return kDIErrInvalidArg; - - /* set fpImg so calls that rely on it will work; we un-set it later */ - assert(fpImg == NULL); - SetDiskImg(pDiskImg); - - LOGI(" ProDOS formatting disk image"); - - /* write ProDOS blocks */ - dierr = fpImg->OverrideFormat(fpImg->GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, fpImg->GetSectorOrder()); - if (dierr != kDIErrNone) - goto bail; - - formatBlocks = pDiskImg->GetNumBlocks(); - if (formatBlocks > 65536) { - LOGI(" ProDOS: rejecting format req blocks=%ld", formatBlocks); - assert(false); - return kDIErrInvalidArg; - } - if (formatBlocks == 65536) { - LOGI(" ProDOS: trimming FS size from 65536 to 65535"); - formatBlocks = 65535; - } - - /* - * We should now zero out the disk blocks, but on a 32MB volume that can - * take a little while. The blocks are zeroed for us when a disk is - * created, so this is really only needed if we're re-formatting an - * existing disk. CiderPress currently doesn't do that, so we're going - * to skip it here. - */ -// dierr = fpImg->ZeroImage(); - LOGI(" ProDOS (not zeroing blocks)"); - - /* - * Start by writing blocks 0 and 1 (the boot blocks). This is done from - * a standard boot block image that happens to be essentially the same - * for all types of disks. (Apparently these blocks are only used when - * booting 5.25" disks?) - */ - dierr = WriteBootBlocks(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Write the four-block disk volume entry. Start by writing the three - * empty ones (which only have the prev/next pointers), and finish by - * writing the first block, which has the volume directory header. - */ - int i; - memset(blkBuf, 0, sizeof(blkBuf)); - for (i = kVolHeaderBlock+1; i < kVolHeaderBlock+kVolDirExpectedNumBlocks; i++) - { - PutShortLE(&blkBuf[0x00], i-1); - if (i == kVolHeaderBlock+kVolDirExpectedNumBlocks-1) - PutShortLE(&blkBuf[0x02], 0); - else - PutShortLE(&blkBuf[0x02], i+1); - - dierr = fpImg->WriteBlock(i, blkBuf); - if (dierr != kDIErrNone) { - LOGI(" Format: block %d write failed (err=%d)", i, dierr); - goto bail; - } - } - - char upperName[A2FileProDOS::kMaxFileName+1]; - uint16_t lcFlags; - time_t now; - - now = time(NULL); - - /* - * Compute the lower-case flags, if desired. The test for "allowLowerCase" - * is probably bogus, because in most cases we just got created by the - * DiskImg and the app hasn't had time to set the "allow lower" flag. - * So it defaults to "enabled", which means the app needs to manually - * change the volume name to lower case. - */ - UpperCaseName(upperName, volName); - lcFlags = 0; - if (allowLowerCase) - lcFlags = GenerateLowerCaseBits(upperName, volName, false); - - PutShortLE(&blkBuf[0x00], 0); - PutShortLE(&blkBuf[0x02], kVolHeaderBlock+1); - blkBuf[0x04] = strlen(upperName) | (A2FileProDOS::kStorageVolumeDirHeader << 4); - strncpy((char*) &blkBuf[0x05], upperName, A2FileProDOS::kMaxFileName); - PutLongLE(&blkBuf[0x16], A2FileProDOS::ConvertProDate(now)); - PutShortLE(&blkBuf[0x1a], lcFlags); - PutLongLE(&blkBuf[0x1c], A2FileProDOS::ConvertProDate(now)); - blkBuf[0x20] = 0; // GS/OS uses 5? - /* min_version is zero */ - blkBuf[0x22] = 0xe3; // access (format/rename/backup/write/read) - blkBuf[0x23] = 0x27; // entry_length: always $27 - blkBuf[0x24] = 0x0d; // entries_per_block: always $0d - /* file_count is zero - does not include volume dir */ - PutShortLE(&blkBuf[0x27], kVolHeaderBlock + kVolDirExpectedNumBlocks); // bit_map_pointer - PutShortLE(&blkBuf[0x29], (uint16_t) formatBlocks); // total_blocks - dierr = fpImg->WriteBlock(kVolHeaderBlock, blkBuf); - if (dierr != kDIErrNone) { - LOGI(" Format: block %d write failed (err=%d)", - kVolHeaderBlock, dierr); - goto bail; - } - - /* check our work, and set some object fields, by reading what we wrote */ - dierr = LoadVolHeader(); - if (dierr != kDIErrNone) { - LOGI(" GLITCH: couldn't read header we just wrote (err=%d)", dierr); - goto bail; - } - - /* - * Generate the initial block usage map. The only entries in use are - * right at the start of the disk. When we finish, scan what we just - * created into - */ - CreateEmptyBlockMap(); - - /* don't do this -- assume they're going to call Initialize() later */ - //ScanVolBitmap(); - -bail: - SetDiskImg(NULL); // shouldn't really be set by us - return dierr; -} - - -/* - * The standard boot block found on ProDOS disks. The same thing appears - * to be written to both 5.25" and 3.5" disks, with some modifications - * made for HD images. - * - * This is block 0; block 1 is either zeroed out or filled with a repeating - * pattern. - */ -const uint8_t gFloppyBlock0[512] = { - 0x01, 0x38, 0xb0, 0x03, 0x4c, 0x32, 0xa1, 0x86, 0x43, 0xc9, 0x03, 0x08, - 0x8a, 0x29, 0x70, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, 0xa0, - 0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, 0xd0, 0x3a, 0xb0, 0x0e, 0xa9, - 0x03, 0x8d, 0x00, 0x08, 0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b, 0x48, - 0x60, 0x85, 0x40, 0x85, 0x48, 0xa0, 0x63, 0xb1, 0x48, 0x99, 0x94, 0x09, - 0xc8, 0xc0, 0xeb, 0xd0, 0xf6, 0xa2, 0x06, 0xbc, 0x1d, 0x09, 0xbd, 0x24, - 0x09, 0x99, 0xf2, 0x09, 0xbd, 0x2b, 0x09, 0x9d, 0x7f, 0x0a, 0xca, 0x10, - 0xee, 0xa9, 0x09, 0x85, 0x49, 0xa9, 0x86, 0xa0, 0x00, 0xc9, 0xf9, 0xb0, - 0x2f, 0x85, 0x48, 0x84, 0x60, 0x84, 0x4a, 0x84, 0x4c, 0x84, 0x4e, 0x84, - 0x47, 0xc8, 0x84, 0x42, 0xc8, 0x84, 0x46, 0xa9, 0x0c, 0x85, 0x61, 0x85, - 0x4b, 0x20, 0x12, 0x09, 0xb0, 0x68, 0xe6, 0x61, 0xe6, 0x61, 0xe6, 0x46, - 0xa5, 0x46, 0xc9, 0x06, 0x90, 0xef, 0xad, 0x00, 0x0c, 0x0d, 0x01, 0x0c, - 0xd0, 0x6d, 0xa9, 0x04, 0xd0, 0x02, 0xa5, 0x4a, 0x18, 0x6d, 0x23, 0x0c, - 0xa8, 0x90, 0x0d, 0xe6, 0x4b, 0xa5, 0x4b, 0x4a, 0xb0, 0x06, 0xc9, 0x0a, - 0xf0, 0x55, 0xa0, 0x04, 0x84, 0x4a, 0xad, 0x02, 0x09, 0x29, 0x0f, 0xa8, - 0xb1, 0x4a, 0xd9, 0x02, 0x09, 0xd0, 0xdb, 0x88, 0x10, 0xf6, 0x29, 0xf0, - 0xc9, 0x20, 0xd0, 0x3b, 0xa0, 0x10, 0xb1, 0x4a, 0xc9, 0xff, 0xd0, 0x33, - 0xc8, 0xb1, 0x4a, 0x85, 0x46, 0xc8, 0xb1, 0x4a, 0x85, 0x47, 0xa9, 0x00, - 0x85, 0x4a, 0xa0, 0x1e, 0x84, 0x4b, 0x84, 0x61, 0xc8, 0x84, 0x4d, 0x20, - 0x12, 0x09, 0xb0, 0x17, 0xe6, 0x61, 0xe6, 0x61, 0xa4, 0x4e, 0xe6, 0x4e, - 0xb1, 0x4a, 0x85, 0x46, 0xb1, 0x4c, 0x85, 0x47, 0x11, 0x4a, 0xd0, 0xe7, - 0x4c, 0x00, 0x20, 0x4c, 0x3f, 0x09, 0x26, 0x50, 0x52, 0x4f, 0x44, 0x4f, - 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xa5, 0x60, - 0x85, 0x44, 0xa5, 0x61, 0x85, 0x45, 0x6c, 0x48, 0x00, 0x08, 0x1e, 0x24, - 0x3f, 0x45, 0x47, 0x76, 0xf4, 0xd7, 0xd1, 0xb6, 0x4b, 0xb4, 0xac, 0xa6, - 0x2b, 0x18, 0x60, 0x4c, 0xbc, 0x09, 0xa9, 0x9f, 0x48, 0xa9, 0xff, 0x48, - 0xa9, 0x01, 0xa2, 0x00, 0x4c, 0x79, 0xf4, 0x20, 0x58, 0xfc, 0xa0, 0x1c, - 0xb9, 0x50, 0x09, 0x99, 0xae, 0x05, 0x88, 0x10, 0xf7, 0x4c, 0x4d, 0x09, - 0xaa, 0xaa, 0xaa, 0xa0, 0xd5, 0xce, 0xc1, 0xc2, 0xcc, 0xc5, 0xa0, 0xd4, - 0xcf, 0xa0, 0xcc, 0xcf, 0xc1, 0xc4, 0xa0, 0xd0, 0xd2, 0xcf, 0xc4, 0xcf, - 0xd3, 0xa0, 0xaa, 0xaa, 0xaa, 0xa5, 0x53, 0x29, 0x03, 0x2a, 0x05, 0x2b, - 0xaa, 0xbd, 0x80, 0xc0, 0xa9, 0x2c, 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe9, - 0x01, 0xd0, 0xf7, 0xa6, 0x2b, 0x60, 0xa5, 0x46, 0x29, 0x07, 0xc9, 0x04, - 0x29, 0x03, 0x08, 0x0a, 0x28, 0x2a, 0x85, 0x3d, 0xa5, 0x47, 0x4a, 0xa5, - 0x46, 0x6a, 0x4a, 0x4a, 0x85, 0x41, 0x0a, 0x85, 0x51, 0xa5, 0x45, 0x85, - 0x27, 0xa6, 0x2b, 0xbd, 0x89, 0xc0, 0x20, 0xbc, 0x09, 0xe6, 0x27, 0xe6, - 0x3d, 0xe6, 0x3d, 0xb0, 0x03, 0x20, 0xbc, 0x09, 0xbc, 0x88, 0xc0, 0x60, - 0xa5, 0x40, 0x0a, 0x85, 0x53, 0xa9, 0x00, 0x85, 0x54, 0xa5, 0x53, 0x85, - 0x50, 0x38, 0xe5, 0x51, 0xf0, 0x14, 0xb0, 0x04, 0xe6, 0x53, 0x90, 0x02, - 0xc6, 0x53, 0x38, 0x20, 0x6d, 0x09, 0xa5, 0x50, 0x18, 0x20, 0x6f, 0x09, - 0xd0, 0xe3, 0xa0, 0x7f, 0x84, 0x52, 0x08, 0x28, 0x38, 0xc6, 0x52, 0xf0, - 0xce, 0x18, 0x08, 0x88, 0xf0, 0xf5, 0xbd, 0x8c, 0xc0, 0x10, 0xfb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -const uint8_t gHDBlock0[] = { - 0x01, 0x38, 0xb0, 0x03, 0x4c, 0x1c, 0x09, 0x78, 0x86, 0x43, 0xc9, 0x03, - 0x08, 0x8a, 0x29, 0x70, 0x4a, 0x4a, 0x4a, 0x4a, 0x09, 0xc0, 0x85, 0x49, - 0xa0, 0xff, 0x84, 0x48, 0x28, 0xc8, 0xb1, 0x48, 0xd0, 0x3a, 0xb0, 0x0e, - 0xa9, 0x03, 0x8d, 0x00, 0x08, 0xe6, 0x3d, 0xa5, 0x49, 0x48, 0xa9, 0x5b, - 0x48, 0x60, 0x85, 0x40, 0x85, 0x48, 0xa0, 0x5e, 0xb1, 0x48, 0x99, 0x94, - 0x09, 0xc8, 0xc0, 0xeb, 0xd0, 0xf6, 0xa2, 0x06, 0xbc, 0x32, 0x09, 0xbd, - 0x39, 0x09, 0x99, 0xf2, 0x09, 0xbd, 0x40, 0x09, 0x9d, 0x7f, 0x0a, 0xca, - 0x10, 0xee, 0xa9, 0x09, 0x85, 0x49, 0xa9, 0x86, 0xa0, 0x00, 0xc9, 0xf9, - 0xb0, 0x2f, 0x85, 0x48, 0x84, 0x60, 0x84, 0x4a, 0x84, 0x4c, 0x84, 0x4e, - 0x84, 0x47, 0xc8, 0x84, 0x42, 0xc8, 0x84, 0x46, 0xa9, 0x0c, 0x85, 0x61, - 0x85, 0x4b, 0x20, 0x27, 0x09, 0xb0, 0x66, 0xe6, 0x61, 0xe6, 0x61, 0xe6, - 0x46, 0xa5, 0x46, 0xc9, 0x06, 0x90, 0xef, 0xad, 0x00, 0x0c, 0x0d, 0x01, - 0x0c, 0xd0, 0x52, 0xa9, 0x04, 0xd0, 0x02, 0xa5, 0x4a, 0x18, 0x6d, 0x23, - 0x0c, 0xa8, 0x90, 0x0d, 0xe6, 0x4b, 0xa5, 0x4b, 0x4a, 0xb0, 0x06, 0xc9, - 0x0a, 0xf0, 0x71, 0xa0, 0x04, 0x84, 0x4a, 0xad, 0x20, 0x09, 0x29, 0x0f, - 0xa8, 0xb1, 0x4a, 0xd9, 0x20, 0x09, 0xd0, 0xdb, 0x88, 0x10, 0xf6, 0xa0, - 0x16, 0xb1, 0x4a, 0x4a, 0x6d, 0x1f, 0x09, 0x8d, 0x1f, 0x09, 0xa0, 0x11, - 0xb1, 0x4a, 0x85, 0x46, 0xc8, 0xb1, 0x4a, 0x85, 0x47, 0xa9, 0x00, 0x85, - 0x4a, 0xa0, 0x1e, 0x84, 0x4b, 0x84, 0x61, 0xc8, 0x84, 0x4d, 0x20, 0x27, - 0x09, 0xb0, 0x35, 0xe6, 0x61, 0xe6, 0x61, 0xa4, 0x4e, 0xe6, 0x4e, 0xb1, - 0x4a, 0x85, 0x46, 0xb1, 0x4c, 0x85, 0x47, 0x11, 0x4a, 0xd0, 0x18, 0xa2, - 0x01, 0xa9, 0x00, 0xa8, 0x91, 0x60, 0xc8, 0xd0, 0xfb, 0xe6, 0x61, 0xea, - 0xea, 0xca, 0x10, 0xf4, 0xce, 0x1f, 0x09, 0xf0, 0x07, 0xd0, 0xd8, 0xce, - 0x1f, 0x09, 0xd0, 0xca, 0x58, 0x4c, 0x00, 0x20, 0x4c, 0x47, 0x09, 0x02, - 0x26, 0x50, 0x52, 0x4f, 0x44, 0x4f, 0x53, 0xa5, 0x60, 0x85, 0x44, 0xa5, - 0x61, 0x85, 0x45, 0x6c, 0x48, 0x00, 0x08, 0x1e, 0x24, 0x3f, 0x45, 0x47, - 0x76, 0xf4, 0xd7, 0xd1, 0xb6, 0x4b, 0xb4, 0xac, 0xa6, 0x2b, 0x18, 0x60, - 0x4c, 0xbc, 0x09, 0x20, 0x58, 0xfc, 0xa0, 0x14, 0xb9, 0x58, 0x09, 0x99, - 0xb1, 0x05, 0x88, 0x10, 0xf7, 0x4c, 0x55, 0x09, 0xd5, 0xce, 0xc1, 0xc2, - 0xcc, 0xc5, 0xa0, 0xd4, 0xcf, 0xa0, 0xcc, 0xcf, 0xc1, 0xc4, 0xa0, 0xd0, - 0xd2, 0xcf, 0xc4, 0xcf, 0xd3, 0xa5, 0x53, 0x29, 0x03, 0x2a, 0x05, 0x2b, - 0xaa, 0xbd, 0x80, 0xc0, 0xa9, 0x2c, 0xa2, 0x11, 0xca, 0xd0, 0xfd, 0xe9, - 0x01, 0xd0, 0xf7, 0xa6, 0x2b, 0x60, 0xa5, 0x46, 0x29, 0x07, 0xc9, 0x04, - 0x29, 0x03, 0x08, 0x0a, 0x28, 0x2a, 0x85, 0x3d, 0xa5, 0x47, 0x4a, 0xa5, - 0x46, 0x6a, 0x4a, 0x4a, 0x85, 0x41, 0x0a, 0x85, 0x51, 0xa5, 0x45, 0x85, - 0x27, 0xa6, 0x2b, 0xbd, 0x89, 0xc0, 0x20, 0xbc, 0x09, 0xe6, 0x27, 0xe6, - 0x3d, 0xe6, 0x3d, 0xb0, 0x03, 0x20, 0xbc, 0x09, 0xbc, 0x88, 0xc0, 0x60, - 0xa5, 0x40, 0x0a, 0x85, 0x53, 0xa9, 0x00, 0x85, 0x54, 0xa5, 0x53, 0x85, - 0x50, 0x38, 0xe5, 0x51, 0xf0, 0x14, 0xb0, 0x04, 0xe6, 0x53, 0x90, 0x02, - 0xc6, 0x53, 0x38, 0x20, 0x6d, 0x09, 0xa5, 0x50, 0x18, 0x20, 0x6f, 0x09, - 0xd0, 0xe3, 0xa0, 0x7f, 0x84, 0x52, 0x08, 0x28, 0x38, 0xc6, 0x52, 0xf0, - 0xce, 0x18, 0x08, 0x88, 0xf0, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - -/* - * Write the ProDOS boot blocks onto the disk image. - */ -DIError DiskFSProDOS::WriteBootBlocks(void) -{ - DIError dierr; - uint8_t block0[512]; - uint8_t block1[512]; - bool isHD; - - assert(fpImg->GetHasBlocks()); - - if (fpImg->GetNumBlocks() == 280 || fpImg->GetNumBlocks() == 1600) - isHD = false; - else - isHD = true; - - if (isHD) { - memcpy(block0, gHDBlock0, sizeof(block0)); - // repeating 0x42 0x48 pattern - int i; - uint8_t* ucp; - for (i = 0, ucp = block1; i < (int)sizeof(block1); i++) - *ucp++ = 0x42 + 6 * (i & 0x01); - } else { - memcpy(block0, gFloppyBlock0, sizeof(block0)); - memset(block1, 0, sizeof(block1)); - } - - dierr = fpImg->WriteBlock(0, block0); - if (dierr != kDIErrNone) { - LOGI(" WriteBootBlocks: block0 write failed (err=%d)", dierr); - return dierr; - } - dierr = fpImg->WriteBlock(1, block1); - if (dierr != kDIErrNone) { - LOGI(" WriteBootBlocks: block1 write failed (err=%d)", dierr); - return dierr; - } - - return kDIErrNone; -} - -/* - * Create a new, empty file. There are three different kinds of files we - * need to be able to handle: - * (1) Standard file. Create the directory entry and an empty "seedling" - * file with one block allocated. It does not appear that "sparse" - * allocation applies to seedlings. - * (2) Extended file. Create the directory entry, the extended key block, - * and allocate one seedling block for each fork. - * (3) Subdirectory. Allocate a block for the subdir and fill in the - * details in the subdir header. - * - * In all cases we need to add a new directory entry as well. - * - * By not flushing the updated block usage map and the updated directory - * block(s) until we're done, we can abort our changes at any time if we - * encounter a damaged sector or run out of disk space. We do need to be - * careful when updating our internal copies of things like file storage - * types and lengths, updating them only after everything else has - * succeeded. - * - * NOTE: if we detect an empty directory holder, "*ppNewFile" does NOT - * end up pointing at a file. - * - * NOTE: kParm_CreateUnique does *not* apply to creating subdirectories. - */ -DIError DiskFSProDOS::CreateFile(const CreateParms* pParms, A2File** ppNewFile) -{ - DIError dierr = kDIErrNone; - char* normalizedPath = NULL; - char* basePath = NULL; - char* fileName = NULL; - A2FileProDOS* pSubdir = NULL; - A2FileDescr* pOpenSubdir = NULL; - A2FileProDOS* pNewFile = NULL; - uint8_t* subdirBuf = NULL; - const bool allowLowerCase = (GetParameter(kParmProDOS_AllowLowerCase) != 0); - const bool createUnique = (GetParameter(kParm_CreateUnique) != 0); - char upperName[A2FileProDOS::kMaxFileName+1]; - char lowerName[A2FileProDOS::kMaxFileName+1]; - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - assert(pParms != NULL); - assert(pParms->pathName != NULL); - assert(pParms->storageType == A2FileProDOS::kStorageSeedling || - pParms->storageType == A2FileProDOS::kStorageExtended || - pParms->storageType == A2FileProDOS::kStorageDirectory); - // kStorageVolumeDirHeader not allowed -- that's created by Format - LOGI(" ProDOS ---v--- CreateFile '%s'", pParms->pathName); - *ppNewFile = NULL; - - /* - * Normalize the pathname so that all components are ProDOS-safe - * and separated by ':'. - */ - assert(pParms->pathName != NULL); - dierr = DoNormalizePath(pParms->pathName, pParms->fssep, - &normalizedPath); - if (dierr != kDIErrNone) - goto bail; - assert(normalizedPath != NULL); - - /* - * Split the base path and filename apart. - */ - char* cp; - cp = strrchr(normalizedPath, A2FileProDOS::kFssep); - if (cp == NULL) { - assert(basePath == NULL); - fileName = normalizedPath; - } else { - fileName = new char[strlen(cp+1) +1]; - strcpy(fileName, cp+1); - *cp = '\0'; - basePath = normalizedPath; - } - normalizedPath = NULL; // either fileName or basePath points here now - - assert(fileName != NULL); - //LOGI(" ProDOS normalized to '%s':'%s'", - // basePath == NULL ? "" : basePath, fileName); - - /* - * Open the base path. If it doesn't exist, create it recursively. - */ - if (basePath != NULL) { - LOGI(" ProDOS Creating '%s' in '%s'", fileName, basePath); - /* open the named subdir, creating it if it doesn't exist */ - pSubdir = (A2FileProDOS*)GetFileByName(basePath); - if (pSubdir == NULL) { - LOGI(" ProDOS Creating subdir '%s'", basePath); - A2File* pNewSub; - CreateParms newDirParms; - newDirParms.pathName = basePath; - newDirParms.fssep = A2FileProDOS::kFssep; - newDirParms.storageType = A2FileProDOS::kStorageDirectory; - newDirParms.fileType = kTypeDIR; // 0x0f - newDirParms.auxType = 0; - newDirParms.access = 0xe3; // unlocked, backup bit set - newDirParms.createWhen = newDirParms.modWhen = time(NULL); - dierr = this->CreateFile(&newDirParms, &pNewSub); - if (dierr != kDIErrNone) - goto bail; - assert(pNewSub != NULL); - - pSubdir = (A2FileProDOS*) pNewSub; - } - - /* - * And now the annoying part. We need to reconstruct basePath out - * of the filenames actually present, rather than relying on the - * argument passed in. That's because some directories might have - * lower-case flags and some might not, and we do case-insensitive - * comparisons. It's not crucial for our inner workings, but the - * linear file list in the DiskFS should have accurate strings. - * (It'll work just fine, but the display might show the wrong values - * for parent directories until they reload the disk.) - * - * On the bright side, we know exactly how long the string needs - * to be, so we can just stomp on it in place. Assuming, of course, - * that the filename created matches up with what the filename - * normalizer came up with, which we can guarantee since (a) everybody - * uses the same normalizer and (b) the "uniqueify" stuff doesn't - * kick in for subdirs because we wouldn't be creating a new subdir - * if it didn't already exist. - * - * This is essentially the same as RegeneratePathName(), but that's - * meant for a situation where the filename already exists. - */ - A2FileProDOS* pBaseDir = pSubdir; - int basePathLen = strlen(basePath); - while (!pBaseDir->IsVolumeDirectory()) { - const char* fixedName = pBaseDir->GetFileName(); - int fixedLen = strlen(fixedName); - if (fixedLen > basePathLen) { - assert(false); - break; - } - assert(basePathLen == fixedLen || - *(basePath + (basePathLen-fixedLen-1)) == kDIFssep); - memcpy(basePath + (basePathLen-fixedLen), fixedName, fixedLen); - basePathLen -= fixedLen+1; - - pBaseDir = (A2FileProDOS*) pBaseDir->GetParent(); - assert(pBaseDir != NULL); - } - // check the math - if (pSubdir->IsVolumeDirectory()) - assert(basePathLen == 0); - else - assert(basePathLen == -1); - } else { - /* open the volume directory */ - LOGI(" ProDOS Creating '%s' in volume dir", fileName); - /* volume dir must be first in the list */ - pSubdir = (A2FileProDOS*) GetNextFile(NULL); - assert(pSubdir != NULL); - assert(pSubdir->IsVolumeDirectory()); - } - if (pSubdir == NULL) { - LOGI(" ProDOS Unable to open subdir '%s'", basePath); - dierr = kDIErrFileNotFound; - goto bail; - } - - /* - * Load the block usage map into memory. All changes, to the end of this - * function, are made to the in-memory copy and can be "undone" by simply - * throwing the temporary map away. - */ - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - return dierr; - - /* - * Load the subdir or volume dir into memory, and alloc a new directory - * entry. - */ - dierr = pSubdir->Open(&pOpenSubdir, false); - if (dierr != kDIErrNone) - goto bail; - - uint8_t* dirEntryPtr; - long dirLen; - uint16_t dirBlock, dirKeyBlock; - int dirEntrySlot; - dierr = AllocDirEntry(pOpenSubdir, &subdirBuf, &dirLen, &dirEntryPtr, - &dirKeyBlock, &dirEntrySlot, &dirBlock); - if (dierr != kDIErrNone) - goto bail; - - assert(subdirBuf != NULL); - assert(dirLen > 0); - assert(dirKeyBlock > 0); - assert(dirEntrySlot >= 0); - assert(dirBlock > 0); - - /* - * Create a copy of the filename with everything in upper case and spaces - * changed to periods. - */ - UpperCaseName(upperName, fileName); - - /* - * Make the name unique within the current directory. This requires - * appending digits until the name doesn't match any others. - * - * The filename buffer ("upperName") must be able to hold kMaxFileName+1 - * chars. It will be modified in place. - */ - if (createUnique && - pParms->storageType != A2FileProDOS::kStorageDirectory) - { - MakeFileNameUnique(subdirBuf, dirLen, upperName); - } else { - /* check to see if it already exists */ - if (NameExistsInDir(subdirBuf, dirLen, upperName)) { - if (pParms->storageType == A2FileProDOS::kStorageDirectory) - dierr = kDIErrDirectoryExists; - else - dierr = kDIErrFileExists; - goto bail; - } - } - - /* - * Allocate file storage and initialize: - * - For directory, a single block with the directory header. - * - For seedling, an empty block. - * - For extended, an extended key block entry and two empty blocks. - */ - long keyBlock; - int blocksUsed; - int newEOF; - keyBlock = -1; - blocksUsed = newEOF = -1; - - dierr = AllocInitialFileStorage(pParms, upperName, dirBlock, - dirEntrySlot, &keyBlock, &blocksUsed, &newEOF); - if (dierr != kDIErrNone) - goto bail; - - assert(blocksUsed > 0); - assert(keyBlock > 0); - assert(newEOF >= 0); - - /* - * Fill out the newly-created directory entry pointed to by "dirEntryPtr". - * - * ProDOS filenames are always stored in upper case. ProDOS 8 v1.8 and - * later allow lower-case names with '.' converting to ' '. We optionally - * set the flags here, using the original file name to decide which parts - * are lower case. (Some parts of the original may have been stomped - * when the name was made unique, so we need to watch for that.) - */ - dirEntryPtr[0x00] = (pParms->storageType << 4) | strlen(upperName); - strncpy((char*) &dirEntryPtr[0x01], upperName, A2FileProDOS::kMaxFileName); - if (pParms->fileType <= 0xff) - dirEntryPtr[0x10] = (uint8_t) pParms->fileType; - else - dirEntryPtr[0x10] = 0; // HFS long type? - PutShortLE(&dirEntryPtr[0x11], (uint16_t) keyBlock); - PutShortLE(&dirEntryPtr[0x13], blocksUsed); - PutShortLE(&dirEntryPtr[0x15], newEOF); - dirEntryPtr[0x17] = 0; // high byte of EOF - PutLongLE(&dirEntryPtr[0x18], A2FileProDOS::ConvertProDate(pParms->createWhen)); - if (allowLowerCase) { - uint16_t lcBits; - lcBits = GenerateLowerCaseBits(upperName, fileName, false); - GenerateLowerCaseName(upperName, lowerName, lcBits, false); - lowerName[strlen(upperName)] = '\0'; - - PutShortLE(&dirEntryPtr[0x1c], lcBits); - } else { - strcpy(lowerName, upperName); - PutShortLE(&dirEntryPtr[0x1c], 0); // version, min_version - } - dirEntryPtr[0x1e] = pParms->access; - if (pParms->auxType <= 0xffff) - PutShortLE(&dirEntryPtr[0x1f], (uint16_t) pParms->auxType); - else - PutShortLE(&dirEntryPtr[0x1f], 0); - PutLongLE(&dirEntryPtr[0x21], A2FileProDOS::ConvertProDate(pParms->modWhen)); - PutShortLE(&dirEntryPtr[0x25], dirKeyBlock); - - /* - * Write updated directory. If this succeeds, we can no longer undo - * what we have done by simply bailing. If this fails partway through, - * we might have a corrupted disk, so it's best to ensure that it's not - * going to fail before we call. - * - * Assuming this isn't a nibble image with I/O errors, the only way we - * can really fail is by running out of disk space. The block has been - * pre-allocated, so this should always work. - */ - dierr = pOpenSubdir->Write(subdirBuf, dirLen); - if (dierr != kDIErrNone) { - LOGI(" ProDOS directory write failed (dirLen=%ld)", dirLen); - goto bail; - } - - /* - * Flush updated block usage map. - */ - dierr = SaveVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Success! - * - * Create an A2File entry for this, and add it to the list. The calls - * below will re-process some of what we just created, which is slightly - * inefficient but helps guarantee that we aren't creating bogus data - * structures that won't match what we see when the disk is reloaded. - * - * - Regen or update internal VolumeUsage map?? Throw it away or mark - * it as invalid? - */ - pNewFile = new A2FileProDOS(this); - - A2FileProDOS::DirEntry* pEntry; - pEntry = &pNewFile->fDirEntry; - - A2FileProDOS::InitDirEntry(pEntry, dirEntryPtr); - - pNewFile->fParentDirBlock = dirBlock; - pNewFile->fParentDirIdx = (dirEntrySlot-1) % kEntriesPerBlock; - pNewFile->fSparseDataEof = 0; - pNewFile->fSparseRsrcEof = 0; - - /* - * Get the properly-cased filename for the file list. We already have - * a name in "lowerName", but it doesn't take AppleWorks aux type - * case stuff into account. If necessary, deal with it now. - */ - if (A2FileProDOS::UsesAppleWorksAuxType(pNewFile->fDirEntry.fileType)) { - DiskFSProDOS::GenerateLowerCaseName(pNewFile->fDirEntry.fileName, - lowerName, pNewFile->fDirEntry.auxType, true); - } - pNewFile->SetPathName(basePath == NULL ? "" : basePath, lowerName); - - if (pEntry->storageType == A2FileProDOS::kStorageExtended) { - dierr = ReadExtendedInfo(pNewFile); - if (dierr != kDIErrNone) { - LOGI(" ProDOS GLITCH: readback of extended block failed!"); - delete pNewFile; - goto bail; - } - } - - pNewFile->SetParent(pSubdir); - //pNewFile->Dump(); - - /* - * Because we're hierarchical, and we guarantee that the contents of - * subdirectories are grouped together, we must insert the file into an - * appropriate place in the list rather than just throwing it onto the - * end. - * - * The proper location for the new file in the linear list is after the - * previous file in our subdir. If we're the first item in the subdir, - * we get added right after the parent. If not, we need to scan, starting - * from the parent, for an entry in the file list whose key block pointer - * matches that of the previous item in the list. - * - * We wouldn't be this far if the disk were damaged, so we don't have to - * worry too much about weirdness. The directory entry allocator always - * returns the first available, so we know the previous entry is valid. - */ - uint8_t* prevDirEntryPtr; - prevDirEntryPtr = GetPrevDirEntry(subdirBuf, dirEntryPtr); - if (prevDirEntryPtr == NULL) { - /* previous entry is volume or subdir header */ - InsertFileInList(pNewFile, pNewFile->GetParent()); - LOGI("Inserted '%s' after '%s'", - pNewFile->GetPathName(), pNewFile->GetParent()->GetPathName()); - } else { - /* dig out the key block pointer and find the matching file */ - uint16_t prevKeyBlock; - assert((prevDirEntryPtr[0x00] & 0xf0) != 0); // verify storage type - prevKeyBlock = GetShortLE(&prevDirEntryPtr[0x11]); - A2File* pPrev; - pPrev = FindFileByKeyBlock(pNewFile->GetParent(), prevKeyBlock); - if (pPrev == NULL) { - /* should be impossible! */ - assert(false); - AddFileToList(pNewFile); - } else { - /* insert the new file in the list after the previous file */ - InsertFileInList(pNewFile, pPrev); - } - } -// LOGI("LIST NOW:"); -// DumpFileList(); - - *ppNewFile = pNewFile; - pNewFile = NULL; - -bail: - delete pNewFile; - if (pOpenSubdir != NULL) - pOpenSubdir->Close(); // writes updated dir entry in parent dir - FreeVolBitmap(); - delete[] normalizedPath; - delete[] subdirBuf; - delete[] fileName; - delete[] basePath; - LOGI(" ProDOS ---^--- CreateFile '%s' DONE", pParms->pathName); - return dierr; -} - -/* - * Run through the DiskFS file list, looking for an entry with a matching - * key block. - */ -A2File* DiskFSProDOS::FindFileByKeyBlock(A2File* pStart, uint16_t keyBlock) -{ - while (pStart != NULL) { - A2FileProDOS* pPro = (A2FileProDOS*) pStart; - - if (pPro->fDirEntry.keyPointer == keyBlock) - return pStart; - - pStart = GetNextFile(pStart); - } - - return NULL; -} - -/* - * Allocate the initial storage (key blocks, directory header) for a new file. - * - * Output values are the key block for the new file, the number of blocks - * used, and an EOF value. - * - * "upperName" is the upper-case name for the file. "dirBlock" and - * "dirEntrySlot" refer to the entry in the higher-level directory for this - * file, and are only needed when creating a new subdir (because the first - * entry in a subdir points to its entry in the parent dir). - */ -DIError DiskFSProDOS::AllocInitialFileStorage(const CreateParms* pParms, - const char* upperName, uint16_t dirBlock, int dirEntrySlot, - long* pKeyBlock, int* pBlocksUsed, int* pNewEOF) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - long keyBlock; - int blocksUsed; - int newEOF; - - blocksUsed = -1; - keyBlock = -1; - newEOF = 0; - memset(blkBuf, 0, sizeof(blkBuf)); - - if (pParms->storageType == A2FileProDOS::kStorageSeedling) { - keyBlock = AllocBlock(); - if (keyBlock == -1) { - dierr = kDIErrDiskFull; - goto bail; - } - blocksUsed = 1; - - /* write zeroed block */ - dierr = fpImg->WriteBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - } else if (pParms->storageType == A2FileProDOS::kStorageExtended) { - long dataBlock, rsrcBlock; - - dataBlock = AllocBlock(); - rsrcBlock = AllocBlock(); - keyBlock = AllocBlock(); - if (dataBlock < 0 || rsrcBlock < 0 || keyBlock < 0) { - dierr = kDIErrDiskFull; - goto bail; - } - blocksUsed = 3; - newEOF = kBlkSize; - - /* write zeroed block */ - dierr = fpImg->WriteBlock(dataBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - dierr = fpImg->WriteBlock(rsrcBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - /* fill in extended key block details */ - blkBuf[0x00] = blkBuf[0x100] = A2FileProDOS::kStorageSeedling; - PutShortLE(&blkBuf[0x01], (uint16_t) dataBlock); - PutShortLE(&blkBuf[0x101], (uint16_t) rsrcBlock); - blkBuf[0x03] = blkBuf[0x103] = 1; // blocks used (lo byte) - /* 3 bytes at 0x05 hold EOF, currently 0 */ - - dierr = fpImg->WriteBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - } else if (pParms->storageType == A2FileProDOS::kStorageDirectory) { - keyBlock = AllocBlock(); - if (keyBlock == -1) { - dierr = kDIErrDiskFull; - goto bail; - } - blocksUsed = 1; - newEOF = kBlkSize; - - /* fill in directory header fields */ - // 0x00: prev, set to zero - // 0x02: next, set to zero - blkBuf[0x04] = (A2FileProDOS::kStorageSubdirHeader << 4) | strlen(upperName); - strncpy((char*) &blkBuf[0x05], upperName, A2FileProDOS::kMaxFileName); - blkBuf[0x14] = 0x76; // 0x75 under old P8, 0x76 under GS/OS - PutLongLE(&blkBuf[0x1c], A2FileProDOS::ConvertProDate(pParms->createWhen)); - blkBuf[0x20] = 5; // 0 under 1.0, 3 under v1.4?, 5 under GS/OS - blkBuf[0x21] = 0; - blkBuf[0x22] = pParms->access; - blkBuf[0x23] = kEntryLength; - blkBuf[0x24] = kEntriesPerBlock; - PutShortLE(&blkBuf[0x25], 0); // file count - PutShortLE(&blkBuf[0x27], dirBlock); - blkBuf[0x29] = (uint8_t) dirEntrySlot; - blkBuf[0x2a] = kEntryLength; // the parent dir's entry length - - dierr = fpImg->WriteBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - } else { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - *pKeyBlock = keyBlock; - *pBlocksUsed = blocksUsed; - *pNewEOF = newEOF; - -bail: - return dierr; -} - -/* - * Scan for damaged files and mysterious or conflicting block usage map - * entries. - * - * Appends some entries to the DiskImg notes, so this should only be run - * once per DiskFS. - * - * This function doesn't set anything; it's effectively "const" except - * that LoadVolBitmap is inherently non-const. - * - * Returns "true" if disk appears to be perfect, "false" otherwise. - */ -bool DiskFSProDOS::CheckDiskIsGood(void) -{ - DIError dierr; - bool result = true; - int i; - - if (fEarlyDamage) - result = false; - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * Check the system blocks to see if any of them are marked as free. - * If so, refuse to write to this disk. - */ - if (!GetBlockUseEntry(0) || !GetBlockUseEntry(1)) { - fpImg->AddNote(DiskImg::kNoteWarning, "Block 0/1 marked as free."); - result = false; - } - for (i = GetNumBitmapBlocks(); i > 0; i--) { - if (!GetBlockUseEntry(fBitMapPointer + i -1)) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more bitmap blocks are marked as free."); - result = false; - break; - } - } - - /* - * Check for used blocks that aren't marked in-use. - * - * This requires that VolumeUsage be accurate. Since this function is - * only run during initial startup, any later deviation between VU and - * the block use map is irrelevant. - */ - VolumeUsage::ChunkState cstate; - long blk, notMarked, extraUsed, conflicts; - notMarked = extraUsed = conflicts = 0; - for (blk = 0; blk < fVolumeUsage.GetNumChunks(); blk++) { - dierr = fVolumeUsage.GetChunkState(blk, &cstate); - if (dierr != kDIErrNone) { - fpImg->AddNote(DiskImg::kNoteWarning, - "Internal volume usage error on blk=%ld.", blk); - result = false; - goto bail; - } - - if (cstate.isUsed && !cstate.isMarkedUsed) - notMarked++; - if (!cstate.isUsed && cstate.isMarkedUsed) - extraUsed++; - if (cstate.purpose == VolumeUsage::kChunkPurposeConflict) - conflicts++; - } - if (extraUsed > 0) { - fpImg->AddNote(DiskImg::kNoteInfo, - "%ld block%s marked used but not part of any file.", - extraUsed, extraUsed == 1 ? " is" : "s are"); - // not a problem, really - } - if (notMarked > 0) { - fpImg->AddNote(DiskImg::kNoteWarning, - "%ld block%s used by files but not marked used.", - notMarked, notMarked == 1 ? " is" : "s are"); - result = false; // very bad -- any change could trash files - } - if (conflicts > 0) { - fpImg->AddNote(DiskImg::kNoteWarning, - "%ld block%s used by more than one file.", - conflicts, conflicts == 1 ? " is" : "s are"); - result = false; // kinda bad -- file deletion leads to trouble - } - - /* - * Check for bits set past the end of the actually-needed bits. For - * some reason P8 and GS/OS both examine these bits, and GS/OS will - * freak out completely and claim the disk is unrecognizeable ("would - * you like to format?") if they're set. - */ - if (ScanForExtraEntries()) { - fpImg->AddNote(DiskImg::kNoteWarning, - "Blocks past the end of the disk are marked 'in use' in the" - " volume bitmap."); - /* don't flunk the disk just for this */ - } - - /* - * Scan for "damaged" or "suspicious" files diagnosed earlier. - */ - bool damaged, suspicious; - ScanForDamagedFiles(&damaged, &suspicious); - - if (damaged) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files are damaged."); - result = false; - } else if (suspicious) { - fpImg->AddNote(DiskImg::kNoteWarning, - "One or more files look suspicious."); - result = false; - } - -bail: - FreeVolBitmap(); - return result; -} - -/* - * Test a string for validity as a ProDOS volume name. Syntax is the same as - * ProDOS file names, but we also disallow spaces. - */ -/*static*/ bool DiskFSProDOS::IsValidVolumeName(const char* name) -{ - assert((int) A2FileProDOS::kMaxFileName == (int) kMaxVolumeName); - if (!IsValidFileName(name)) - return false; - while (*name != '\0') { - if (*name++ == ' ') - return false; - } - return true; -} - -/* - * Test a string for validity as a ProDOS file name. Names may be 1-15 - * characters long, must start with a letter, and may contain letters and - * digits. - * - * Lower case and spaces (a/k/a lower-case '.') are accepted. Trailing - * spaces are not allowed. - */ -/*static*/ bool DiskFSProDOS::IsValidFileName(const char* name) -{ - if (name == NULL) { - assert(false); - return false; - } - - /* must be 1-15 characters long */ - if (name[0] == '\0') - return false; - if (strlen(name) > A2FileProDOS::kMaxFileName) - return false; - - /* must begin with letter; this also catches zero-length filenames */ - if (toupper(name[0]) < 'A' || toupper(name[0]) > 'Z') - return false; - - /* no trailing spaces */ - if (name[strlen(name)-1] == ' ') - return false; - - /* must be A-Za-z 0-9 '.' ' ' */ - name++; - while (*name != '\0') { - if (!( (toupper(*name) >= 'A' && toupper(*name) <= 'Z') || - (*name >= '0' && *name <= '9') || - (*name == '.') || - (*name == ' ') - )) - { - return false; - } - - name++; - } - - return true; -} - -/* - * Generate lower case flags by comparing "upperName" to "lowerName". - * - * It's okay for "lowerName" to be longer than "upperName". The extra chars - * are just ignored. Similarly, "lowerName" does not need to be - * null-terminated. "lowerName" does need to point to storage with at least - * as many valid bytes as "upperName", though, or we could crash. - * - * Returns the mask to use in a ProDOS dir. If "forAppleWorks" is set to - * "true", the mask is modified for use with an AppleWorks aux type. - */ -/*static*/ uint16_t DiskFSProDOS::GenerateLowerCaseBits(const char* upperName, - const char* lowerName, bool forAppleWorks) -{ - uint16_t caseMask = 0x8000; - uint16_t caseBit = 0x8000; - int len, i; - char lowch; - - len = strlen(upperName); - assert(len <= A2FileProDOS::kMaxFileName); - - for (i = 0; i < len; i++) { - caseBit >>= 1; - lowch = A2FileProDOS::NameToLower(upperName[i]); - if (lowch == lowerName[i]) - caseMask |= caseBit; - } - - if (forAppleWorks) { - uint16_t adjusted; - caseMask <<= 1; - adjusted = caseMask << 8 | caseMask >> 8; - return adjusted; - } else { - if (caseMask == 0x8000) - return 0; // all upper case, don't freak out pre-v1.8 - else - return caseMask; - } -} - -/* - * Generate the lower-case version of a ProDOS filename, using the supplied - * lower case flags. "lowerName" must be able to hold 15 chars (enough for - * a filename or volname). - * - * The string will NOT be null-terminated, but the output buffer will be padded - * with NULs out to the maximum filename len. This makes it suitable for - * copying directly into directory block buffers. - * - * It's okay to pass the same buffer for "upperName" and "lowerName". - * - * "lcFlags" is either ProDOS directory flags or AppleWorks aux type flags, - * depending on the value of "fromAppleWorks". - */ -/*static*/ void DiskFSProDOS::GenerateLowerCaseName(const char* upperName, - char* lowerName, uint16_t lcFlags, bool fromAppleWorks) -{ - int nameLen = strlen(upperName); - int bit; - assert(nameLen <= A2FileProDOS::kMaxFileName); - - if (fromAppleWorks) { - /* handle AppleWorks lower-case-in-auxtype */ - uint16_t caseMask = // swap bytes - (lcFlags << 8) | (lcFlags >> 8); - for (bit = 0; bit < nameLen ; bit++) { - if ((caseMask & 0x8000) != 0) - lowerName[bit] = A2FileProDOS::NameToLower(upperName[bit]); - else - lowerName[bit] = upperName[bit]; - caseMask <<= 1; - } - for ( ; bit < A2FileProDOS::kMaxFileName; bit++) - lowerName[bit] = '\0'; - } else { - /* handle lower-case conversion; see GS/OS tech note #8 */ - if (lcFlags != 0 && !(lcFlags & 0x8000)) { - // Should be zero or 0x8000 plus other bits; shouldn't be - // bunch of bits without 0x8000 or 0x8000 by itself. Not - // really a problem, just unexpected. - assert(false); - memcpy(lowerName, upperName, A2FileProDOS::kMaxFileName); - return; - } - for (bit = 0; bit < nameLen; bit++) { - lcFlags <<= 1; - if ((lcFlags & 0x8000) != 0) - lowerName[bit] = A2FileProDOS::NameToLower(upperName[bit]); - else - lowerName[bit] = upperName[bit]; - } - } - for ( ; bit < A2FileProDOS::kMaxFileName; bit++) - lowerName[bit] = '\0'; -} - -/* - * Normalize a ProDOS path. Invokes DoNormalizePath and handles the buffer - * management (if the normalized path doesn't fit in "*pNormalizedBufLen" - * bytes, we set "*pNormalizedBufLen to the required length). - * - * This is invoked from the generalized "add" function in CiderPress, which - * doesn't want to understand the ins and outs of ProDOS pathnames. - */ -DIError DiskFSProDOS::NormalizePath(const char* path, char fssep, - char* normalizedBuf, int* pNormalizedBufLen) -{ - DIError dierr = kDIErrNone; - char* normalizedPath = NULL; - int len; - - assert(pNormalizedBufLen != NULL); - assert(normalizedBuf != NULL || *pNormalizedBufLen == 0); - - dierr = DoNormalizePath(path, fssep, &normalizedPath); - if (dierr != kDIErrNone) - goto bail; - - assert(normalizedPath != NULL); - len = strlen(normalizedPath); - if (normalizedBuf == NULL || *pNormalizedBufLen <= len) { - /* too short */ - dierr = kDIErrDataOverrun; - } else { - /* fits */ - strcpy(normalizedBuf, normalizedPath); - } - - *pNormalizedBufLen = len+1; // alloc room for the '\0' - -bail: - delete[] normalizedPath; - return dierr; -} - -/* - * Normalize a ProDOS path. This requires separating each path component - * out, making it ProDOS-compliant, and then putting it back in. - * The fssep could be anything, so we need to change it to kFssep. - * - * We don't try to identify duplicates here. If more than one subdir maps - * to the same thing, then you're just going to end up with lots of files - * in the same subdir. If this is unacceptable then it will have to be - * fixed at a higher level. - * - * Lower-case letters and spaces are left in place. They're expected to - * be removed later. - * - * The caller must delete[] "*pNormalizedPath". - */ -DIError DiskFSProDOS::DoNormalizePath(const char* path, char fssep, - char** pNormalizedPath) -{ - DIError dierr = kDIErrNone; - char* workBuf = NULL; - char* partBuf = NULL; - char* outputBuf = NULL; - char* start; - char* end; - char* outPtr; - - assert(path != NULL); - workBuf = new char[strlen(path)+1]; - partBuf = new char[strlen(path)+1 +1]; // need +1 for prepending letter - outputBuf = new char[strlen(path) * 2]; - if (workBuf == NULL || partBuf == NULL || outputBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - strcpy(workBuf, path); - outputBuf[0] = '\0'; - - outPtr = outputBuf; - start = workBuf; - while (*start != '\0') { - //char* origStart = start; // need for debug msg - int partIdx; - - if (fssep == '\0') { - end = NULL; - } else { - end = strchr(start, fssep); - if (end != NULL) - *end = '\0'; - } - partIdx = 0; - - /* - * Skip over everything up to the first letter. If we encounter a - * number or a '\0' first, insert a leading letter. - */ - while (*start != '\0') { - if (toupper(*start) >= 'A' && toupper(*start) <= 'Z') { - partBuf[partIdx++] = *start++; - break; - } - if (*start >= '0' && *start <= '9') { - partBuf[partIdx++] = 'A'; - break; - } - - start++; - } - if (partIdx == 0) - partBuf[partIdx++] = 'Z'; - - /* - * Continue copying, dropping all illegal chars. - */ - while (*start != '\0') { - if ((toupper(*start) >= 'A' && toupper(*start) <= 'Z') || - (*start >= '0' && *start <= '9') || - (*start == '.') || - (*start == ' ') ) - { - partBuf[partIdx++] = *start++; - } else { - start++; - } - } - - /* - * Truncate at 15 chars, preserving anything that looks like a - * filename extension. "partIdx" represents the length of the - * string at this point. "partBuf" holds the string, which we - * want to null-terminate before proceeding. - */ - partBuf[partIdx] = '\0'; - if (partIdx > A2FileProDOS::kMaxFileName) { - const char* pDot = strrchr(partBuf, '.'); - //int DEBUGDOTLEN = pDot - partBuf; - if (pDot != NULL && partIdx - (pDot-partBuf) <= kMaxExtensionLen) { - int dotLen = partIdx - (pDot-partBuf); - memmove(partBuf + (A2FileProDOS::kMaxFileName - dotLen), - pDot, dotLen); // don't use memcpy, move might overlap - } - partIdx = A2FileProDOS::kMaxFileName; - } - partBuf[partIdx] = '\0'; - - //LOGI(" ProDOS Converted component '%s' to '%s'", - // origStart, partBuf); - - if (outPtr != outputBuf) - *outPtr++ = A2FileProDOS::kFssep; - strcpy(outPtr, partBuf); - outPtr += partIdx; - - /* - * Continue with next segment. - */ - if (end == NULL) - break; - start = end+1; - } - - *outPtr = '\0'; - - LOGI(" ProDOS Converted path '%s' to '%s' (fssep='%c')", - path, outputBuf, fssep); - assert(*outputBuf != '\0'); - - *pNormalizedPath = outputBuf; - outputBuf = NULL; - -bail: - delete[] workBuf; - delete[] partBuf; - delete[] outputBuf; - return dierr; -} - -/* - * Create a copy of the filename with everything in upper case and spaces - * changed to periods. - * - * "upperName" must be a buffer that holds at least kMaxFileName+1 characters. - * If "name" is longer than kMaxFileName, it will be truncated. - */ -void DiskFSProDOS::UpperCaseName(char* upperName, const char* name) -{ - int i; - - for (i = 0; i < A2FileProDOS::kMaxFileName; i++) { - char ch = name[i]; - if (ch == '\0') - break; - else if (ch == ' ') - upperName[i] = '.'; - else - upperName[i] = toupper(ch); - } - - /* null terminate with prejudice -- we memcpy this buffer into subdirs */ - for ( ; i <= A2FileProDOS::kMaxFileName; i++) - upperName[i] = '\0'; -} - -/* - * Allocate a new directory entry. We start by reading the entire thing - * into memory. If the current set of allocated directory blocks is full, - * and we're not operating on the volume dir, we extend the directory. - * - * This just allocates the space; it does not fill in any details, except - * for the prev/next block pointers and the file count in the header. (One - * small exception: if we have to extend the directory, the "prev/next" fields - * of the new block will be filled in.) - * - * The volume in-use block map must be loaded before this is called. If - * this needs to extend the directory, a new block will be allocated. - * - * Returns a pointer to the new entry, and a whole bunch of other stuff: - * "ppDir" gets a pointer to newly-allocated memory with the whole directory - * "pDirLen" is the size of the *ppDir buffer - * "ppDirEntry" gets a memory pointer to the start of the created entry - * "pDirKeyBlock" gets the key block of the directory as a whole - * "pDirEntrySlot" gets the slot number within the directory block (first is 1) - * "pDirBlock" gets the actual block in which the created entry resides - * - * The caller should Write the entire thing to "pOpenSubdir" after filling - * in the new details for the entry. - * - * Possible reasons for failure: disk is out of space, volume dir is out - * of space, pOpenSubdir is screwy. - * - * We guarantee that we will return the first available entry in the current - * directory. - */ -DIError DiskFSProDOS::AllocDirEntry(A2FileDescr* pOpenSubdir, uint8_t** ppDir, - long* pDirLen, uint8_t** ppDirEntry, uint16_t* pDirKeyBlock, - int* pDirEntrySlot, uint16_t* pDirBlock) -{ - assert(pOpenSubdir != NULL); - *ppDirEntry = NULL; - *pDirLen = -1; - *pDirKeyBlock = 0; - *pDirEntrySlot = -1; - *pDirBlock = 0; - - DIError dierr = kDIErrNone; - uint8_t* dirBuf = NULL; - long dirLen; - A2FileProDOS* pFile; - long newBlock = -1; - - /* - * Load the subdir into memory. - */ - pFile = (A2FileProDOS*) pOpenSubdir->GetFile(); - dirLen = (long) pFile->GetDataLength(); - if (dirLen < 512 || (dirLen % 512) != 0) { - LOGI(" ProDOS GLITCH: funky dir EOF %ld (quality=%d)", - dirLen, pFile->GetQuality()); - dierr = kDIErrBadFile; - goto bail; - } - dirBuf = new uint8_t[dirLen]; - if (dirBuf == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = pOpenSubdir->Read(dirBuf, dirLen); - if (dierr != kDIErrNone) - goto bail; - - if (dirBuf[0x23] != kEntryLength || - dirBuf[0x24] != kEntriesPerBlock) - { - LOGI(" ProDOS GLITCH: funky entries per block %d", dirBuf[0x24]); - dierr = kDIErrBadDirectory; - goto bail; - } - - /* - * Find the first available entry (storage_type is zero). We need to - * step through this by blocks, because the data is block-oriented. - * If we run off the end of the last block, (re)alloc a new one. - */ - uint8_t* pDirEntry; - int blockIdx; - int entryIdx; - - pDirEntry = NULL; // make the compiler happy - entryIdx = -1; // make the compiler happy - - for (blockIdx = 0; blockIdx < dirLen / 512; blockIdx++) { - pDirEntry = dirBuf + 512*blockIdx + 4; // skip 4 bytes of prev/next - - for (entryIdx = 0; entryIdx < kEntriesPerBlock; - entryIdx++, pDirEntry += kEntryLength) - { - if ((pDirEntry[0x00] & 0xf0) == 0) { - LOGI(" ProDOS Found empty dir entry in slot %d", entryIdx); - break; // found one; break out of inner loop - } - } - if (entryIdx < kEntriesPerBlock) - break; // out of outer loop - } - if (blockIdx == dirLen / 512) { - if (((dirBuf[0x04] & 0xf0) >> 4) == A2FileProDOS::kStorageVolumeDirHeader) - { - /* can't extend the volume dir */ - dierr = kDIErrVolumeDirFull; - goto bail; - } - - LOGI(" ProDOS ran out of directory space, adding another block"); - - /* - * Request an unused block from the system. Point the "next" pointer - * in the last block at it, so that when we go to write this dir - * we will know where to put it. - */ - uint8_t* pBlock; - pBlock = dirBuf + 512 * (blockIdx-1); - if (pBlock[0x02] != 0) { - LOGI(" ProDOS GLITCH: adding to block with nonzero next ptr!"); - dierr = kDIErrBadDirectory; - goto bail; - } - - newBlock = AllocBlock(); - if (newBlock < 0) { - dierr = kDIErrDiskFull; - goto bail; - } - - PutShortLE(&pBlock[0x02], (uint16_t) newBlock); // set "next" - - /* - * Extend our memory buffer to hold the new entry. - */ - uint8_t* newSpace = new uint8_t[dirLen + 512]; - if (newSpace == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - memcpy(newSpace, dirBuf, dirLen); - memset(newSpace + dirLen, 0, 512); - delete[] dirBuf; - dirBuf = newSpace; - dirLen += 512; - - /* - * Set the "prev" pointer in the new block to point at the last - * block of the existing directory structure. - */ - long lastBlock; - dierr = pOpenSubdir->GetStorage(blockIdx-1, &lastBlock); - if (dierr != kDIErrNone) - goto bail; - pBlock = dirBuf + 512 * blockIdx; - PutShortLE(&pBlock[0x00], (uint16_t) lastBlock); // set "prev" - assert(GetShortLE(&pBlock[0x02]) == 0); // "next" pointer - - /* - * Finally, point pDirEntry at the first entry in the new area. - */ - pDirEntry = pBlock + 4; - entryIdx = 0; - assert(pDirEntry[0x00] == 0x00); - } - - /* - * Success. Update the file count in the header. - */ - uint16_t count; - count = GetShortLE(&dirBuf[0x25]); - count++; - PutShortLE(&dirBuf[0x25], count); - - long whichBlock; - - *ppDir = dirBuf; - *pDirLen = dirLen; - *ppDirEntry = pDirEntry; - *pDirKeyBlock = pFile->fDirEntry.keyPointer; - *pDirEntrySlot = entryIdx +1; - if (blockIdx == ((A2FDProDOS*)pOpenSubdir)->GetBlockCount()) { - /* not yet added to block list, so can't use GetStorage */ - assert(newBlock > 0); - *pDirBlock = (uint16_t) newBlock; - } else { - assert(newBlock < 0); - dierr = pOpenSubdir->GetStorage(blockIdx, &whichBlock); - assert(dierr == kDIErrNone); - *pDirBlock = (uint16_t) whichBlock; - } - dirBuf = NULL; - -bail: - delete[] dirBuf; - return dierr; -} - -/* - * Given a pointer to a directory buffer and a pointer to an entry, find the - * previous entry. (This is handy when trying to figure out where to insert - * a new entry into the DiskFS linear file list.) - * - * If the previous entry is the first in the list (i.e. it's a volume or - * subdir header), this returns NULL. - * - * This is a little awkward because the directories are chopped up into - * 512-byte blocks, with 13 entries per block (which doesn't completely fill - * the block, leaving gaps we have to skip around). If the previous entry is - * in the same block we can just return (ptr-0x27), but if it's in a previous - * block we need to return the last entry in the previous. - */ -uint8_t* DiskFSProDOS::GetPrevDirEntry(uint8_t* buf, uint8_t* ptr) -{ - assert(buf != NULL); - assert(ptr != NULL); - - const int kStartOffset = 4; - - if (ptr == buf + kStartOffset || ptr == buf + kStartOffset + kEntryLength) - return NULL; - - while (ptr - buf > 512) - buf += 512; - - assert((ptr - buf - kStartOffset) % kEntryLength == 0); - - if (ptr == buf + kStartOffset) { - /* whoops, went too far */ - buf -= 512; - return buf + kStartOffset + kEntryLength * (kEntriesPerBlock-1); - } else { - return ptr - kEntryLength; - } -} - -/* - * Make the name pointed to by "fileName" unique within the directory - * loaded in "subdirBuf". The name should already be trimmed to 15 chars - * or less and converted to upper-case only, and be in a buffer that can - * hold at least kMaxFileName+1 bytes. - * - * Returns an error on failure, which should only happen if there are a - * large number of files with similar names. - */ -DIError DiskFSProDOS::MakeFileNameUnique(const uint8_t* dirBuf, long dirLen, - char* fileName) -{ - assert(dirBuf != NULL); - assert(dirLen > 0); - assert((dirLen % 512) == 0); - assert(fileName != NULL); - assert(strlen(fileName) <= A2FileProDOS::kMaxFileName); - - if (!NameExistsInDir(dirBuf, dirLen, fileName)) - return kDIErrNone; - - LOGI(" ProDOS found duplicate of '%s', making unique", fileName); - - int nameLen = strlen(fileName); - int dotOffset=0, dotLen=0; - char dotBuf[kMaxExtensionLen+1]; - - /* ensure the result will be null-terminated */ - memset(fileName + nameLen, 0, (A2FileProDOS::kMaxFileName - nameLen) +1); - - /* - * If this has what looks like a filename extension, grab it. We want - * to preserve ".gif", ".c", etc., since the filetypes don't necessarily - * do everything we need. - * - * This will tend to screw up the upper/lower case stuff, especially - * since what we think is a '.' might actually be a ' '. We could work - * around this, but it's probably not necessary. - */ - const char* cp = strrchr(fileName, '.'); - if (cp != NULL) { - int tmpOffset = cp - fileName; - if (tmpOffset > 0 && nameLen - tmpOffset <= kMaxExtensionLen) { - LOGI(" ProDOS (keeping extension '%s')", cp); - assert(strlen(cp) <= kMaxExtensionLen); - strcpy(dotBuf, cp); - dotOffset = tmpOffset; - dotLen = nameLen - dotOffset; - } - } - - const int kMaxDigits = 999; - int digits = 0; - int digitLen; - int copyOffset; - char digitBuf[4]; - do { - if (digits == kMaxDigits) - return kDIErrFileExists; - digits++; - - /* not the most efficient way to do this, but it'll do */ - sprintf(digitBuf, "%d", digits); - digitLen = strlen(digitBuf); - if (nameLen + digitLen > A2FileProDOS::kMaxFileName) - copyOffset = A2FileProDOS::kMaxFileName - dotLen - digitLen; - else - copyOffset = nameLen - dotLen; - memcpy(fileName + copyOffset, digitBuf, digitLen); - if (dotLen != 0) - memcpy(fileName + copyOffset + digitLen, dotBuf, dotLen); - } while (NameExistsInDir(dirBuf, dirLen, fileName)); - - LOGI(" ProDOS converted to unique name: %s", fileName); - - return kDIErrNone; -} - -/* - * Determine whether the specified file name exists in the raw directory - * buffer. - * - * This should be called with the upper-case-only version of the filename. - */ -bool DiskFSProDOS::NameExistsInDir(const uint8_t* dirBuf, long dirLen, - const char* fileName) -{ - const uint8_t* pDirEntry; - int blockIdx; - int entryIdx; - int nameLen = strlen(fileName); - - assert(nameLen <= A2FileProDOS::kMaxFileName); - - for (blockIdx = 0; blockIdx < dirLen / 512; blockIdx++) { - pDirEntry = dirBuf + 512*blockIdx + 4; // skip 4 bytes of prev/next - - for (entryIdx = 0; entryIdx < kEntriesPerBlock; - entryIdx++, pDirEntry += kEntryLength) - { - /* skip directory header */ - if (blockIdx == 0 && entryIdx == 0) - continue; - - if ((pDirEntry[0x00] & 0xf0) != 0 && - (pDirEntry[0x00] & 0x0f) == nameLen && - strncmp((char*) &pDirEntry[0x01], fileName, nameLen) == 0) - { - return true; - } - } - } - - return false; -} - -/* - * Delete a file. - * - * There are three fairly simple steps: (1) mark all blocks used by the file as - * free, (2) set the storage type in the directory entry to 0, and (3) - * decrement the file count in the directory header. We then remove it from - * the DiskFS file list. - * - * We only allow deletion of a subdirectory when the subdir is empty. - */ -DIError DiskFSProDOS::DeleteFile(A2File* pGenericFile) -{ - DIError dierr = kDIErrNone; - long blockCount = -1; - long indexCount = -1; - uint16_t* blockList = NULL; - uint16_t* indexList = NULL; - - if (pGenericFile == NULL) { - assert(false); - return kDIErrInvalidArg; - } - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - if (pGenericFile->IsFileOpen()) - return kDIErrFileOpen; - - /* - * If they try to delete all entries, we don't want to spit back a - * failure message over our "fake" volume dir entry. So we just silently - * ignore the request. - */ - if (pGenericFile->IsVolumeDirectory()) { - LOGI("ProDOS not deleting volume directory"); - return kDIErrNone; - } - - A2FileProDOS* pFile = (A2FileProDOS*) pGenericFile; - - LOGI(" Deleting '%s'", pFile->GetPathName()); - - dierr = LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - switch (pFile->fDirEntry.storageType) { - case A2FileProDOS::kStorageExtended: - // handle rsrc fork here, fall out for data fork - dierr = pFile->LoadBlockList( - pFile->fExtRsrc.storageType, - pFile->fExtRsrc.keyBlock, - pFile->fExtRsrc.eof, - &blockCount, &blockList, - &indexCount, &indexList); - if (dierr != kDIErrNone) - goto bail; - FreeBlocks(blockCount, blockList); - if (indexList != NULL) // no indices for seedling - FreeBlocks(indexCount, indexList); - delete[] blockList; - delete[] indexList; - indexList = NULL; - - // handle the key block "manually" - blockCount = 1; - blockList = new uint16_t[blockCount]; - blockList[0] = pFile->fDirEntry.keyPointer; - FreeBlocks(blockCount, blockList); - delete[] blockList; - blockList = NULL; - - dierr = pFile->LoadBlockList( - pFile->fExtData.storageType, - pFile->fExtData.keyBlock, - pFile->fExtData.eof, - &blockCount, &blockList, - &indexCount, &indexList); - break; // fall out - - case A2FileProDOS::kStorageDirectory: - dierr = pFile->LoadDirectoryBlockList( - pFile->fDirEntry.keyPointer, - pFile->fDirEntry.eof, - &blockCount, &blockList); - break; // fall out - - case A2FileProDOS::kStorageSeedling: - case A2FileProDOS::kStorageSapling: - case A2FileProDOS::kStorageTree: - dierr = pFile->LoadBlockList( - pFile->fDirEntry.storageType, - pFile->fDirEntry.keyPointer, - pFile->fDirEntry.eof, - &blockCount, &blockList, - &indexCount, &indexList); - break; // fall out - - default: - LOGI("ProDOS can't delete unknown storage type %d", - pFile->fDirEntry.storageType); - dierr = kDIErrBadDirectory; - break; // fall out - } - - if (dierr != kDIErrNone) - goto bail; - - FreeBlocks(blockCount, blockList); - if (indexList != NULL) - FreeBlocks(indexCount, indexList); - - /* - * Update the directory entry. After this point, failure gets ugly. - * - * It might be "proper" to open the subdir file, find the correct entry, - * and write it back, but the A2FileProDOS structure has the directory - * block and entry index stored in it. Makes it a little easier. - */ - uint8_t blkBuf[kBlkSize]; - uint8_t* ptr; - assert(pFile->fParentDirBlock > 0); - assert(pFile->fParentDirIdx >= 0 && - pFile->fParentDirIdx < kEntriesPerBlock); - dierr = fpImg->ReadBlock(pFile->fParentDirBlock, blkBuf); - if (dierr != kDIErrNone) { - LOGI("ProDOS unable to read directory block %u", - pFile->fParentDirBlock); - goto bail; - } - - ptr = blkBuf + 4 + pFile->fParentDirIdx * kEntryLength; - if ((*ptr) >> 4 != pFile->fDirEntry.storageType) { - LOGI("ProDOS GLITCH: mismatched storage types (%d vs %d)", - (*ptr) >> 4, pFile->fDirEntry.storageType); - assert(false); - dierr = kDIErrBadDirectory; - goto bail; - } - ptr[0x00] = 0; // zap both storage type and name length - dierr = fpImg->WriteBlock(pFile->fParentDirBlock, blkBuf); - if (dierr != kDIErrNone) { - LOGI("ProDOS unable to write directory block %u", - pFile->fParentDirBlock); - goto bail; - } - - /* - * Save our updated copy of the volume bitmap to disk. - */ - dierr = SaveVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - /* - * One last little thing: decrement the file count in the directory - * header. We can find the appropriate place pretty easily because - * we know it's the first block in pFile->fpParent, which for a dir is - * always the block pointed to by the key pointer. - * - * Strictly speaking, failure to update this correctly isn't fatal. I - * doubt most utilities pay any attention to this. Still, it's important - * to keep the filesystem in a consistent state, so we at least must - * report the error. They'll need to run the ProSel volume repair util - * to fix it. - */ - A2FileProDOS* pParent; - uint16_t fileCount; - int storageType; - pParent = (A2FileProDOS*) pFile->GetParent(); - assert(pParent != NULL); - assert(pParent->fDirEntry.keyPointer >= kVolHeaderBlock); - dierr = fpImg->ReadBlock(pParent->fDirEntry.keyPointer, blkBuf); - if (dierr != kDIErrNone) { - LOGI("ProDOS unable to read parent dir block %u", - pParent->fDirEntry.keyPointer); - goto bail; - } - ptr = NULL; - - storageType = (blkBuf[0x04] & 0xf0) >> 4; - if (storageType != A2FileProDOS::kStorageSubdirHeader && - storageType != A2FileProDOS::kStorageVolumeDirHeader) - { - LOGI("ProDOS invalid storage type %d in dir header block", - storageType); - DebugBreak(); - dierr = kDIErrBadDirectory; - goto bail; - } - fileCount = GetShortLE(&blkBuf[0x25]); - if (fileCount > 0) - fileCount--; - PutShortLE(&blkBuf[0x25], fileCount); - dierr = fpImg->WriteBlock(pParent->fDirEntry.keyPointer, blkBuf); - if (dierr != kDIErrNone) { - LOGI("ProDOS unable to write parent dir block %u", - pParent->fDirEntry.keyPointer); - goto bail; - } - - /* - * Remove the A2File* from the list. - */ - DeleteFileFromList(pFile); - -bail: - FreeVolBitmap(); - delete[] blockList; - delete[] indexList; - return kDIErrNone; -} - -/* - * Mark all of the blocks in the blockList as free. - * - * The in-use map must already be loaded. - */ -DIError DiskFSProDOS::FreeBlocks(long blockCount, uint16_t* blockList) -{ - VolumeUsage::ChunkState cstate; - int i; - - //LOGI(" +++ FreeBlocks (blockCount=%d blockList=0x%08lx)", - // blockCount, blockList); - assert(blockCount >= 0 && blockCount < 65536); - assert(blockList != NULL); - - cstate.isUsed = false; - cstate.isMarkedUsed = false; - cstate.purpose = VolumeUsage::kChunkPurposeUnknown; - - for (i = 0; i < blockCount; i++) { - if (blockList[i] == 0) // expected for "sparse" files - continue; - - if (!GetBlockUseEntry(blockList[i])) { - LOGI("WARNING: freeing unallocated block %u", blockList[i]); - assert(false); // impossible unless disk is "damaged" - } - SetBlockUseEntry(blockList[i], false); - - fVolumeUsage.SetChunkState(blockList[i], &cstate); - } - - return kDIErrNone; -} - -/* - * Rename a file. - * - * Pass in a pointer to the file and a string with the new filename (just - * the filename, not a pathname -- this function doesn't move files - * between directories). The new name must already be normalized. - * - * Renaming the magic volume directory "file" is not allowed. - * - * Things to note: - * - Renaming subdirs is annoying. The name has to be changed in two - * places, and the "pathname" value cached in A2FileProDOS must be - * updated for all children of the subdir. - * - Must check for duplicates. - * - If it's an AppleWorks file type, we need to change the aux type - * according to the upper/lower case flags. This holds even if the - * "allow lower case" flag is disabled. - */ -DIError DiskFSProDOS::RenameFile(A2File* pGenericFile, const char* newName) -{ - DIError dierr = kDIErrNone; - A2FileProDOS* pFile = (A2FileProDOS*) pGenericFile; - char upperName[A2FileProDOS::kMaxFileName+1]; - char upperComp[A2FileProDOS::kMaxFileName+1]; - - if (pFile == NULL || newName == NULL) - return kDIErrInvalidArg; - if (!IsValidFileName(newName)) - return kDIErrInvalidArg; - if (pFile->IsVolumeDirectory()) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (!fDiskIsGood) - return kDIErrBadDiskImage; - - LOGI(" ProDOS renaming '%s' to '%s'", pFile->GetPathName(), newName); - - /* - * Check for duplicates. We do this by getting the parent subdir and - * running through it looking for an upper-case-converted match. - * - * We start in the list at our parent node, knowing that the kids are - * grouped together after it. However, we can't stop right away, - * because some of the kids might be subdirectories themselves. So we - * will probably run through a significant chunk of the list. - */ - A2File* pParent = pFile->GetParent(); - A2File* pCur; - - UpperCaseName(upperName, newName); - pCur = GetNextFile(pParent); - assert(pCur != NULL); // at the very least, pFile is in this dir - while (pCur != NULL) { - if (pCur != pFile && pCur->GetParent() == pParent) { - /* one of our siblings; see if the name matches */ - UpperCaseName(upperComp, pCur->GetFileName()); - if (strcmp(upperName, upperComp) == 0) { - LOGI(" ProDOS rename dup found"); - return kDIErrFileExists; - } - } - - pCur = GetNextFile(pCur); - } - - /* - * Grab the directory block and update the filename in the entry. If this - * was a subdir we also need to update its directory header entry. To - * minimize the chances of a partial update, we load both blocks up - * front, modify both, then write them both back. - */ - uint8_t parentDirBuf[kBlkSize]; - uint8_t thisDirBuf[kBlkSize]; - - dierr = fpImg->ReadBlock(pFile->fParentDirBlock, parentDirBuf); - if (dierr != kDIErrNone) - goto bail; - if (pFile->IsDirectory()) { - dierr = fpImg->ReadBlock(pFile->fDirEntry.keyPointer, thisDirBuf); - if (dierr != kDIErrNone) - goto bail; - } - - /* compute lower case flags as needed */ - uint16_t lcFlags, lcAuxType; - bool allowLowerCase, isAW; - - allowLowerCase = GetParameter(kParmProDOS_AllowLowerCase) != 0; - isAW = A2FileProDOS::UsesAppleWorksAuxType((uint8_t)pFile->GetFileType()); - - if (allowLowerCase) - lcFlags = GenerateLowerCaseBits(upperName, newName, false); - else - lcFlags = 0; - if (isAW) - lcAuxType = GenerateLowerCaseBits(upperName, newName, true); - else - lcAuxType = 0; - - /* - * Possible optimization: if "upperName" matches what's in the block on - * disk and the "lcFlags"/"lcAuxType" values match as well, we don't - * need to write the blocks back. - * - * It's difficult to test for this earlier, because we need to do the - * update if (a) they're just changing the capitalization or (b) we're - * changing the capitalization for them because the "allow lower case" - * flag got turned off. - */ - - /* find the right entry, and copy our filename in */ - uint8_t* ptr; - assert(pFile->fParentDirIdx >= 0 && - pFile->fParentDirIdx < kEntriesPerBlock); - ptr = parentDirBuf + 4 + pFile->fParentDirIdx * kEntryLength; - if ((*ptr) >> 4 != pFile->fDirEntry.storageType) { - LOGI("ProDOS GLITCH: mismatched storage types (%d vs %d)", - (*ptr) >> 4, pFile->fDirEntry.storageType); - assert(false); - dierr = kDIErrBadDirectory; - goto bail; - } - ptr[0x00] = (ptr[0x00] & 0xf0) | strlen(upperName); - memcpy(&ptr[0x01], upperName, A2FileProDOS::kMaxFileName); - PutShortLE(&ptr[0x1c], lcFlags); // version/min_version - if (isAW) - PutShortLE(&ptr[0x1f], lcAuxType); - - if (pFile->IsDirectory()) { - ptr = thisDirBuf + 4; - if ((*ptr) >> 4 != A2FileProDOS::kStorageSubdirHeader) { - LOGI("ProDOS GLITCH: bad storage type in subdir header (%d)", - (*ptr) >> 4); - assert(false); - dierr = kDIErrBadDirectory; - goto bail; - } - ptr[0x00] = (ptr[0x00] & 0xf0) | strlen(upperName); - memcpy(&ptr[0x01], upperName, A2FileProDOS::kMaxFileName); - PutShortLE(&ptr[0x1c], lcFlags); // version/min_version - } - - /* write the updated data back to the disk */ - dierr = fpImg->WriteBlock(pFile->fParentDirBlock, parentDirBuf); - if (dierr != kDIErrNone) - goto bail; - if (pFile->IsDirectory()) { - dierr = fpImg->WriteBlock(pFile->fDirEntry.keyPointer, thisDirBuf); - if (dierr != kDIErrNone) - goto bail; - } - - /* - * At this point the ProDOS filesystem is back in a consistent state. - * Everything we do from here on is self-inflicted. - * - * We need to update this entry's A2FileProDOS::fDirEntry.fileName, - * as well as the A2FileProDOS::fPathName. If this was a subdir, then - * we need to update A2FileProDOS::fPathName for all files inside the - * directory (including children of children). - * - * The latter is somewhat awkward, so we just re-acquire the pathname - * for every file on the disk. Less efficient but easier to code. - */ - if (isAW) - GenerateLowerCaseName(upperName, pFile->fDirEntry.fileName, - lcAuxType, true); - else - GenerateLowerCaseName(upperName, pFile->fDirEntry.fileName, - lcFlags, false); - assert(pFile->fDirEntry.fileName[A2FileProDOS::kMaxFileName] == '\0'); - - if (pFile->IsDirectory()) { - /* do all files that come after us */ - pCur = pFile; - while (pCur != NULL) { - RegeneratePathName((A2FileProDOS*) pCur); - pCur = GetNextFile(pCur); - } - } else { - RegeneratePathName(pFile); - } - - LOGI("Okay!"); - -bail: - return dierr; -} - -/* - * Regenerate fPathName for the specified file. - * - * Has no effect on the magic volume dir entry. - * - * This could be implemented more efficiently, but it's only used when - * renaming files, so there's not much point. - */ -DIError DiskFSProDOS::RegeneratePathName(A2FileProDOS* pFile) -{ - A2FileProDOS* pParent; - char* buf = NULL; - int len; - - /* nothing to do here */ - if (pFile->IsVolumeDirectory()) - return kDIErrNone; - - /* compute the length of the path name */ - len = strlen(pFile->GetFileName()); - pParent = (A2FileProDOS*) pFile->GetParent(); - while (!pParent->IsVolumeDirectory()) { - len++; // leave space for the ':' - len += strlen(pParent->GetFileName()); - - pParent = (A2FileProDOS*) pParent->GetParent(); - } - - buf = new char[len+1]; - if (buf == NULL) - return kDIErrMalloc; - - /* generate the new path name */ - int partLen; - partLen = strlen(pFile->GetFileName()); - strcpy(buf + len - partLen, pFile->GetFileName()); - len -= partLen; - - pParent = (A2FileProDOS*) pFile->GetParent(); - while (!pParent->IsVolumeDirectory()) { - assert(len > 0); - buf[--len] = kDIFssep; - - partLen = strlen(pParent->GetFileName()); - strncpy(buf + len - partLen, pParent->GetFileName(), partLen); - len -= partLen; - assert(len >= 0); - - pParent = (A2FileProDOS*) pParent->GetParent(); - } - - LOGI("Replacing '%s' with '%s'", pFile->GetPathName(), buf); - pFile->SetPathName("", buf); - delete[] buf; - - return kDIErrNone; -} - -/* - * Change the attributes of the specified file. - * - * Subdirectories have access bits in the subdir header as well as their - * file entry. The BASIC.SYSTEM "lock" command only changes the access - * bits of the file; the permissions inside the subdir remain 0xe3. (Which - * might explain why you can still add files to a locked subdir.) I'm going - * to mimic this behavior. - * - * This does, of course, mean that there's no meaning in attempts to change - * the file access permissions of the volume directory. - */ -DIError DiskFSProDOS::SetFileInfo(A2File* pGenericFile, uint32_t fileType, - uint32_t auxType, uint32_t accessFlags) -{ - DIError dierr = kDIErrNone; - A2FileProDOS* pFile = (A2FileProDOS*) pGenericFile; - - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - if (pFile == NULL) { - assert(false); - return kDIErrInvalidArg; - } - if ((fileType & ~(0xff)) != 0 || - (auxType & ~(0xffff)) != 0 || - (accessFlags & ~(0xff)) != 0) - { - return kDIErrInvalidArg; - } - if (pFile->IsVolumeDirectory()) { - LOGI(" ProDOS refusing to change file info for volume dir"); - return kDIErrAccessDenied; // not quite right - } - - LOGI("ProDOS changing values for '%s' to 0x%02x 0x%04x 0x%02x", - pFile->GetPathName(), fileType, auxType, accessFlags); - - /* load the directory block for this file */ - uint8_t thisDirBuf[kBlkSize]; - dierr = fpImg->ReadBlock(pFile->fParentDirBlock, thisDirBuf); - if (dierr != kDIErrNone) - goto bail; - - /* find the right entry, and set the fields */ - uint8_t* ptr; - assert(pFile->fParentDirIdx >= 0 && - pFile->fParentDirIdx < kEntriesPerBlock); - ptr = thisDirBuf + 4 + pFile->fParentDirIdx * kEntryLength; - if ((*ptr) >> 4 != pFile->fDirEntry.storageType) { - LOGI("ProDOS GLITCH: mismatched storage types (%d vs %d)", - (*ptr) >> 4, pFile->fDirEntry.storageType); - assert(false); - dierr = kDIErrBadDirectory; - goto bail; - } - if ((size_t) (*ptr & 0x0f) != strlen(pFile->fDirEntry.fileName)) { - LOGW("ProDOS GLITCH: wrong file? (len=%d vs %u)", - *ptr & 0x0f, (unsigned int) strlen(pFile->fDirEntry.fileName)); - assert(false); - dierr = kDIErrBadDirectory; - goto bail; - } - - ptr[0x10] = (uint8_t) fileType; - ptr[0x1e] = (uint8_t) accessFlags; - PutShortLE(&ptr[0x1f], (uint16_t) auxType); - - dierr = fpImg->WriteBlock(pFile->fParentDirBlock, thisDirBuf); - if (dierr != kDIErrNone) - goto bail; - - /* update our local copy */ - pFile->fDirEntry.fileType = (uint8_t) fileType; - pFile->fDirEntry.auxType = (uint16_t) auxType; - pFile->fDirEntry.access = (uint8_t) accessFlags; - -bail: - return dierr; -} - -/* - * Change the disk volume name. - * - * This is a lot like renaming a subdirectory, except that there's no parent - * directory to update, and the name of the volume dir doesn't affect the - * pathname of anything else. There's also no risk of a duplicate. - * - * Internally we need to update the "fake" entry and the cached copies in - * fVolumeName and fVolumeID. - */ -DIError DiskFSProDOS::RenameVolume(const char* newName) -{ - DIError dierr = kDIErrNone; - char upperName[A2FileProDOS::kMaxFileName+1]; - A2FileProDOS* pFile; - - if (!IsValidVolumeName(newName)) - return kDIErrInvalidArg; - if (fpImg->GetReadOnly()) - return kDIErrAccessDenied; - - pFile = (A2FileProDOS*) GetNextFile(NULL); - assert(pFile != NULL); - assert(strcmp(pFile->GetFileName(), fVolumeName) == 0); - - LOGI(" ProDOS renaming volume '%s' to '%s'", - pFile->GetPathName(), newName); - - /* - * Figure out the lower-case flags. - */ - uint16_t lcFlags; - bool allowLowerCase; - - UpperCaseName(upperName, newName); - allowLowerCase = GetParameter(kParmProDOS_AllowLowerCase) != 0; - if (allowLowerCase) - lcFlags = GenerateLowerCaseBits(upperName, newName, false); - else - lcFlags = 0; - - /* - * Update the volume dir header. - */ - uint8_t thisDirBuf[kBlkSize]; - uint8_t* ptr; - assert(pFile->fDirEntry.keyPointer == kVolHeaderBlock); - - dierr = fpImg->ReadBlock(pFile->fDirEntry.keyPointer, thisDirBuf); - if (dierr != kDIErrNone) - goto bail; - - ptr = thisDirBuf + 4; - if ((*ptr) >> 4 != A2FileProDOS::kStorageVolumeDirHeader) { - LOGI("ProDOS GLITCH: bad storage type in voldir header (%d)", - (*ptr) >> 4); - assert(false); - dierr = kDIErrBadDirectory; - goto bail; - } - ptr[0x00] = (ptr[0x00] & 0xf0) | strlen(upperName); - memcpy(&ptr[0x01], upperName, A2FileProDOS::kMaxFileName); - PutShortLE(&ptr[0x16], lcFlags); // reserved fields - - dierr = fpImg->WriteBlock(pFile->fDirEntry.keyPointer, thisDirBuf); - if (dierr != kDIErrNone) - goto bail; - - /* - * Set the volume name, based on the upper-case name and lower-case flags - * we just wrote. If "allowLowerCase" was set to false, it may not be - * the same as what's in "newName". - */ - char lowerName[A2FileProDOS::kMaxFileName+1]; - memset(lowerName, 0, sizeof(lowerName)); // lowerName won't be term'ed - GenerateLowerCaseName(upperName, lowerName, lcFlags, false); - - strcpy(fVolumeName, lowerName); - SetVolumeID(); - strcpy(pFile->fDirEntry.fileName, lowerName); - - /* update the entry in the linear file list */ - pFile->SetPathName(":", fVolumeName); - -bail: - return dierr; -} - - -/* - * =========================================================================== - * A2FileProDOS - * =========================================================================== - */ - -/* - * Convert from ProDOS compact date format to a time_t. - * - * Byte 0 and 1: yyyyyyymmmmddddd - * Byte 2 and 3: 000hhhhh00mmmmmm - * - * The field is set entirely to zero if no date was assigned (which cannot - * be a valid date since "day" ranges from 1 to 31). If this is found then - * ((time_t) 0) is returned. - */ -/*static*/ time_t A2FileProDOS::ConvertProDate(ProDate proDate) -{ - uint16_t prodosDate, prodosTime; - int year, month, day, hour, minute, second; - - if (proDate == 0) - return 0; - - prodosDate = (uint16_t) (proDate & 0x0000ffff); - prodosTime = (uint16_t) ((proDate >> 16) & 0x0000ffff); - - second = 0; - minute = prodosTime & 0x3f; - hour = (prodosTime >> 8) & 0x1f; - day = prodosDate & 0x1f; - month = (prodosDate >> 5) & 0x0f; - year = (prodosDate >> 9) & 0x7f; - if (year < 40) - year += 100; /* P8 uses 0-39 for 2000-2039 */ - - struct tm tmbuf; - time_t when; - - tmbuf.tm_sec = second; - tmbuf.tm_min = minute; - tmbuf.tm_hour = hour; - tmbuf.tm_mday = day; - tmbuf.tm_mon = month-1; // ProDOS uses 1-12 - tmbuf.tm_year = year; - tmbuf.tm_wday = 0; - tmbuf.tm_yday = 0; - tmbuf.tm_isdst = -1; // let it figure DST and time zone - when = mktime(&tmbuf); - - if (when == (time_t) -1) - when = 0; - - return when; -} - -/* - * Convert a time_t to a ProDOS-format date. - * - * CiderPress uses kDateInvalid==-1 and kDateNone==-2. - */ -/*static*/ A2FileProDOS::ProDate A2FileProDOS::ConvertProDate(time_t unixDate) -{ - ProDate proDate; - uint32_t prodosDate, prodosTime; - struct tm* ptm; - int year; - - if (unixDate == 0 || unixDate == -1 || unixDate == -2) - return 0; - - ptm = localtime(&unixDate); - if (ptm == NULL) - return 0; // must've been invalid or unspecified - - year = ptm->tm_year; -#ifdef OLD_PRODOS_DATES - /* ProSel-16 volume repair complaints about dates < 1980 and >= Y2K */ - if (year > 100) - year -= 20; -#endif - - if (year >= 100) - year -= 100; - if (year < 0 || year >= 128) { - LOGI("WHOOPS: got year %d from %d", year, ptm->tm_year); - year = 70; - } - - prodosDate = year << 9 | (ptm->tm_mon+1) << 5 | ptm->tm_mday; - prodosTime = ptm->tm_hour << 8 | ptm->tm_min; - - proDate = prodosTime << 16 | prodosDate; - return proDate; -} - -/* - * Return the file creation time as a time_t. - */ -time_t A2FileProDOS::GetCreateWhen(void) const -{ - return ConvertProDate(fDirEntry.createWhen); -} - -/* - * Return the file modification time as a time_t. - */ -time_t A2FileProDOS::GetModWhen(void) const -{ - return ConvertProDate(fDirEntry.modWhen); -} - -/* - * Set the full pathname to a combination of the base path and the - * current file's name. - * - * If we're in the volume directory, pass in "" for the base path (not NULL). - */ -void A2FileProDOS::SetPathName(const char* basePath, const char* fileName) -{ - assert(basePath != NULL && fileName != NULL); - if (fPathName != NULL) - delete[] fPathName; - - int baseLen = strlen(basePath); - fPathName = new char[baseLen + 1 + strlen(fileName)+1]; - strcpy(fPathName, basePath); - if (baseLen != 0 && - !(baseLen == 1 && basePath[0] == ':')) - { - *(fPathName + baseLen) = kFssep; - baseLen++; - } - strcpy(fPathName + baseLen, fileName); -} - -/* - * Convert a character in a ProDOS name to lower case. - * - * This is special in that '.' is considered upper case, with ' ' as its - * lower-case counterpart. - */ -/*static*/ char A2FileProDOS::NameToLower(char ch) -{ - if (ch == '.') - return ' '; - else - return tolower(ch); -} - -/* - * Init the fields in the DirEntry struct from the values in the ProDOS - * directory entry pointed to by "entryBuf". - * - * Deals with lower case conversions on the filename. - */ -/*static*/ void A2FileProDOS::InitDirEntry(A2FileProDOS::DirEntry* pEntry, - const uint8_t* entryBuf) -{ - int nameLen; - - pEntry->storageType = (entryBuf[0x00] & 0xf0) >> 4; - nameLen = entryBuf[0x00] & 0x0f; - memcpy(pEntry->fileName, &entryBuf[0x01], nameLen); - pEntry->fileName[nameLen] = '\0'; - pEntry->fileType = entryBuf[0x10]; - pEntry->keyPointer = GetShortLE(&entryBuf[0x11]); - pEntry->blocksUsed = GetShortLE(&entryBuf[0x13]); - pEntry->eof = GetLongLE(&entryBuf[0x15]); - pEntry->eof &= 0x00ffffff; - pEntry->createWhen = GetLongLE(&entryBuf[0x18]); - pEntry->version = entryBuf[0x1c]; - pEntry->minVersion = entryBuf[0x1d]; - pEntry->access = entryBuf[0x1e]; - pEntry->auxType = GetShortLE(&entryBuf[0x1f]); - pEntry->modWhen = GetLongLE(&entryBuf[0x21]); - pEntry->headerPointer = GetShortLE(&entryBuf[0x25]); - - /* generate the name into the buffer; does not null-terminate */ - if (UsesAppleWorksAuxType(pEntry->fileType)) { - DiskFSProDOS::GenerateLowerCaseName(pEntry->fileName, pEntry->fileName, - pEntry->auxType, true); - } else if (pEntry->minVersion & 0x80) { - DiskFSProDOS::GenerateLowerCaseName(pEntry->fileName, pEntry->fileName, - GetShortLE(&entryBuf[0x1c]), false); - } - pEntry->fileName[sizeof(pEntry->fileName)-1] = '\0'; -} - -/* - * Open one fork of this file. - * - * I really, really dislike forked files. - */ -DIError A2FileProDOS::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*= false*/) -{ - DIError dierr = kDIErrNone; - A2FDProDOS* pOpenFile = NULL; - - LOGI(" ProDOS Open(ro=%d, rsrc=%d) on '%s'", - readOnly, rsrcFork, fPathName); - //Dump(); - - if (!readOnly) { - if (fpDiskFS->GetDiskImg()->GetReadOnly()) - return kDIErrAccessDenied; - if (fpDiskFS->GetFSDamaged()) - return kDIErrBadDiskImage; - } - - if (fpOpenFile != NULL) { - dierr = kDIErrAlreadyOpen; - goto bail; - } - if (rsrcFork && fDirEntry.storageType != kStorageExtended) { - dierr = kDIErrForkNotFound; - goto bail; - } - - pOpenFile = new A2FDProDOS(this); - if (pOpenFile == NULL) - return kDIErrMalloc; - - pOpenFile->fOpenRsrcFork = false; - - if (fDirEntry.storageType == kStorageExtended) { - if (rsrcFork) { - dierr = LoadBlockList(fExtRsrc.storageType, fExtRsrc.keyBlock, - fExtRsrc.eof, &pOpenFile->fBlockCount, - &pOpenFile->fBlockList); - pOpenFile->fOpenEOF = fExtRsrc.eof; - pOpenFile->fOpenBlocksUsed = fExtRsrc.blocksUsed; - pOpenFile->fOpenStorageType = fExtRsrc.storageType; - pOpenFile->fOpenRsrcFork = true; - } else { - dierr = LoadBlockList(fExtData.storageType, fExtData.keyBlock, - fExtData.eof, &pOpenFile->fBlockCount, - &pOpenFile->fBlockList); - pOpenFile->fOpenEOF = fExtData.eof; - pOpenFile->fOpenBlocksUsed = fExtData.blocksUsed; - pOpenFile->fOpenStorageType = fExtData.storageType; - } - } else if (fDirEntry.storageType == kStorageDirectory || - fDirEntry.storageType == kStorageVolumeDirHeader) - { - dierr = LoadDirectoryBlockList(fDirEntry.keyPointer, - fDirEntry.eof, &pOpenFile->fBlockCount, - &pOpenFile->fBlockList); - pOpenFile->fOpenEOF = fDirEntry.eof; - pOpenFile->fOpenBlocksUsed = fDirEntry.blocksUsed; - pOpenFile->fOpenStorageType = fDirEntry.storageType; - } else if (fDirEntry.storageType == kStorageSeedling || - fDirEntry.storageType == kStorageSapling || - fDirEntry.storageType == kStorageTree) - { - dierr = LoadBlockList(fDirEntry.storageType, fDirEntry.keyPointer, - fDirEntry.eof, &pOpenFile->fBlockCount, - &pOpenFile->fBlockList); - pOpenFile->fOpenEOF = fDirEntry.eof; - pOpenFile->fOpenBlocksUsed = fDirEntry.blocksUsed; - pOpenFile->fOpenStorageType = fDirEntry.storageType; - } else { - LOGI("PrODOS can't open unknown storage type %d", - fDirEntry.storageType); - dierr = kDIErrBadDirectory; - goto bail; - } - if (dierr != kDIErrNone) { - LOGI(" ProDOS open failed"); - goto bail; - } - - pOpenFile->fOffset = 0; - //pOpenFile->DumpBlockList(); - - fpOpenFile = pOpenFile; // add it to our single-member "open file set" - *ppOpenFile = pOpenFile; - pOpenFile = NULL; - -bail: - delete pOpenFile; - return dierr; -} - -/* - * Gather a linear, non-sparse list of file blocks into an array. - * - * Pass in the storage type and top-level key block. Separation of - * extended files should have been handled by the caller. This loads the - * list for only one fork. - * - * There are two kinds of sparse: sparse *inside* data, and sparse - * *past* data. The latter is interesting, because there is no need - * to create space in index blocks to hold it. Thus, a sapling could - * hold a file with an EOF of 16MB. - * - * If "pIndexBlockCount" and "pIndexBlockList" are non-NULL, then we - * also accumulate the list of index blocks and return those as well. - * For a Tree-structured file, the first entry in the index list is - * the master index block. - * - * The caller must delete[] "*pBlockList" and "*pIndexBlockList". - */ -DIError A2FileProDOS::LoadBlockList(int storageType, uint16_t keyBlock, - long eof, long* pBlockCount, uint16_t** pBlockList, - long* pIndexBlockCount, uint16_t** pIndexBlockList) -{ - if (storageType == kStorageDirectory || - storageType == kStorageVolumeDirHeader) - { - assert(pIndexBlockList == NULL && pIndexBlockCount == NULL); - return LoadDirectoryBlockList(keyBlock, eof, pBlockCount, pBlockList); - } - - assert(keyBlock != 0); - assert(pBlockCount != NULL); - assert(pBlockList != NULL); - assert(*pBlockList == NULL); - if (storageType != kStorageSeedling && - storageType != kStorageSapling && - storageType != kStorageTree) - { - /* - * We can get here if somebody puts a bad storage type inside the - * extended key block of a forked file. Bad storage types on other - * kinds of files are caught earlier. - */ - LOGI(" ProDOS unexpected storageType %d in '%s'", - storageType, GetPathName()); - return kDIErrNotSupported; - } - - DIError dierr = kDIErrNone; - uint16_t* list = NULL; - long count; - - assert(eof < 1024*1024*16); - count = (eof + kBlkSize -1) / kBlkSize; - if (count == 0) - count = 1; - list = new uint16_t[count+1]; - if (list == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - if (pIndexBlockList != NULL) { - assert(pIndexBlockCount != NULL); - assert(*pIndexBlockList == NULL); - } - - /* this should take care of trailing sparse entries */ - memset(list, 0, sizeof(uint16_t) * count); - list[count] = kInvalidBlockNum; // overrun check - - if (storageType == kStorageSeedling) { - list[0] = keyBlock; - - if (pIndexBlockList != NULL) { - *pIndexBlockCount = 0; - *pIndexBlockList = NULL; - } - } else if (storageType == kStorageSapling) { - dierr = LoadIndexBlock(keyBlock, list, count); - if (dierr != kDIErrNone) - goto bail; - - if (pIndexBlockList != NULL) { - *pIndexBlockCount = 1; - *pIndexBlockList = new uint16_t[1]; - **pIndexBlockList = keyBlock; - } - } else if (storageType == kStorageTree) { - uint8_t blkBuf[kBlkSize]; - uint16_t* listPtr = list; - uint16_t* outIndexPtr = NULL; - long countDown = count; - int idx = 0; - - dierr = fpDiskFS->GetDiskImg()->ReadBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - if (pIndexBlockList != NULL) { - int numIndices = (count + kMaxBlocksPerIndex-1) / kMaxBlocksPerIndex; - numIndices++; // add one for the master index block - *pIndexBlockList = new uint16_t[numIndices]; - outIndexPtr = *pIndexBlockList; - *outIndexPtr++ = keyBlock; - *pIndexBlockCount = 1; - } - - while (countDown) { - long blockCount = countDown; - if (blockCount > kMaxBlocksPerIndex) - blockCount = kMaxBlocksPerIndex; - uint16_t idxBlock; - - idxBlock = blkBuf[idx] | (uint16_t) blkBuf[idx+256] << 8; - if (idxBlock == 0) { - /* fully sparse index block */ - //LOGI(" ProDOS that's seriously sparse (%d)!", idx); - memset(listPtr, 0, blockCount * sizeof(uint16_t)); - if (pIndexBlockList != NULL) { - *outIndexPtr++ = idxBlock; - (*pIndexBlockCount)++; - } - } else { - dierr = LoadIndexBlock(idxBlock, listPtr, blockCount); - if (dierr != kDIErrNone) - goto bail; - - if (pIndexBlockList != NULL) { - *outIndexPtr++ = idxBlock; - (*pIndexBlockCount)++; - } - } - - idx++; - listPtr += blockCount; - countDown -= blockCount; - } - } else { - assert(false); - } - - assert(list[count] == kInvalidBlockNum); - - dierr = ValidateBlockList(list, count); - if (dierr != kDIErrNone) - goto bail; - - *pBlockCount = count; - *pBlockList = list; - -bail: - if (dierr != kDIErrNone) { - delete[] list; - assert(*pBlockList == NULL); - - if (pIndexBlockList != NULL && *pIndexBlockList != NULL) { - delete[] *pIndexBlockList; - *pIndexBlockList = NULL; - } - } - return dierr; -} - -/* - * Make sure all values in the block list fall in accepted ranges. - * - * We allow zero (used for sparse blocks), but disallow values in the "system" - * area (block 1 through the end of the usage map). - * - * It's hard to say whether we should compare against the DiskImg block count - * (representing blocks we can physically read but aren't necessarily part - * of the filesystem) or the filesystem "total blocks" value from the volume - * header. Using the one in the volume header is correct, but sometimes the - * value is off on an otherwise reasonable disk. - * - * I'm falling on the side of generosity, allowing files that reference - * potentially bad data to appear okay. My main reason is that, except for - * CFFA volumes that have been tweaked by CiderPress users, very few ProDOS - * disks will have a large disparity between the two numbers unless somebody - * has trashed the volume dir header. - * - * What we really need is three states for each file: good, suspect, damaged. - */ -DIError A2FileProDOS::ValidateBlockList(const uint16_t* list, long count) -{ - DiskImg* pImg = fpDiskFS->GetDiskImg(); - bool foundBad = false; - - while (count--) { - if (*list > pImg->GetNumBlocks() || - (*list > 0 && *list <= 2)) // not enough, but it'll do - { - LOGI("Invalid block %d in '%s'", *list, fDirEntry.fileName); - SetQuality(kQualityDamaged); - return kDIErrBadFile; - } - if (*list > fpDiskFS->GetFSNumBlocks()) - foundBad = true; - list++; - } - - if (foundBad) { - LOGI(" --- found out-of-range block in '%s'", GetPathName()); - SetQuality(kQualitySuspicious); - } - - return kDIErrNone; -} - -/* - * Copy the entries from the index block in "block" to "list", copying - * at most "maxCount" entries. - */ -DIError A2FileProDOS::LoadIndexBlock(uint16_t block, uint16_t* list, - int maxCount) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - int i; - - if (maxCount > kMaxBlocksPerIndex) - maxCount = kMaxBlocksPerIndex; - - dierr = fpDiskFS->GetDiskImg()->ReadBlock(block, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - //LOGI("LOADING 0x%04x", block); - for (i = 0; i < maxCount; i++) { - *list++ = blkBuf[i] | (uint16_t) blkBuf[i+256] << 8; - } - -bail: - return dierr; -} - -/* - * Load the block list from a directory, which is essentially a linear - * linked list. - */ -DIError A2FileProDOS::LoadDirectoryBlockList(uint16_t keyBlock, - long eof, long* pBlockCount, uint16_t** pBlockList) -{ - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - uint16_t* list = NULL; - uint16_t* listPtr; - int iterations; - long count; - - assert(eof < 1024*1024*16); - count = (eof + kBlkSize -1) / kBlkSize; - if (count == 0) - count = 1; - list = new uint16_t[count+1]; - if (list == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - /* this should take care of trailing sparse entries */ - memset(list, 0, sizeof(uint16_t) * count); - list[count] = kInvalidBlockNum; // overrun check - - iterations = 0; - listPtr = list; - - while (keyBlock && iterations < kMaxCatalogIterations) { - if (keyBlock < 2 || - keyBlock >= fpDiskFS->GetDiskImg()->GetNumBlocks()) - { - LOGI(" ProDOS ERROR: directory block %u out of range", keyBlock); - dierr = kDIErrInvalidBlock; - goto bail; - } - - *listPtr++ = keyBlock; - - dierr = fpDiskFS->GetDiskImg()->ReadBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - keyBlock = GetShortLE(&blkBuf[0x02]); - iterations++; - } - if (iterations == kMaxCatalogIterations) { - LOGI(" ProDOS subdir iteration count exceeded"); - dierr = kDIErrDirectoryLoop; - goto bail; - } - - assert(list[count] == kInvalidBlockNum); - - *pBlockCount = count; - *pBlockList = list; - -bail: - if (dierr != kDIErrNone) - delete[] list; - return dierr; -} - -/* - * Dump the contents. - */ -void A2FileProDOS::Dump(void) const -{ - LOGI(" ProDOS file '%s' (path='%s')", - fDirEntry.fileName, fPathName); - LOGI(" fileType=0x%02x auxType=0x%04x storage=%d", - fDirEntry.fileType, fDirEntry.auxType, fDirEntry.storageType); - LOGI(" keyPointer=%d blocksUsed=%d eof=%d", - fDirEntry.keyPointer, fDirEntry.blocksUsed, fDirEntry.eof); - LOGI(" access=0x%02x create=0x%08x mod=0x%08x", - fDirEntry.access, fDirEntry.createWhen, fDirEntry.modWhen); - LOGI(" version=%d minVersion=%d headerPtr=%d", - fDirEntry.version, fDirEntry.minVersion, fDirEntry.headerPointer); - if (fDirEntry.storageType == kStorageExtended) { - LOGI(" DATA storage=%d keyBlk=%d blkUsed=%d eof=%d", - fExtData.storageType, fExtData.keyBlock, fExtData.blocksUsed, - fExtData.eof); - LOGI(" RSRC storage=%d keyBlk=%d blkUsed=%d eof=%d", - fExtRsrc.storageType, fExtRsrc.keyBlock, fExtRsrc.blocksUsed, - fExtRsrc.eof); - } - LOGI(" * sparseData=%ld sparseRsrc=%ld", - (long) fSparseDataEof, (long) fSparseRsrcEof); -} - - -/* - * =========================================================================== - * A2FDProDOS - * =========================================================================== - */ - -/* - * Read a chunk of data from whichever fork is open. - */ -DIError A2FDProDOS::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" ProDOS reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - //if (fBlockList == NULL) - // return kDIErrNotReady; - - if (fOffset + (long)len > fOpenEOF) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (long) (fOpenEOF - fOffset); - } - if (pActual != NULL) - *pActual = len; -// - long incrLen = len; - - DIError dierr = kDIErrNone; - uint8_t blkBuf[kBlkSize]; - long blockIndex = (long) (fOffset / kBlkSize); - int bufOffset = (int) (fOffset % kBlkSize); // (& 0x01ff) - size_t thisCount; - long progressCounter = 0; - - if (len == 0) { - ///* one block allocated for empty file */ - //SetLastBlock(fBlockList[0], true); - return kDIErrNone; - } - assert(fOpenEOF != 0); - - assert(blockIndex >= 0 && blockIndex < fBlockCount); - - while (len) { - if (fBlockList[blockIndex] == 0) { - //LOGI(" ProDOS sparse index %d", blockIndex); - memset(blkBuf, 0, sizeof(blkBuf)); - } else { - //LOGI(" ProDOS non-sparse index %d", blockIndex); - dierr = fpFile->GetDiskFS()->GetDiskImg()->ReadBlock(fBlockList[blockIndex], - blkBuf); - if (dierr != kDIErrNone) { - LOGI(" ProDOS error reading block [%ld]=%d of '%s'", - blockIndex, fBlockList[blockIndex], fpFile->GetPathName()); - return dierr; - } - } - thisCount = kBlkSize - bufOffset; - if (thisCount > len) - thisCount = len; - - memcpy(buf, blkBuf + bufOffset, thisCount); - len -= thisCount; - buf = (char*)buf + thisCount; - - bufOffset = 0; - blockIndex++; - - progressCounter++; - if (progressCounter > 100 && len) { - progressCounter = 0; - /* - * Show progress within the current read request. This only - * kicks in for large reads, e.g. reformatting the entire file. - * For smaller reads, used when we're extracting w/o reformatting, - * "progressCounter" never gets large enough. - */ - if (!UpdateProgress(fOffset + incrLen - len)) { - dierr = kDIErrCancelled; - return dierr; - } - //::Sleep(100); // DEBUG DEBUG - } - } - - fOffset += incrLen; - - if (!UpdateProgress(fOffset)) - dierr = kDIErrCancelled; - - return dierr; -} - -/* - * Write data at the current offset. - * - * For simplicity, we assume that there can only be one of two situations: - * (1) We're writing a directory, which might expand by one block; or - * (2) We're writing all of a brand-new file in one shot. - * - * Modifies fOpenEOF, fOpenBlocksUsed, fStorageType, and sets fModified. - * - * HEY: ProSel-16 describes these as fragmented, and it's probably right. - * The correct way to do this is to allocate index blocks before allocating - * the blocks they refer to, so that we don't have to jump all over the disk - * to read the indexes (which, at the moment, appear at the end of the file). - * A bit tricky, but doable. - */ -DIError A2FDProDOS::Write(const void* buf, size_t len, size_t* pActual) -{ - DIError dierr = kDIErrNone; - A2FileProDOS* pFile = (A2FileProDOS*) fpFile; - DiskFSProDOS* pDiskFS = (DiskFSProDOS*) fpFile->GetDiskFS(); - bool allocSparse = (pDiskFS->GetParameter(DiskFS::kParmProDOS_AllocSparse) != 0); - uint8_t blkBuf[kBlkSize]; - uint16_t keyBlock; - - if (len >= 0x01000000) { // 16MB - assert(false); - return kDIErrInvalidArg; - } - - /* use separate function for directories */ - if (pFile->fDirEntry.storageType == A2FileProDOS::kStorageDirectory || - pFile->fDirEntry.storageType == A2FileProDOS::kStorageVolumeDirHeader) - { - return WriteDirectory(buf, len, pActual); - } - - dierr = pDiskFS->LoadVolBitmap(); - if (dierr != kDIErrNone) - goto bail; - - assert(fOffset == 0); // big simplifying assumption - assert(fOpenEOF == 0); // another one - assert(fOpenBlocksUsed == 1); - assert(buf != NULL); - - /* nothing to do for zero-length write; don't even set fModified */ - if (len == 0) - goto bail; - - if (pFile->fDirEntry.storageType != A2FileProDOS::kStorageExtended) - keyBlock = pFile->fDirEntry.keyPointer; - else { - if (fOpenRsrcFork) - keyBlock = pFile->fExtRsrc.keyBlock; - else - keyBlock = pFile->fExtData.keyBlock; - } - - /* - * Special-case seedling files. Just write the data into the key block - * and we're done. - */ - if (len <= (size_t)kBlkSize) { - memset(blkBuf, 0, sizeof(blkBuf)); - memcpy(blkBuf, buf, len); - dierr = pDiskFS->GetDiskImg()->WriteBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - fOpenEOF = len; - fOpenBlocksUsed = 1; - assert(fOpenStorageType == A2FileProDOS::kStorageSeedling); - fOffset += len; - fModified = true; - goto bail; - } - - /* - * Start by allocating space for the block list. The list is always the - * same size, regardless of sparse allocations. - * - * We over-alloc by one so we can have an overrun detection entry. - */ - fBlockCount = (len + kBlkSize-1) / kBlkSize; - assert(fBlockCount > 0); - delete[] fBlockList; - fBlockList = new uint16_t[fBlockCount+1]; - if (fBlockList == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - fBlockList[fBlockCount] = A2FileProDOS::kInvalidBlockNum; - - /* - * Write the data blocks to disk, allocating as we go. We have to treat - * the last entry specially because it might not fill an entire block. - */ - const uint8_t* blkPtr; - long blockIdx; - bool allZero; - long progressCounter; - - progressCounter = 0; - allZero = true; - blkPtr = (const uint8_t*) buf; - for (blockIdx = 0; blockIdx < fBlockCount; blockIdx++) { - long newBlock; - - if (blockIdx == fBlockCount-1) { - /* for last block, copy partial and move blkPtr */ - int copyLen = len - (blockIdx * kBlkSize); - assert(copyLen > 0 && copyLen <= kBlkSize); - memset(blkBuf, 0, sizeof(blkBuf)); - memcpy(blkBuf, blkPtr, copyLen); - blkPtr = blkBuf; - } - - if (allocSparse && IsEmptyBlock(blkPtr)) - newBlock = 0; - else { - newBlock = pDiskFS->AllocBlock(); - fOpenBlocksUsed++; - allZero = false; - } - - if (newBlock < 0) { - LOGI(" ProDOS disk full during write!"); - dierr = kDIErrDiskFull; - goto bail; - } - - fBlockList[blockIdx] = (uint16_t) newBlock; - - if (newBlock != 0) { - dierr = pDiskFS->GetDiskImg()->WriteBlock(newBlock, blkPtr); - if (dierr != kDIErrNone) - goto bail; - } - - blkPtr += kBlkSize; - - /* - * Update the progress counter and check to see if the "cancel" button - * has been hit. We don't call UpdateProgress on the last block - * because we could be passing an offset value larger than "len". - * Also, we don't want the progress bar to hit 100% until we've - * actually finished. - * - * We do NOT want to check this after we start writing index blocks. - * If we do, we need to make sure that whatever index blocks the file - * has match up with what we've allocated in the disk block map. - * - * We don't want to save the disk block map if the user cancels here, - * because then the blocks will be marked as "used" even though the - * index blocks for this file haven't been written yet. - * - * It's tricky to get this right, which is why we allocate space - * for the index blocks now -- running out of disk space and - * user cancellation are handled the same way. Once we get to the - * point where we're updating the file structure, we can neither be - * cancelled nor run out of space. (We can still hit a bad block, - * though, which we currently don't handle.) - */ - progressCounter++; // update every N blocks - if (progressCounter > 100 && blockIdx != fBlockCount) { - progressCounter = 0; - if (!UpdateProgress(blockIdx * kBlkSize)) { - dierr = kDIErrCancelled; - goto bail; - } - } - } - - assert(fBlockList[fBlockCount] == A2FileProDOS::kInvalidBlockNum); - - /* - * Now we have a full block map. Allocate any needed index blocks and - * write them. - * - * If our block map is empty, i.e. the entire file is sparse, then - * there's no need to create a sapling. We just leave the file in - * seedling form. This can only happen for a completely empty file. - */ - if (allZero) { - LOGI("+++ ProDOS storing large but empty file as seedling"); - /* make sure key block is empty */ - memset(blkBuf, 0, sizeof(blkBuf)); - dierr = pDiskFS->GetDiskImg()->WriteBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - fOpenStorageType = A2FileProDOS::kStorageSeedling; - fBlockList[0] = keyBlock; - } else if (fBlockCount <= 256) { - /* sapling file, write an index block into the key block */ - //bool allzero = true; <-- should this be getting used? - assert(fBlockCount > 1); - memset(blkBuf, 0, sizeof(blkBuf)); - int i; - for (i = 0; i < fBlockCount; i++) { - //if (fBlockList[i] != 0) - // allzero = false; - blkBuf[i] = fBlockList[i] & 0xff; - blkBuf[256 + i] = (fBlockList[i] >> 8) & 0xff; - } - - dierr = pDiskFS->GetDiskImg()->WriteBlock(keyBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - fOpenStorageType = A2FileProDOS::kStorageSapling; - } else { - /* tree file, write two or more indexes and write master into key */ - uint8_t masterBlk[kBlkSize]; - int idx; - - memset(masterBlk, 0, sizeof(masterBlk)); - - for (idx = 0; idx < fBlockCount; ) { - long newBlock; - int i; - - memset(blkBuf, 0, sizeof(blkBuf)); - for (i = 0; i < 256 && idx < fBlockCount; i++, idx++) { - blkBuf[i] = fBlockList[idx] & 0xff; - blkBuf[256+i] = (fBlockList[idx] >> 8) & 0xff; - } - - /* allocate a new index block, if needed */ - if (allocSparse && IsEmptyBlock(blkBuf)) - newBlock = 0; - else { - newBlock = pDiskFS->AllocBlock(); - fOpenBlocksUsed++; - } - if (newBlock != 0) { - dierr = pDiskFS->GetDiskImg()->WriteBlock(newBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - } - - masterBlk[(idx-1) / 256] = (uint8_t) newBlock; - masterBlk[256 + (idx-1)/256] = (uint8_t) (newBlock >> 8); - } - - dierr = pDiskFS->GetDiskImg()->WriteBlock(keyBlock, masterBlk); - if (dierr != kDIErrNone) - goto bail; - fOpenStorageType = A2FileProDOS::kStorageTree; - } - - fOpenEOF = len; - fOffset += len; - fModified = true; - -bail: - if (dierr == kDIErrNone) - dierr = pDiskFS->SaveVolBitmap(); - - /* - * We need to check UpdateProgress *after* the volume bitmap has been - * saved. Otherwise we'll have blocks allocated in the file's structure - * but not marked in-use in the map when the "dierr" check above fails. - */ - if (dierr == kDIErrNone) { - if (!UpdateProgress(fOffset)) - dierr = kDIErrCancelled; - } - - pDiskFS->FreeVolBitmap(); - return dierr; -} - -/* - * Determine whether a block is filled entirely with zeroes. - */ -bool A2FDProDOS::IsEmptyBlock(const uint8_t* blk) -{ - int i; - - for (i = 0; i < kBlkSize; i++) { - if (*blk++ != 0) - return false; - } - - return true; -} - -/* - * Write a directory, possibly extending it by one block. - * - * If we're growing, the extra block will already have been allocated, and is - * pointed to by the "next" pointer in the next-to-last block. (This - * pre-allocation makes our lives easier, and avoids a situation where we - * would have to update the volume bitmap when another function is already - * making lots of changes to it.) - */ -DIError A2FDProDOS::WriteDirectory(const void* buf, size_t len, size_t* pActual) -{ - DIError dierr = kDIErrNone; - - LOGD("ProDOS writing %lu bytes to directory '%s'", - (unsigned long) len, fpFile->GetPathName()); - - assert(len >= (size_t)kBlkSize); - assert((len % kBlkSize) == 0); - assert(len == (size_t)fOpenEOF || len == (size_t)fOpenEOF + kBlkSize); - - if (len > (size_t)fOpenEOF) { - /* - * Extend the block list, remembering that we add an extra item - * on the end to check for overruns. - */ - uint16_t* newBlockList; - - fBlockCount++; - newBlockList = new uint16_t[fBlockCount+1]; - memcpy(newBlockList, fBlockList, - sizeof(uint16_t) * fBlockCount); - newBlockList[fBlockCount] = A2FileProDOS::kInvalidBlockNum; - - uint8_t* blkPtr; - blkPtr = (uint8_t*)buf + fOpenEOF - kBlkSize; - assert(blkPtr >= buf); - assert(GetShortLE(&blkPtr[0x02]) != 0); - newBlockList[fBlockCount-1] = GetShortLE(&blkPtr[0x02]); - - delete[] fBlockList; - fBlockList = newBlockList; - - LOGI(" ProDOS updated block list for subdir:"); - DumpBlockList(); - } - - /* - * Now just run down the block list writing the directory. - */ - assert(len == (size_t)fBlockCount * kBlkSize); - int idx; - for (idx = 0; idx < fBlockCount; idx++) { - assert(fBlockList[idx] >= kVolHeaderBlock); - dierr = fpFile->GetDiskFS()->GetDiskImg()->WriteBlock(fBlockList[idx], - (uint8_t*)buf + idx * kBlkSize); - if (dierr != kDIErrNone) { - LOGI(" ProDOS failed writing dir, block=%d", fBlockList[idx]); - goto bail; - } - } - - fOpenEOF = len; - fOpenBlocksUsed = (uint16_t) fBlockCount; // very simple for subdirs - //fOpenStorageType - fModified = true; - -bail: - return dierr; -} - -/* - * Seek to a new position within the file. - */ -DIError A2FDProDOS::Seek(di_off_t offset, DIWhence whence) -{ - DIError dierr = kDIErrNone; - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fOpenEOF) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fOpenEOF) - return kDIErrInvalidArg; - fOffset = fOpenEOF + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fOpenEOF - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fOpenEOF); - - return dierr; -} - -/* - * Return current offset. - */ -di_off_t A2FDProDOS::Tell(void) -{ - //if (fBlockList == NULL) - // return kDIErrNotReady; - - return fOffset; -} - -/* - * Release file state. - * - * Most applications don't check the value of "Close", or call it from a - * destructor, so we call CloseDescr whether we succeed or not. - */ -DIError A2FDProDOS::Close(void) -{ - DIError dierr = kDIErrNone; - - if (fModified) { - A2FileProDOS* pFile = (A2FileProDOS*) fpFile; - uint8_t blkBuf[kBlkSize]; - uint8_t newStorageType = fOpenStorageType; - uint16_t newBlocksUsed = fOpenBlocksUsed; - uint32_t newEOF = (uint32_t) fOpenEOF; // TODO: assert range - uint16_t combinedBlocksUsed; - uint32_t combinedEOF; - - /* - * If this is an extended file, fix the entries in the extended - * key block, and adjust the values to be stored in the directory. - */ - if (pFile->fDirEntry.storageType == A2FileProDOS::kStorageExtended) { - /* these two don't change */ - newStorageType = pFile->fDirEntry.storageType; - - dierr = fpFile->GetDiskFS()->GetDiskImg()->ReadBlock( - pFile->fDirEntry.keyPointer, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - int offset = 0; - if (fOpenRsrcFork) - offset = 256; - - blkBuf[0x00 + offset] = fOpenStorageType; - // key block doesn't change - PutShortLE(&blkBuf[0x03 + offset], newBlocksUsed); - blkBuf[0x05 + offset] = (uint8_t) newEOF; - blkBuf[0x06 + offset] = (uint8_t) (newEOF >> 8); - blkBuf[0x07 + offset] = (uint8_t) (newEOF >> 16); - - dierr = fpFile->GetDiskFS()->GetDiskImg()->WriteBlock( - pFile->fDirEntry.keyPointer, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - // file blocks used is sum of data and rsrc block counts +1 for key - combinedBlocksUsed = - GetShortLE(&blkBuf[0x03]) + GetShortLE(&blkBuf[0x103]) +1; - combinedEOF = 512; // for some reason this gets stuffed in - } else { - combinedBlocksUsed = newBlocksUsed; - combinedEOF = newEOF; - } - - /* - * Update fields in the file's directory entry. Unless, of course, - * this is the volume directory itself. - */ - if (pFile->fParentDirBlock != 0) { - dierr = fpFile->GetDiskFS()->GetDiskImg()->ReadBlock( - pFile->fParentDirBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - - uint8_t* pParentPtr; - pParentPtr = blkBuf + 0x04 + pFile->fParentDirIdx * kEntryLength; - assert(pParentPtr + kEntryLength < blkBuf + kBlkSize); - if (toupper(pParentPtr[0x01]) != toupper(pFile->fDirEntry.fileName[0])) - { - LOGW("ProDOS ERROR: parent pointer has wrong entry??"); - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - /* update the fields from the open file */ - pParentPtr[0x00] = - (pParentPtr[0x00] & 0x0f) | (newStorageType << 4); - PutShortLE(&pParentPtr[0x13], combinedBlocksUsed); - if (pFile->fDirEntry.storageType != A2FileProDOS::kStorageExtended) - { - PutShortLE(&pParentPtr[0x15], (uint16_t) newEOF); - pParentPtr[0x17] = (uint8_t) (newEOF >> 16); - } - /* don't update the mod date for now */ - //PutLongLE(&pParentPtr[0x21], A2FileProDOS::ConvertProDate(time(NULL))); - - dierr = fpFile->GetDiskFS()->GetDiskImg()->WriteBlock( - pFile->fParentDirBlock, blkBuf); - if (dierr != kDIErrNone) - goto bail; - } - - /* - * Find the #of sparse blocks. - */ - int sparseCount = 0; - for (int i = 0; i < fBlockCount; i++) { - if (fBlockList[i] == 0) - sparseCount++; - } - - /* - * Update our internal copies of stuff. The EOFs have changed, and - * in theory we'd want to update the modification date. In practice - * we're usually shuffling data from one archive to another and want - * to preserve the mod date. (Could be a DiskFS global pref?) - */ - pFile->fDirEntry.storageType = newStorageType; - pFile->fDirEntry.blocksUsed = combinedBlocksUsed; - pFile->fDirEntry.eof = combinedEOF; - - if (newStorageType == A2FileProDOS::kStorageExtended) { - if (!fOpenRsrcFork) { - pFile->fExtData.storageType = fOpenStorageType; - pFile->fExtData.blocksUsed = newBlocksUsed; - pFile->fExtData.eof = newEOF; - pFile->fSparseDataEof = (di_off_t) newEOF - (sparseCount * kBlkSize); - if (pFile->fSparseDataEof < 0) - pFile->fSparseDataEof = 0; - } else { - pFile->fExtRsrc.storageType = fOpenStorageType; - pFile->fExtRsrc.blocksUsed = newBlocksUsed; - pFile->fExtRsrc.eof = newEOF; - pFile->fSparseRsrcEof = (di_off_t) newEOF - (sparseCount * kBlkSize); - if (pFile->fSparseRsrcEof < 0) - pFile->fSparseRsrcEof = 0; - } - } else { - pFile->fSparseDataEof = (di_off_t) newEOF - (sparseCount * kBlkSize); - if (pFile->fSparseDataEof < 0) - pFile->fSparseDataEof = 0; - } - // update mod date? - - //LOGI("File '%s' closed", pFile->GetPathName()); - //pFile->Dump(); - } - -bail: - fpFile->CloseDescr(this); - return dierr; -} - - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDProDOS::GetSectorCount(void) const -{ - //if (fBlockList == NULL) - // return kDIErrNotReady; - return fBlockCount * 2; -} - -long A2FDProDOS::GetBlockCount(void) const -{ - //if (fBlockList == NULL) - // return kDIErrNotReady; - return fBlockCount; -} - -/* - * Return the Nth track/sector in this file. - */ -DIError A2FDProDOS::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - //if (fBlockList == NULL) - // return kDIErrNotReady; - long prodosIdx = sectorIdx / 2; - if (prodosIdx < 0 || prodosIdx >= fBlockCount) - return kDIErrInvalidIndex; - long prodosBlock = fBlockList[prodosIdx]; - - if (prodosBlock == 0) - *pTrack = *pSector = 0; // special-case to avoid returning (0,1) - else - BlockToTrackSector(prodosBlock, (sectorIdx & 0x01) != 0, pTrack, pSector); - return kDIErrNone; -} -/* - * Return the Nth 512-byte block in this file. - */ -DIError A2FDProDOS::GetStorage(long blockIdx, long* pBlock) const -{ - //if (fBlockList == NULL) - // return kDIErrNotReady; - if (blockIdx < 0 || blockIdx >= fBlockCount) - return kDIErrInvalidIndex; - long prodosBlock = fBlockList[blockIdx]; - - *pBlock = prodosBlock; - assert(*pBlock < fpFile->GetDiskFS()->GetDiskImg()->GetNumBlocks()); - return kDIErrNone; -} - -/* - * Dump the list of blocks from an open file, skipping over - * "sparsed-out" entries. - */ -void A2FDProDOS::DumpBlockList(void) const -{ - long ll; - - LOGI(" ProDOS file block list (count=%ld)", fBlockCount); - for (ll = 0; ll <= fBlockCount; ll++) { - if (fBlockList[ll] != 0) { - LOGI(" %5ld: 0x%04x", ll, fBlockList[ll]); - } - } -} diff --git a/ciderpress/diskimg/RDOS.cpp b/ciderpress/diskimg/RDOS.cpp deleted file mode 100644 index e6ac8c7..0000000 --- a/ciderpress/diskimg/RDOS.cpp +++ /dev/null @@ -1,716 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskFSRDOS class. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSRDOS - * =========================================================================== - */ - -const int kSctSize = 256; -const int kCatTrack = 1; -const int kNumCatSectors = 11; // 0 through 10 -const int kDirectoryEntryLen = 32; -const int kNumDirEntryPerSect = (256 / kDirectoryEntryLen); // 8 - -/* - * See if this looks like a RDOS volume. - * - * There are three variants: - * RDOS32 (e.g. ComputerAmbush.nib): - * 13-sector disk - * sector (1,0) starts with "RDOS 2" - * sector (1,12) has catalog code, CHAIN in (1,11) - * uses "physical" ordering - * NOTE: track 0 may be unreadable with RDOS 3.2 NibbleDescr - * RDOS33 (e.g. disk #199): - * 16-sector disk - * sector (1,0) starts with "RDOS 3" - * sector (1,12) has catalog code - * uses "ProDOS" ordering - * RDOS3 (e.g. disk #108): - * 16-sector disk, but only 13 sectors of each track are used - * sector (1,0) starts with "RDOS 2" - * sector (0,1) has catalog code - * uses "physical" orering - * - * In all cases: - * catalog found on (1,0) through (1,10) - * - * The initial value of "pFormatFound" is ignored, because we can reliably - * detect which variant we're looking at. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, - DiskImg::FSFormat* pFormatFound) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - - - if (pImg->GetNumSectPerTrack() == 13) { - /* must be a nibble image; check it for RDOS 3.2 */ - dierr = pImg->ReadTrackSectorSwapped(kCatTrack, 0, sctBuf, - imageOrder, DiskImg::kSectorOrderPhysical); - if (dierr != kDIErrNone) - goto bail; - } else if (pImg->GetNumSectPerTrack() == 16) { - /* could be RDOS3 or RDOS 3.3 */ - dierr = pImg->ReadTrackSectorSwapped(kCatTrack, 0, sctBuf, - imageOrder, DiskImg::kSectorOrderPhysical); - if (dierr != kDIErrNone) - goto bail; - } else { - LOGI(" RDOS neither 13 nor 16 sector, bailing"); - goto bail; - } - - /* check for RDOS string and correct #of blocks */ - if (!( sctBuf[0] == 'R'+0x80 && - sctBuf[1] == 'D'+0x80 && - sctBuf[2] == 'O'+0x80 && - sctBuf[3] == 'S'+0x80 && - sctBuf[4] == ' '+0x80) || - !(sctBuf[25] == 26 || sctBuf[25] == 32)) - { - LOGI(" RDOS no signature found on (%d,0)", kCatTrack); - dierr = kDIErrGeneric; - goto bail; - } - - /* - * Guess at the format based on the first catalog entry, which usually - * begins "RDOS 2.0", "RDOS 2.1", or "RDOS 3.3". - */ - if (pImg->GetNumSectPerTrack() == 13) { - *pFormatFound = DiskImg::kFormatRDOS32; - } else { - if (sctBuf[5] == '2'+0x80) - *pFormatFound = DiskImg::kFormatRDOS3; - else - *pFormatFound = DiskImg::kFormatRDOS33; - } - - /* - * The above came from sector 0, which doesn't help us figure out the - * sector ordering. Look for the catalog code. - */ - { - int track, sector, offset; - uint8_t orMask; - static const char* kCompare = ""; - DiskImg::SectorOrder order; - - if (*pFormatFound == DiskImg::kFormatRDOS32 || - *pFormatFound == DiskImg::kFormatRDOS3) - { - track = 1; - sector = 12; - offset = 0xa2; - orMask = 0x80; - order = DiskImg::kSectorOrderPhysical; - } else { - track = 0; - sector = 1; - offset = 0x98; - orMask = 0; - order = DiskImg::kSectorOrderProDOS; - } - - dierr = pImg->ReadTrackSectorSwapped(track, sector, sctBuf, - imageOrder, order); - if (dierr != kDIErrNone) - goto bail; - - int i; - for (i = strlen(kCompare)-1; i >= 0; i--) { - if (sctBuf[offset+i] != ((uint8_t)kCompare[i] | orMask)) - break; - } - if (i >= 0) { - dierr = kDIErrGeneric; - goto bail; - } - - LOGI(" RDOS found '%s' signature (order=%d)", kCompare, imageOrder); - } - - dierr = kDIErrNone; - -bail: - return dierr; -} - -/* - * Common RDOS test code. - */ -/*static*/ DIError DiskFSRDOS::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - if (!pImg->GetHasSectors()) { - LOGI(" RDOS - image doesn't have sectors, not trying"); - return kDIErrFilesystemNotFound; - } - if (pImg->GetNumTracks() != 35) { - LOGI(" RDOS - not a 35-track disk, not trying"); - return kDIErrFilesystemNotFound; - } - DiskImg::FSFormat formatFound; - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i], &formatFound) == kDIErrNone) { - *pFormat = formatFound; - *pOrder = ordering[i]; - //*pFormat = DiskImg::kFormatXXX; - return kDIErrNone; - } - } - - LOGI(" RDOS didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -#if 0 -/* - * Test to see if the image is an RDOS 3.3 disk. - */ -/*static*/ DIError DiskFSRDOS::TestFS33(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - FSLeniency leniency) -{ - DIError dierr; - DiskImg::FSFormat formatFound = DiskImg::kFormatUnknown; - - dierr = TestCommon(pImg, pOrder, leniency, &formatFound); - if (dierr != kDIErrNone) - return dierr; - if (formatFound != DiskImg::kFormatRDOS33) { - LOGI(" RDOS found RDOS but wrong type"); - return kDIErrFilesystemNotFound; - } - - return kDIErrNone; -} - -/* - * Test to see if the image is an RDOS 3.2 disk. - */ -/*static*/ DIError DiskFSRDOS::TestFS32(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - FSLeniency leniency) -{ - DIError dierr; - DiskImg::FSFormat formatFound = DiskImg::kFormatUnknown; - - dierr = TestCommon(pImg, pOrder, leniency, &formatFound); - if (dierr != kDIErrNone) - return dierr; - if (formatFound != DiskImg::kFormatRDOS32) { - LOGI(" RDOS found RDOS but wrong type"); - return kDIErrFilesystemNotFound; - } - - return kDIErrNone; -} - -/* - * Test to see if the image is an RDOS 3 (cracked 3.2) disk. - */ -/*static*/ DIError DiskFSRDOS::TestFS3(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - FSLeniency leniency) -{ - DIError dierr; - DiskImg::FSFormat formatFound = DiskImg::kFormatUnknown; - - dierr = TestCommon(pImg, pOrder, leniency, &formatFound); - if (dierr != kDIErrNone) - return dierr; - if (formatFound != DiskImg::kFormatRDOS3) { - LOGI(" RDOS found RDOS but wrong type"); - return kDIErrFilesystemNotFound; - } - - return kDIErrNone; -} -#endif - - -/* - * Get things rolling. - * - * Since we're assured that this is a valid disk, errors encountered from here - * on out must be handled somehow, possibly by claiming that the disk is - * completely full and has no files on it. - */ -DIError DiskFSRDOS::Initialize(void) -{ - DIError dierr = kDIErrNone; - const char* volStr; - - switch (GetDiskImg()->GetFSFormat()) { - case DiskImg::kFormatRDOS33: - volStr = "RDOS 3.3"; - fOurSectPerTrack = 16; - break; - case DiskImg::kFormatRDOS32: - volStr = "RDOS 3.2"; - fOurSectPerTrack = 13; - break; - case DiskImg::kFormatRDOS3: - volStr = "RDOS 3"; - fOurSectPerTrack = 13; - break; - default: - assert(false); - return kDIErrInternal; - } - assert(strlen(volStr) < sizeof(fVolumeName)); - strcpy(fVolumeName, volStr); - - dierr = ReadCatalog(); - if (dierr != kDIErrNone) - goto bail; - - fVolumeUsage.Create(fpImg->GetNumTracks(), fOurSectPerTrack); - dierr = ScanFileUsage(); - if (dierr != kDIErrNone) { - /* this might not be fatal; just means that *some* files are bad */ - goto bail; - } - fVolumeUsage.Dump(); - - //A2File* pFile; - //pFile = GetNextFile(NULL); - //while (pFile != NULL) { - // pFile->Dump(); - // pFile = GetNextFile(pFile); - //} - -bail: - return dierr; -} - - -/* - * Read the catalog from the disk. - * - * To make life easy we slurp the whole thing into memory. - */ -DIError DiskFSRDOS::ReadCatalog(void) -{ - DIError dierr = kDIErrNone; - uint8_t* dir = NULL; - uint8_t* dirPtr; - int track, sector; - - dir = new uint8_t[kSctSize * kNumCatSectors]; - if (dir == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - track = kCatTrack; - dirPtr = dir; - for (sector = 0; sector < kNumCatSectors; sector++) { - dierr = fpImg->ReadTrackSector(track, sector, dirPtr); - if (dierr != kDIErrNone) - goto bail; - - dirPtr += kSctSize; - } - - int i; - A2FileRDOS* pFile; - dirPtr = dir; - for (i = 0; i < kNumCatSectors * kNumDirEntryPerSect; - i++, dirPtr += kDirectoryEntryLen) - { - if (dirPtr[0] == 0x80 || dirPtr[24] == 0xa0) // deleted file - continue; - if (dirPtr[24] == 0x00) // unused entry; must be at end of catalog - break; - - pFile = new A2FileRDOS(this); - - memcpy(pFile->fFileName, dirPtr, A2FileRDOS::kMaxFileName); - pFile->fFileName[A2FileRDOS::kMaxFileName] = '\0'; - pFile->FixFilename(); - - switch (dirPtr[24]) { - case 'A'+0x80: pFile->fFileType = A2FileRDOS::kTypeApplesoft; break; - case 'B'+0x80: pFile->fFileType = A2FileRDOS::kTypeBinary; break; - case 'T'+0x80: pFile->fFileType = A2FileRDOS::kTypeText; break; - // 0x00 is end of catalog, ' '+0x80 is deleted file, both handled above - default: pFile->fFileType = A2FileRDOS::kTypeUnknown; break; - } - pFile->fNumSectors = dirPtr[25]; - pFile->fLoadAddr = GetShortLE(&dirPtr[26]); - pFile->fLength = GetShortLE(&dirPtr[28]); - pFile->fStartSector = GetShortLE(&dirPtr[30]); - - if (pFile->fStartSector + pFile->fNumSectors > - fpImg->GetNumTracks() * fOurSectPerTrack) - { - LOGI(" RDOS invalid start/count (%d + %d) (max %ld) '%s'", - pFile->fStartSector, pFile->fNumSectors, fpImg->GetNumBlocks(), - pFile->fFileName); - pFile->fStartSector = pFile->fNumSectors = 0; - pFile->fLength = 0; - pFile->SetQuality(A2File::kQualityDamaged); - } - - AddFileToList(pFile); - } - -bail: - delete[] dir; - return dierr; -} - - -/* - * Create the volume usage map. Since RDOS volumes have neither - * in-use maps nor index blocks, this is pretty straightforward. - */ -DIError DiskFSRDOS::ScanFileUsage(void) -{ - int track, sector, block, count; - - A2FileRDOS* pFile; - pFile = (A2FileRDOS*) GetNextFile(NULL); - while (pFile != NULL) { - block = pFile->fStartSector; - count = pFile->fNumSectors; - while (count--) { - track = block / fOurSectPerTrack; - sector = block % fOurSectPerTrack; - - SetSectorUsage(track, sector, VolumeUsage::kChunkPurposeUserData); - - block++; - } - - pFile = (A2FileRDOS*) GetNextFile(pFile); - } - - return kDIErrNone; -} - -/* - * Update an entry in the usage map. - */ -void DiskFSRDOS::SetSectorUsage(long track, long sector, - VolumeUsage::ChunkPurpose purpose) -{ - VolumeUsage::ChunkState cstate; - - fVolumeUsage.GetChunkState(track, sector, &cstate); - if (cstate.isUsed) { - cstate.purpose = VolumeUsage::kChunkPurposeConflict; - LOGI(" RDOS conflicting uses for sct=(%ld,%ld)", track, sector); - } else { - cstate.isUsed = true; - cstate.isMarkedUsed = true; - cstate.purpose = purpose; - } - fVolumeUsage.SetChunkState(track, sector, &cstate); -} - - -/* - * =========================================================================== - * A2FileRDOS - * =========================================================================== - */ - -/* - * Convert RDOS file type to ProDOS file type. - */ -uint32_t A2FileRDOS::GetFileType(void) const -{ - uint32_t retval; - - switch (fFileType) { - case kTypeText: retval = 0x04; break; // TXT - case kTypeApplesoft: retval = 0xfc; break; // BAS - case kTypeBinary: retval = 0x06; break; // BIN - case kTypeUnknown: - default: retval = 0x00; break; // NON - } - - return retval; -} - -/* - * Dump the contents of the A2File structure. - */ -void A2FileRDOS::Dump(void) const -{ - LOGI("A2FileRDOS '%s' (type=%d)", fFileName, fFileType); - LOGI(" start=%d num=%d len=%d addr=0x%04x", - fStartSector, fNumSectors, fLength, fLoadAddr); -} - -/* - * "Fix" an RDOS filename. Convert DOS-ASCII to normal ASCII, and strip - * trailing spaces. - * - * It's possible that RDOS 3.3 forces the filename to high-ASCII, because - * one disk (#938A) has a file left by the crackers whose name is in - * low-ASCII. The inverse-mode correction turns it into punctuation, but - * I don't see a good way around it. Or any particular need to fix it. - */ -void A2FileRDOS::FixFilename(void) -{ - DiskFSDOS33::LowerASCII((uint8_t*)fFileName, kMaxFileName); - TrimTrailingSpaces(fFileName); -} - -/* - * Trim the spaces off the end of a filename. - * - * Assumes the filename has already been converted to low ASCII. - */ -void A2FileRDOS::TrimTrailingSpaces(char* filename) -{ - char* lastspc = filename + strlen(filename); - - assert(*lastspc == '\0'); - - while (--lastspc) { - if (*lastspc != ' ') - break; - } - - *(lastspc+1) = '\0'; -} - -/* - * Not a whole lot to do, since there's no fancy index blocks. - */ -DIError A2FileRDOS::Open(A2FileDescr** ppOpenFile, bool readOnly, - bool rsrcFork /*=false*/) -{ - if (fpOpenFile != NULL) - return kDIErrAlreadyOpen; - if (rsrcFork) - return kDIErrForkNotFound; - assert(readOnly == true); - - A2FDRDOS* pOpenFile = new A2FDRDOS(this); - - pOpenFile->fOffset = 0; - //fOpen = true; - - fpOpenFile = pOpenFile; - *ppOpenFile = pOpenFile; - pOpenFile = NULL; - - return kDIErrNone; -} - - -/* - * =========================================================================== - * A2FDRDOS - * =========================================================================== - */ - -/* - * Read a chunk of data from the current offset. - */ -DIError A2FDRDOS::Read(void* buf, size_t len, size_t* pActual) -{ - LOGD(" RDOS reading %lu bytes from '%s' (offset=%ld)", - (unsigned long) len, fpFile->GetPathName(), (long) fOffset); - //if (!fOpen) - // return kDIErrNotReady; - - A2FileRDOS* pFile = (A2FileRDOS*) fpFile; - - /* don't allow them to read past the end of the file */ - if (fOffset + (long)len > pFile->fLength) { - if (pActual == NULL) - return kDIErrDataUnderrun; - len = (size_t) (pFile->fLength - fOffset); - } - if (pActual != NULL) - *pActual = len; - long incrLen = len; - - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - long block = pFile->fStartSector + (long) (fOffset / kSctSize); - int bufOffset = (int) (fOffset % kSctSize); // (& 0xff) - int ourSectPerTrack = GetOurSectPerTrack(); - size_t thisCount; - - if (len == 0) { - ///* one block allocated for empty file */ - //SetLastBlock(block, true); - return kDIErrNone; - } - assert(pFile->fLength != 0); - - while (len) { - assert(block >= pFile->fStartSector && - block < pFile->fStartSector + pFile->fNumSectors); - - dierr = pFile->GetDiskFS()->GetDiskImg()->ReadTrackSector(block / ourSectPerTrack, - block % ourSectPerTrack, sctBuf); - if (dierr != kDIErrNone) { - LOGI(" RDOS error reading file '%s'", pFile->fFileName); - return dierr; - } - thisCount = kSctSize - bufOffset; - if (thisCount > len) - thisCount = len; - - memcpy(buf, sctBuf + bufOffset, thisCount); - len -= thisCount; - buf = (char*)buf + thisCount; - - bufOffset = 0; - block++; - } - - fOffset += incrLen; - - return dierr; -} - -/* - * Write data at the current offset. - */ -DIError A2FDRDOS::Write(const void* buf, size_t len, size_t* pActual) -{ - //if (!fOpen) - // return kDIErrNotReady; - return kDIErrNotSupported; -} - -/* - * Seek to a new offset. - */ -DIError A2FDRDOS::Seek(di_off_t offset, DIWhence whence) -{ - //if (!fOpen) - // return kDIErrNotReady; - - long fileLen = ((A2FileRDOS*) fpFile)->fLength; - - switch (whence) { - case kSeekSet: - if (offset < 0 || offset > fileLen) - return kDIErrInvalidArg; - fOffset = offset; - break; - case kSeekEnd: - if (offset > 0 || offset < -fileLen) - return kDIErrInvalidArg; - fOffset = fileLen + offset; - break; - case kSeekCur: - if (offset < -fOffset || - offset >= (fileLen - fOffset)) - { - return kDIErrInvalidArg; - } - fOffset += offset; - break; - default: - assert(false); - return kDIErrInvalidArg; - } - - assert(fOffset >= 0 && fOffset <= fileLen); - return kDIErrNone; -} - -/* - * Return current offset. - */ -di_off_t A2FDRDOS::Tell(void) -{ - //if (!fOpen) - // return kDIErrNotReady; - - return fOffset; -} - -/* - * Release file state, such as it is. - */ -DIError A2FDRDOS::Close(void) -{ - fpFile->CloseDescr(this); - return kDIErrNone; -} - -/* - * Return the #of sectors/blocks in the file. - */ -long A2FDRDOS::GetSectorCount(void) const -{ - //if (!fOpen) - // return kDIErrNotReady; - return ((A2FileRDOS*) fpFile)->fNumSectors; -} - -long A2FDRDOS::GetBlockCount(void) const -{ - //if (!fOpen) - // return kDIErrNotReady; - return ((A2FileRDOS*) fpFile)->fNumSectors / 2; -} - -/* - * Return the Nth track/sector in this file. - */ -DIError A2FDRDOS::GetStorage(long sectorIdx, long* pTrack, long* pSector) const -{ - //if (!fOpen) - // return kDIErrNotReady; - A2FileRDOS* pFile = (A2FileRDOS*) fpFile; - long rdosBlock = pFile->fStartSector + sectorIdx; - int ourSectPerTrack = GetOurSectPerTrack(); - if (rdosBlock >= pFile->fStartSector + pFile->fNumSectors) - return kDIErrInvalidIndex; - - *pTrack = rdosBlock / ourSectPerTrack; - *pSector = rdosBlock % ourSectPerTrack; - - return kDIErrNone; -} - -/* - * Return the Nth 512-byte block in this file. - */ -DIError A2FDRDOS::GetStorage(long blockIdx, long* pBlock) const -{ - //if (!fOpen) - // return kDIErrNotReady; - A2FileRDOS* pFile = (A2FileRDOS*) fpFile; - long rdosBlock = pFile->fStartSector + blockIdx*2; - if (rdosBlock >= pFile->fStartSector + pFile->fNumSectors) - return kDIErrInvalidIndex; - - *pBlock = rdosBlock / 2; - - if (pFile->GetDiskFS()->GetDiskImg()->GetHasBlocks()) { - assert(*pBlock < pFile->GetDiskFS()->GetDiskImg()->GetNumBlocks()); - } - return kDIErrNone; -} diff --git a/ciderpress/diskimg/README.md b/ciderpress/diskimg/README.md deleted file mode 100644 index 05f10d3..0000000 --- a/ciderpress/diskimg/README.md +++ /dev/null @@ -1,245 +0,0 @@ -CiderPress Disk Image Library -============================= - -This library provides access to files stored in Apple II disk images. It -was developed as part of CiderPress, but can be used independently. It -builds on Windows and Linux. - -The MDC (Multi-Disk Catalog) application uses the DiskImg DLL (on Windows) -or library (on Linux) to examine disk image files. - - -Disk Image Structure --------------------- - -The Apple II supported a number of different filesystems and physical -formats. There are several different disk image formats, some supported -by Apple II software, some only usable by emulators. This section -provides a quick summary. - -#### Filesystems #### - -- DOS 3.2/3.3. The classic Apple II filesystems. The typical DOS 3.3 -disk has 35 tracks, 16 sectors per track, 256 bytes per sector, for a -total of 140K. The format allows up to 32 sectors and 50 tracks (400K). -It worked very well on 5.25" floppy disks, but was awkward to use on -larger volumes. Filenames could be up to 30 characters, and sometimes -embedded control characters or used flashing/inverse character values. - -- ProDOS. Designed to work on larger media, ProDOS addresses data as -512-byte blocks, and leaves any track/sector mapping to device-specific -code. Block numbers were stored as unsigned 16-bit values, allowing -volumes up to 32MB. Filenames were limited to 15 upper-case or numeric -ASCII values. Later versions added support for forked files and -case-preserved (but still case-insensitive) filenames. - -- UCSD Pascal. Another block-oriented filesystem, used with the UCSD -Pascal operating system. Very simple, and very efficient when reading, -but required explicit defragmentation from time to time. - -- HFS. Originally developed for the Macintosh, it was often used on -the Apple IIgs as hard drive sizes increased. HFS supports forked files, -and "MacRoman" filenames up to 31 characters. - -- CP/M. Z-80 cards allowed Apple II users to run CP/M software, using -the established CP/M filesystem layout. It featured 1K blocks and 8.3 -filenames. - -- SSI RDOS. A custom format developed by Strategic Simulations, Inc. for -use with their games. This was used on 13-sector and 16-sector 5.25" -disks. The operating system used Applesoft ampersand commands, and was -ported to ProDOS. - -- Gutenberg. A custom format developed by Micromation Limited for use -with the Gutenberg word processor. - -DOS, ProDOS, HFS, and UCSD Pascal are fully supported by the DiskImg -library. CP/M, RDOS, and Gutenberg are treated as read-only. - - -#### Disk Image Formats #### - -Disk image files can be "unadorned", meaning they're just a series of -blocks or sectors, or they can have fancy headers and compressed contents. -Block-oriented images are easy to deal with, as they're generally just -the blocks in sequential order. Sector-oriented images can be tricky, -because the sector order is subject to interpretation. - -The most common sector orderings are "DOS" and "ProDOS". If you read -a 5.25" disk sequentially from DOS, starting with track 0 sector 0, and -wrote the contents to a file, you would end up with a DOS-ordered image. -If you read that same disk from ProDOS, starting with block 0, you would -end up with a ProDOS-ordered image. The difference occurs because ProDOS -blocks are 512 bytes -- two DOS sectors -- and ProDOS interleaves the -sectors as an optimization. While you might expect ProDOS block 0 to be -comprised of DOS T0 S0 and T0 S1, it's actually T0 S0 and T0 S2. - -There have been various attempts at defining storage formats for disk -images over the years. The library handles most of them. - -- Unadorned block/sector files (.po, .do, .d13, .raw, .hdv, .iso, most .dc6). -The image file holds data from the file and nothing else. - -- Unadorned nibble-format files (.nib, .nb2). Some 5.25" disks were a -bit "creative" with their physical format, so some image formats allow -for extraction of the data as bits directly off the disk. Such formats -are unusual in that it's possible to have "bad sectors" in a disk image. - -- Universal Disk Image (.2mg, .2img). The format was designed specifically -for Apple II emulators. It supports DOS-order, ProDOS-order, and nibble -images. - -- Copy ][ Plus (.img). Certain versions of the Copy ][ Plus Apple II -utility had the ability to create disk images. The format was simple -unadorned sectors, but with a twist: the sectors were in physical order, -which is different from DOS and ProDOS. - -- Dalton's Disk Disintegrator (.ddd). DDD was developed as an alternative -to "disk slicer" programs for uploading disk images to BBS systems. -Because it used compression, 5.25" disk images could be held on 5.25" disks. -DOS and ProDOS versions of the program were available. A fancier version, -called DDD Deluxe, was developed later. - -- ShrinkIt (.shk, .sdk). ShrinkIt was initially developed as an improved -version of DDD, incorporating LZW compression and CRC error checking. It -grew into a general-purpose file archiver. - -- DiskCopy 4.2 (.dsk). The format used by the Mac DiskCopy program for -making images of 800K floppies. Includes a checksum, but no compression. - -- TrackStar (.app). The TrackStar was essentially an Apple II built -into a PC ISA expansion card. The 5.25" disk images use a variable-length -nibble format with 40 tracks. - -- Formatted Disk Image (.fdi). Files generated by the Disk2FDI program. -These contain raw signal data obtained from a PC floppy drive. It was -long said that reading an Apple II floppy from a PC drive was impossible; -this program proved otherwise. - -- Sim //e HDV (.hdv). Used for images of ProDOS drives for use with a -specific emulator, this is just a ProDOS block image with a short header. - -All of these, with the exception of DDD Deluxe, are fully supported by -the DiskImg library. - - -#### Meta-Formats #### - -As disks got larger, older filesystems could no longer use all of the -available space with a single volume. Some "meta-formats" were developed. -These allow a single disk image to hold multiple filesystems. - -- UNIDOS / AmDOS / OzDOS. These allow two 400K DOS 3.3 volumes to -exist on a single 800K disk. - -- ProSel Uni-DOS / DOS Master. These allow multiple 140K DOS 3.3 -volumes to reside on a ProDOS volume. You can, on a single 800K disk, -provide a small ProDOS launcher that will boot into DOS 3.3 and launch a -specific file from one of five 140K DOS volumes. - -- Macintosh-style disk partitioning. This was widely used on hard -drives, CD-ROMs, and other large disks. - -- CFFA-style disk partitioning. The CFFA card for the Apple II allows -the use of Compact Flash cards for storage. There is no partitioning -done on the CF card itself -- the CFFA card has a fixed arrangement. - -- ///SHH Systeme MicroDrive partitioning. Another disk partition format, -developed for use with the MicroDrive. Allows up to 16 partitions on -an IDE hard drive. - -- Parsons Engineering FocusDrive partitioning. Developed for use with -the FocusDrive. Allows up to 30 partitions. - -It's also possible to create a "hybrid" DOS / ProDOS disk, because the -essential file catalog areas don't overlap. (See HYBRID.CREATE on the -Beagle Bros "Extra K" disk.) - -All of these are fully supported by the DiskImg library. - - -#### Wrapper Formats #### - -While 800K may not seem like a lot these days, it used to be a decent -chunk of data, so it was common for disk images that didn't use compression -to be compressed with another program. The most common are ZIP and gzip. - - -#### Apple II File Formats #### - -For filesystems like ProDOS, the body of the file contains just the file -contents. The directory entry holds the file type, auxiliary type, and -the file's length in bytes. - -For DOS 3.2/3.3, the first few bytes of the file specified details like -the load address, and for text files there is no reliable indication of -the file length. The DiskImg library does what it can to conceal -filesystem quirks. - - - -DiskImg Library Classes ------------------------ - -The library provides several C++ classes. This section gives an overview -of what each does. - -The basic classes are defined in DiskImg.h. Specialized sub-classes are -declared in DiskImgDetail.h. - -The API is a bit more complicated than it could be, and there's a bit of -redundancy in the filesystem code. Some of this is due to the way the -library evolved -- disk image access was originally intended to be read-only, -and that didn't change until CiderPress 2.0. - - -#### DiskImg #### - -The `DiskImg` class represents a single disk image. It may have sub-images, -each of which is its own instance of DiskImg. Operations on a DiskImg -are similar to what you'd expect from a device driver, e.g. reading and -writing individual blocks. - -The DiskImg has several characteristics: - -- OuterFormat. This is the "outer wrapper", which may be ZIP (.zip) or -gzip (.gz). The wrapper is handled transparently -- the contents are -uncompressed when opened, and recompressed if necessary when closed. - -- FileFormat. The disk image file's format, once the outer wrapper has -been stripped away. "Unadorned", 2MG, and ShrinkIt are examples. - -- PhysicalFormat. Identifies whether the data is "raw" or "cooked", i.e. -if it's a series of blocks/sectors or raw nibbles. - -- SectorOrder. ProDOS, DOS, Physical. - -- FSFormat. What type of filesystem is in this image. This includes -common formats, like DOS 3.3 and ProDOS, as well as meta-formats like -UNIDOS and Macintosh partition. - - -#### DiskFS #### - -A `DiskFS` instance is typically paired with a DiskImg. It represents -the filesystem, operating at a level roughly equivalent to a GS/OS -File System Translator (FST). - -Using DiskFS, you can read, write, and rename files, format disks, -check how much free space is available, and search for sub-volumes. - - -#### A2File #### - -One instance of `A2File` represents one file on disk. The object holds -the filename and attributes, and provides a call to open the file. - - -#### A2FileDescr #### - -This is essentially a file descriptor for an Apple II file. You can -read or write data. - -To make the implementation easier, files must be written with a single -write call, and only one fork of a forked file may be open. - diff --git a/ciderpress/diskimg/SCSIDefs.h b/ciderpress/diskimg/SCSIDefs.h deleted file mode 100644 index 58ae883..0000000 --- a/ciderpress/diskimg/SCSIDefs.h +++ /dev/null @@ -1,308 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Definitions for SCSI (Small Computer System Interface). - * - * These structures and defines are passed to the SCSI driver, so they work - * equally well for ASPI and SPTI - * - * Consult the SCSI-2 and MMC-2 specifications for details. - */ -#ifndef DISKIMG_SCSIDEFS_H -#define DISKIMG_SCSIDEFS_H - -/* - * SCSI-2 operation codes. - */ -typedef enum { - kScsiOpTestUnitReady = 0x00, - kScsiOpRezeroUnit = 0x01, - kScsiOpRewind = 0x01, - kScsiOpRequestBlockAddr = 0x02, - kScsiOpRequestSense = 0x03, - kScsiOpFormatUnit = 0x04, - kScsiOpReadBlockLimits = 0x05, - kScsiOpReassignBlocks = 0x07, - kScsiOpRead6 = 0x08, - kScsiOpReceive = 0x08, - kScsiOpWrite6 = 0x0a, - kScsiOpPrint = 0x0a, - kScsiOpSend = 0x0a, - kScsiOpSeek6 = 0x0b, - kScsiOpTrackSelect = 0x0b, - kScsiOpSlewPrint = 0x0b, - kScsiOpSeekBlock = 0x0c, - kScsiOpPartition = 0x0d, - kScsiOpReadReverse = 0x0f, - kScsiOpWriteFilemarks = 0x10, - kScsiOpFlushBuffer = 0x10, - kScsiOpSpace = 0x11, - kScsiOpInquiry = 0x12, - kScsiOpVerify6 = 0x13, - kScsiOpRecoverBufferedData = 0x14, - kScsiOpModeSelect = 0x15, - kScsiOpReserveUnit = 0x16, - kScsiOpReleaseUnit = 0x17, - kScsiOpCopy = 0x18, - kScsiOpErase = 0x19, - kScsiOpModeSense = 0x1a, - kScsiOpStartStopUnit = 0x1b, - kScsiOpStopPrint = 0x1b, - kScsiOpLoadUnload = 0x1b, - kScsiOpReceiveDiagnosticResults = 0x1c, - kScsiOpSendDiagnostic = 0x1d, - kScsiOpMediumRemoval = 0x1e, - kScsiOpReadFormattedCapacity = 0x23, - kScsiOpReadCapacity = 0x25, - kScsiOpRead = 0x28, // READ(10) - kScsiOpWrite = 0x2a, // WRITE(10) - kScsiOpSeek = 0x2b, - kScsiOpLocate = 0x2b, - kScsiOpPositionToElement = 0x2b, - kScsiOpWriteVerify = 0x2e, - kScsiOpVerify = 0x2f, // VERIFY(10) - kScsiOpSearchDataHigh = 0x30, - kScsiOpSearchDataEqual = 0x31, - kScsiOpSearchDataLow = 0x32, - kScsiOpSetLimits = 0x33, - kScsiOpReadPosition = 0x34, - kScsiOpSynchronizeCache = 0x35, - kScsiOpCompare = 0x39, - kScsiOpCopyAndVerify = 0x3a, - kScsiOpWriteBuffer = 0x3b, - kScsiOpReadBuffer = 0x3c, - kScsiOpChangeDefinition = 0x40, - kScsiOpReadSubChannel = 0x42, - kScsiOpReadTOC = 0x43, // READ TOC/PMA/ATIP - kScsiOpReadHeader = 0x44, - kScsiOpPlayAudio = 0x45, - kScsiOpPlayAudioMSF = 0x47, - kScsiOpPlayTrackIndex = 0x48, - kScsiOpPlayTrackRelative = 0x49, - kScsiOpPauseResume = 0x4b, - kScsiOpLogSelect = 0x4c, - kScsiOpLogSense = 0x4c, - kScsiOpStopPlayScan = 0x4e, - kScsiOpReadDiscInformation = 0x51, - kScsiOpReadTrackInformation = 0x52, - kScsiOpSendOPCInformation = 0x54, - kScsiOpModeSelect10 = 0x55, - kScsiOpRepairTrack = 0x58, - kScsiOpModeSense10 = 0x5a, - kScsiOpReportLuns = 0xa0, - kScsiOpVerify12 = 0xa2, - kScsiOpSendKey = 0xa3, - kScsiOpReportKey = 0xa4, - kScsiOpMoveMedium = 0xa5, - kScsiOpLoadUnloadSlot = 0xa6, - kScsiOpExchangeMedium = 0xa6, - kScsiOpSetReadAhead = 0xa7, - kScsiOpReadDVDStructure = 0xad, - kScsiOpWriteAndVerify = 0xae, - kScsiOpRequestVolElement = 0xb5, - kScsiOpSendVolumeTag = 0xb6, - kScsiOpReadElementStatus = 0xb8, - kScsiOpReadCDMSF = 0xb9, - kScsiOpScanCD = 0xba, - kScsiOpSetCDSpeed = 0xbb, - kScsiOpPlayCD = 0xbc, - kScsiOpMechanismStatus = 0xbd, - kScsiOpReadCD = 0xbe, - kScsiOpInitElementRange = 0xe7, -} SCSIOperationCode; - - -/* - * SCSI status codes. - */ -typedef enum { - kScsiStatGood = 0x00, - kScsiStatCheckCondition = 0x02, - kScsiStatConditionMet = 0x04, - kScsiStatBusy = 0x08, - kScsiStatIntermediate = 0x10, - kScsiStatIntermediateCondMet = 0x14, - kScsiStatReservationConflict = 0x18, - kScsiStatCommandTerminated = 0x22, - kScsiStatQueueFull = 0x28, -} SCSIStatus; - -/* - * SCSI sense codes. - */ -typedef enum { - kScsiSenseNoSense = 0x00, - kScsiSenseRecoveredError = 0x01, - kScsiSenseNotReady = 0x02, - kScsiSenseMediumError = 0x03, - kScsiSenseHardwareError = 0x04, - kScsiSenseIllegalRequest = 0x05, - kScsiSenseUnitAttention = 0x06, - kScsiSenseDataProtect = 0x07, - kScsiSenseBlankCheck = 0x08, - kScsiSenseUnqiue = 0x09, - kScsiSenseCopyAborted = 0x0a, - kScsiSenseAbortedCommand = 0x0b, - kScsiSenseEqual = 0x0c, - kScsiSenseVolOverflow = 0x0d, - kScsiSenseMiscompare = 0x0e, - kScsiSenseReserved = 0x0f, -} SCSISenseCode; - - -/* - * SCSI additional sense codes. - */ -typedef enum { - kScsiAdSenseNoSense = 0x00, - kScsiAdSenseInvalidMedia = 0x30, - kScsiAdSenseNoMediaInDevice = 0x3a, -} SCSIAdSenseCode; - -/* - * SCSI device types. - */ -typedef enum { - kScsiDevTypeDASD = 0x00, // Disk Device - kScsiDevTypeSEQD = 0x01, // Tape Device - kScsiDevTypePRNT = 0x02, // Printer - kScsiDevTypePROC = 0x03, // Processor - kScsiDevTypeWORM = 0x04, // Write-once read-multiple - kScsiDevTypeCDROM = 0x05, // CD-ROM device - kScsiDevTypeSCAN = 0x06, // Scanner device - kScsiDevTypeOPTI = 0x07, // Optical memory device - kScsiDevTypeJUKE = 0x08, // Medium Changer device - kScsiDevTypeCOMM = 0x09, // Communications device - kScsiDevTypeRESL = 0x0a, // Reserved (low) - kScsiDevTypeRESH = 0x1e, // Reserved (high) - kScsiDevTypeUNKNOWN = 0x1f, // Unknown or no device type -} SCSIDeviceType; - -/* - * Generic 6-byte request block. - */ -typedef struct CDB6 { - unsigned char operationCode; - unsigned char immediate : 1; - unsigned char commandUniqueBits : 4; - unsigned char logicalUnitNumber : 3; - unsigned char commandUniqueBytes[3]; - unsigned char link : 1; - unsigned char flag : 1; - unsigned char reserved : 4; - unsigned char vendorUnique : 2; -} CDB6; - -/* - * Generic 10-byte request block. - * - * Use for READ(10), READ CAPACITY. - */ -typedef struct CDB10 { - unsigned char operationCode; - unsigned char relativeAddress : 1; - unsigned char reserved1 : 2; - unsigned char forceUnitAccess : 1; - unsigned char disablePageOut : 1; - unsigned char logicalUnitNumber : 3; - unsigned char logicalBlockAddr0; // MSB - unsigned char logicalBlockAddr1; - unsigned char logicalBlockAddr2; - unsigned char logicalBlockAddr3; // LSB - unsigned char reserved2; - unsigned char transferLength0; // MSB - unsigned char transferLength1; // LSB - unsigned char control; -} CDB10; - -/* - * INQUIRY request block. - */ -typedef struct CDB6Inquiry { - unsigned char operationCode; - unsigned char EVPD : 1; - unsigned char reserved1 : 4; - unsigned char logicalUnitNumber : 3; - unsigned char pageCode; - unsigned char reserved2; - unsigned char allocationLength; - unsigned char control; -} CDB6Inquiry; - -/* - * Sense data (ASPI SenseArea). - */ -typedef struct CDB_SenseData { - unsigned char errorCode:7; - unsigned char valid:1; - unsigned char segmentNumber; - unsigned char senseKey:4; - unsigned char reserved:1; - unsigned char incorrectLength:1; - unsigned char endOfMedia:1; - unsigned char fileMark:1; - unsigned char information[4]; - unsigned char additionalSenseLength; - unsigned char commandSpecificInformation[4]; - unsigned char additionalSenseCode; // ASC - unsigned char additionalSenseCodeQualifier; // ASCQ - unsigned char fieldReplaceableUnitCode; - unsigned char senseKeySpecific[3]; -} CDB_SenseData; - -/* - * Default sense buffer size. - */ -#define kSenseBufferSize 18 - - -//#define INQUIRYDATABUFFERSIZE 36 - -/* - * Result from INQUIRY. - */ -typedef struct CDB_InquiryData { - unsigned char deviceType : 5; - unsigned char deviceTypeQualifier : 3; - unsigned char deviceTypeModifier : 7; - unsigned char removableMedia : 1; - unsigned char versions; - unsigned char responseDataFormat : 4; - unsigned char reserved1 : 2; - unsigned char trmIOP : 1; - unsigned char AENC : 1; - unsigned char additionalLength; - unsigned char reserved2[2]; - unsigned char softReset : 1; - unsigned char commandQueue : 1; - unsigned char reserved3 : 1; - unsigned char linkedCommands : 1; - unsigned char synchronous : 1; - unsigned char wide16Bit : 1; - unsigned char wide32Bit : 1; - unsigned char relativeAddressing : 1; - unsigned char vendorId[8]; - unsigned char productId[16]; - unsigned char productRevisionLevel[4]; - unsigned char vendorSpecific[20]; - unsigned char reserved4[40]; -} CDB_InquiryData; - -/* - * Result from READ CAPACITY. - */ -typedef struct CDB_ReadCapacityData { - unsigned char logicalBlockAddr0; // MSB - unsigned char logicalBlockAddr1; - unsigned char logicalBlockAddr2; - unsigned char logicalBlockAddr3; // LSB - unsigned char bytesPerBlock0; // MSB - unsigned char bytesPerBlock1; - unsigned char bytesPerBlock2; - unsigned char bytesPerBlock3; // LSB -} CDB_ReadCapacityData; - -#endif /*DISKIMG_SCSIDEFS_H*/ diff --git a/ciderpress/diskimg/SPTI.cpp b/ciderpress/diskimg/SPTI.cpp deleted file mode 100644 index 10f962f..0000000 --- a/ciderpress/diskimg/SPTI.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of some SPTI functions. - */ -#include "StdAfx.h" -#ifdef _WIN32 - -#include "DiskImgPriv.h" -#include "SCSIDefs.h" -#include "CP_ntddscsi.h" -#include "SPTI.h" - - -/* - * Get the capacity of the device. - * - * Returns the LBA of the last valid block and the device's block size. - */ -/*static*/ DIError SPTI::GetDeviceCapacity(HANDLE handle, uint32_t* pLastBlock, - uint32_t* pBlockSize) -{ - SCSI_PASS_THROUGH_DIRECT sptd; - uint32_t lba, blockLen; - CDB_ReadCapacityData dataBuf; - DWORD cb; - BOOL status; - - assert(sizeof(dataBuf) == 8); // READ CAPACITY returns two longs - - memset(&sptd, 0, sizeof(sptd)); - sptd.Length = sizeof(sptd); - sptd.PathId = 0; // SCSI card ID filled in by ioctl - sptd.TargetId = 0; // SCSI target ID filled in by ioctl - sptd.Lun = 0; // SCSI lun ID filled in by ioctl - sptd.CdbLength = 10; // CDB size is 10 for READ CAPACITY - sptd.SenseInfoLength = 0; // don't return any sense data - sptd.DataIn = SCSI_IOCTL_DATA_IN; // will be data from drive - sptd.DataTransferLength = sizeof(dataBuf); - sptd.TimeOutValue = 10; // SCSI timeout value, in seconds - sptd.DataBuffer = (PVOID) &dataBuf; - sptd.SenseInfoOffset = 0; // offset to request-sense buffer - - CDB10* pCdb = (CDB10*) &sptd.Cdb; - pCdb->operationCode = kScsiOpReadCapacity; - // rest of CDB is zero - - status = ::DeviceIoControl(handle, IOCTL_SCSI_PASS_THROUGH_DIRECT, - &sptd, sizeof(sptd), NULL, 0, &cb, NULL); - - if (!status) { - DWORD lastError = ::GetLastError(); - LOGE("DeviceIoControl(SCSI READ CAPACITY) failed, err=%ld", - ::GetLastError()); - if (lastError == ERROR_IO_DEVICE) // no disc in drive - return kDIErrDeviceNotReady; - else - return kDIErrSPTIFailure; - } - - lba = (uint32_t) dataBuf.logicalBlockAddr0 << 24 | - (uint32_t) dataBuf.logicalBlockAddr1 << 16 | - (uint32_t) dataBuf.logicalBlockAddr2 << 8 | - (uint32_t) dataBuf.logicalBlockAddr3; - blockLen = (uint32_t) dataBuf.bytesPerBlock0 << 24 | - (uint32_t) dataBuf.bytesPerBlock1 << 16 | - (uint32_t) dataBuf.bytesPerBlock2 << 8 | - (uint32_t) dataBuf.bytesPerBlock3; - - *pLastBlock = lba; - *pBlockSize = blockLen; - - return kDIErrNone; -} - - -/* - * Read one or more blocks from the specified SCSI device. - * - * "buf" must be able to hold (numBlocks * blockSize) bytes. - */ -/*static*/ DIError SPTI::ReadBlocks(HANDLE handle, long startBlock, - short numBlocks, long blockSize, void* buf) -{ - SCSI_PASS_THROUGH_DIRECT sptd; - DWORD cb; - BOOL status; - - assert(startBlock >= 0); - assert(numBlocks > 0); - assert(buf != NULL); - - LOGD(" SPTI phys read block (%ld) %d", startBlock, numBlocks); - - memset(&sptd, 0, sizeof(sptd)); - sptd.Length = sizeof(sptd); // size of struct (+ request-sense buffer) - sptd.ScsiStatus = 0; - sptd.PathId = 0; // SCSI card ID filled in by ioctl - sptd.TargetId = 0; // SCSI target ID filled in by ioctl - sptd.Lun = 0; // SCSI lun ID filled in by ioctl - sptd.CdbLength = 10; // CDB size is 10 for READ CAPACITY - sptd.SenseInfoLength = 0; // don't return any sense data - sptd.DataIn = SCSI_IOCTL_DATA_IN; // will be data from drive - sptd.DataTransferLength = blockSize * numBlocks; - sptd.TimeOutValue = 10; // SCSI timeout value (in seconds) - sptd.DataBuffer = (PVOID) buf; - sptd.SenseInfoOffset = 0; // offset from start of struct to request-sense - - CDB10* pCdb = (CDB10*) &sptd.Cdb; - pCdb->operationCode = kScsiOpRead; - pCdb->logicalBlockAddr0 = (uint8_t) (startBlock >> 24); // MSB - pCdb->logicalBlockAddr1 = (uint8_t) (startBlock >> 16); - pCdb->logicalBlockAddr2 = (uint8_t) (startBlock >> 8); - pCdb->logicalBlockAddr3 = (uint8_t) startBlock; // LSB - pCdb->transferLength0 = (uint8_t) (numBlocks >> 8); // MSB - pCdb->transferLength1 = (uint8_t) numBlocks; // LSB - - status = ::DeviceIoControl(handle, IOCTL_SCSI_PASS_THROUGH_DIRECT, - &sptd, sizeof(sptd), NULL, 0, &cb, NULL); - - if (!status) { - LOGE("DeviceIoControl(SCSI READ(10)) failed, err=%ld", - ::GetLastError()); - return kDIErrReadFailed; // close enough - } - if (sptd.ScsiStatus != 0) { - LOGE("SCSI READ(10) failed, status=%d", sptd.ScsiStatus); - return kDIErrReadFailed; - } - - return kDIErrNone; -} - -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/SPTI.h b/ciderpress/diskimg/SPTI.h deleted file mode 100644 index 0f14ef4..0000000 --- a/ciderpress/diskimg/SPTI.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Declarations for the Win32 SCSI Pass-Through Interface. - */ -#ifndef DISKIMG_SPTI_H -#define DISKIMG_SPTI_H - -#ifdef _WIN32 - -namespace DiskImgLib { - -/* - * This is currently implemented as a set of static functions. Do not - * instantiate the class. - */ -class DISKIMG_API SPTI { -public: - // Read blocks from the device. - static DIError ReadBlocks(HANDLE handle, long startBlock, short numBlocks, - long blockSize, void* buf); - - // Get the capacity, expressed as the highest-available LBA and the device - // block size. - static DIError GetDeviceCapacity(HANDLE handle, uint32_t* pLastBlock, - uint32_t* pBlockSize); - -private: - SPTI(void) {} - ~SPTI(void) {} -}; - -} // namespace DiskImgLib - -#endif /*_WIN32*/ - -#endif /*DISKIMG_SPTI_H*/ diff --git a/ciderpress/diskimg/StdAfx.cpp b/ciderpress/diskimg/StdAfx.cpp deleted file mode 100644 index 610fe81..0000000 --- a/ciderpress/diskimg/StdAfx.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// stdafx.cpp : source file that includes just the standard includes -// diskimg.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "StdAfx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/ciderpress/diskimg/StdAfx.h b/ciderpress/diskimg/StdAfx.h deleted file mode 100644 index 358b93d..0000000 --- a/ciderpress/diskimg/StdAfx.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#include -#include -#include -#include - -#ifndef _WIN32 - -/* UNIX includes */ -#include -#include -#include -#include -#include -#include - -#define O_BINARY 0 - -#define HAVE_VSNPRINTF -#define HAVE_FSEEKO -#define HAVE_FTRUNCATE - -// gcc wants special compile options; just ignore this for now -#define override - -#else /*_WIN32*/ - -#if !defined(AFX_STDAFX_H__1CB7B33E_42BF_4A98_B814_4198EA8ACC58__INCLUDED_) -#define AFX_STDAFX_H__1CB7B33E_42BF_4A98_B814_4198EA8ACC58__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#define HAVE_WINDOWS_CDROM // enable CD-ROM access under Windows -#define HAVE_CHSIZE - - -// Insert your headers here -# define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - -#include "../app/targetver.h" - -#include -#include -#include -#include - -#ifdef HAVE_WINDOWS_CDROM -# include -#endif - -#ifndef _SSIZE_T_DEFINED -typedef unsigned int ssize_t; -#define _SSIZE_T_DEFINED -#endif - -#define HAVE__VSNPRINTF -#define strcasecmp stricmp -#define snprintf _snprintf - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_STDAFX_H__1CB7B33E_42BF_4A98_B814_4198EA8ACC58__INCLUDED_) -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/TwoImg.cpp b/ciderpress/diskimg/TwoImg.cpp deleted file mode 100644 index 9d21cb0..0000000 --- a/ciderpress/diskimg/TwoImg.cpp +++ /dev/null @@ -1,567 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for 2MG/2IMG wrapper files. - * - * This needs to be directly accessible from external applications, so try not - * to hook into private DiskImg state. - */ -#include "StdAfx.h" -#include "TwoImg.h" -#include "DiskImgPriv.h" - -///*static*/ const char* TwoImgHeader::kMagic = "2IMG"; // file magic # -///*static*/ const char* TwoImgHeader::kCreator = "CdrP"; // our "creator" ID - - -/* - * Initialize a header to default values, using the supplied image length - * where appropriate. - * - * Sets up a header for a 2MG file with the disk data and nothing else. - * - * Returns 0 on success, -1 if one of the arguments was bad. - */ -int TwoImgHeader::InitHeader(int imageFormat, uint32_t imageSize, - uint32_t imageBlockCount) -{ - if (imageSize == 0) - return -1; - if (imageFormat < kImageFormatDOS || imageFormat > (int) kImageFormatNibble) - return -1; - - if (imageFormat != kImageFormatNibble && - imageSize != imageBlockCount * 512) - { - LOGW("2MG InitHeader: bad sizes %d %u %u", imageFormat, - imageSize, imageBlockCount); - return -1; - } - - assert(fComment == NULL); - - //memcpy(fMagic, kMagic, 4); - //memcpy(fCreator, kCreator, 4); - fMagic = kMagic; - fCreator = kCreatorCiderPress; - fHeaderLen = kOurHeaderLen; - fVersion = kOurVersion; - fImageFormat = imageFormat; - fFlags = 0; - fNumBlocks = imageBlockCount; - fDataOffset = kOurHeaderLen; - fDataLen = imageSize; - fCmtOffset = 0; - fCmtLen = 0; - fCreatorOffset = 0; - fCreatorLen = 0; - fSpare[0] = fSpare[1] = fSpare[2] = fSpare[3] = 0; - - return 0; -} - -/* - * Get the DOS volume number. - * - * If not set, we currently return the initial value (-1), rather than the - * default volume number. For the way we currently make use of this, this - * makes the most sense. - */ -int16_t TwoImgHeader::GetDOSVolumeNum(void) const -{ - assert(fFlags & kDOSVolumeSet); - return fDOSVolumeNum; -} - -/* - * Set the DOS volume number. - */ -void TwoImgHeader::SetDOSVolumeNum(short dosVolumeNum) -{ - assert(dosVolumeNum >= 0 && dosVolumeNum < 256); - fFlags |= dosVolumeNum; - fFlags |= kDOSVolumeSet; -} - -/* - * Set the comment. - */ -void TwoImgHeader::SetComment(const char* comment) -{ - delete[] fComment; - if (comment == NULL) { - fComment = NULL; - } else { - fComment = new char[strlen(comment)+1]; - if (fComment != NULL) - strcpy(fComment, comment); - // else throw alloc failure - } - - if (fComment == NULL) { - fCmtLen = 0; - fCmtOffset = 0; - if (fCreatorOffset > 0) - fCreatorOffset = fDataOffset + fDataLen; - } else { - fCmtLen = strlen(fComment); - fCmtOffset = fDataOffset + fDataLen; - if (fCreatorOffset > 0) - fCreatorOffset = fCmtOffset + fCmtLen; - } -} - -/* - * Set the creator chunk. - */ -void TwoImgHeader::SetCreatorChunk(const void* chunk, long len) -{ - assert(len >= 0); - - delete[] fCreatorChunk; - if (chunk == NULL || len == 0) { - fCreatorChunk = NULL; - } else { - fCreatorChunk = new char[len]; - if (fCreatorChunk != NULL) - memcpy(fCreatorChunk, chunk, len); - // else throw alloc failure - } - - if (fCreatorChunk == NULL) { - fCreatorLen = 0; - fCreatorOffset = 0; - } else { - fCreatorLen = len; - if (fCmtOffset > 0) - fCreatorOffset = fCmtOffset + fCmtLen; - else - fCreatorOffset = fDataOffset + fDataLen; - } -} - -/* - * Read the header from a 2IMG file. Pass in "totalLength" as a sanity check. - * - * THOUGHT: provide a simple GenericFD conversion for FILE*, and then just - * call the GenericFD version of ReadHeader. - * - * Returns 0 on success, nonzero on error or invalid header. - */ -int TwoImgHeader::ReadHeader(FILE* fp, uint32_t totalLength) -{ - uint8_t buf[kOurHeaderLen]; - - fread(buf, kOurHeaderLen, 1, fp); - if (ferror(fp)) - return errno ? errno : -1; - - if (UnpackHeader(buf, totalLength) != 0) - return -1; - - /* - * Extract the comment, if any. - */ - if (fCmtOffset > 0 && fCmtLen > 0) { - if (GetChunk(fp, fCmtOffset - kOurHeaderLen, fCmtLen, - (void**) &fComment) != 0) - { - LOGI("Throwing comment away"); - fCmtLen = 0; - fCmtOffset = 0; - } else { - LOGI("Got comment: '%s'", fComment); - } - } - - /* - * Extract the creator chunk, if any. - */ - if (fCreatorOffset > 0 && fCreatorLen > 0) { - if (GetChunk(fp, fCreatorOffset - kOurHeaderLen, fCreatorLen, - (void**) &fCreatorChunk) != 0) - { - LOGI("Throwing creator chunk away"); - fCreatorLen = 0; - fCreatorOffset = 0; - } else { - //LOGI("Got creator chunk: '%s'", fCreatorChunk); - } - } - - return 0; -} - -/* - * Read the header from a 2IMG file. Pass in "totalLength" as a sanity check. - * - * Returns 0 on success, nonzero on error or invalid header. - */ -int TwoImgHeader::ReadHeader(GenericFD* pGFD, uint32_t totalLength) -{ - DIError dierr; - uint8_t buf[kOurHeaderLen]; - - dierr = pGFD->Read(buf, kOurHeaderLen); - if (dierr != kDIErrNone) - return -1; - - if (UnpackHeader(buf, totalLength) != 0) - return -1; - - /* - * Extract the comment, if any. - */ - if (fCmtOffset > 0 && fCmtLen > 0) { - if (GetChunk(pGFD, fCmtOffset - kOurHeaderLen, fCmtLen, - (void**) &fComment) != 0) - { - LOGI("Throwing comment away"); - fCmtLen = 0; - fCmtOffset = 0; - } else { - LOGI("Got comment: '%s'", fComment); - } - } - - /* - * Extract the creator chunk, if any. - */ - if (fCreatorOffset > 0 && fCreatorLen > 0) { - if (GetChunk(pGFD, fCreatorOffset - kOurHeaderLen, fCreatorLen, - (void**) &fCreatorChunk) != 0) - { - LOGI("Throwing creator chunk away"); - fCreatorLen = 0; - fCreatorOffset = 0; - } else { - //LOGI("Got creator chunk: '%s'", fCreatorChunk); - } - } - - return 0; -} - -/* - * Grab a chunk of data from a relative offset. - */ -int TwoImgHeader::GetChunk(GenericFD* pGFD, di_off_t relOffset, long len, - void** pBuf) -{ - DIError dierr; - di_off_t curPos; - - /* remember current offset */ - curPos = pGFD->Tell(); - - /* seek out to chunk and grab it */ - dierr = pGFD->Seek(relOffset, kSeekCur); - if (dierr != kDIErrNone) { - LOGI("2MG seek to chunk failed"); - return -1; - } - - assert(*pBuf == NULL); - *pBuf = new char[len+1]; // one extra, for null termination - - dierr = pGFD->Read(*pBuf, len); - if (dierr != kDIErrNone) { - LOGI("2MG chunk read failed"); - delete[] (char*) (*pBuf); - *pBuf = NULL; - (void) pGFD->Seek(curPos, kSeekSet); - return -1; - } - - /* null-terminate, in case this was a string */ - ((char*) *pBuf)[len] = '\0'; - - /* seek back to where we were */ - (void) pGFD->Seek(curPos, kSeekSet); - - return 0; -} - -/* - * Grab a chunk of data from a relative offset. - */ -int TwoImgHeader::GetChunk(FILE* fp, di_off_t relOffset, long len, - void** pBuf) -{ - long curPos; - int count; - - /* remember current offset */ - curPos = ftell(fp); - LOGI("Current offset=%ld", curPos); - - /* seek out to chunk and grab it */ - if (fseek(fp, (long) relOffset, SEEK_CUR) == -1) { - LOGI("2MG seek to chunk failed"); - return errno ? errno : -1;; - } - - assert(*pBuf == NULL); - *pBuf = new char[len+1]; // one extra, for null termination - - count = fread(*pBuf, len, 1, fp); - if (!count || ferror(fp) || feof(fp)) { - LOGI("2MG chunk read failed"); - delete[] (char*) (*pBuf); - *pBuf = NULL; - (void) fseek(fp, curPos, SEEK_SET); - clearerr(fp); - return errno ? errno : -1;; - } - - /* null-terminate, in case this was a string */ - ((char*) *pBuf)[len] = '\0'; - - /* seek back to where we were */ - (void) fseek(fp, curPos, SEEK_SET); - - return 0; -} - - -/* - * Unpack the 64-byte 2MG header. - * - * Performs some sanity checks. Returns 0 on success, -1 on failure. - */ -int TwoImgHeader::UnpackHeader(const uint8_t* buf, uint32_t totalLength) -{ - fMagic = GetLongBE(&buf[0x00]); - fCreator = GetLongBE(&buf[0x04]); - fHeaderLen = GetShortLE(&buf[0x08]); - fVersion = GetShortLE(&buf[0x0a]); - fImageFormat = GetLongLE(&buf[0x0c]); - fFlags = GetLongLE(&buf[0x10]); - fNumBlocks = GetLongLE(&buf[0x14]); - fDataOffset = GetLongLE(&buf[0x18]); - fDataLen = GetLongLE(&buf[0x1c]); - fCmtOffset = GetLongLE(&buf[0x20]); - fCmtLen = GetLongLE(&buf[0x24]); - fCreatorOffset = GetLongLE(&buf[0x28]); - fCreatorLen = GetLongLE(&buf[0x2c]); - fSpare[0] = GetLongLE(&buf[0x30]); - fSpare[1] = GetLongLE(&buf[0x34]); - fSpare[2] = GetLongLE(&buf[0x38]); - fSpare[3] = GetLongLE(&buf[0x3c]); - - fMagicStr[0] = (char) (fMagic >> 24); - fMagicStr[1] = (char) (fMagic >> 16); - fMagicStr[2] = (char) (fMagic >> 8); - fMagicStr[3] = (char) fMagic; - fMagicStr[4] = '\0'; - fCreatorStr[0] = (char) (fCreator >> 24); - fCreatorStr[1] = (char) (fCreator >> 16); - fCreatorStr[2] = (char) (fCreator >> 8); - fCreatorStr[3] = (char) fCreator; - fCreatorStr[4] = '\0'; - - if (fMagic != kMagic) { - LOGI("Magic number does not match 2IMG"); - return -1; - } - - if (fVersion > 1) { - LOGW("ERROR: unsupported version=%d", fVersion); - return -1; // bad header until I hear otherwise - } - - if (fFlags & kDOSVolumeSet) - fDOSVolumeNum = fFlags & kDOSVolumeMask; - - DumpHeader(); - - /* fix broken 'WOOF' images from Sweet-16 */ - if (fCreator == kCreatorSweet16 && fDataLen == 0 && - fImageFormat != kImageFormatNibble) - { - fDataLen = fNumBlocks * kBlockSize; - LOGI("NOTE: fixing zero dataLen in 'WOOF' image (set to %u)", - fDataLen); - } - - /* - * Perform some sanity checks. - */ - if (fImageFormat != kImageFormatNibble && - fNumBlocks * kBlockSize != fDataLen) - { - LOGW("numBlocks/dataLen mismatch (%u vs %u)", - fNumBlocks * kBlockSize, fDataLen); - return -1; - } - if (fDataLen + fDataOffset > totalLength) { - LOGW("Invalid dataLen/offset/fileLength (dl=%u, off=%u, tlen=%u)", - fDataLen, fDataOffset, totalLength); - return -1; - } - if (fImageFormat < kImageFormatDOS || fImageFormat > kImageFormatNibble) { - LOGW("Invalid image format %u", fImageFormat); - return -1; - } - - if (fCmtOffset > 0 && fCmtOffset < fDataOffset + fDataLen) { - LOGW("2MG comment is inside the data section (off=%u, data end=%u)", - fCmtOffset, fDataOffset+fDataLen); - DebugBreak(); - // ignore the comment - fCmtOffset = 0; - fCmtLen = 0; - } - if (fCreatorOffset > 0 && fCreatorLen > 0) { - uint32_t prevEnd = fDataOffset + fDataLen + fCmtLen; - - if (fCreatorOffset < prevEnd) { - LOGW("2MG creator chunk is inside prev data (off=%u, data end=%u)", - fCreatorOffset, prevEnd); - DebugBreak(); - // ignore the creator chunk - fCreatorOffset = 0; - fCreatorLen = 0; - } - } - - return 0; -} - -/* - * Write the header to a 2IMG file. - * - * Returns 0 on success, or an errno value on failure. - */ -int -TwoImgHeader::WriteHeader(FILE* fp) const -{ - uint8_t buf[kOurHeaderLen]; - - PackHeader(buf); - if (fwrite(buf, kOurHeaderLen, 1, fp) != 1) - return errno ? errno : -1; - return 0; -} - -/* - * Write the header to a 2IMG file. - * - * Returns 0 on success, or an errno value on failure. - */ -int -TwoImgHeader::WriteHeader(GenericFD* pGFD) const -{ - uint8_t buf[kOurHeaderLen]; - - PackHeader(buf); - - if (pGFD->Write(buf, kOurHeaderLen) != kDIErrNone) - return -1; - - return 0; -} - -/* - * Write the footer. File must be seeked to end of data chunk. - */ -int -TwoImgHeader::WriteFooter(FILE* fp) const -{ - LOGI("Writing footer at offset=%ld", (long) ftell(fp)); - - if (fCmtLen) { - fwrite(fComment, fCmtLen, 1, fp); - } - if (fCreatorLen) { - fwrite(fCreatorChunk, fCreatorLen, 1, fp); - } - if (ferror(fp)) - return errno ? errno : -1; - - return 0; -} - - -/* - * Write the footer. File must be seeked to end of data chunk. - */ -int -TwoImgHeader::WriteFooter(GenericFD* pGFD) const -{ - LOGI("Writing footer at offset=%ld", (long) pGFD->Tell()); - - if (fCmtLen) { - if (pGFD->Write(fComment, fCmtLen) != kDIErrNone) - return -1; - } - if (fCreatorLen) { - if (pGFD->Write(fCreatorChunk, fCreatorLen) != kDIErrNone) - return -1; - } - - return 0; -} - -/* - * Pack the header values into a 64-byte buffer. - */ -void -TwoImgHeader::PackHeader(uint8_t* buf) const -{ - if (fCmtLen > 0 && fCmtOffset == 0) { - assert(false); - } - if (fCreatorLen > 0 && fCreatorOffset == 0) { - assert(false); - } - - PutLongBE(&buf[0x00], fMagic); - PutLongBE(&buf[0x04], fCreator); - PutShortLE(&buf[0x08], fHeaderLen); - PutShortLE(&buf[0x0a], fVersion); - PutLongLE(&buf[0x0c], fImageFormat); - PutLongLE(&buf[0x10], fFlags); - PutLongLE(&buf[0x14], fNumBlocks); - PutLongLE(&buf[0x18], fDataOffset); - PutLongLE(&buf[0x1c], fDataLen); - PutLongLE(&buf[0x20], fCmtOffset); - PutLongLE(&buf[0x24], fCmtLen); - PutLongLE(&buf[0x28], fCreatorOffset); - PutLongLE(&buf[0x2c], fCreatorLen); - PutLongLE(&buf[0x30], fSpare[0]); - PutLongLE(&buf[0x34], fSpare[1]); - PutLongLE(&buf[0x38], fSpare[2]); - PutLongLE(&buf[0x3c], fSpare[3]); -} - -/* - * Dump the contents of an ImgHeader. - */ -void -TwoImgHeader::DumpHeader(void) const -{ - LOGI("--- header contents:"); - LOGI("\tmagic = '%s' (0x%08x)", fMagicStr, fMagic); - LOGI("\tcreator = '%s' (0x%08x)", fCreatorStr, fCreator); - LOGI("\theaderLen = %u", fHeaderLen); - LOGI("\tversion = %u", fVersion); - LOGI("\timageFormat = %u", fImageFormat); - LOGI("\tflags = 0x%08x", fFlags); - LOGI("\t locked = %s", - (fFlags & kFlagLocked) ? "true" : "false"); - LOGI("\t DOS volume = %s (%d)", - (fFlags & kDOSVolumeSet) ? "true" : "false", - fFlags & kDOSVolumeMask); - LOGI("\tnumBlocks = %u", fNumBlocks); - LOGI("\tdataOffset = %u", fDataOffset); - LOGI("\tdataLen = %u", fDataLen); - LOGI("\tcmtOffset = %u", fCmtOffset); - LOGI("\tcmtLen = %u", fCmtLen); - LOGI("\tcreatorOffset = %u", fCreatorOffset); - LOGI("\tcreatorLen = %u", fCreatorLen); - LOGI("---"); -} diff --git a/ciderpress/diskimg/TwoImg.h b/ciderpress/diskimg/TwoImg.h deleted file mode 100644 index 0956299..0000000 --- a/ciderpress/diskimg/TwoImg.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for the "2MG"/"2IMG" disk image format. - * - * This gets its own header because CiderPress uses these definitions and - * functions directly. - */ -#ifndef DISKIMG_TWOIMG_H -#define DISKIMG_TWOIMG_H - -#include "DiskImg.h" - -namespace DiskImgLib { - -/* - * 2IMG header definition (was on http://www.magnet.ch/emutech/Tech/, - * now on http://www.a2central.com/programming/filetypes/ftne00130.html - * as filetype $e0/$0130). - * - * Meaning of "flags": - * bit 31 : disk is "locked"; used by emulators as write-protect sticker. - * bit 8 : if set, bits 0-7 specify DOS 3.3 volume number - * bit 0-7: if bit 8 is set, use this as DOS volume; else use 254 - * - * All values are stored little-endian. - */ -class DISKIMG_API TwoImgHeader { -public: - TwoImgHeader(void) : fDOSVolumeNum(-1), fComment(NULL), fCreatorChunk(NULL) - {} - virtual ~TwoImgHeader(void) { - delete[] fComment; - delete[] fCreatorChunk; - } - - /* - * Header fields. - */ - //char fMagic[4]; - //char fCreator[4]; - uint32_t fMagic; - uint32_t fCreator; - uint16_t fHeaderLen; - uint16_t fVersion; - uint32_t fImageFormat; - uint32_t fFlags; // may include DOS volume num - uint32_t fNumBlocks; // 512-byte blocks - uint32_t fDataOffset; - uint32_t fDataLen; - uint32_t fCmtOffset; - uint32_t fCmtLen; - uint32_t fCreatorOffset; - uint32_t fCreatorLen; - uint32_t fSpare[4]; - - /* - * Related constants. - */ - enum { - // imageFormat - kImageFormatDOS = 0, - kImageFormatProDOS = 1, - kImageFormatNibble = 2, - // flags - kFlagLocked = (1L<<31), - kDOSVolumeSet = (1L<<8), - kDOSVolumeMask = (0xff), - kDefaultVolumeNum = 254, - - // constants used when creating a new header - kOurHeaderLen = 64, - kOurVersion = 1, - - kBlockSize = 512, - kMagic = 0x32494d47, // 2IMG - kCreatorCiderPress = 0x43647250, // CdrP - kCreatorSweet16 = 0x574f4f46, // WOOF - }; - - /* - * Basic functions. - * - * The read header function will read the comment, but the write - * header function will not. This is because the GenericFD functions - * don't allow seeking past the current EOF. - * - * ReadHeader/WriteHeader expect the file to be seeked to the initial - * offset. WriteFooter expects the file to be seeked just past the - * end of the data section. This is done in case the file has some - * sort of wrapper outside the 2MG header. - */ - int InitHeader(int imageFormat, uint32_t imageSize, uint32_t imageBlockCount); - int ReadHeader(FILE* fp, uint32_t totalLength); - int ReadHeader(GenericFD* pGFD, uint32_t totalLength); - int WriteHeader(FILE* fp) const; - int WriteHeader(GenericFD* pGFD) const; - int WriteFooter(FILE* fp) const; - int WriteFooter(GenericFD* pGFD) const; - void DumpHeader(void) const; // for debugging - - /* - * Getters & setters. - */ - const char* GetMagicStr(void) const { return fMagicStr; } - const char* GetCreatorStr(void) const { return fCreatorStr; } - - int16_t GetDOSVolumeNum(void) const; - void SetDOSVolumeNum(short dosVolumeNum); - const char* GetComment(void) const { return fComment; } - void SetComment(const char* comment); - const void* GetCreatorChunk(void) const { return fCreatorChunk; } - void SetCreatorChunk(const void* creatorBlock, long len); - -private: - int UnpackHeader(const uint8_t* buf, uint32_t totalLength); - void PackHeader(uint8_t* buf) const; - int GetChunk(GenericFD* pGFD, di_off_t relOffset, long len, void** pBuf); - int GetChunk(FILE* fp, di_off_t relOffset, long len, void** pBuf); - - int16_t fDOSVolumeNum; // 8-bit volume number, or -1 - char fMagicStr[5]; - char fCreatorStr[5]; - - char* fComment; - char* fCreatorChunk; -}; - -} // namespace DiskImgLib - -#endif /*DISKIMG_TWOIMG_H*/ diff --git a/ciderpress/diskimg/UNIDOS.cpp b/ciderpress/diskimg/UNIDOS.cpp deleted file mode 100644 index f33144e..0000000 --- a/ciderpress/diskimg/UNIDOS.cpp +++ /dev/null @@ -1,359 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Implementation of DiskFSUNIDOS class. - * - * The "UNIDOS" filesystem doesn't actually hold files. Instead, it holds - * two 400K DOS 3.3 volumes on an 800K disk. - * - * We do have a test here for "wide" DOS 3.3, which is largely a clone of - * the standard DOS 3.3 test. The trick is that we have to adjust our - * detection to account for 32-sector tracks, and do so while the object - * is still in a state where it believes it has 16 sectors per track. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * =========================================================================== - * DiskFSUNIDOS - * =========================================================================== - */ - -const int kExpectedNumBlocks = 1600; -const int kExpectedTracks = 50; -const int kExpectedSectors = 32; -const int kVTOCTrack = 17; -const int kVTOCSector = 0; -const int kSctSize = 256; - -//const int kCatalogEntrySize = 0x23; // length in bytes of catalog entries -//const int kCatalogEntriesPerSect = 7; // #of entries per catalog sector -const int kMaxTSPairs = 0x7a; // 122 entries for 256-byte sectors -//const int kTSOffset = 0x0c; // first T/S entry in a T/S list - -//const int kMaxTSIterations = 32; -//const int kMaxCatalogIterations = 64; - - -/* - * Read a track/sector, adjusting for 32-sector disks being treated as - * if they were 16-sector. - */ -static DIError ReadTrackSectorAdjusted(DiskImg* pImg, int track, int sector, - int trackOffset, uint8_t* buf, DiskImg::SectorOrder imageOrder) -{ - track += trackOffset; - track *= 2; - if (sector >= 16) { - track++; - sector -= 16; - } - return pImg->ReadTrackSectorSwapped(track, sector, buf, imageOrder, - DiskImg::kSectorOrderDOS); -} - -/* - * Test for presence of 400K DOS 3.3 volumes. - */ -static DIError TestImageHalf(DiskImg* pImg, int trackOffset, - DiskImg::SectorOrder imageOrder, int* pGoodCount) -{ - DIError dierr = kDIErrNone; - uint8_t sctBuf[kSctSize]; - int numTracks, numSectors; - int catTrack, catSect; - int foundGood = 0; - int iterations = 0; - - *pGoodCount = 0; - - dierr = ReadTrackSectorAdjusted(pImg, kVTOCTrack, kVTOCSector, - trackOffset, sctBuf, imageOrder); - if (dierr != kDIErrNone) - goto bail; - - catTrack = sctBuf[0x01]; - catSect = sctBuf[0x02]; - numTracks = sctBuf[0x34]; - numSectors = sctBuf[0x35]; - - if (!(sctBuf[0x27] == kMaxTSPairs) || - /*!(sctBuf[0x36] == 0 && sctBuf[0x37] == 1) ||*/ // bytes per sect - !(numTracks == kExpectedTracks) || - !(numSectors == 32) || - !(catTrack < numTracks && catSect < numSectors) || - 0) - { - LOGI(" UNI/Wide DOS header test failed"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - foundGood++; // score one for a valid-looking VTOC - - /* - * Walk through the catalog track to try to figure out ordering. - */ - while (catTrack != 0 && catSect != 0 && - iterations < DiskFSDOS33::kMaxCatalogSectors) - { - dierr = ReadTrackSectorAdjusted(pImg, catTrack, catSect, - trackOffset, sctBuf, imageOrder); - if (dierr != kDIErrNone) { - dierr = kDIErrNone; - break; /* allow it if earlier stuff was okay */ - } - - if (catTrack == sctBuf[1] && catSect == sctBuf[2] +1) - foundGood++; - else if (catTrack == sctBuf[1] && catSect == sctBuf[2]) { - LOGI(" WideDOS detected self-reference on cat (%d,%d)", - catTrack, catSect); - break; - } - - catTrack = sctBuf[1]; - catSect = sctBuf[2]; - iterations++; // watch for infinite loops - } - if (iterations >= DiskFSDOS33::kMaxCatalogSectors) { - dierr = kDIErrDirectoryLoop; - LOGI(" WideDOS directory links cause a loop"); - goto bail; - } - - LOGI(" WideDOS foundGood=%d off=%d swap=%d", foundGood, - trackOffset, imageOrder); - *pGoodCount = foundGood; - -bail: - return dierr; -} - -/* - * Test both of the DOS partitions. - */ -static DIError TestImage(DiskImg* pImg, DiskImg::SectorOrder imageOrder, - int* pGoodCount) -{ - DIError dierr; - int goodCount1, goodCount2; - - *pGoodCount = 0; - - LOGI(" UNIDOS checking first half (imageOrder=%d)", imageOrder); - dierr = TestImageHalf(pImg, 0, imageOrder, &goodCount1); - if (dierr != kDIErrNone) - return dierr; - - LOGI(" UNIDOS checking second half (imageOrder=%d)", imageOrder); - dierr = TestImageHalf(pImg, kExpectedTracks, imageOrder, &goodCount2); - if (dierr != kDIErrNone) - return dierr; - - if (goodCount1 > goodCount2) - *pGoodCount = goodCount1; - else - *pGoodCount = goodCount2; - - return kDIErrNone; -} - -/* - * Test to see if the image is a UNIDOS volume. - */ -/*static*/ DIError DiskFSUNIDOS::TestFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - /* only on 800K disks (at the least, insist on numTracks being even) */ - if (pImg->GetNumBlocks() != kExpectedNumBlocks) - return kDIErrFilesystemNotFound; - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown; - int bestCount = 0; - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - int goodCount = 0; - - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImage(pImg, ordering[i], &goodCount) == kDIErrNone) { - if (goodCount > bestCount) { - bestCount = goodCount; - bestOrder = ordering[i]; - } - } - } - - if (bestCount >= 4 || - (leniency == kLeniencyVery && bestCount >= 2)) - { - LOGI(" WideDOS test: bestCount=%d for order=%d", bestCount, bestOrder); - assert(bestOrder != DiskImg::kSectorOrderUnknown); - *pOrder = bestOrder; - *pFormat = DiskImg::kFormatUNIDOS; - return kDIErrNone; - } - - LOGI(" UNIDOS didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - -/* - * Test to see if the image is a 'wide' (32-sector) DOS3.3 volume, i.e. - * half of a UNIDOS volume (usually found embedded in ProDOS). - * - * Trying all possible formats is important here, because the wrong value for - * swap can return a "good" value of 7 (much less than the expected 30, but - * above a threshold of reasonableness). - */ -/*static*/ DIError DiskFSUNIDOS::TestWideFS(DiskImg* pImg, DiskImg::SectorOrder* pOrder, - DiskImg::FSFormat* pFormat, FSLeniency leniency) -{ - /* only on 400K "disks" */ - if (pImg->GetNumBlocks() != kExpectedNumBlocks/2) { - LOGI(" WideDOS ignoring volume (numBlocks=%ld)", - pImg->GetNumBlocks()); - return kDIErrFilesystemNotFound; - } - - DiskImg::SectorOrder ordering[DiskImg::kSectorOrderMax]; - - DiskImg::GetSectorOrderArray(ordering, *pOrder); - - DiskImg::SectorOrder bestOrder = DiskImg::kSectorOrderUnknown; - int bestCount = 0; - - for (int i = 0; i < DiskImg::kSectorOrderMax; i++) { - int goodCount = 0; - - if (ordering[i] == DiskImg::kSectorOrderUnknown) - continue; - if (TestImageHalf(pImg, 0, ordering[i], &goodCount) == kDIErrNone) { - if (goodCount > bestCount) { - bestCount = goodCount; - bestOrder = ordering[i]; - } - } - } - - if (bestCount >= 4 || - (leniency == kLeniencyVery && bestCount >= 2)) - { - LOGI(" UNI/Wide test: bestCount=%d for order=%d", bestCount, bestOrder); - assert(bestOrder != DiskImg::kSectorOrderUnknown); - *pOrder = bestOrder; - *pFormat = DiskImg::kFormatDOS33; - // up to the caller to adjust numTracks/numSectPerTrack - return kDIErrNone; - } - - LOGI(" UNI/Wide didn't find valid FS"); - return kDIErrFilesystemNotFound; -} - - -/* - * Set up our sub-volumes. - */ -DIError DiskFSUNIDOS::Initialize(void) -{ - DIError dierr = kDIErrNone; - - if (fScanForSubVolumes != kScanSubDisabled) { - dierr = OpenSubVolume(0); - if (dierr != kDIErrNone) - return dierr; - - dierr = OpenSubVolume(1); - if (dierr != kDIErrNone) - return dierr; - } else { - LOGI(" UNIDOS not scanning for sub-volumes"); - } - - SetVolumeUsageMap(); - - return kDIErrNone; -} - -/* - * Open up one of the DOS 3.3 sub-volumes. - */ -DIError DiskFSUNIDOS::OpenSubVolume(int idx) -{ - DIError dierr = kDIErrNone; - DiskFS* pNewFS = NULL; - DiskImg* pNewImg = NULL; - - pNewImg = new DiskImg; - if (pNewImg == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - - dierr = pNewImg->OpenImage(fpImg, kExpectedTracks * idx, 0, - kExpectedTracks * kExpectedSectors); - if (dierr != kDIErrNone) { - LOGI(" UNISub: OpenImage(%d,0,%d) failed (err=%d)", - kExpectedTracks * idx, kExpectedTracks * kExpectedSectors, dierr); - goto bail; - } - - dierr = pNewImg->AnalyzeImage(); - if (dierr != kDIErrNone) { - LOGI(" UNISub: analysis failed (err=%d)", dierr); - goto bail; - } - - if (pNewImg->GetFSFormat() == DiskImg::kFormatUnknown || - pNewImg->GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - LOGI(" UNISub: unable to identify filesystem"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* open a DiskFS for the sub-image */ - LOGI(" UNISub %d succeeded!", idx); - pNewFS = pNewImg->OpenAppropriateDiskFS(); - if (pNewFS == NULL) { - LOGI(" UNISub: OpenAppropriateDiskFS failed"); - dierr = kDIErrUnsupportedFSFmt; - goto bail; - } - - /* load the files from the sub-image */ - dierr = pNewFS->Initialize(pNewImg, kInitFull); - if (dierr != kDIErrNone) { - LOGE(" UNISub: error %d reading list of files from disk", dierr); - goto bail; - } - - /* if this really is DOS 3.3, override the "volume name" */ - if (pNewImg->GetFSFormat() == DiskImg::kFormatDOS33) { - DiskFSDOS33* pDOS = (DiskFSDOS33*) pNewFS; /* eek, a downcast */ - pDOS->SetDiskVolumeNum(idx+1); - } - - /* - * Success, add it to the sub-volume list. - */ - AddSubVolumeToList(pNewImg, pNewFS); - -bail: - if (dierr != kDIErrNone) { - delete pNewFS; - delete pNewImg; - } - return dierr; -} diff --git a/ciderpress/diskimg/VolumeUsage.cpp b/ciderpress/diskimg/VolumeUsage.cpp deleted file mode 100644 index 14d40f5..0000000 --- a/ciderpress/diskimg/VolumeUsage.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Support for the VolumeUsage sub-class in DiskFS. - */ -#include "StdAfx.h" -#include "DiskImgPriv.h" - - -/* - * Initialize structures for a block-structured disk. - */ -DIError DiskFS::VolumeUsage::Create(long numBlocks) -{ - if (numBlocks <= 0 || numBlocks > 32*1024*1024) // 16GB - return kDIErrInvalidArg; - - fByBlocks = true; - fNumSectors = -1; - fTotalChunks = numBlocks; - fListSize = numBlocks; - fList = new unsigned char[fListSize]; - if (fList == NULL) - return kDIErrMalloc; - - memset(fList, 0, fListSize); - - return kDIErrNone; -} - -/* - * Initialize structures for a track/sector-structured disk. - */ -DIError DiskFS::VolumeUsage::Create(long numTracks, long numSectors) -{ - long count = numTracks * numSectors; - if (numTracks <= 0 || count <= 0 || count > 32*1024*1024) - return kDIErrInvalidArg; - - fByBlocks = false; - fNumSectors = numSectors; - fTotalChunks = count; - fListSize = count; - fList = new unsigned char[fListSize]; - if (fList == NULL) - return kDIErrMalloc; - - memset(fList, 0, fListSize); - - return kDIErrNone; -} - -/* - * Return the state of a particular chunk. - */ -DIError DiskFS::VolumeUsage::GetChunkState(long block, ChunkState* pState) const -{ - if (!fByBlocks) - return kDIErrInvalidArg; - return GetChunkStateIdx(block, pState); -} - -DIError DiskFS::VolumeUsage::GetChunkState(long track, long sector, - ChunkState* pState) const -{ - if (fByBlocks) - return kDIErrInvalidArg; - if (track < 0 || sector < 0 || sector >= fNumSectors) - return kDIErrInvalidArg; - return GetChunkStateIdx(track * fNumSectors + sector, pState); -} - -DIError DiskFS::VolumeUsage::GetChunkStateIdx(int idx, ChunkState* pState) const -{ - if (fList == NULL || idx < 0 || idx >= fListSize) { - assert(false); - return kDIErrInvalidArg; - } - - unsigned char val = fList[idx]; - pState->isUsed = (val & kChunkUsedFlag) != 0; - pState->isMarkedUsed = (val & kChunkMarkedUsedFlag) != 0; - pState->purpose = (ChunkPurpose)(val & kChunkPurposeMask); - - return kDIErrNone; -} - -/* - * Set the state of a particular chunk. - */ -DIError DiskFS::VolumeUsage::SetChunkState(long block, const ChunkState* pState) -{ - if (!fByBlocks) - return kDIErrInvalidArg; - return SetChunkStateIdx(block, pState); -} - -DIError DiskFS::VolumeUsage::SetChunkState(long track, long sector, - const ChunkState* pState) -{ - if (fByBlocks) - return kDIErrInvalidArg; - if (track < 0 || sector < 0 || sector >= fNumSectors) - return kDIErrInvalidArg; - return SetChunkStateIdx(track * fNumSectors + sector, pState); -} - -DIError DiskFS::VolumeUsage::SetChunkStateIdx(int idx, const ChunkState* pState) -{ - if (fList == NULL || idx < 0 || idx >= fListSize) { - assert(false); - return kDIErrInvalidArg; - } - - unsigned char val = 0; - if (pState->isUsed) { - if ((pState->purpose & ~kChunkPurposeMask) != 0) { - assert(false); - return kDIErrInvalidArg; - } - val |= kChunkUsedFlag; - val |= (int)pState->purpose; - } - if (pState->isMarkedUsed) - val |= kChunkMarkedUsedFlag; - - fList[idx] = val; - - return kDIErrNone; -} - -/* - * Count up the #of free chunks. - */ -long DiskFS::VolumeUsage::GetActualFreeChunks(void) const -{ - ChunkState cstate; // could probably do this bitwise... - int freeCount = 0; - int funkyCount = 0; - - for (int i = 0; i < fTotalChunks; i++) { - if (GetChunkStateIdx(i, &cstate) != kDIErrNone) { - assert(false); - return -1; - } - - if (!cstate.isUsed && !cstate.isMarkedUsed) - freeCount++; - - if ((!cstate.isUsed && cstate.isMarkedUsed) || - (cstate.isUsed && !cstate.isMarkedUsed) || - (cstate.isUsed && cstate.purpose == kChunkPurposeConflict)) - { - funkyCount++; - } - } - - LOGI(" VU total=%ld free=%d funky=%d", - fTotalChunks, freeCount, funkyCount); - - return freeCount; -} - -/* - * Convert a ChunkState into a single, hopefully meaningful, character. - * - * Possible states: - * '.' - !inuse, !marked (free space) - * 'X' - !inuse, marked (could be embedded volume) - * '!' - inuse, !marked (danger!) - * '#' - inuse, marked, used by more than one thing - * 'S' - inuse, marked, used by system (directories, volume bit map) - * 'I' - inuse, marked, used by file structure (index block) - * 'F' - inuse, marked, used by file - */ -char DiskFS::VolumeUsage::StateToChar(ChunkState* pState) const -{ - if (!pState->isUsed && !pState->isMarkedUsed) - return '.'; - if (!pState->isUsed && pState->isMarkedUsed) - return 'X'; - if (pState->isUsed && !pState->isMarkedUsed) - return '!'; - assert(pState->isUsed && pState->isMarkedUsed); - if (pState->purpose == kChunkPurposeUnknown) - return '?'; - if (pState->purpose == kChunkPurposeConflict) - return '#'; - if (pState->purpose == kChunkPurposeSystem) - return 'S'; - if (pState->purpose == kChunkPurposeVolumeDir) - return 'V'; - if (pState->purpose == kChunkPurposeSubdir) - return 'D'; - if (pState->purpose == kChunkPurposeUserData) - return 'F'; - if (pState->purpose == kChunkPurposeFileStruct) - return 'I'; - if (pState->purpose == kChunkPurposeEmbedded) - return 'E'; - - assert(false); - return '?'; -} - -/* - * Dump the list. - */ -void DiskFS::VolumeUsage::Dump(void) const -{ -#define kMapInit "--------------------------------" - if (fList == NULL) { - LOGI(" VU asked to dump empty list?"); - return; - } - - LOGI(" VU VolumeUsage dump (%ld free chunks):", - GetActualFreeChunks()); - - if (fByBlocks) { - ChunkState cstate; - char freemap[32+1] = kMapInit; - int block; - const int kEntriesPerLine = 32; // use 20 to match Copy][+ - - for (block = 0; block < fTotalChunks; block++) { - if (GetChunkState(block, &cstate) != kDIErrNone) { - assert(false); - return; - } - - freemap[block % kEntriesPerLine] = StateToChar(&cstate); - if ((block % kEntriesPerLine) == kEntriesPerLine-1) { - LOGI(" 0x%04x: %s", block-(kEntriesPerLine-1), freemap); - } - } - if ((block % kEntriesPerLine) != 0) { - memset(freemap + (block % kEntriesPerLine), '-', - kEntriesPerLine - (block % kEntriesPerLine)); - LOGI(" 0x%04x: %s", block-(kEntriesPerLine-1), freemap); - } - } else { - ChunkState cstate; - char freemap[32+1] = kMapInit; - long numTracks = fTotalChunks / fNumSectors; - int track, sector; - - if (fNumSectors > 32) { - LOGI(" VU too many sectors (%ld)", fNumSectors); - return; - } - - LOGI(" map 0123456789abcdef"); - - for (track = 0; track < numTracks; track++) { - for (sector = 0; sector < fNumSectors; sector++) { - if (GetChunkState(track, sector, &cstate) != kDIErrNone) { - assert(false); - return; - } - freemap[sector] = StateToChar(&cstate); - } - LOGI(" %2d: %s", track, freemap); - } - } -} diff --git a/ciderpress/diskimg/Win32BlockIO.cpp b/ciderpress/diskimg/Win32BlockIO.cpp deleted file mode 100644 index 45ae6de..0000000 --- a/ciderpress/diskimg/Win32BlockIO.cpp +++ /dev/null @@ -1,2127 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Win32 block I/O routines. - * - * See the header file for an explanation of why. - */ -#include "StdAfx.h" -#ifdef _WIN32 -#include "DiskImgPriv.h" -#include "SCSIDefs.h" -#include "ASPI.h" -#include "SPTI.h" - - -/* - * This is an ugly hack until I can figure out the right way to do this. To - * help prevent people from trashing their Windows volumes, we refuse to - * open "C:\" or physical0 for writing. On most systems, this is fine. - * - * The problem is that, on some systems with a mix of SATA and IDE devices, - * the boot disk is not physical disk 0. There should be a way to determine - * which physical disk is used for booting, or which physical disk - * corresponds to "C:", but I haven't been able to find it. - * - * So, for now, allow a global setting that disables the protection. I'm - * doing it this way rather than passing a parameter through because it - * requires adding an argument to several layers of "open", and I'm hoping - * to make this go away. - */ -//extern bool DiskImgLib::gAllowWritePhys0; - - -/* - * =========================================================================== - * Win32VolumeAccess - * =========================================================================== - */ - -/* - * Open a logical volume. - */ -DIError Win32VolumeAccess::Open(const WCHAR* deviceName, bool readOnly) -{ - DIError dierr = kDIErrNone; - - assert(deviceName != NULL); - - if (fpBlockAccess != NULL) { - assert(false); - return kDIErrAlreadyOpen; - } - -#ifdef WANT_ASPI - if (strncmp(deviceName, kASPIDev, strlen(kASPIDev)) == 0) { - fpBlockAccess = new ASPIBlockAccess; - if (fpBlockAccess == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = fpBlockAccess->Open(deviceName, readOnly); - if (dierr != kDIErrNone) - goto bail; - } else -#endif - if (deviceName[0] >= 'A' && deviceName[0] <= 'Z') { - fpBlockAccess = new LogicalBlockAccess; - if (fpBlockAccess == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = fpBlockAccess->Open(deviceName, readOnly); - if (dierr != kDIErrNone) - goto bail; - } else if (deviceName[0] >= '0' && deviceName[0] <= '9') { - fpBlockAccess = new PhysicalBlockAccess; - if (fpBlockAccess == NULL) { - dierr = kDIErrMalloc; - goto bail; - } - dierr = fpBlockAccess->Open(deviceName, readOnly); - if (dierr != kDIErrNone) - goto bail; - } else { - LOGI(" Win32VA: '%s' isn't the name of a device", deviceName); - return kDIErrInternal; - } - - // Need to do this now so we can use floppy geometry. - dierr = fpBlockAccess->DetectCapacity(&fTotalBlocks); - if (dierr != kDIErrNone) - goto bail; - assert(fTotalBlocks >= 0); - -bail: - if (dierr != kDIErrNone) { - delete fpBlockAccess; - fpBlockAccess = NULL; - } - return dierr; -} - -/* - * Close the device. - */ -void Win32VolumeAccess::Close(void) -{ - if (fpBlockAccess != NULL) { - DIError dierr; - LOGI(" Win32VolumeAccess closing"); - - dierr = FlushCache(true); - if (dierr != kDIErrNone) { - LOGI("WARNING: Win32VA Close: FlushCache failed (err=%ld)", - dierr); - // not much we can do - } - dierr = fpBlockAccess->Close(); - if (dierr != kDIErrNone) { - LOGI("WARNING: Win32VolumeAccess BlockAccess Close failed (err=%ld)", - dierr); - } - delete fpBlockAccess; - fpBlockAccess = NULL; - } -} - -/* - * Read a range of blocks from the device. - * - * Because some things like to read partial blocks, we cache the last block - * we read whenever the caller asks for a single block. This results in - * increased data copying, but since we know we're reading from a logical - * volume it's safe to assume that memory is *many* times faster than - * reading from this handle. - * - * Returns with an error if any of the blocks could not be read. - */ -DIError Win32VolumeAccess::ReadBlocks(long startBlock, short blockCount, - void* buf) -{ - DIError dierr = kDIErrNone; - - assert(startBlock >= 0); - assert(blockCount > 0); - assert(buf != NULL); - assert(fpBlockAccess != NULL); - - if (blockCount == 1) { - if (fBlockCache.IsBlockInCache(startBlock)) { - fBlockCache.GetFromCache(startBlock, buf); - return kDIErrNone; - } - } else { - // If they're reading in large stretches, we don't need to use - // the cache. There is some chance that what we're about to - // read might include dirty blocks currently in the cache, so - // we flush what we have before the read. - dierr = FlushCache(true); - if (dierr != kDIErrNone) - return dierr; - } - - /* go read from the volume */ - dierr = fpBlockAccess->ReadBlocks(startBlock, blockCount, buf); - if (dierr != kDIErrNone) - goto bail; - - - /* if we're doing single-block reads, put it in the cache */ - if (blockCount == 1) { - if (!fBlockCache.IsRoomInCache(startBlock)) { - dierr = FlushCache(true); // make room - if (dierr != kDIErrNone) - goto bail; - } - - // after flushing, this should never fail - dierr = fBlockCache.PutInCache(startBlock, buf, false); - if (dierr != kDIErrNone) - goto bail; - } - -bail: - return dierr; -} - -/* - * Write a range of blocks to the device. For the most part this just - * writes to the cache. - * - * Returns with an error if any of the blocks could not be read. - */ -DIError Win32VolumeAccess::WriteBlocks(long startBlock, short blockCount, - const void* buf) -{ - DIError dierr = kDIErrNone; - - assert(startBlock >= 0); - assert(blockCount > 0); - assert(buf != NULL); - - if (blockCount == 1) { - /* is this block already in the cache? */ - if (!fBlockCache.IsBlockInCache(startBlock)) { - /* not present, make sure it fits */ - if (!fBlockCache.IsRoomInCache(startBlock)) { - dierr = FlushCache(true); // make room - if (dierr != kDIErrNone) - goto bail; - } - } - - // after flushing, this should never fail - dierr = fBlockCache.PutInCache(startBlock, buf, true); - if (dierr != kDIErrNone) - goto bail; - } else { - // If they're writing in large stretches, we don't need to use - // the cache. We need to flush the cache in case what we're about - // to write would overwrite something in the cache -- if we don't, - // what's in the cache will effectively revert what we're about to - // write. - dierr = FlushCache(true); - if (dierr != kDIErrNone) - goto bail; - dierr = DoWriteBlocks(startBlock, blockCount, buf); - if (dierr != kDIErrNone) - goto bail; - } - -bail: - return dierr; -} - -/* - * Write all blocks in the cache to disk if any of them are dirty. In some - * ways this is wasteful -- we could be writing stuff that isn't dirty, - * which isn't a great idea on (say) a CF volume -- but in practice we - * don't mix a lot of adjacent reads and writes, so this is pretty - * harmless. - * - * The goal was to write whole tracks on floppies. It's easy enough to - * disable the cache for CF devices if need be. - * - * If "purge" is set, we discard the blocks after writing them. - */ -DIError Win32VolumeAccess::FlushCache(bool purge) -{ - DIError dierr = kDIErrNone; - - //LOGI(" Win32VA: FlushCache (%d)", purge); - - if (fBlockCache.IsDirty()) { - long firstBlock; - int numBlocks; - void* buf; - - fBlockCache.GetCachePointer(&firstBlock, &numBlocks, &buf); - - LOGI("FlushCache writing first=%d num=%d (purge=%d)", - firstBlock, numBlocks, purge); - dierr = DoWriteBlocks(firstBlock, numBlocks, buf); - if (dierr != kDIErrNone) { - LOGI(" Win32VA: FlushCache write blocks failed (err=%d)", - dierr); - goto bail; - } - - // all written, clear the dirty flags - fBlockCache.Scrub(); - } - - if (purge) - fBlockCache.Purge(); - -bail: - return dierr; -} - - -/* - * =========================================================================== - * BlockAccess - * =========================================================================== - */ - -/* - * Detect the capacity of a drive using the SCSI READ CAPACITY command. Only - * works on CD-ROM drives and SCSI devices. - * - * Unfortunately, if you're accessing a hard drive through the BIOS, SPTI - * doesn't work. There must be a better way. - * - * On success, "*pNumBlocks" gets the number of 512-byte blocks. - */ -DIError Win32VolumeAccess::BlockAccess::DetectCapacitySPTI(HANDLE handle, - bool isCDROM, long* pNumBlocks) -{ -#ifndef HAVE_WINDOWS_CDROM - if (isCDROM) - return kDIErrCDROMNotSupported; -#endif - - DIError dierr = kDIErrNone; - uint32_t lba, blockLen; - - dierr = SPTI::GetDeviceCapacity(handle, &lba, &blockLen); - if (dierr != kDIErrNone) - goto bail; - - LOGI("READ CAPACITY reports lba=%u blockLen=%u (total=%u)", - lba, blockLen, lba*blockLen); - - if (isCDROM && blockLen != kCDROMSectorSize) { - LOGW("Unacceptable CD-ROM blockLen=%ld, bailing", blockLen); - dierr = kDIErrReadFailed; - goto bail; - } - - // The LBA is the last valid block on the disk. To get the disk size, - // we need to add one. - - *pNumBlocks = (blockLen/512) * (lba+1); - LOGI(" SPTI returning 512-byte block count %ld", *pNumBlocks); - -bail: - return dierr; -} - -/* - * Figure out how large this disk volume is by probing for readable blocks. - * We take some guesses for common sizes and then binary-search if necessary. - * - * CF cards typically don't have as much space as they're rated for, possibly - * because of bad-block mapping (either bad blocks or space reserved for when - * blocks do go bad). - * - * This sets "*pNumBlocks" on success. The largest size this will detect - * is currently 8GB. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::ScanCapacity(BlockAccess* pThis, - long* pNumBlocks) -{ - DIError dierr = kDIErrNone; - // max out at 8GB (-1 block) - const long kLargest = DiskImgLib::kVolumeMaxBlocks; - const long kTypicalSizes[] = { - // these must be in ascending order - 720*1024 / BlockAccess::kBlockSize, // 720K floppy - 1440*1024 / BlockAccess::kBlockSize, // 1.44MB floppy - 32*1024*1024 / BlockAccess::kBlockSize, // 32MB flash card - 64*1024*1024 / BlockAccess::kBlockSize, // 64MB flash card - 128*1024*1024 / BlockAccess::kBlockSize, // 128MB flash card - 256*1024*1024 / BlockAccess::kBlockSize, // 256MB flash card - //512*1024*1024 / BlockAccess::kBlockSize, // 512MB flash card - 2*1024*1024*(1024/BlockAccess::kBlockSize), // 2GB mark - kLargest - }; - long totalBlocks = 0; - int i; - - - // Trivial check to make sure *anything* works, and to establish block 0 - // as valid in case we have to bin-search. - if (!CanReadBlock(pThis, 0)) { - LOGI(" Win32VolumeAccess: can't read block 0"); - dierr = kDIErrReadFailed; - goto bail; - } - - for (i = 0; i < NELEM(kTypicalSizes); i++) { - if (!CanReadBlock(pThis, kTypicalSizes[i])) { - /* failed reading, see if N-1 is readable */ - if (CanReadBlock(pThis, kTypicalSizes[i] - 1)) { - /* found it */ - totalBlocks = kTypicalSizes[i]; - break; - } else { - /* we overshot, binary-search backwards */ - LOGI("OVERSHOT at %ld", kTypicalSizes[i]); - long good, bad; - if (i == 0) - good = 0; - else - good = kTypicalSizes[i-1]; // know this one is good - bad = kTypicalSizes[i]-1; // know this one is bad - - while (good != bad-1) { - long check = (good + bad) / 2; - assert(check > good); - assert(check < bad); - - if (CanReadBlock(pThis, check)) - good = check; - else - bad = check; - } - totalBlocks = bad; - break; - } - } - } - if (totalBlocks == 0) { - if (i == NELEM(kTypicalSizes)) { - /* huge volume, we never found a bad block */ - totalBlocks = kLargest; - } else { - /* we never found a good block */ - LOGI(" Win32VolumeAccess unable to determine size"); - dierr = kDIErrReadFailed; - goto bail; - } - } - - if (totalBlocks > (3 * 1024 * 1024) / BlockAccess::kBlockSize) { - LOGI(" GFDWinVolume: size is %ld (%.2fMB)", - totalBlocks, (float) totalBlocks / (2048.0)); - } else { - LOGI(" GFDWinVolume: size is %ld (%.2fKB)", - totalBlocks, (float) totalBlocks / 2.0); - } - *pNumBlocks = totalBlocks; - -bail: - return dierr; -} - -/* - * Figure out if the block at "blockNum" exists. - */ -/*static*/ bool Win32VolumeAccess::BlockAccess::CanReadBlock(BlockAccess* pThis, - long blockNum) -{ - DIError dierr; - uint8_t buf[BlockAccess::kBlockSize]; - - dierr = pThis->ReadBlocks(blockNum, 1, buf); - if (dierr == kDIErrNone) { - LOGI(" +++ Checked %ld (good)", blockNum); - return true; - } else { - LOGI(" +++ Checked %ld (bad)", blockNum); - return false; - } -} - - -#ifndef INVALID_SET_FILE_POINTER -# define INVALID_SET_FILE_POINTER 0xFFFFFFFF -#endif - - -/* - * Most of these definitions come from MSFT knowledge base article #174569, - * "BUG: Int 21 Read/Write Track on Logical Drive Fails on OSR2 and Later". - */ -#define VWIN32_DIOC_DOS_IOCTL 1 // Int 21h 4400h through 4411h -#define VWIN32_DIOC_DOS_INT25 2 -#define VWIN32_DIOC_DOS_INT26 3 -#define VWIN32_DIOC_DOS_INT13 4 -#define VWIN32_DIOC_DOS_DRIVEINFO 6 // Int 21h 730x commands - -typedef struct _DIOC_REGISTERS { - DWORD reg_EBX; - DWORD reg_EDX; - DWORD reg_ECX; - DWORD reg_EAX; - DWORD reg_EDI; - DWORD reg_ESI; - DWORD reg_Flags; -} DIOC_REGISTERS, *PDIOC_REGISTERS; - -#define CARRY_FLAG 1 - -#pragma pack(1) -typedef struct _DISKIO { - DWORD dwStartSector; // starting logical sector number - WORD wSectors; // number of sectors - DWORD dwBuffer; // address of read/write buffer -} DISKIO, *PDISKIO; -typedef struct _DRIVEMAPINFO { - BYTE dmiAllocationLength; - BYTE dmiInfoLength; - BYTE dmiFlags; - BYTE dmiInt13Unit; - DWORD dmiAssociatedDriveMap; - DWORD dmiPartitionStartRBA_lo; - DWORD dmiPartitionStartRBA_hi; -} DRIVEMAPINFO, *PDRIVEMAPINFO; -#pragma pack() - -#define kInt13StatusMissingAddrMark 2 // sector number above media format -#define kInt13StatusWriteProtected 3 // disk is write protected -#define kInt13StatusSectorNotFound 4 // sector number above drive cap -// == 10 for bad blocks?? -#define kInt13StatusTimeout 128 // drive not responding - -#if 0 -/* - * Determine the mapping between a logical device number (A=1, B=2, etc) - * and the Int13h unit number (floppy=00h, hard drive=80h, etc). - * - * Pass in the vwin32 handle. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::GetInt13Unit(HANDLE handle, - int driveNum, int* pInt13Unit) -{ - DIError dierr = kDIErrNone; - BOOL result; - DWORD cb; - DRIVEMAPINFO driveMapInfo = {0}; - DIOC_REGISTERS reg = {0}; - const int kGetDriveMapInfo = 0x6f; - const int kDeviceCategory1 = 0x08; // for older stuff - const int kDeviceCategory2 = 0x48; // for FAT32 - - assert(handle != NULL); - assert(driveNum > 0 && driveNum <= kNumLogicalVolumes); - assert(pInt13Unit != NULL); - - *pInt13Unit = -1; - - driveMapInfo.dmiAllocationLength = sizeof(DRIVEMAPINFO); // should be 16 - - reg.reg_EAX = 0x440d; // generic IOCTL - reg.reg_EBX = driveNum; - reg.reg_ECX = MAKEWORD(kGetDriveMapInfo, kDeviceCategory1); - reg.reg_EDX = (DWORD) &driveMapInfo; - - result = ::DeviceIoControl(handle, VWIN32_DIOC_DOS_IOCTL, - ®, sizeof(reg), - ®, sizeof(reg), &cb, 0); - if (result == 0) { - LOGI(" DeviceIoControl(Int21h, 6fh) FAILED (err=%ld)", - GetLastError()); - dierr = LastErrorToDIError(); - goto bail; - } - - if (reg.reg_Flags & CARRY_FLAG) { - LOGI(" --- retrying GDMI with alternate device category (ax=%ld)", - reg.reg_EAX); - reg.reg_EAX = 0x440d; // generic IOCTL - reg.reg_EBX = driveNum; - reg.reg_ECX = MAKEWORD(kGetDriveMapInfo, kDeviceCategory2); - reg.reg_EDX = (DWORD) &driveMapInfo; - result = ::DeviceIoControl(handle, VWIN32_DIOC_DOS_IOCTL, - ®, sizeof(reg), - ®, sizeof(reg), &cb, 0); - } - if (result == 0) { - LOGI(" DeviceIoControl(Int21h, 6fh)(retry) FAILED (err=%ld)", - GetLastError()); - dierr = LastErrorToDIError(); - goto bail; - } - if (reg.reg_Flags & CARRY_FLAG) { - LOGI(" --- GetDriveMapInfo call (retry) failed (ax=%ld)", - reg.reg_EAX); - dierr = kDIErrReadFailed; // close enough - goto bail; - } - - LOGI(" +++ DriveMapInfo len=%d flags=%d unit=%d", - driveMapInfo.dmiInfoLength, driveMapInfo.dmiFlags, - driveMapInfo.dmiInt13Unit); - LOGI(" +++ driveMap=0x%08lx RBA=0x%08lx 0x%08lx", - driveMapInfo.dmiAssociatedDriveMap, - driveMapInfo.dmiPartitionStartRBA_hi, - driveMapInfo.dmiPartitionStartRBA_lo); - - if (driveMapInfo.dmiInfoLength < 4) { - /* results not covered in reply? */ - dierr = kDIErrReadFailed; // not close, but it'll do - goto bail; - } - - *pInt13Unit = driveMapInfo.dmiInt13Unit; - -bail: - return dierr; -} -#endif - -#if 0 -/* - * Look up the geometry for a floppy disk whose total size is "totalBlocks". - * There is no BIOS function to detect the media size and geometry, so - * we have to do it this way. PC "FAT" disks have a size indication - * in the boot block, but we can't rely on that. - * - * Returns "true" if the geometry is known, "false" otherwise. When "true" - * is returned, "*pNumTracks", "*pNumHeads", and "*pNumSectors" will receive - * values if the pointers are non-NULL. - */ -/*static*/ bool Win32VolumeAccess::BlockAccess::LookupFloppyGeometry(long totalBlocks, - DiskGeometry* pGeometry) -{ - static const struct { - FloppyKind kind; - long blockCount; // total #of blocks on the disk - int numCyls; // #of cylinders - int numHeads; // #of heads per cylinder - int numSectors; // #of sectors/track - } floppyGeometry[] = { - { kFloppyUnknown, -1, -1, -1, -1 }, - { kFloppy525_360, 360*2, 40, 2, 9 }, - { kFloppy525_1200, 1200*2, 80, 2, 15 }, - { kFloppy35_720, 720*2, 80, 2, 9 }, - { kFloppy35_1440, 1440*2, 80, 2, 18 }, - { kFloppy35_2880, 2880*2, 80, 2, 36 } - }; - - /* verify that we can directly index the table with the enum */ - for (int chk = 0; chk < NELEM(floppyGeometry); chk++) { - assert(floppyGeometry[chk].kind == chk); - } - assert(chk == kFloppyMax); - - if (totalBlocks <= 0) { - // still auto-detecting volume size? - return false; - } - - int i; - for (i = 0; i < NELEM(floppyGeometry); i++) - if (floppyGeometry[i].blockCount == totalBlocks) - break; - - if (i == NELEM(floppyGeometry)) { - LOGI( "GetFloppyGeometry: no match for blocks=%ld", totalBlocks); - return false; - } - - pGeometry->numCyls = floppyGeometry[i].numCyls; - pGeometry->numHeads = floppyGeometry[i].numHeads; - pGeometry->numSectors = floppyGeometry[i].numSectors; - - return true; -} -#endif - -/* - * Convert a block number to a cylinder/head/sector offset. Also figures - * out what the last block on the current track is. Sectors are returned - * in 1-based form. - * - * Returns "true" on success, "false" on failure. - */ -/*static*/ bool Win32VolumeAccess::BlockAccess::BlockToCylinderHeadSector(long blockNum, - const DiskGeometry* pGeometry, int* pCylinder, int* pHead, - int* pSector, long* pLastBlockOnTrack) -{ - int cylinder, head, sector; - long lastBlockOnTrack; - int leftOver; - - cylinder = blockNum / (pGeometry->numSectors * pGeometry->numHeads); - leftOver = blockNum - cylinder * (pGeometry->numSectors * pGeometry->numHeads); - head = leftOver / pGeometry->numSectors; - sector = leftOver - (head * pGeometry->numSectors); - - assert(cylinder >= 0 && cylinder < pGeometry->numCyls); - assert(head >= 0 && head < pGeometry->numHeads); - assert(sector >= 0 && sector < pGeometry->numSectors); - - lastBlockOnTrack = blockNum + (pGeometry->numSectors - sector -1); - - if (pCylinder != NULL) - *pCylinder = cylinder; - if (pHead != NULL) - *pHead = head; - if (pSector != NULL) - *pSector = sector+1; - if (pLastBlockOnTrack != NULL) - *pLastBlockOnTrack = lastBlockOnTrack; - - return true; -} - -/* - * Get the floppy drive kind (*not* the media kind) using Int13h func 8. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::GetFloppyDriveKind(HANDLE handle, - int unitNum, FloppyKind* pKind) -{ - DIOC_REGISTERS reg = {0}; - DWORD cb; - BOOL result; - - reg.reg_EAX = MAKEWORD(0, 0x08); // Read Diskette Drive Parameters - reg.reg_EDX = MAKEWORD(unitNum, 0); - - result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, ®, - sizeof(reg), ®, sizeof(reg), &cb, 0); - - if (result == 0 || (reg.reg_Flags & CARRY_FLAG)) { - LOGI(" GetFloppyKind failed: result=%d flags=0x%04x AH=%d", - result, reg.reg_Flags, HIBYTE(reg.reg_EAX)); - return kDIErrGeneric; - } - - int bl = LOBYTE(reg.reg_EBX); - if (bl > 0 && bl < 6) - *pKind = (FloppyKind) bl; - else { - LOGI(" GetFloppyKind: unrecognized kind %d", bl); - return kDIErrGeneric; - } - - return kDIErrNone; -} - -/* - * Read one or more blocks using Int13h services. This only works on - * floppy drives due to Win9x limitations. - * - * The average BIOS will only read one or two tracks reliably, and may - * not work well when straddling tracks. It's up to the caller to - * ensure that the parameters are set properly. - * - * "cylinder" and "head" are 0-based, "sector" is 1-based. - * - * Returns 0 on success, the status code from AH on failure. If the call - * fails but AH is zero, -1 is returned. - */ -/*static*/ int Win32VolumeAccess::BlockAccess::ReadBlocksInt13h(HANDLE handle, - int unitNum, int cylinder, int head, int sector, short blockCount, void* buf) -{ - DIOC_REGISTERS reg = {0}; - DWORD cb; - BOOL result; - - if (unitNum < 0 || unitNum >= 4) { - assert(false); - return kDIErrInternal; - } - - for (int retry = 0; retry < kMaxFloppyRetries; retry++) { - reg.reg_EAX = MAKEWORD(blockCount, 0x02); // read N sectors - reg.reg_EBX = (DWORD) buf; - reg.reg_ECX = MAKEWORD(sector, cylinder); - reg.reg_EDX = MAKEWORD(unitNum, head); - - //LOGI(" DIOC Int13h read c=%d h=%d s=%d rc=%d", - // cylinder, head, sector, blockCount); - result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, ®, - sizeof(reg), ®, sizeof(reg), &cb, 0); - - if (result != 0 && !(reg.reg_Flags & CARRY_FLAG)) - break; // success! - - /* if it's an invalid sector request, bail out immediately */ - if (HIBYTE(reg.reg_EAX) == kInt13StatusSectorNotFound || - HIBYTE(reg.reg_EAX) == kInt13StatusMissingAddrMark) - { - break; - } - LOGI(" DIOC soft read failure, ax=0x%08lx", reg.reg_EAX); - } - if (!result || (reg.reg_Flags & CARRY_FLAG)) { - int ah = HIBYTE(reg.reg_EAX); - LOGI(" DIOC read failed, result=%d ah=%ld", result, ah); - if (ah != 0) - return ah; - else - return -1; - } - - return 0; -} - -/* - * Read one or more blocks using Int13h services. This only works on - * floppy drives due to Win9x limitations. - * - * It's important to be able to read multiple blocks for performance - * reasons. Because this is fairly "raw", we have to retry it 3x before - * giving up. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::ReadBlocksInt13h(HANDLE handle, - int unitNum, const DiskGeometry* pGeometry, long startBlock, short blockCount, - void* buf) -{ - int cylinder, head, sector; - int status, runCount; - long lastBlockOnTrack; - - if (unitNum < 0 || unitNum >= 4) { - assert(false); - return kDIErrInternal; - } - if (startBlock < 0 || blockCount <= 0) - return kDIErrInvalidArg; - if (startBlock + blockCount > pGeometry->blockCount) { - LOGI(" ReadInt13h: invalid request for start=%ld count=%d", - startBlock, blockCount); - return kDIErrReadFailed; - } - - while (blockCount) { - int result; - result = BlockToCylinderHeadSector(startBlock, pGeometry, - &cylinder, &head, §or, &lastBlockOnTrack); - assert(result); - - /* - * According to "The Undocumented PC", the average BIOS will read - * at most one or two tracks reliably. It's really geared toward - * writing a single track or cylinder with one call. We want to - * be sure that our read doesn't straddle tracks, so we break it - * down as needed. - */ - runCount = lastBlockOnTrack - startBlock +1; - if (runCount > blockCount) - runCount = blockCount; - //LOGI("R runCount=%d lastBlkOnT=%d start=%d", - // runCount, lastBlockOnTrack, startBlock); - assert(runCount > 0); - - status = ReadBlocksInt13h(handle, unitNum, cylinder, head, sector, - runCount, buf); - if (status != 0) { - LOGI(" DIOC read failed (status=%d)", status); - return kDIErrReadFailed; - } - - startBlock += runCount; - blockCount -= runCount; - } - - return kDIErrNone; -} - -/* - * Write one or more blocks using Int13h services. This only works on - * floppy drives due to Win9x limitations. - * - * "cylinder" and "head" are 0-based, "sector" is 1-based. - * - * It's important to be able to write multiple blocks for performance - * reasons. Because this is fairly "raw", we have to retry it 3x before - * giving up. - */ -/*static*/ int Win32VolumeAccess::BlockAccess::WriteBlocksInt13h(HANDLE handle, - int unitNum, int cylinder, int head, int sector, short blockCount, - const void* buf) -{ - DIOC_REGISTERS reg = {0}; - DWORD cb; - BOOL result; - - for (int retry = 0; retry < kMaxFloppyRetries; retry++) { - reg.reg_EAX = MAKEWORD(blockCount, 0x03); // write N sectors - reg.reg_EBX = (DWORD) buf; - reg.reg_ECX = MAKEWORD(sector, cylinder); - reg.reg_EDX = MAKEWORD(unitNum, head); - - LOGI(" DIOC Int13h write c=%d h=%d s=%d rc=%d", - cylinder, head, sector, blockCount); - result = DeviceIoControl(handle, VWIN32_DIOC_DOS_INT13, ®, - sizeof(reg), ®, sizeof(reg), &cb, 0); - - if (result != 0 && !(reg.reg_Flags & CARRY_FLAG)) - break; // success! - - if (HIBYTE(reg.reg_EAX) == kInt13StatusWriteProtected) - break; // no point retrying this - LOGI(" DIOC soft write failure, ax=0x%08lx", reg.reg_EAX); - } - if (!result || (reg.reg_Flags & CARRY_FLAG)) { - int ah = HIBYTE(reg.reg_EAX); - LOGI(" DIOC write failed, result=%d ah=%ld", result, ah); - if (ah != 0) - return ah; - else - return -1; - } - - return 0; -} - -/* - * Write one or more blocks using Int13h services. This only works on - * floppy drives. - * - * It's important to be able to write multiple blocks for performance - * reasons. Because this is fairly "raw", we have to retry it 3x before - * giving up. - * - * Returns "true" on success, "false" on failure. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::WriteBlocksInt13h(HANDLE handle, - int unitNum, const DiskGeometry* pGeometry, long startBlock, short blockCount, - const void* buf) -{ - int cylinder, head, sector; - int status, runCount; - long lastBlockOnTrack; - - // make sure this is a floppy drive and we know which unit it is - if (unitNum < 0 || unitNum >= 4) { - assert(false); - return kDIErrInternal; - } - if (startBlock < 0 || blockCount <= 0) - return kDIErrInvalidArg; - if (startBlock + blockCount > pGeometry->blockCount) { - LOGI(" WriteInt13h: invalid request for start=%ld count=%d", - startBlock, blockCount); - return kDIErrWriteFailed; - } - - while (blockCount) { - int result; - result = BlockToCylinderHeadSector(startBlock, pGeometry, - &cylinder, &head, §or, &lastBlockOnTrack); - assert(result); - - runCount = lastBlockOnTrack - startBlock +1; - if (runCount > blockCount) - runCount = blockCount; - //LOGI("W runCount=%d lastBlkOnT=%d start=%d", - // runCount, lastBlockOnTrack, startBlock); - assert(runCount > 0); - - status = WriteBlocksInt13h(handle, unitNum, cylinder, head, sector, - runCount, buf); - if (status != 0) { - LOGI(" DIOC write failed (status=%d)", status); - if (status == kInt13StatusWriteProtected) - return kDIErrWriteProtected; - else - return kDIErrWriteFailed; - } - - startBlock += runCount; - blockCount -= runCount; - } - - return kDIErrNone; -} - -/* - * Read blocks from a Win9x logical volume, using Int21h func 7305h. Pass in - * a handle to vwin32 and the logical drive number (A=1, B=2, etc). - * - * Works on Win95 OSR2 and later. Earlier versions require Int25 or Int13. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::ReadBlocksInt21h(HANDLE handle, - int driveNum, long startBlock, short blockCount, void* buf) -{ -#if 0 - assert(false); // discouraged - BOOL result; - DWORD cb; - DIOC_REGISTERS reg = {0}; - DISKIO dio = {0}; - - dio.dwStartSector = startBlock; - dio.wSectors = (WORD) blockCount; - dio.dwBuffer = (DWORD) buf; - - reg.reg_EAX = fDriveNum - 1; // Int 25h drive numbers are 0-based. - reg.reg_EBX = (DWORD)&dio; - reg.reg_ECX = 0xFFFF; // use DISKIO struct - - LOGI(" Int25 read start=%d count=%d", - startBlock, blockCount); - result = ::DeviceIoControl(handle, VWIN32_DIOC_DOS_INT25, - ®, sizeof(reg), - ®, sizeof(reg), &cb, 0); - - // Determine if the DeviceIoControl call and the read succeeded. - result = result && !(reg.reg_Flags & CARRY_FLAG); - - LOGI(" +++ read from block %ld (result=%d)", startBlock, result); - if (!result) - return kDIErrReadFailed; -#else - BOOL result; - DWORD cb; - DIOC_REGISTERS reg = {0}; - DISKIO dio = {0}; - - dio.dwStartSector = startBlock; - dio.wSectors = (WORD) blockCount; - dio.dwBuffer = (DWORD) buf; - - reg.reg_EAX = 0x7305; // Ext_ABSDiskReadWrite - reg.reg_EBX = (DWORD)&dio; - reg.reg_ECX = -1; - reg.reg_EDX = driveNum; // Int 21h, fn 7305h drive numbers are 1-based - assert(reg.reg_ESI == 0); // read/write flag - - LOGI(" Int21/7305h read start=%d count=%d", - startBlock, blockCount); - result = ::DeviceIoControl(handle, VWIN32_DIOC_DOS_DRIVEINFO, - ®, sizeof(reg), - ®, sizeof(reg), &cb, 0); - - // Determine if the DeviceIoControl call and the read succeeded. - result = result && !(reg.reg_Flags & CARRY_FLAG); - - LOGI(" +++ RB21h %ld %d (result=%d lastError=%ld)", - startBlock, blockCount, result, GetLastError()); - if (!result) - return kDIErrReadFailed; -#endif - - return kDIErrNone; -} - -/* - * Write blocks to a Win9x logical volume. Pass in a handle to vwin32 and - * the logical drive number (A=1, B=2, etc). - * - * Works on Win95 OSR2 and later. Earlier versions require Int26 or Int13. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::WriteBlocksInt21h(HANDLE handle, - int driveNum, long startBlock, short blockCount, const void* buf) -{ - BOOL result; - DWORD cb; - DIOC_REGISTERS reg = {0}; - DISKIO dio = {0}; - - dio.dwStartSector = startBlock; - dio.wSectors = (WORD) blockCount; - dio.dwBuffer = (DWORD) buf; - - reg.reg_EAX = 0x7305; // Ext_ABSDiskReadWrite - reg.reg_EBX = (DWORD)&dio; - reg.reg_ECX = -1; - reg.reg_EDX = driveNum; // Int 21h, fn 7305h drive numbers are 1-based - reg.reg_ESI = 0x6001; // write normal data (bit flags) - - //LOGI(" Int21/7305h write start=%d count=%d", - // startBlock, blockCount); - result = ::DeviceIoControl(handle, VWIN32_DIOC_DOS_DRIVEINFO, - ®, sizeof(reg), - ®, sizeof(reg), &cb, 0); - - // Determine if the DeviceIoControl call and the read succeeded. - result = result && !(reg.reg_Flags & CARRY_FLAG); - - LOGI(" +++ WB21h %ld %d (result=%d lastError=%ld)", - startBlock, blockCount, result, GetLastError()); - if (!result) - return kDIErrWriteFailed; - - return kDIErrNone; -} - -/* - * Read blocks from a Win2K logical or physical volume. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::ReadBlocksWin2K(HANDLE handle, - long startBlock, short blockCount, void* buf) -{ - /* - * Try to read the blocks. Under Win2K the seek and read calls appear - * to succeed, but the value in "actual" is set to zero to indicate - * that we're trying to read past EOF. - */ - DWORD posn, actual; - - /* - * Win2K: the 3rd argument holds the high 32 bits of the distance to - * move. This isn't supported in Win9x, which means Win9x is limited - * to 2GB files. - */ - LARGE_INTEGER li; - li.QuadPart = (LONGLONG) startBlock * (LONGLONG) kBlockSize; - posn = ::SetFilePointer(handle, li.LowPart, &li.HighPart, - FILE_BEGIN); - if (posn == INVALID_SET_FILE_POINTER) { - DWORD lerr = GetLastError(); - LOGI(" GFDWinVolume ReadBlock: SFP failed (err=%ld)", lerr); - return LastErrorToDIError(); - } - - //LOGI(" ReadFile block start=%d count=%d", startBlock, blockCount); - - BOOL result; - result = ::ReadFile(handle, buf, blockCount * kBlockSize, &actual, NULL); - if (!result) { - DWORD lerr = GetLastError(); - LOGI(" ReadBlocksWin2K: ReadFile failed (start=%ld count=%d err=%ld)", - startBlock, blockCount, lerr); - return LastErrorToDIError(); - } - if ((long) actual != blockCount * kBlockSize) - return kDIErrEOF; - - return kDIErrNone; -} - -/* - * Write blocks to a Win2K logical or physical volume. - */ -/*static*/ DIError Win32VolumeAccess::BlockAccess::WriteBlocksWin2K(HANDLE handle, - long startBlock, short blockCount, const void* buf) -{ - DWORD posn, actual; - - posn = ::SetFilePointer(handle, startBlock * kBlockSize, NULL, - FILE_BEGIN); - if (posn == INVALID_SET_FILE_POINTER) { - DWORD lerr = GetLastError(); - LOGI(" GFDWinVolume ReadBlocks: SFP %ld failed (err=%ld)", - startBlock * kBlockSize, lerr); - return LastErrorToDIError(); - } - - BOOL result; - result = ::WriteFile(handle, buf, blockCount * kBlockSize, &actual, NULL); - if (!result) { - DWORD lerr = GetLastError(); - LOGI(" GFDWinVolume WriteBlocks: WriteFile failed (err=%ld)", - lerr); - return LastErrorToDIError(); - } - if ((long) actual != blockCount * kBlockSize) - return kDIErrEOF; // unexpected on a write call? - - return kDIErrNone; -} - - -/* - * =========================================================================== - * LogicalBlockAccess - * =========================================================================== - */ - -/* - * Open a logical device. The device name should be of the form "A:\". - */ -DIError Win32VolumeAccess::LogicalBlockAccess::Open(const WCHAR* deviceName, - bool readOnly) -{ - DIError dierr = kDIErrNone; - const bool kPreferASPI = true; - - assert(fHandle == NULL); - fIsCDROM = false; - fDriveNum = -1; - - if (deviceName[0] < 'A' || deviceName[0] > 'Z' || - deviceName[1] != ':' || deviceName[2] != '\\' || - deviceName[3] != '\0') - { - LOGI(" LogicalBlockAccess: invalid device name '%s'", deviceName); - assert(false); - dierr = kDIErrInvalidArg; - goto bail; - } - if (deviceName[0] == 'C') { - if (readOnly == false) { - LOGI(" REFUSING WRITE ACCESS TO C:\\ "); - return kDIErrVWAccessForbidden; - } - } - - DWORD access; - if (readOnly) - access = GENERIC_READ; - else - access = GENERIC_READ | GENERIC_WRITE; - - UINT driveType; - driveType = GetDriveType(deviceName); - if (driveType == DRIVE_CDROM) { - if (!Global::GetHasSPTI() && !Global::GetHasASPI()) - return kDIErrCDROMNotSupported; - - fIsCDROM = true; - // SPTI needs this -- maybe to enforce exclusive access? - access |= GENERIC_WRITE; - } - - if (fIsWin9x) { - if (fIsCDROM) - return kDIErrCDROMNotSupported; - - fHandle = CreateFile(_T("\\\\.\\vwin32"), 0, 0, NULL, - OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (fHandle == INVALID_HANDLE_VALUE) { - DWORD lastError = GetLastError(); - LOGI(" Win32LVOpen: CreateFile(vwin32) failed (err=%ld)", - lastError); - dierr = LastErrorToDIError(); - goto bail; - } - fDriveNum = deviceName[0] - 'A' +1; - assert(fDriveNum > 0 && fDriveNum <= kNumLogicalVolumes); - -#if 0 - int int13Unit; - dierr = GetInt13Unit(fHandle, fDriveNum, &int13Unit); - if (dierr != kDIErrNone) - goto bail; - if (int13Unit < 4) { - fFloppyUnit = int13Unit; - LOGI(" Logical volume #%d looks like floppy unit %d", - fDriveNum, fFloppyUnit); - } -#endif - } else { - WCHAR device[7] = _T("\\\\.\\_:"); - device[4] = deviceName[0]; - LOGI("Opening '%s'", device); - - // If we're reading, allow others to write. If we're writing, insist - // upon exclusive access to the volume. - DWORD shareMode = FILE_SHARE_READ; - if (access == GENERIC_READ) - shareMode |= FILE_SHARE_WRITE; - - fHandle = CreateFile(device, access, shareMode, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fHandle == INVALID_HANDLE_VALUE) { - DWORD lastError = GetLastError(); - dierr = LastErrorToDIError(); - if (lastError == ERROR_INVALID_PARAMETER && shareMode == FILE_SHARE_READ) - { - // Win2K spits this back if the volume is open and we're - // not specifying FILE_SHARE_WRITE. Give it a try, just to - // see if it works, so we can tell the difference between - // an internal library error and an active filesystem. - HANDLE tmpHandle; - tmpHandle = CreateFile(device, access, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (tmpHandle != INVALID_HANDLE_VALUE) { - dierr = kDIErrNoExclusiveAccess; - ::CloseHandle(tmpHandle); - } - } - LOGI(" LBAccess Open: CreateFile failed (err=%ld dierr=%d)", - lastError, dierr); - goto bail; - } - } - - assert(fHandle != NULL && fHandle != INVALID_HANDLE_VALUE); - -#if 0 - if (fIsCDROM) { - assert(Global::GetHasSPTI() || Global::GetHasASPI()); - if (Global::GetHasASPI() && (!Global::GetHasSPTI() || kPreferASPI)) { - LOGI(" LBAccess using ASPI"); - fCDBaggage.fUseASPI = true; - } else { - LOGI(" LBAccess using SPTI"); - fCDBaggage.fUseASPI = false; - } - - if (fCDBaggage.fUseASPI) { - dierr = FindASPIDriveMapping(deviceName[0]); - if (dierr != kDIErrNone) { - LOGI("ERROR: couldn't find ASPI drive mapping"); - dierr = kDIErrNoASPIMapping; - goto bail; - } - } - } -#endif - -bail: - if (dierr != kDIErrNone) { - if (fHandle != NULL && fHandle != INVALID_HANDLE_VALUE) - ::CloseHandle(fHandle); - fHandle = NULL; - } - return dierr; -} - -/* - * Close the device handle. - */ -DIError Win32VolumeAccess::LogicalBlockAccess::Close(void) -{ - if (fHandle != NULL) { - ::CloseHandle(fHandle); - fHandle = NULL; - } - return kDIErrNone; -} - -/* - * Read 512-byte blocks from CD-ROM media using SPTI calls. - */ -DIError Win32VolumeAccess::LogicalBlockAccess::ReadBlocksCDROM(HANDLE handle, - long startBlock, short blockCount, void* buf) -{ -#ifdef HAVE_WINDOWS_CDROM - DIError dierr; - - assert(handle != NULL); - assert(startBlock >= 0); - assert(blockCount > 0); - assert(buf != NULL); - - //LOGI(" CDROM read block %ld (%ld)", startBlock, block); - - /* alloc sector buffer on first use */ - if (fLastSectorCache == NULL) { - fLastSectorCache = new uint8_t[kCDROMSectorSize]; - if (fLastSectorCache == NULL) - return kDIErrMalloc; - assert(fLastSectorNum == -1); - } - - /* - * Map a range of 512-byte blocks to a range of 2048-byte blocks. - */ - assert(kCDROMSectorSize % kBlockSize == 0); - const int kFactor = kCDROMSectorSize / kBlockSize; - long sectorIndex = startBlock / kFactor; - int sectorOffset = (int) (startBlock % kFactor); // 0-3 - - /* - * When possible, do multi-block reads directly into "buf". The first - * and last block may require special handling. - */ - while (blockCount) { - assert(blockCount > 0); - - if (sectorOffset != 0 || blockCount < kFactor) { - assert(sectorOffset >= 0 && sectorOffset < kFactor); - - /* get from single-block cache or from disc */ - if (sectorIndex != fLastSectorNum) { - fLastSectorNum = -1; // invalidate, in case of error - - dierr = SPTI::ReadBlocks(handle, sectorIndex, 1, - kCDROMSectorSize, fLastSectorCache); - if (dierr != kDIErrNone) - return dierr; - - fLastSectorNum = sectorIndex; - } - - int thisNumBlocks; - thisNumBlocks = kFactor - sectorOffset; - if (thisNumBlocks > blockCount) - thisNumBlocks = blockCount; - - //LOGI(" Small copy (sectIdx=%ld off=%d*512 size=%d*512)", - // sectorIndex, sectorOffset, thisNumBlocks); - memcpy(buf, fLastSectorCache + sectorOffset * kBlockSize, - thisNumBlocks * kBlockSize); - - blockCount -= thisNumBlocks; - buf = (uint8_t*) buf + (thisNumBlocks * kBlockSize); - - sectorOffset = 0; - sectorIndex++; - } else { - fLastSectorNum = -1; // invalidate single-block cache - - int numSectors; - numSectors = blockCount / kFactor; // rounds down - - //LOGI(" Big read (sectIdx=%ld numSect=%d)", - // sectorIndex, numSectors); - dierr = SPTI::ReadBlocks(handle, sectorIndex, numSectors, - kCDROMSectorSize, buf); - if (dierr != kDIErrNone) - return dierr; - - blockCount -= numSectors * kFactor; - buf = (uint8_t*) buf + (numSectors * kCDROMSectorSize); - - sectorIndex += numSectors; - } - } - - return kDIErrNone; - -#else - return kDIErrCDROMNotSupported; -#endif -} - - -/* - * =========================================================================== - * PhysicalBlockAccess - * =========================================================================== - */ - -/* - * Open a physical device. The device name should be of the form "80:\". - */ -DIError Win32VolumeAccess::PhysicalBlockAccess::Open(const WCHAR* deviceName, - bool readOnly) -{ - DIError dierr = kDIErrNone; - - // initialize all local state - assert(fHandle == NULL); - fInt13Unit = -1; - fFloppyKind = kFloppyUnknown; - memset(&fGeometry, 0, sizeof(fGeometry)); - - // sanity-check name; not this only works for first 10 devices - if (deviceName[0] < '0' || deviceName[0] > '9' || - deviceName[1] < '0' || deviceName[1] > '9' || - deviceName[2] != ':' || deviceName[3] != '\\' || - deviceName[4] != '\0') - { - LOGI(" PhysicalBlockAccess: invalid device name '%s'", deviceName); - assert(false); - dierr = kDIErrInvalidArg; - goto bail; - } - - if (deviceName[0] == '8' && deviceName[1] == '0') { - if (!gAllowWritePhys0 && readOnly == false) { - LOGI(" REFUSING WRITE ACCESS TO 80:\\ "); - return kDIErrVWAccessForbidden; - } - } - - fInt13Unit = (deviceName[0] - '0') * 16 + deviceName[1] - '0'; - if (!fIsWin9x && fInt13Unit < 0x80) { - LOGI("GLITCH: can't open floppy as physical unit in Win2K"); - dierr = kDIErrInvalidArg; - goto bail; - } - if (fIsWin9x && fInt13Unit >= 0x80) { - LOGI("GLITCH: can't access physical HD in Win9x"); - dierr = kDIErrInvalidArg; - goto bail; - } - if ((fInt13Unit >= 0x00 && fInt13Unit < 0x04) || - (fInt13Unit >= 0x80 && fInt13Unit < 0x88)) - { - LOGI(" Win32VA/P: opening unit %02xh", fInt13Unit); - } else { - LOGI("GLITCH: converted '%s' to %02xh", deviceName, fInt13Unit); - dierr = kDIErrInternal; - goto bail; - } - - DWORD access; - if (readOnly) - access = GENERIC_READ; - else - access = GENERIC_READ | GENERIC_WRITE; - - if (fIsWin9x) { - fHandle = CreateFile(_T("\\\\.\\vwin32"), 0, 0, NULL, - OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (fHandle == INVALID_HANDLE_VALUE) { - DWORD lastError = GetLastError(); - LOGI(" Win32VA/PBOpen: CreateFile(vwin32) failed (err=%ld)", - lastError); - dierr = LastErrorToDIError(); - goto bail; - } - - /* figure out the geometry */ - dierr = DetectFloppyGeometry(); - if (dierr != kDIErrNone) - goto bail; - } else { - WCHAR device[19] = _T("\\\\.\\PhysicalDrive_"); - assert(fInt13Unit >= 0x80 && fInt13Unit <= 0x89); - device[17] = fInt13Unit - 0x80 + '0'; - LOGI("Opening '%s' (access=0x%02x)", device, access); - - // If we're reading, allow others to write. If we're writing, insist - // upon exclusive access to the volume. - DWORD shareMode = FILE_SHARE_READ; - if (access == GENERIC_READ) - shareMode |= FILE_SHARE_WRITE; - - fHandle = CreateFile(device, access, shareMode, - NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (fHandle == INVALID_HANDLE_VALUE) { - DWORD lastError = GetLastError(); - dierr = LastErrorToDIError(); - LOGI(" PBAccess Open: CreateFile failed (err=%ld dierr=%d)", - lastError, dierr); - goto bail; - } - } - - assert(fHandle != NULL && fHandle != INVALID_HANDLE_VALUE); - -bail: - if (dierr != kDIErrNone) { - if (fHandle != NULL && fHandle != INVALID_HANDLE_VALUE) - ::CloseHandle(fHandle); - fHandle = NULL; - } - - return dierr; -} - -/* - * Auto-detect the geometry of a floppy drive. - * - * Sets "fFloppyKind" and "fGeometry". - */ -DIError Win32VolumeAccess::PhysicalBlockAccess::DetectFloppyGeometry(void) -{ - DIError dierr = kDIErrNone; - static const struct { - FloppyKind kind; - DiskGeometry geom; - } floppyGeometry[] = { - { kFloppyUnknown, { -1, -1, -1, -1 } }, - { kFloppy525_360, { 40, 2, 9, 360*2 } }, - { kFloppy525_1200, { 80, 2, 15, 1200*2 } }, - { kFloppy35_720, { 80, 2, 9, 720*2 } }, - { kFloppy35_1440, { 80, 2, 18, 1440*2 } }, - { kFloppy35_2880, { 80, 2, 36, 2880*2 } } - }; - uint8_t buf[kBlockSize]; - FloppyKind driveKind; - int status; - - /* verify that we can directly index the table with the enum */ - int chk; - for (chk = 0; chk < NELEM(floppyGeometry); chk++) { - assert(floppyGeometry[chk].kind == chk); - } - assert(chk == kFloppyMax); - - - /* - * Issue a BIOS call to determine the kind of drive we're looking at. - */ - dierr = GetFloppyDriveKind(fHandle, fInt13Unit, &driveKind); - if (dierr != kDIErrNone) - goto bail; - - switch (driveKind) { - case kFloppy35_2880: - status = ReadBlocksInt13h(fHandle, fInt13Unit, 0, 0, 36, 1, buf); - if (status == 0) { - fFloppyKind = kFloppy35_2880; - break; - } - // else, fall through - case kFloppy35_1440: - status = ReadBlocksInt13h(fHandle, fInt13Unit, 0, 0, 18, 1, buf); - if (status == 0) { - fFloppyKind = kFloppy35_1440; - break; - } - // else, fall through - case kFloppy35_720: - status = ReadBlocksInt13h(fHandle, fInt13Unit, 0, 0, 9, 1, buf); - if (status == 0) { - fFloppyKind = kFloppy35_720; - break; - } - // else, fail - dierr = kDIErrReadFailed; - goto bail; - - case kFloppy525_1200: - status = ReadBlocksInt13h(fHandle, fInt13Unit, 0, 0, 15, 1, buf); - if (status == 0) { - fFloppyKind = kFloppy525_1200; - break; - } - // else, fall through - case kFloppy525_360: - status = ReadBlocksInt13h(fHandle, fInt13Unit, 0, 0, 9, 1, buf); - if (status == 0) { - fFloppyKind = kFloppy525_360; - break; - } - // else, fail - dierr = kDIErrReadFailed; - goto bail; - - default: - LOGI(" Unknown driveKind %d", driveKind); - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - LOGI("PBA: floppy disk appears to be type=%d", fFloppyKind); - fGeometry = floppyGeometry[fFloppyKind].geom; - -bail: - return dierr; -} - -#if 0 -/* - * Flush the system disk cache. - */ -DIError Win32VolumeAccess::PhysicalBlockAccess::FlushBlockDevice(void) -{ - DIError dierr = kDIErrNone; - - if (::FlushFileBuffers(fHandle) == FALSE) { - DWORD lastError = GetLastError(); - LOGI(" Win32VA/PBAFlush: FlushFileBuffers failed (err=%ld)", - lastError); - dierr = LastErrorToDIError(); - } - return dierr; -} -#endif - -/* - * Close the device handle. - */ -DIError Win32VolumeAccess::PhysicalBlockAccess::Close(void) -{ - if (fHandle != NULL) { - ::CloseHandle(fHandle); - fHandle = NULL; - } - return kDIErrNone; -} - - -/* - * =========================================================================== - * ASPIBlockAccess - * =========================================================================== - */ -#ifdef WANT_ASPI - -/* - * Unpack device name and verify that the device is a CD-ROM drive or - * direct-access storage device. - */ -DIError Win32VolumeAccess::ASPIBlockAccess::Open(const char* deviceName, - bool readOnly) -{ - DIError dierr = kDIErrNone; - - if (fpASPI != NULL) - return kDIErrAlreadyOpen; - - fpASPI = Global::GetASPI(); - if (fpASPI == NULL) - return kDIErrASPIFailure; - - if (strncmp(deviceName, kASPIDev, strlen(kASPIDev)) != 0) { - assert(false); - dierr = kDIErrInternal; - goto bail; - } - - const char* cp; - int adapter, target, lun; - int result; - - cp = deviceName + strlen(kASPIDev); - result = 0; - result |= ExtractInt(&cp, &adapter); - result |= ExtractInt(&cp, &target); - result |= ExtractInt(&cp, &lun); - if (result != 0) { - LOGI(" Win32VA couldn't parse '%s'", deviceName); - dierr = kDIErrInternal; - goto bail; - } - - fAdapter = adapter; - fTarget = target; - fLun = lun; - - uint8_t deviceType; - dierr = fpASPI->GetDeviceType(fAdapter, fTarget, fLun, &deviceType); - if (dierr != kDIErrNone || - (deviceType != kScsiDevTypeCDROM && deviceType != kScsiDevTypeDASD)) - { - LOGI(" Win32VA bad GetDeviceType err=%d type=%d", - dierr, deviceType); - dierr = kDIErrInternal; // should not be here at all - goto bail; - } - if (deviceType == kScsiDevTypeCDROM) - fReadOnly = true; - else - fReadOnly = readOnly; - - LOGI(" Win32VA successful 'open' of '%s' on %d:%d:%d", - deviceName, fAdapter, fTarget, fLun); - -bail: - if (dierr != kDIErrNone) - fpASPI = NULL; - return dierr; -} - -/* - * Extract an integer from a string, advancing to the next integer after - * doing so. - * - * Returns 0 on success, -1 on failure. - */ -int Win32VolumeAccess::ASPIBlockAccess::ExtractInt(const char** pStr, int* pResult) -{ - char* end = NULL; - - if (*pStr == NULL) { - assert(false); - return -1; - } - - *pResult = (int) strtol(*pStr, &end, 10); - - if (end == NULL) - *pStr = NULL; - else - *pStr = end+1; - - return 0; -} - -/* - * Return the device capacity in 512-byte blocks. - * - * Sets fChunkSize as a side-effect. - */ -DIError Win32VolumeAccess::ASPIBlockAccess::DetectCapacity(long* pNumBlocks) -{ - DIError dierr = kDIErrNone; - unsigned long lba, blockLen; - - dierr = fpASPI->GetDeviceCapacity(fAdapter, fTarget, fLun, &lba, - &blockLen); - if (dierr != kDIErrNone) - goto bail; - - LOGI("READ CAPACITY reports lba=%lu blockLen=%lu (total=%lu)", - lba, blockLen, lba*blockLen); - - fChunkSize = blockLen; - - if ((blockLen % 512) != 0) { - LOGI("Unacceptable CD-ROM blockLen=%ld, bailing", blockLen); - dierr = kDIErrReadFailed; - goto bail; - } - - // The LBA is the last valid block on the disk. To get the disk size, - // we need to add one. - - *pNumBlocks = (blockLen/512) * (lba+1); - LOGI(" ASPI returning 512-byte block count %ld", *pNumBlocks); - -bail: - return dierr; -} - -/* - * Read one or more 512-byte blocks from the device. - * - * SCSI doesn't promise it'll be in a chunk size we like, but it's pretty - * safe to assume that it'll be at least 512 bytes, and divisible by 512. - */ -DIError Win32VolumeAccess::ASPIBlockAccess::ReadBlocks(long startBlock, - short blockCount, void* buf) -{ - DIError dierr; - - // we're expecting fBlockSize to be 512 or 2048 - assert(fChunkSize >= kBlockSize && fChunkSize <= 65536); - assert((fChunkSize % kBlockSize) == 0); - - /* alloc chunk buffer on first use */ - if (fLastChunkCache == NULL) { - fLastChunkCache = new uint8_t[fChunkSize]; - if (fLastChunkCache == NULL) - return kDIErrMalloc; - assert(fLastChunkNum == -1); - } - - /* - * Map a range of N-byte blocks to a range of 2048-byte blocks. - */ - const int kFactor = fChunkSize / kBlockSize; - long chunkIndex = startBlock / kFactor; - int chunkOffset = (int) (startBlock % kFactor); // small integer - - /* - * When possible, do multi-block reads directly into "buf". The first - * and last block may require special handling. - */ - while (blockCount) { - assert(blockCount > 0); - - if (chunkOffset != 0 || blockCount < kFactor) { - assert(chunkOffset >= 0 && chunkOffset < kFactor); - - /* get from single-block cache or from disc */ - if (chunkIndex != fLastChunkNum) { - fLastChunkNum = -1; // invalidate, in case of error - - dierr = fpASPI->ReadBlocks(fAdapter, fTarget, fLun, chunkIndex, - 1, fChunkSize, fLastChunkCache); - if (dierr != kDIErrNone) - return dierr; - - fLastChunkNum = chunkIndex; - } - - int thisNumBlocks; - thisNumBlocks = kFactor - chunkOffset; - if (thisNumBlocks > blockCount) - thisNumBlocks = blockCount; - - //LOGI(" Small copy (chIdx=%ld off=%d*512 size=%d*512)", - // chunkIndex, chunkOffset, thisNumBlocks); - memcpy(buf, fLastChunkCache + chunkOffset * kBlockSize, - thisNumBlocks * kBlockSize); - - blockCount -= thisNumBlocks; - buf = (uint8_t*) buf + (thisNumBlocks * kBlockSize); - - chunkOffset = 0; - chunkIndex++; - } else { - fLastChunkNum = -1; // invalidate single-block cache - - int numChunks; - numChunks = blockCount / kFactor; // rounds down - - //LOGI(" Big read (chIdx=%ld numCh=%d)", - // chunkIndex, numChunks); - dierr = fpASPI->ReadBlocks(fAdapter, fTarget, fLun, chunkIndex, - numChunks, fChunkSize, buf); - if (dierr != kDIErrNone) - return dierr; - - blockCount -= numChunks * kFactor; - buf = (uint8_t*) buf + (numChunks * fChunkSize); - - chunkIndex += numChunks; - } - } - - return kDIErrNone; -} - -/* - * Write one or more 512-byte blocks to the device. - * - * SCSI doesn't promise it'll be in a chunk size we like, but it's pretty - * safe to assume that it'll be at least 512 bytes, and divisible by 512. - */ -DIError Win32VolumeAccess::ASPIBlockAccess::WriteBlocks(long startBlock, - short blockCount, const void* buf) -{ - DIError dierr; - - if (fReadOnly) - return kDIErrAccessDenied; - - // we're expecting fBlockSize to be 512 or 2048 - assert(fChunkSize >= kBlockSize && fChunkSize <= 65536); - assert((fChunkSize % kBlockSize) == 0); - - /* throw out the cache */ - fLastChunkNum = -1; - - /* - * Map a range of N-byte blocks to a range of 2048-byte blocks. - */ - const int kFactor = fChunkSize / kBlockSize; - long chunkIndex = startBlock / kFactor; - int chunkOffset = (int) (startBlock % kFactor); // small integer - - /* - * When possible, do multi-block writes directly from "buf". The first - * and last block may require special handling. - */ - while (blockCount) { - assert(blockCount > 0); - - if (chunkOffset != 0 || blockCount < kFactor) { - assert(chunkOffset >= 0 && chunkOffset < kFactor); - - /* read the chunk we're writing a part of */ - dierr = fpASPI->ReadBlocks(fAdapter, fTarget, fLun, chunkIndex, - 1, fChunkSize, fLastChunkCache); - if (dierr != kDIErrNone) - return dierr; - - int thisNumBlocks; - thisNumBlocks = kFactor - chunkOffset; - if (thisNumBlocks > blockCount) - thisNumBlocks = blockCount; - - LOGI(" Small copy out (chIdx=%ld off=%d*512 size=%d*512)", - chunkIndex, chunkOffset, thisNumBlocks); - memcpy(fLastChunkCache + chunkOffset * kBlockSize, buf, - thisNumBlocks * kBlockSize); - - blockCount -= thisNumBlocks; - buf = (const uint8_t*) buf + (thisNumBlocks * kBlockSize); - - chunkOffset = 0; - chunkIndex++; - } else { - int numChunks; - numChunks = blockCount / kFactor; // rounds down - - LOGI(" Big write (chIdx=%ld numCh=%d)", - chunkIndex, numChunks); - dierr = fpASPI->WriteBlocks(fAdapter, fTarget, fLun, chunkIndex, - numChunks, fChunkSize, buf); - if (dierr != kDIErrNone) - return dierr; - - blockCount -= numChunks * kFactor; - buf = (const uint8_t*) buf + (numChunks * fChunkSize); - - chunkIndex += numChunks; - } - } - - return kDIErrNone; -} - -/* - * Not much to do, really, since we're not holding onto any OS structures. - */ -DIError -Win32VolumeAccess::ASPIBlockAccess::Close(void) -{ - fpASPI = NULL; - return kDIErrNone; -} -#endif - - -/* - * =========================================================================== - * CBCache - * =========================================================================== - */ - -/* - * Determine whether we're holding a block in the cache. - */ -bool CBCache::IsBlockInCache(long blockNum) const -{ - if (fFirstBlock == kEmpty) - return false; - assert(fNumBlocks > 0); - - if (blockNum >= fFirstBlock && blockNum < fFirstBlock + fNumBlocks) - return true; - else - return false; -} - -/* - * Retrieve a single block from the cache. - */ -DIError CBCache::GetFromCache(long blockNum, void* buf) -{ - if (!IsBlockInCache(blockNum)) { - assert(false); - return kDIErrInternal; - } - - //LOGI(" CBCache: getting block %d from cache", blockNum); - int offset = (blockNum - fFirstBlock) * kBlockSize; - assert(offset >= 0); - - memcpy(buf, fCache + offset, kBlockSize); - return kDIErrNone; -} - -/* - * Determine whether a block will "fit" in the cache. There are two - * criteria: (1) there must actually be room at the end, and (2) the - * block in question must be the next consecutive block. - */ -bool CBCache::IsRoomInCache(long blockNum) const -{ - if (fFirstBlock == kEmpty) - return true; - - // already in cache? - if (blockNum >= fFirstBlock && blockNum < fFirstBlock + fNumBlocks) - return true; - - // running off the end? - if (fNumBlocks == kMaxCachedBlocks) - return false; - - // is it the exact next one? - if (fFirstBlock + fNumBlocks != blockNum) - return false; - - return true; -} - -/* - * Add a block to the cache. - * - * We might be adding it after a read or a write. The "isDirty" flag - * tells us what the deal is. If somebody tries to overwrite a dirty - * block with a new one and doesn't have "isDirty" set, it probably means - * they're trying to overwrite dirty cached data with the result of a new - * read, which is a bug. Trap it here. - */ -DIError CBCache::PutInCache(long blockNum, const void* buf, bool isDirty) -{ - int blockOffset = -1; - if (!IsRoomInCache(blockNum)) { - assert(false); - return kDIErrInternal; - } - - if (fFirstBlock == kEmpty) { - //LOGI(" CBCache: starting anew with block %ld", blockNum); - fFirstBlock = blockNum; - fNumBlocks = 1; - blockOffset = 0; - } else if (blockNum == fFirstBlock + fNumBlocks) { - //LOGI(" CBCache: appending block %ld", blockNum); - blockOffset = fNumBlocks; - fNumBlocks++; - } else if (blockNum >= fFirstBlock && blockNum < fFirstBlock + fNumBlocks) { - blockOffset = blockNum - fFirstBlock; - } else { - assert(false); - return kDIErrInternal; - } - assert(blockOffset != -1); - assert(blockOffset < kMaxCachedBlocks); - - if (fDirty[blockOffset] && !isDirty) { - LOGI("BUG: CBCache trying to clear dirty flag for block %ld", - blockNum); - assert(false); - return kDIErrInternal; - } - fDirty[blockOffset] = isDirty; - - //LOGI(" CBCache: adding block %d to cache at %d", blockNum, blockOffset); - int offset = blockOffset * kBlockSize; - assert(offset >= 0); - - memcpy(fCache + offset, buf, kBlockSize); - return kDIErrNone; -} - -/* - * Determine whether there are any dirty blocks in the cache. - */ -bool CBCache::IsDirty(void) const -{ - if (fFirstBlock == kEmpty) - return false; - - assert(fNumBlocks > 0); - for (int i = 0; i < fNumBlocks; i++) { - if (fDirty[i]) { - //LOGI(" CBCache: dirty blocks found"); - return true; - } - } - - //LOGI(" CBCache: no dirty blocks found"); - return false; -} - -/* - * Return a pointer to the cache goodies, so that the object sitting - * on the disk hardware can write our stuff. - */ -void CBCache::GetCachePointer(long* pFirstBlock, int* pNumBlocks, void** pBuf) const -{ - assert(fFirstBlock != kEmpty); // not essential, but why call here if not? - - *pFirstBlock = fFirstBlock; - *pNumBlocks = fNumBlocks; - *pBuf = (void*) fCache; -} - -/* - * Clear all the dirty flags. - */ -void CBCache::Scrub(void) -{ - if (fFirstBlock == kEmpty) - return; - - for (int i = 0; i < fNumBlocks; i++) - fDirty[i] = false; -} - -/* - * Trash all of our entries. If any are dirty, scream bloody murder. - */ -void CBCache::Purge(void) -{ - if (fFirstBlock == kEmpty) - return; - - if (IsDirty()) { - // Should only happen after a write failure causes us to clean up. - LOGE("HEY: CBCache purging dirty blocks!"); - //assert(false); - } - Scrub(); - - fFirstBlock = kEmpty; - fNumBlocks = 0; -} - -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/Win32BlockIO.h b/ciderpress/diskimg/Win32BlockIO.h deleted file mode 100644 index 0bb1844..0000000 --- a/ciderpress/diskimg/Win32BlockIO.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#ifdef _WIN32 -/* - * Structures and functions for performing block-level I/O on Win32 logical - * and physical volumes. - * - * Under Win2K/XP this is pretty straightforward: open the volume and - * issue seek and read calls. It's not quite that simple -- reads need to - * be in 512-byte sectors for floppy and hard drives, seeks need to be on - * sector boundaries, and you can't seek from the end, which makes it hard - * to figure out how big the volume is -- but it's palatable. - * - * Under Win95/Win98/WinME, life is more difficult. You need to use the - * Int21h/7305h services to access logical volumes. Of course, those weren't - * available until Win95 OSR2, before which you used Int25h/6000h, but those - * don't work with FAT32 volumes. Access to physical devices requires Int13h, - * which is fine for floppies but requires 16-bit flat thunks for hard drives - * (see Q137176: "DeviceIoControl Int 13h Does Not Support Hard Disks"). - * - * If Win98 can't recognize the volume on a floppy, it tries to reacquire - * the volume information every time you ask it to read a sector. This makes - * things *VERY* slow. The solution is to use the physical drive Int13h - * services. These come in two variants, one of which will work on just - * about any machine but only works with floppies. The other will work on - * anything built since about 1996. - * - * Figuring out whether something is or isn't a floppy requires yet - * another call. All things considered it's quite an ordeal. The block I/O - * functions are wrapped up in classes so nobody else has to worry about all - * this mess. - * - * Implementation note: this class is broken down by how the devices are - * opened, e.g. logical, physical, or ASPI address. Breaking it down by device - * type seems more appropriate, but Win98 vs Win2K can require completely - * different approaches (e.g. physical vs. logical for floppy disk, logical - * vs. ASPI for CD-ROM). There is no perfect decomposition. - * - * Summary: - * Win9x/ME physical drive: Int13h (doesn't work for hard drives) - * Win9x/ME logical drive: Int21h/7305h - * Win9x/ME SCSI drive or CD-ROM drive: ASPI - * Win2K/XP physical drive: CreateFile("\\.\PhysicalDriveN") - * Win2K/XP logical drive: CreateFile("\\.\X") - * Win2K/XP SCSI drive or CD-ROM drive: SPTI - */ -#ifndef DISKIMG_WIN32BLOCKIO_H -#define DISKIMG_WIN32BLOCKIO_H - - -namespace DiskImgLib { - -extern bool IsWin9x(void); - - -/* - * Cache a contiguous set of blocks. This was originally motivated by poor - * write performance, but that problem was largely solved in other ways. - * It's still handy to write an entire track at once under Win98 though. - * - * Only storing continuous runs of blocks makes the cache less useful, but - * much easier to write, and hence less likely to break in unpleasant ways. - * - * This class just manages the blocks. The FlushCache() function in - * Win32LogicalVolume is responsible for actually pushing the writes through. - * - * (I'm not entirely happy with this, especially since it doesn't take into - * account the underlying device block size. This could've been a good place - * to handle the 2048-byte CD-ROM block size, rather than caching it again in - * the CD-ROM handler.) - */ -class CBCache { -public: - CBCache(void) : fFirstBlock(kEmpty), fNumBlocks(0) - { - for (int i = 0; i < kMaxCachedBlocks; i++) - fDirty[i] = false; - } - virtual ~CBCache(void) { Purge(); } - - enum { kEmpty = -1 }; - - // is the block we want in the cache? - bool IsBlockInCache(long blockNum) const; - // read block out of cache (after verifying that it's present) - DIError GetFromCache(long blockNum, void* buf); - // can the cache store this block? - bool IsRoomInCache(long blockNum) const; - // write block to cache (after verifying that it will fit) - DIError PutInCache(long blockNum, const void* buf, bool isDirty); - - // are there any dirty blocks in the cache? - bool IsDirty(void) const; - // get start, count, and buffer so we can write the cached data - void GetCachePointer(long* pFirstBlock, int* pNumBlocks, void** pBuf) const; - // clear all the dirty flags - void Scrub(void); - // purge all cache entries (ideally after writing w/help from GetCachePtr) - void Purge(void); - -private: - enum { - kMaxCachedBlocks = 18, // one track on 1.4MB floppy - kBlockSize = 512, // must match with Win32LogicalVolume:: - }; - - long fFirstBlock; // set to kEmpty when cache is empty - int fNumBlocks; - bool fDirty[kMaxCachedBlocks]; - unsigned char fCache[kMaxCachedBlocks * kBlockSize]; -}; - - -/* - * This class encapsulates block access to a logical or physical volume. - */ -class Win32VolumeAccess { -public: - Win32VolumeAccess(void) : fpBlockAccess(NULL) - {} - virtual ~Win32VolumeAccess(void) { - if (fpBlockAccess != NULL) { - FlushCache(true); - fpBlockAccess->Close(); - } - } - - // "deviceName" has the form "X:\" (logical), "81:\" (physical), or - // "ASPI:x:y:z\" (ASPI) - DIError Open(const WCHAR* deviceName, bool readOnly); - // close the device - void Close(void); - // is the device open and working? - bool Ready(void) const { return fpBlockAccess != NULL; } - - // return the volume's EOF - long GetTotalBlocks(void) const { return fTotalBlocks; } - // return the block size for this volume (always a power of 2) - int GetBlockSize(void) const { return BlockAccess::kBlockSize; } - - // read one or more consecutive blocks - DIError ReadBlocks(long startBlock, short blockCount, void* buf); - // write one or more consecutive blocks - DIError WriteBlocks(long startBlock, short blockCount, const void* buf); - // flush our internal cache - DIError FlushCache(bool purge); - -private: - /* - * Abstract base class with some handy functions. - */ - class BlockAccess { - public: - BlockAccess(void) { fIsWin9x = DiskImgLib::IsWin9x(); } - virtual ~BlockAccess(void) {} - - typedef struct { - int numCyls; - int numHeads; - int numSectors; - long blockCount; // total #of blocks on this kind of disk - } DiskGeometry; - - // generic interfaces - virtual DIError Open(const WCHAR* deviceName, bool readOnly) = 0; - virtual DIError DetectCapacity(long* pNumBlocks) = 0; - virtual DIError ReadBlocks(long startBlock, short blockCount, - void* buf) = 0; - virtual DIError WriteBlocks(long startBlock, short blockCount, - const void* buf) = 0; - virtual DIError Close(void) = 0; - - static bool BlockToCylinderHeadSector(long blockNum, - const DiskGeometry* pGeometry, int* pCylinder, int* pHead, - int* pSector, long* pLastBlockOnTrack); - - enum { - kNumLogicalVolumes = 26, // A-Z - kBlockSize = 512, - kCDROMSectorSize = 2048, - kMaxFloppyRetries = 3, // retry floppy reads/writes - }; - - // BIOS floppy disk drive type; doubles here as media type - typedef enum { - kFloppyUnknown = 0, - kFloppy525_360 = 1, - kFloppy525_1200 = 2, - kFloppy35_720 = 3, - kFloppy35_1440 = 4, - kFloppy35_2880 = 5, - - kFloppyMax - } FloppyKind; - - protected: - static DIError GetFloppyDriveKind(HANDLE handle, int unitNum, - FloppyKind* pKind); - // detect the #of blocks on the volume - static DIError ScanCapacity(BlockAccess* pThis, long* pNumBlocks); - // determine whether a block is readable - static bool CanReadBlock(BlockAccess* pThis, long blockNum); - // try to detect device capacity using SPTI - DIError DetectCapacitySPTI(HANDLE handle, - bool isCDROM, long* pNumBlocks); - - static int ReadBlocksInt13h(HANDLE handle, int unitNum, - int cylinder, int head, int sector, short blockCount, void* buf); - static DIError ReadBlocksInt13h(HANDLE handle, int unitNum, - const DiskGeometry* pGeometry, long startBlock, short blockCount, - void* buf); - static int WriteBlocksInt13h(HANDLE handle, int unitNum, - int cylinder, int head, int sector, short blockCount, - const void* buf); - static DIError WriteBlocksInt13h(HANDLE handle, int unitNum, - const DiskGeometry* pGeometry, long startBlock, short blockCount, - const void* buf); - - static DIError ReadBlocksInt21h(HANDLE handle, int driveNum, - long startBlock, short blockCount, void* buf); - static DIError WriteBlocksInt21h(HANDLE handle, int driveNum, - long startBlock, short blockCount, const void* buf); - - static DIError ReadBlocksWin2K(HANDLE handle, - long startBlock, short blockCount, void* buf); - static DIError WriteBlocksWin2K(HANDLE handle, - long startBlock, short blockCount, const void* buf); - - bool fIsWin9x; // Win9x/ME=true, Win2K/XP=false - }; - - /* - * Access to a logical volume (e.g. "C:\") under Win9x and Win2K/XP. - */ - class LogicalBlockAccess : public BlockAccess { - public: - LogicalBlockAccess(void) : fHandle(NULL), fIsCDROM(false), - fDriveNum(-1), fLastSectorCache(NULL), fLastSectorNum(-1) - {} - virtual ~LogicalBlockAccess(void) { - if (fHandle != NULL) { - //LOGI("HEY: LogicalBlockAccess: forcing close"); - Close(); - } - delete[] fLastSectorCache; - } - - virtual DIError Open(const WCHAR* deviceName, bool readOnly); - virtual DIError DetectCapacity(long* pNumBlocks) { - /* use SCSI length value if at all possible */ - DIError dierr; - dierr = DetectCapacitySPTI(fHandle, fIsCDROM, pNumBlocks); - if (fIsCDROM) - return dierr; // SPTI should always work for CD-ROM - if (dierr != kDIErrNone) - return ScanCapacity(this, pNumBlocks); // fall back on scan - else - return dierr; - } - virtual DIError ReadBlocks(long startBlock, short blockCount, - void* buf) - { - if (fIsCDROM) - return ReadBlocksCDROM(fHandle, startBlock, blockCount, buf); - if (fIsWin9x) - return ReadBlocksInt21h(fHandle, fDriveNum, startBlock, - blockCount, buf); - else - return ReadBlocksWin2K(fHandle, startBlock, blockCount, buf); - } - virtual DIError WriteBlocks(long startBlock, short blockCount, - const void* buf) - { - if (fIsCDROM) - return kDIErrWriteProtected; - if (fIsWin9x) - return WriteBlocksInt21h(fHandle, fDriveNum, startBlock, - blockCount, buf); - else - return WriteBlocksWin2K(fHandle, startBlock, blockCount, buf); - } - virtual DIError Close(void); - - private: - //DIError DetectCapacitySPTI(long* pNumBlocks); - DIError ReadBlocksCDROM(HANDLE handle, - long startBlock, short numBlocks, void* buf); - - // Win2K/XP and Win9x/ME - HANDLE fHandle; - bool fIsCDROM; // set for CD-ROM devices - // Win9x/ME - int fDriveNum; // 1=A, 3=C, etc - // CD-ROM goodies - unsigned char* fLastSectorCache; - long fLastSectorNum; - }; - - /* - * Access to a physical volume (e.g. 00h or 80h) under Win9x and - * Win2K/XP. - */ - class PhysicalBlockAccess : public BlockAccess { - public: - PhysicalBlockAccess(void) : fHandle(NULL), fInt13Unit(-1) {} - virtual ~PhysicalBlockAccess(void) {} - - virtual DIError Open(const WCHAR* deviceName, bool readOnly); - virtual DIError DetectCapacity(long* pNumBlocks) { - /* try SPTI in case it happens to work */ - DIError dierr; - dierr = DetectCapacitySPTI(fHandle, false, pNumBlocks); - if (dierr != kDIErrNone) - return ScanCapacity(this, pNumBlocks); - else - return dierr; - } - virtual DIError ReadBlocks(long startBlock, short blockCount, - void* buf) - { - if (fIsWin9x) - return ReadBlocksInt13h(fHandle, fInt13Unit, - &fGeometry, startBlock, blockCount, buf); - else - return ReadBlocksWin2K(fHandle, - startBlock, blockCount, buf); - } - virtual DIError WriteBlocks(long startBlock, short blockCount, - const void* buf) - { - if (fIsWin9x) - return WriteBlocksInt13h(fHandle, fInt13Unit, - &fGeometry, startBlock, blockCount, buf); - else - return WriteBlocksWin2K(fHandle, - startBlock, blockCount, buf); - } - virtual DIError Close(void); - - private: - DIError DetectFloppyGeometry(void); - - // Win2K/XP - HANDLE fHandle; - // Win9x/ME - int fInt13Unit; // 00h=floppy #1, 80h=HD#1 - FloppyKind fFloppyKind; - DiskGeometry fGeometry; - }; - -#ifdef WANT_ASPI - /* - * Access to a SCSI volume via the ASPI interface. - */ - class ASPIBlockAccess : public BlockAccess { - public: - ASPIBlockAccess(void) : fpASPI(NULL), - fAdapter(0xff), fTarget(0xff), fLun(0xff), fReadOnly(false), - fLastChunkCache(NULL), fLastChunkNum(-1), fChunkSize(-1) - {} - virtual ~ASPIBlockAccess(void) { delete[] fLastChunkCache; } - - virtual DIError Open(const char* deviceName, bool readOnly); - virtual DIError DetectCapacity(long* pNumBlocks); - virtual DIError ReadBlocks(long startBlock, short blockCount, - void* buf); - virtual DIError WriteBlocks(long startBlock, short blockCount, - const void* buf); - virtual DIError Close(void); - - private: - int ExtractInt(const char** pStr, int* pResult); - - ASPI* fpASPI; - unsigned char fAdapter; - unsigned char fTarget; - unsigned char fLun; - - bool fReadOnly; - - // block cache - unsigned char* fLastChunkCache; - long fLastChunkNum; - long fChunkSize; // set by DetectCapacity - }; -#endif /*WANT_ASPI*/ - - // write a series of blocks to the volume - DIError DoWriteBlocks(long startBlock, short blockCount, const void* buf) - { - return fpBlockAccess->WriteBlocks(startBlock, blockCount, buf); - } - - long fTotalBlocks; - BlockAccess* fpBlockAccess; // really LogicalBA or PhysicalBA - CBCache fBlockCache; -}; - - -}; // namespace DiskImgLib - -#endif /*DISKIMG_WIN32BLOCKIO_H*/ - -#endif /*_WIN32*/ diff --git a/ciderpress/diskimg/Win32Extra.h b/ciderpress/diskimg/Win32Extra.h deleted file mode 100644 index fa77ea3..0000000 --- a/ciderpress/diskimg/Win32Extra.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Visual C++ 6.0 doesn't have this definition, because it wasn't added until - * WinXP. - * - * (Do we want IOCTL_DISK_GET_DRIVE_LAYOUT_EX too?) - */ -#ifndef DISKIMG_WIN32EXTRA_H -#define DISKIMG_WIN32EXTRA_H - -#include // base definitions - -#ifndef IOCTL_DISK_GET_DRIVE_GEOMETRY_EX - -/* -BOOL DeviceIoControl( - (HANDLE) hDevice, // handle to volume - IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, // dwIoControlCode - NULL, // lpInBuffer - 0, // nInBufferSize - (LPVOID) lpOutBuffer, // output buffer - (DWORD) nOutBufferSize, // size of output buffer - (LPDWORD) lpBytesReturned, // number of bytes returned - (LPOVERLAPPED) lpOverlapped // OVERLAPPED structure -); -*/ - -#define IOCTL_DISK_GET_DRIVE_GEOMETRY_EX \ - CTL_CODE(IOCTL_DISK_BASE, 0x0028, METHOD_BUFFERED, FILE_ANY_ACCESS) - -typedef struct _DISK_GEOMETRY_EX { - DISK_GEOMETRY Geometry; - LARGE_INTEGER DiskSize; - BYTE Data[1]; -} DISK_GEOMETRY_EX, *PDISK_GEOMETRY_EX; - -#if 0 -typedef struct _DISK_DETECTION_INFO { - DWORD SizeOfDetectInfo; - DETECTION_TYPE DetectionType; - union { - struct { - DISK_INT13_INFO Int13; - DISK_EX_INT13_INFO ExInt13; - }; - }; -} DISK_DETECTION_INFO, *PDISK_DETECTION_INFO; - -PDISK_DETECTION_INFO DiskGeometryGetDetect(PDISK_GEOMETRY_EX Geometry); -PDISK_PARTITION_INFO DiskGeometryGetPartition(PDISK_GEOMETRY_EX Geometry); -#endif - - -#endif /*IOCTL_DISK_GET_DRIVE_GEOMETRY_EX*/ - -#endif /*DISKIMG_WIN32EXTRA_H*/ diff --git a/ciderpress/diskimg/diskimg.vcxproj b/ciderpress/diskimg/diskimg.vcxproj deleted file mode 100644 index 05780c5..0000000 --- a/ciderpress/diskimg/diskimg.vcxproj +++ /dev/null @@ -1,217 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4} - 8.1 - - - - DynamicLibrary - v120_xp - false - Unicode - - - DynamicLibrary - v120_xp - false - Unicode - - - - - - - - - - - - - <_ProjectFileVersion>12.0.30501.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - diskimg5 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - diskimg5 - - - - MaxSpeed - OnlyExplicitInline - WIN32;NDEBUGX;_WINDOWS;_USRDLL;DISKIMG_EXPORTS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - MultiThreadedDLL - true - Use - stdafx.h - $(IntDir)$(TargetName).pch - $(IntDir) - $(IntDir) - $(IntDir)vc$(PlatformToolsetVersion).pdb - Level3 - true - true - - - %(AdditionalDependencies) - $(OutDir)$(TargetName)$(TargetExt) - false - $(OutDir)$(TargetName).pdb - $(OutDir)$(TargetName).lib - MachineX86 - - Windows - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/diskimg.tlb - - - - - - - - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;DISKIMG_EXPORTS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - Use - stdafx.h - $(IntDir)$(TargetName).pch - $(IntDir) - $(IntDir) - $(IntDir)vc$(PlatformToolsetVersion).pdb - Level3 - true - EditAndContinue - true - - - $(OutDir)$(TargetName)$(TargetExt) - false - true - $(OutDir)$(TargetName).pdb - $(OutDir)$(TargetName).lib - MachineX86 - - Windows - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/diskimg.tlb - - - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - - - - - - - - - - - - - - - - - {c48ae53b-3dcb-43b1-9207-b7c5b6bb78af} - - - {b66109f4-217b-43c0-86aa-eb55657e5ac0} - - - {0fa742e9-8c07-43dd-aff8-ce31faf70821} - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Create - Create - - - - - - - - - - \ No newline at end of file diff --git a/ciderpress/diskimg/diskimg.vcxproj.filters b/ciderpress/diskimg/diskimg.vcxproj.filters deleted file mode 100644 index 18d2b7f..0000000 --- a/ciderpress/diskimg/diskimg.vcxproj.filters +++ /dev/null @@ -1,156 +0,0 @@ - - - - - {b6ac9831-b535-4474-a308-d3bf6fdf4488} - cpp;c;cxx;rc;def;r;odl;idl;hpj;bat - - - {def4def1-c9b0-48b3-a80b-c137f7ebf9bd} - h;hpp;hxx;hm;inl - - - {ed4624b4-6280-4672-8e7f-e8aac6e178ef} - ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/ciderpress/diskimg/diskimg.vcxproj.user b/ciderpress/diskimg/diskimg.vcxproj.user deleted file mode 100644 index ef5ff2a..0000000 --- a/ciderpress/diskimg/diskimg.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ciderpress/diskimg/libhfs/CMakeLists.txt b/ciderpress/diskimg/libhfs/CMakeLists.txt deleted file mode 100644 index 185b3e3..0000000 --- a/ciderpress/diskimg/libhfs/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -set(CMAKE_BUILD_TYPE DEBUG) - -set(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -set(PROJECT_NAME hfs) -set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) -project(${PROJECT_NAME}) - - -set(ALL_DEFINES "-DHAVE_CONFIG_H -Wwrite-strings -Wno-pointer-sign -Wpointer-arith -Wshadow -Wstrict-prototypes -D_FILE_OFFSET_BITS=64" ) -set(DEBUG_OPT "-O0 -g3 " ) -set(RELEASE_OPT "-O3 " ) - -set(CMAKE_C_FLAGS "-Wall ${ALL_DEFINES} ") -set(CMAKE_CXX_FLAGS "-Wall ${ALL_DEFINES} ") - -set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_OPT} ") -set(CMAKE_CXX_FLAGS_RELEASE "${RELEASE_OPT}") -set(CMAKE_C_FLAGS_DEBUG "${DEBUG_OPT} ") -set(CMAKE_C_FLAGS_RELEASE "${RELEASE_OPT}") - -set(FIND_LIBRARY_USE_LIB64_PATHS TRUE) - -set (SOURCE -block.c -btree.c -data.c -file.c -hfs.c -low.c -medium.c -memcmp.c -node.c -os.c -record.c -version.c -volume.c -) - -include_directories(BEFORE - ${PROJECT_ROOT} -) - -add_library( ${PROJECT_NAME} SHARED ${SOURCE}) -add_library( ${PROJECT_NAME}_static STATIC ${SOURCE}) - -target_link_libraries ( -${PROJECT_NAME} -) - - - diff --git a/ciderpress/diskimg/libhfs/COPYRIGHT b/ciderpress/diskimg/libhfs/COPYRIGHT deleted file mode 100644 index 0960cef..0000000 --- a/ciderpress/diskimg/libhfs/COPYRIGHT +++ /dev/null @@ -1,21 +0,0 @@ - - hfsutils - tools for reading and writing Macintosh HFS volumes - Copyright (C) 1996-1998 Robert Leslie - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program 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 General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - If you would like to negotiate alternate licensing terms, you may do - so by contacting the author: Robert Leslie - diff --git a/ciderpress/diskimg/libhfs/Makefile b/ciderpress/diskimg/libhfs/Makefile deleted file mode 100644 index d7e6066..0000000 --- a/ciderpress/diskimg/libhfs/Makefile +++ /dev/null @@ -1,47 +0,0 @@ -# -# DiskImg libhfs makefile for Linux. -# -.SILENT: - -SHELL = /bin/sh -CC = gcc -AR = ar -OPT = -g -DHAVE_CONFIG_H -#OPT = -g -O2 -DHAVE_CONFIG_H -GCC_FLAGS = -Wall -Wwrite-strings -Wpointer-arith -Wshadow -Wstrict-prototypes -CFLAGS = $(OPT) $(GCC_FLAGS) -D_FILE_OFFSET_BITS=64 - -SRCS = os.c data.c block.c low.c medium.c file.c btree.c node.c \ - record.c volume.c hfs.c version.c -OBJS = os.o data.o block.o low.o medium.o file.o btree.o node.o \ - record.o volume.o hfs.o version.o - -STATIC_PRODUCT = libhfs.a -PRODUCT = $(STATIC_PRODUCT) - -all: - -mkdir -p ./build - cd ./build && cmake .. - cd ./build && $(MAKE) - -all_orig: $(PRODUCT) - @true - -$(STATIC_PRODUCT): $(OBJS) - -rm -f $(STATIC_PRODUCT) - $(AR) rcv $@ $(OBJS) - -clean: - -rm -f *.o core - -rm -f $(STATIC_PRODUCT) - -rm -f Makefile.bak - -rm -rf ./build - -tags:: - @ctags -R --totals * - -depend: - makedepend -- $(CFLAGS) -- $(SRCS) - -# DO NOT DELETE THIS LINE -- make depend depends on it. - diff --git a/ciderpress/diskimg/libhfs/README b/ciderpress/diskimg/libhfs/README deleted file mode 100644 index 5f7dec5..0000000 --- a/ciderpress/diskimg/libhfs/README +++ /dev/null @@ -1,5 +0,0 @@ -HFS utility library, part of hfsutils v3.2.6 by Robert Leslie. - -Adapted for use with CiderPress by Andy McFadden. The os_* functions -have been replaced with a callback mechanism, and code that uses global -variables has been (mostly) removed. diff --git a/ciderpress/diskimg/libhfs/apple.h b/ciderpress/diskimg/libhfs/apple.h deleted file mode 100644 index d860874..0000000 --- a/ciderpress/diskimg/libhfs/apple.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -typedef signed char Char; -typedef unsigned char UChar; -typedef signed char SignedByte; -typedef signed short Integer; -typedef unsigned short UInteger; -typedef signed long LongInt; -typedef unsigned long ULongInt; -typedef char Str15[16]; -typedef char Str31[32]; -typedef long OSType; - -typedef struct { - Integer sbSig; /* device signature (should be 0x4552) */ - Integer sbBlkSize; /* block size of the device (in bytes) */ - LongInt sbBlkCount; /* number of blocks on the device */ - Integer sbDevType; /* reserved */ - Integer sbDevId; /* reserved */ - LongInt sbData; /* reserved */ - Integer sbDrvrCount; /* number of driver descriptor entries */ - LongInt ddBlock; /* first driver's starting block */ - Integer ddSize; /* size of the driver, in 512-byte blocks */ - Integer ddType; /* driver operating system type (MacOS = 1) */ - Integer ddPad[243]; /* additional drivers, if any */ -} Block0; - -typedef struct { - Integer pmSig; /* partition signature (0x504d or 0x5453) */ - Integer pmSigPad; /* reserved */ - LongInt pmMapBlkCnt; /* number of blocks in partition map */ - LongInt pmPyPartStart; /* first physical block of partition */ - LongInt pmPartBlkCnt; /* number of blocks in partition */ - Char pmPartName[33]; /* partition name */ - Char pmParType[33]; /* partition type */ - LongInt pmLgDataStart; /* first logical block of data area */ - LongInt pmDataCnt; /* number of blocks in data area */ - LongInt pmPartStatus; /* partition status information */ - LongInt pmLgBootStart; /* first logical block of boot code */ - LongInt pmBootSize; /* size of boot code, in bytes */ - LongInt pmBootAddr; /* boot code load address */ - LongInt pmBootAddr2; /* reserved */ - LongInt pmBootEntry; /* boot code entry point */ - LongInt pmBootEntry2; /* reserved */ - LongInt pmBootCksum; /* boot code checksum */ - Char pmProcessor[17];/* processor type */ - Integer pmPad[188]; /* reserved */ -} Partition; - -typedef struct { - Integer bbID; /* boot blocks signature */ - LongInt bbEntry; /* entry point to boot code */ - Integer bbVersion; /* boot blocks version number */ - Integer bbPageFlags; /* used internally */ - Str15 bbSysName; /* System filename */ - Str15 bbShellName; /* Finder filename */ - Str15 bbDbg1Name; /* debugger filename */ - Str15 bbDbg2Name; /* debugger filename */ - Str15 bbScreenName; /* name of startup screen */ - Str15 bbHelloName; /* name of startup program */ - Str15 bbScrapName; /* name of system scrap file */ - Integer bbCntFCBs; /* number of FCBs to allocate */ - Integer bbCntEvts; /* number of event queue elements */ - LongInt bb128KSHeap; /* system heap size on 128K Mac */ - LongInt bb256KSHeap; /* used internally */ - LongInt bbSysHeapSize; /* system heap size on all machines */ - Integer filler; /* reserved */ - LongInt bbSysHeapExtra; /* additional system heap space */ - LongInt bbSysHeapFract; /* fraction of RAM for system heap */ -} BootBlkHdr; - -typedef struct { - UInteger xdrStABN; /* first allocation block */ - UInteger xdrNumABlks; /* number of allocation blocks */ -} ExtDescriptor; - -typedef ExtDescriptor ExtDataRec[3]; - -typedef struct { - SignedByte xkrKeyLen; /* key length */ - SignedByte xkrFkType; /* fork type (0x00/0xff == data/resource */ - ULongInt xkrFNum; /* file number */ - UInteger xkrFABN; /* starting file allocation block */ -} ExtKeyRec; - -typedef struct { - SignedByte ckrKeyLen; /* key length */ - SignedByte ckrResrv1; /* reserved */ - ULongInt ckrParID; /* parent directory ID */ - Str31 ckrCName; /* catalog node name */ -} CatKeyRec; - -typedef struct { - Integer v; /* vertical coordinate */ - Integer h; /* horizontal coordinate */ -} Point; - -typedef struct { - Integer top; /* top edge of rectangle */ - Integer left; /* left edge */ - Integer bottom; /* bottom edge */ - Integer right; /* right edge */ -} Rect; - -typedef struct { - Rect frRect; /* folder's rectangle */ - Integer frFlags; /* flags */ - Point frLocation; /* folder's location */ - Integer frView; /* folder's view */ -} DInfo; - -typedef struct { - Point frScroll; /* scroll position */ - LongInt frOpenChain; /* directory ID chain of open folders */ - Integer frUnused; /* reserved */ - Integer frComment; /* comment ID */ - LongInt frPutAway; /* directory ID */ -} DXInfo; - -typedef struct { - OSType fdType; /* file type */ - OSType fdCreator; /* file's creator */ - Integer fdFlags; /* flags */ - Point fdLocation; /* file's location */ - Integer fdFldr; /* file's window */ -} FInfo; - -typedef struct { - Integer fdIconID; /* icon ID */ - Integer fdUnused[4]; /* reserved */ - Integer fdComment; /* comment ID */ - LongInt fdPutAway; /* home directory ID */ -} FXInfo; - -typedef struct { - Integer drSigWord; /* volume signature (0x4244 for HFS) */ - LongInt drCrDate; /* date and time of volume creation */ - LongInt drLsMod; /* date and time of last modification */ - Integer drAtrb; /* volume attributes */ - UInteger drNmFls; /* number of files in root directory */ - UInteger drVBMSt; /* first block of volume bit map (always 3) */ - UInteger drAllocPtr; /* start of next allocation search */ - UInteger drNmAlBlks; /* number of allocation blocks in volume */ - ULongInt drAlBlkSiz; /* size (in bytes) of allocation blocks */ - ULongInt drClpSiz; /* default clump size */ - UInteger drAlBlSt; /* first allocation block in volume */ - LongInt drNxtCNID; /* next unused catalog node ID (dir/file ID) */ - UInteger drFreeBks; /* number of unused allocation blocks */ - char drVN[28]; /* volume name (1-27 chars) */ - LongInt drVolBkUp; /* date and time of last backup */ - Integer drVSeqNum; /* volume backup sequence number */ - ULongInt drWrCnt; /* volume write count */ - ULongInt drXTClpSiz; /* clump size for extents overflow file */ - ULongInt drCTClpSiz; /* clump size for catalog file */ - UInteger drNmRtDirs; /* number of directories in root directory */ - ULongInt drFilCnt; /* number of files in volume */ - ULongInt drDirCnt; /* number of directories in volume */ - LongInt drFndrInfo[8]; /* information used by the Finder */ - UInteger drEmbedSigWord; /* type of embedded volume */ - ExtDescriptor drEmbedExtent; /* location of embedded volume */ - ULongInt drXTFlSize; /* size (in bytes) of extents overflow file */ - ExtDataRec drXTExtRec; /* first extent record for extents file */ - ULongInt drCTFlSize; /* size (in bytes) of catalog file */ - ExtDataRec drCTExtRec; /* first extent record for catalog file */ -} MDB; - -typedef enum { - cdrDirRec = 1, - cdrFilRec = 2, - cdrThdRec = 3, - cdrFThdRec = 4 -} CatDataType; - -typedef struct { - SignedByte cdrType; /* record type */ - SignedByte cdrResrv2; /* reserved */ - union { - struct { /* cdrDirRec */ - Integer dirFlags; /* directory flags */ - UInteger dirVal; /* directory valence */ - ULongInt dirDirID; /* directory ID */ - LongInt dirCrDat; /* date and time of creation */ - LongInt dirMdDat; /* date and time of last modification */ - LongInt dirBkDat; /* date and time of last backup */ - DInfo dirUsrInfo; /* Finder information */ - DXInfo dirFndrInfo; /* additional Finder information */ - LongInt dirResrv[4]; /* reserved */ - } dir; - struct { /* cdrFilRec */ - SignedByte - filFlags; /* file flags */ - SignedByte - filTyp; /* file type */ - FInfo filUsrWds; /* Finder information */ - ULongInt filFlNum; /* file ID */ - UInteger filStBlk; /* first alloc block of data fork */ - ULongInt filLgLen; /* logical EOF of data fork */ - ULongInt filPyLen; /* physical EOF of data fork */ - UInteger filRStBlk; /* first alloc block of resource fork */ - ULongInt filRLgLen; /* logical EOF of resource fork */ - ULongInt filRPyLen; /* physical EOF of resource fork */ - LongInt filCrDat; /* date and time of creation */ - LongInt filMdDat; /* date and time of last modification */ - LongInt filBkDat; /* date and time of last backup */ - FXInfo filFndrInfo; /* additional Finder information */ - UInteger filClpSize; /* file clump size */ - ExtDataRec - filExtRec; /* first data fork extent record */ - ExtDataRec - filRExtRec; /* first resource fork extent record */ - LongInt filResrv; /* reserved */ - } fil; - struct { /* cdrThdRec */ - LongInt thdResrv[2]; /* reserved */ - ULongInt thdParID; /* parent ID for this directory */ - Str31 thdCName; /* name of this directory */ - } dthd; - struct { /* cdrFThdRec */ - LongInt fthdResrv[2]; /* reserved */ - ULongInt fthdParID; /* parent ID for this file */ - Str31 fthdCName; /* name of this file */ - } fthd; - } u; -} CatDataRec; - -typedef struct { - ULongInt ndFLink; /* forward link */ - ULongInt ndBLink; /* backward link */ - SignedByte ndType; /* node type */ - SignedByte ndNHeight; /* node level */ - UInteger ndNRecs; /* number of records in node */ - Integer ndResv2; /* reserved */ -} NodeDescriptor; - -enum { - ndIndxNode = (SignedByte) 0x00, - ndHdrNode = (SignedByte) 0x01, - ndMapNode = (SignedByte) 0x02, - ndLeafNode = (SignedByte) 0xff -}; - -typedef struct { - UInteger bthDepth; /* current depth of tree */ - ULongInt bthRoot; /* number of root node */ - ULongInt bthNRecs; /* number of leaf records in tree */ - ULongInt bthFNode; /* number of first leaf node */ - ULongInt bthLNode; /* number of last leaf node */ - UInteger bthNodeSize; /* size of a node */ - UInteger bthKeyLen; /* maximum length of a key */ - ULongInt bthNNodes; /* total number of nodes in tree */ - ULongInt bthFree; /* number of free nodes */ - SignedByte bthResv[76]; /* reserved */ -} BTHdrRec; diff --git a/ciderpress/diskimg/libhfs/block.c b/ciderpress/diskimg/libhfs/block.c deleted file mode 100644 index de6317c..0000000 --- a/ciderpress/diskimg/libhfs/block.c +++ /dev/null @@ -1,807 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include - -# include "libhfs.h" -# include "volume.h" -# include "block.h" -# include "os.h" - -# define INUSE(b) ((b)->flags & HFS_BUCKET_INUSE) -# define DIRTY(b) ((b)->flags & HFS_BUCKET_DIRTY) - -/* - * NAME: block->init() - * DESCRIPTION: initialize a volume's block cache - */ -int b_init(hfsvol *vol) -{ - bcache *cache; - int i; - - ASSERT(vol->cache == 0); - - cache = ALLOC(bcache, 1); - if (cache == 0) - ERROR(ENOMEM, 0); - - vol->cache = cache; - - cache->vol = vol; - cache->tail = &cache->chain[HFS_CACHESZ - 1]; - - cache->hits = 0; - cache->misses = 0; - - for (i = 0; i < HFS_CACHESZ; ++i) - { - bucket *b = &cache->chain[i]; - - b->flags = 0; - b->count = 0; - - b->bnum = 0; - b->data = &cache->pool[i]; - - b->cnext = b + 1; - b->cprev = b - 1; - - b->hnext = 0; - b->hprev = 0; - } - - cache->chain[0].cprev = cache->tail; - cache->tail->cnext = &cache->chain[0]; - - for (i = 0; i < HFS_HASHSZ; ++i) - cache->hash[i] = 0; - - return 0; - -fail: - return -1; -} - -# ifdef DEBUG -/* - * NAME: block->showstats() - * DESCRIPTION: output cache hit/miss ratio - */ -void b_showstats(const bcache *cache) -{ - fprintf(stderr, "BLOCK: CACHE vol 0x%lx \"%s\" hit/miss ratio = %.3f\n", - (unsigned long) cache->vol, cache->vol->mdb.drVN, - (float) cache->hits / (float) cache->misses); -} - -/* - * NAME: block->dumpcache() - * DESCRIPTION: dump the cache tables for a volume - */ -void b_dumpcache(const bcache *cache) -{ - const bucket *b; - int i; - - fprintf(stderr, "BLOCK CACHE DUMP:\n"); - - for (i = 0, b = cache->tail->cnext; i < HFS_CACHESZ; ++i, b = b->cnext) - { - if (INUSE(b)) - { - fprintf(stderr, "\t %lu", b->bnum); - if (DIRTY(b)) - fprintf(stderr, "*"); - - fprintf(stderr, ":%u", b->count); - } - } - - fprintf(stderr, "\n"); - - fprintf(stderr, "BLOCK HASH DUMP:\n"); - - for (i = 0; i < HFS_HASHSZ; ++i) - { - int seen = 0; - - for (b = cache->hash[i]; b; b = b->hnext) - { - if (! seen) - fprintf(stderr, " %d:", i); - - if (INUSE(b)) - { - fprintf(stderr, " %lu", b->bnum); - if (DIRTY(b)) - fprintf(stderr, "*"); - - fprintf(stderr, ":%u", b->count); - } - - seen = 1; - } - - if (seen) - fprintf(stderr, "\n"); - } -} -# endif - -/* - * NAME: fillchain() - * DESCRIPTION: fill a chain of bucket buffers with a single read - */ -static -int fillchain(hfsvol *vol, bucket **bptr, unsigned int *count) -{ - bucket *blist[HFS_BLOCKBUFSZ], **start = bptr; - unsigned long bnum; - unsigned int len, i; - - for (len = 0; len < HFS_BLOCKBUFSZ && - (unsigned int) (bptr - start) < *count; ++bptr) - { - if (INUSE(*bptr)) - continue; - - if (len > 0 && (*bptr)->bnum != bnum) - break; - - blist[len++] = *bptr; - bnum = (*bptr)->bnum + 1; - } - - *count = bptr - start; - - if (len == 0) - goto done; - else if (len == 1) - { - if (b_readpb(vol, vol->vstart + blist[0]->bnum, - blist[0]->data, 1) == -1) - goto fail; - } - else - { - block buffer[HFS_BLOCKBUFSZ]; - - if (b_readpb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1) - goto fail; - - for (i = 0; i < len; ++i) - memcpy(blist[i]->data, buffer[i], HFS_BLOCKSZ); - } - - for (i = 0; i < len; ++i) - { - blist[i]->flags |= HFS_BUCKET_INUSE; - blist[i]->flags &= ~HFS_BUCKET_DIRTY; - } - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: flushchain() - * DESCRIPTION: store a chain of bucket buffers with a single write - */ -static -int flushchain(hfsvol *vol, bucket **bptr, unsigned int *count) -{ - bucket *blist[HFS_BLOCKBUFSZ], **start = bptr; - unsigned long bnum; - unsigned int len, i; - - for (len = 0; len < HFS_BLOCKBUFSZ && - (unsigned int) (bptr - start) < *count; ++bptr) - { - if (! INUSE(*bptr) || ! DIRTY(*bptr)) - continue; - - if (len > 0 && (*bptr)->bnum != bnum) - break; - - blist[len++] = *bptr; - bnum = (*bptr)->bnum + 1; - } - - *count = bptr - start; - - if (len == 0) - goto done; - else if (len == 1) - { - if (b_writepb(vol, vol->vstart + blist[0]->bnum, - blist[0]->data, 1) == -1) - goto fail; - } - else - { - block buffer[HFS_BLOCKBUFSZ]; - - for (i = 0; i < len; ++i) - memcpy(buffer[i], blist[i]->data, HFS_BLOCKSZ); - - if (b_writepb(vol, vol->vstart + blist[0]->bnum, buffer, len) == -1) - goto fail; - } - - for (i = 0; i < len; ++i) - blist[i]->flags &= ~HFS_BUCKET_DIRTY; - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: compare() - * DESCRIPTION: comparison function for qsort of cache bucket pointers - */ -static -int compare(const bucket **b1, const bucket **b2) -{ - long diff; - - diff = (*b1)->bnum - (*b2)->bnum; - - if (diff < 0) - return -1; - else if (diff > 0) - return 1; - else - return 0; -} - -/* - * NAME: dobuckets() - * DESCRIPTION: fill or flush an array of cache buckets to a volume - */ -static -int dobuckets(hfsvol *vol, bucket **chain, unsigned int len, - int (*func)(hfsvol *, bucket **, unsigned int *)) -{ - unsigned int count, i; - int result = 0; - - qsort(chain, len, sizeof(*chain), - (int (*)(const void *, const void *)) compare); - - for (i = 0; i < len; i += count) - { - count = len - i; - if (func(vol, chain + i, &count) == -1) - result = -1; - } - - return result; -} - -# define fillbuckets(vol, chain, len) dobuckets(vol, chain, len, fillchain) -# define flushbuckets(vol, chain, len) dobuckets(vol, chain, len, flushchain) - -/* - * NAME: block->flush() - * DESCRIPTION: commit dirty cache blocks to a volume - */ -int b_flush(hfsvol *vol) -{ - bcache *cache = vol->cache; - bucket *chain[HFS_CACHESZ]; - int i; - - if (cache == 0 || (vol->flags & HFS_VOL_READONLY)) - goto done; - - for (i = 0; i < HFS_CACHESZ; ++i) - chain[i] = &cache->chain[i]; - - if (flushbuckets(vol, chain, HFS_CACHESZ) == -1) - goto fail; - -done: -# ifdef DEBUG - if (cache) - b_showstats(cache); -# endif - - return 0; - -fail: - return -1; -} - -/* - * NAME: block->finish() - * DESCRIPTION: commit and free a volume's block cache - */ -int b_finish(hfsvol *vol) -{ - int result = 0; - - if (vol->cache == 0) - goto done; - -# ifdef DEBUG - b_dumpcache(vol->cache); -# endif - - result = b_flush(vol); - - FREE(vol->cache); - vol->cache = 0; - -done: - return result; -} - -/* - * NAME: findbucket() - * DESCRIPTION: locate a bucket in the cache, and/or its hash slot - */ -static -bucket *findbucket(bcache *cache, unsigned long bnum, bucket ***hslot) -{ - bucket *b; - - *hslot = &cache->hash[bnum & (HFS_HASHSZ - 1)]; - - for (b = **hslot; b; b = b->hnext) - { - if (INUSE(b) && b->bnum == bnum) - break; - } - - return b; -} - -/* - * NAME: reuse() - * DESCRIPTION: free a bucket for reuse, flushing if necessary - */ -static -int reuse(bcache *cache, bucket *b, unsigned long bnum) -{ - bucket *chain[HFS_BLOCKBUFSZ], *bptr; - int i; - -# ifdef DEBUG - if (INUSE(b)) - fprintf(stderr, "BLOCK: CACHE reusing bucket containing " - "vol 0x%lx block %lu:%u\n", - (unsigned long) cache->vol, b->bnum, b->count); -# endif - - if (INUSE(b) && DIRTY(b)) - { - /* flush most recently unused buckets */ - - for (bptr = b, i = 0; i < HFS_BLOCKBUFSZ; ++i) - { - chain[i] = bptr; - bptr = bptr->cprev; - } - - if (flushbuckets(cache->vol, chain, HFS_BLOCKBUFSZ) == -1) - goto fail; - } - - b->flags &= ~HFS_BUCKET_INUSE; - b->count = 1; - b->bnum = bnum; - - return 0; - -fail: - return -1; -} - -/* - * NAME: cplace() - * DESCRIPTION: move a bucket to an appropriate place near head of the chain - */ -static -void cplace(bcache *cache, bucket *b) -{ - bucket *p; - - for (p = cache->tail->cnext; p->count > 1; p = p->cnext) - --p->count; - - b->cnext->cprev = b->cprev; - b->cprev->cnext = b->cnext; - - if (cache->tail == b) - cache->tail = b->cprev; - - b->cprev = p->cprev; - b->cnext = p; - - p->cprev->cnext = b; - p->cprev = b; -} - -/* - * NAME: hplace() - * DESCRIPTION: move a bucket to the head of its hash slot - */ -static -void hplace(bucket **hslot, bucket *b) -{ - if (*hslot != b) - { - if (b->hprev) - *b->hprev = b->hnext; - if (b->hnext) - b->hnext->hprev = b->hprev; - - b->hprev = hslot; - b->hnext = *hslot; - - if (*hslot) - (*hslot)->hprev = &b->hnext; - - *hslot = b; - } -} - -/* - * NAME: getbucket() - * DESCRIPTION: fetch a bucket from the cache, or an empty one to be filled - */ -static -bucket *getbucket(bcache *cache, unsigned long bnum, int fill) -{ - bucket **hslot, *b, *p, *bptr, - *chain[HFS_BLOCKBUFSZ], **slots[HFS_BLOCKBUFSZ]; - - b = findbucket(cache, bnum, &hslot); - - if (b) - { - /* cache hit; move towards head of cache chain */ - - ++cache->hits; - - if (++b->count > b->cprev->count && - b != cache->tail->cnext) - { - p = b->cprev; - - p->cprev->cnext = b; - b->cnext->cprev = p; - - p->cnext = b->cnext; - b->cprev = p->cprev; - - p->cprev = b; - b->cnext = p; - - if (cache->tail == b) - cache->tail = p; - } - } - else - { - /* cache miss; reuse least-used cache bucket */ - - ++cache->misses; - - b = cache->tail; - - if (reuse(cache, b, bnum) == -1) - goto fail; - - if (fill) - { - unsigned int len = 0; - - chain[len] = b; - slots[len++] = hslot; - - for (bptr = b->cprev; - len < (HFS_BLOCKBUFSZ >> 1) && ++bnum < cache->vol->vlen; - bptr = bptr->cprev) - { - if (findbucket(cache, bnum, &hslot)) - break; - - if (reuse(cache, bptr, bnum) == -1) - goto fail; - - chain[len] = bptr; - slots[len++] = hslot; - } - - if (fillbuckets(cache->vol, chain, len) == -1) - goto fail; - - while (--len) - { - cplace(cache, chain[len]); - hplace(slots[len], chain[len]); - } - - hslot = slots[0]; - } - - /* move bucket to appropriate place in chain */ - - cplace(cache, b); - } - - /* insert at front of hash chain */ - - hplace(hslot, b); - - return b; - -fail: - return 0; -} - -/* - * NAME: block->readpb() - * DESCRIPTION: read blocks from the physical medium (bypassing cache) - */ -int b_readpb(hfsvol *vol, unsigned long bnum, block *bp, unsigned int blen) -{ - unsigned long nblocks; - -# ifdef DEBUG - fprintf(stderr, "BLOCK: READ vol 0x%lx block %lu", - (unsigned long) vol, bnum); - if (blen > 1) - fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1); - else - fprintf(stderr, "\n"); -# endif - - nblocks = os_seek(&vol->priv, bnum); - if (nblocks == (unsigned long) -1) - goto fail; - - if (nblocks != bnum) - ERROR(EIO, "block seek failed for read"); - - nblocks = os_read(&vol->priv, bp, blen); - if (nblocks == (unsigned long) -1) - goto fail; - - if (nblocks != blen) - ERROR(EIO, "incomplete block read"); - - return 0; - -fail: - return -1; -} - -/* - * NAME: block->writepb() - * DESCRIPTION: write blocks to the physical medium (bypassing cache) - */ -int b_writepb(hfsvol *vol, unsigned long bnum, const block *bp, - unsigned int blen) -{ - unsigned long nblocks; - -# ifdef DEBUG - fprintf(stderr, "BLOCK: WRITE vol 0x%lx block %lu", - (unsigned long) vol, bnum); - if (blen > 1) - fprintf(stderr, "+%u[..%lu]\n", blen - 1, bnum + blen - 1); - else - fprintf(stderr, "\n"); -# endif - - nblocks = os_seek(&vol->priv, bnum); - if (nblocks == (unsigned long) -1) - goto fail; - - if (nblocks != bnum) - ERROR(EIO, "block seek failed for write"); - - nblocks = os_write(&vol->priv, bp, blen); - if (nblocks == (unsigned long) -1) - goto fail; - - if (nblocks != blen) - ERROR(EIO, "incomplete block write"); - - return 0; - -fail: - return -1; -} - -/* - * NAME: block->readlb() - * DESCRIPTION: read a logical block from a volume (or from the cache) - */ -int b_readlb(hfsvol *vol, unsigned long bnum, block *bp) -{ - if (vol->vlen > 0 && bnum >= vol->vlen) - ERROR(EIO, "read nonexistent logical block"); - - if (vol->cache) - { - bucket *b; - - b = getbucket(vol->cache, bnum, 1); - if (b == 0) - goto fail; - - memcpy(bp, b->data, HFS_BLOCKSZ); - } - else - { - if (b_readpb(vol, vol->vstart + bnum, bp, 1) == -1) - goto fail; - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: block->writelb() - * DESCRIPTION: write a logical block to a volume (or to the cache) - */ -int b_writelb(hfsvol *vol, unsigned long bnum, const block *bp) -{ - if (vol->vlen > 0 && bnum >= vol->vlen) - ERROR(EIO, "write nonexistent logical block"); - - if (vol->cache) - { - bucket *b; - - b = getbucket(vol->cache, bnum, 0); - if (b == 0) - goto fail; - - if (! INUSE(b) || - memcmp(b->data, bp, HFS_BLOCKSZ) != 0) - { - memcpy(b->data, bp, HFS_BLOCKSZ); - b->flags |= HFS_BUCKET_INUSE | HFS_BUCKET_DIRTY; - } - } - else - { - if (b_writepb(vol, vol->vstart + bnum, bp, 1) == -1) - goto fail; - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: block->readab() - * DESCRIPTION: read a block from an allocation block from a volume - */ -int b_readab(hfsvol *vol, unsigned int anum, unsigned int idx, block *bp) -{ - /* verify the allocation block exists and is marked as in-use */ - - if (anum >= vol->mdb.drNmAlBlks) - ERROR(EIO, "read nonexistent allocation block"); - else if (vol->vbm && ! BMTST(vol->vbm, anum)) - ERROR(EIO, "read unallocated block"); - - return b_readlb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + idx, bp); - -fail: - return -1; -} - -/* - * NAME: block->writeab() - * DESCRIPTION: write a block to an allocation block to a volume - */ -int b_writeab(hfsvol *vol, - unsigned int anum, unsigned int idx, const block *bp) -{ - /* verify the allocation block exists and is marked as in-use */ - - if (anum >= vol->mdb.drNmAlBlks) - ERROR(EIO, "write nonexistent allocation block"); - else if (vol->vbm && ! BMTST(vol->vbm, anum)) - ERROR(EIO, "write unallocated block"); - - if (v_dirty(vol) == -1) - goto fail; - - return b_writelb(vol, vol->mdb.drAlBlSt + anum * vol->lpa + idx, bp); - -fail: - return -1; -} - -/* - * NAME: block->size() - * DESCRIPTION: return the number of physical blocks on a volume's medium - */ -unsigned long b_size(hfsvol *vol) -{ - unsigned long low, high, mid; - block b; - - high = os_seek(&vol->priv, -1); - - if (high != (unsigned long) -1 && high > 0) - return high; - - /* manual size detection: first check there is at least 1 block in medium */ - - if (b_readpb(vol, 0, &b, 1) == -1) - ERROR(EIO, "size of medium indeterminable or empty"); - - for (low = 0, high = 2880; - high > 0 && b_readpb(vol, high - 1, &b, 1) != -1; - high <<= 1) - low = high - 1; - - if (high == 0) - ERROR(EIO, "size of medium indeterminable or too large"); - - /* common case: 1440K floppy */ - - if (low == 2879 && b_readpb(vol, 2880, &b, 1) == -1) - return 2880; - - /* binary search for other sizes */ - - while (low < high - 1) - { - mid = (low + high) >> 1; - - if (b_readpb(vol, mid, &b, 1) == -1) - high = mid; - else - low = mid; - } - - return low + 1; - -fail: - return 0; -} diff --git a/ciderpress/diskimg/libhfs/block.h b/ciderpress/diskimg/libhfs/block.h deleted file mode 100644 index a18f007..0000000 --- a/ciderpress/diskimg/libhfs/block.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -int b_init(hfsvol *); -int b_flush(hfsvol *); -int b_finish(hfsvol *); - -int b_readpb(hfsvol *, unsigned long, block *, unsigned int); -int b_writepb(hfsvol *, unsigned long, const block *, unsigned int); - -int b_readlb(hfsvol *, unsigned long, block *); -int b_writelb(hfsvol *, unsigned long, const block *); - -int b_readab(hfsvol *, unsigned int, unsigned int, block *); -int b_writeab(hfsvol *, unsigned int, unsigned int, const block *); - -unsigned long b_size(hfsvol *); - -# ifdef DEBUG -void b_showstats(const bcache *); -void b_dumpcache(const bcache *); -# endif diff --git a/ciderpress/diskimg/libhfs/btree.c b/ciderpress/diskimg/libhfs/btree.c deleted file mode 100644 index 9d4331f..0000000 --- a/ciderpress/diskimg/libhfs/btree.c +++ /dev/null @@ -1,700 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include - -# include "libhfs.h" -# include "btree.h" -# include "data.h" -# include "file.h" -# include "block.h" -# include "node.h" - -/* - * NAME: btree->getnode() - * DESCRIPTION: retrieve a numbered node from a B*-tree file - */ -int bt_getnode(node *np, btree *bt, unsigned long nnum) -{ - block *bp = &np->data; - const byte *ptr; - int i; - - np->bt = bt; - np->nnum = nnum; - -# if 0 - fprintf(stderr, "BTREE: GET vol \"%s\" btree \"%s\" node %lu\n", - bt->f.vol->mdb.drVN, bt->f.name, np->nnum); -# endif - - /* verify the node exists and is marked as in-use */ - - if (nnum > 0 && nnum >= bt->hdr.bthNNodes) - ERROR(EIO, "read nonexistent b*-tree node"); - else if (bt->map && ! BMTST(bt->map, nnum)) - ERROR(EIO, "read unallocated b*-tree node"); - - if (f_getblock(&bt->f, nnum, bp) == -1) - goto fail; - - ptr = *bp; - - d_fetchul(&ptr, &np->nd.ndFLink); - d_fetchul(&ptr, &np->nd.ndBLink); - d_fetchsb(&ptr, &np->nd.ndType); - d_fetchsb(&ptr, &np->nd.ndNHeight); - d_fetchuw(&ptr, &np->nd.ndNRecs); - d_fetchsw(&ptr, &np->nd.ndResv2); - - if (np->nd.ndNRecs > HFS_MAX_NRECS) - ERROR(EIO, "too many b*-tree node records"); - - i = np->nd.ndNRecs + 1; - - ptr = *bp + HFS_BLOCKSZ - (2 * i); - - while (i--) - d_fetchuw(&ptr, &np->roff[i]); - - return 0; - -fail: - return -1; -} - -/* - * NAME: btree->putnode() - * DESCRIPTION: store a numbered node into a B*-tree file - */ -int bt_putnode(node *np) -{ - btree *bt = np->bt; - block *bp = &np->data; - byte *ptr; - int i; - -# if 0 - fprintf(stderr, "BTREE: PUT vol \"%s\" btree \"%s\" node %lu\n", - bt->f.vol->mdb.drVN, bt->f.name, np->nnum); -# endif - - /* verify the node exists and is marked as in-use */ - - if (np->nnum > 0 && np->nnum >= bt->hdr.bthNNodes) - ERROR(EIO, "write nonexistent b*-tree node"); - else if (bt->map && ! BMTST(bt->map, np->nnum)) - ERROR(EIO, "write unallocated b*-tree node"); - - ptr = *bp; - - d_storeul(&ptr, np->nd.ndFLink); - d_storeul(&ptr, np->nd.ndBLink); - d_storesb(&ptr, np->nd.ndType); - d_storesb(&ptr, np->nd.ndNHeight); - d_storeuw(&ptr, np->nd.ndNRecs); - d_storesw(&ptr, np->nd.ndResv2); - - if (np->nd.ndNRecs > HFS_MAX_NRECS) - ERROR(EIO, "too many b*-tree node records"); - - i = np->nd.ndNRecs + 1; - - ptr = *bp + HFS_BLOCKSZ - (2 * i); - - while (i--) - d_storeuw(&ptr, np->roff[i]); - - return f_putblock(&bt->f, np->nnum, bp); - -fail: - return -1; -} - -/* - * NAME: btree->readhdr() - * DESCRIPTION: read the header node of a B*-tree - */ -int bt_readhdr(btree *bt) -{ - const byte *ptr; - byte *map = 0; - int i; - unsigned long nnum; - - if (bt_getnode(&bt->hdrnd, bt, 0) == -1) - goto fail; - - if (bt->hdrnd.nd.ndType != ndHdrNode || - bt->hdrnd.nd.ndNRecs != 3 || - bt->hdrnd.roff[0] != 0x00e || - bt->hdrnd.roff[1] != 0x078 || - bt->hdrnd.roff[2] != 0x0f8 || - bt->hdrnd.roff[3] != 0x1f8) - ERROR(EIO, "malformed b*-tree header node"); - - /* read header record */ - - ptr = HFS_NODEREC(bt->hdrnd, 0); - - d_fetchuw(&ptr, &bt->hdr.bthDepth); - d_fetchul(&ptr, &bt->hdr.bthRoot); - d_fetchul(&ptr, &bt->hdr.bthNRecs); - d_fetchul(&ptr, &bt->hdr.bthFNode); - d_fetchul(&ptr, &bt->hdr.bthLNode); - d_fetchuw(&ptr, &bt->hdr.bthNodeSize); - d_fetchuw(&ptr, &bt->hdr.bthKeyLen); - d_fetchul(&ptr, &bt->hdr.bthNNodes); - d_fetchul(&ptr, &bt->hdr.bthFree); - - for (i = 0; i < 76; ++i) - d_fetchsb(&ptr, &bt->hdr.bthResv[i]); - - if (bt->hdr.bthNodeSize != HFS_BLOCKSZ) - ERROR(EINVAL, "unsupported b*-tree node size"); - - /* read map record; construct btree bitmap */ - /* don't set bt->map until we're done, since getnode() checks it */ - - map = ALLOC(byte, HFS_MAP1SZ); - if (map == 0) - ERROR(ENOMEM, 0); - - memcpy(map, HFS_NODEREC(bt->hdrnd, 2), HFS_MAP1SZ); - bt->mapsz = HFS_MAP1SZ; - - /* read continuation map records, if any */ - - nnum = bt->hdrnd.nd.ndFLink; - - while (nnum) - { - node n; - byte *newmap; - - if (bt_getnode(&n, bt, nnum) == -1) - goto fail; - - if (n.nd.ndType != ndMapNode || - n.nd.ndNRecs != 1 || - n.roff[0] != 0x00e || - n.roff[1] != 0x1fa) - ERROR(EIO, "malformed b*-tree map node"); - - newmap = REALLOC(map, byte, bt->mapsz + HFS_MAPXSZ); - if (newmap == 0) - ERROR(ENOMEM, 0); - - map = newmap; - - memcpy(map + bt->mapsz, HFS_NODEREC(n, 0), HFS_MAPXSZ); - bt->mapsz += HFS_MAPXSZ; - - nnum = n.nd.ndFLink; - } - - bt->map = map; - - return 0; - -fail: - FREE(map); - return -1; -} - -/* - * NAME: btree->writehdr() - * DESCRIPTION: write the header node of a B*-tree - */ -int bt_writehdr(btree *bt) -{ - byte *ptr, *map; - unsigned long mapsz, nnum; - int i; - - ASSERT(bt->hdrnd.bt == bt && - bt->hdrnd.nnum == 0 && - bt->hdrnd.nd.ndType == ndHdrNode && - bt->hdrnd.nd.ndNRecs == 3); - - ptr = HFS_NODEREC(bt->hdrnd, 0); - - d_storeuw(&ptr, bt->hdr.bthDepth); - d_storeul(&ptr, bt->hdr.bthRoot); - d_storeul(&ptr, bt->hdr.bthNRecs); - d_storeul(&ptr, bt->hdr.bthFNode); - d_storeul(&ptr, bt->hdr.bthLNode); - d_storeuw(&ptr, bt->hdr.bthNodeSize); - d_storeuw(&ptr, bt->hdr.bthKeyLen); - d_storeul(&ptr, bt->hdr.bthNNodes); - d_storeul(&ptr, bt->hdr.bthFree); - - for (i = 0; i < 76; ++i) - d_storesb(&ptr, bt->hdr.bthResv[i]); - - memcpy(HFS_NODEREC(bt->hdrnd, 2), bt->map, HFS_MAP1SZ); - - if (bt_putnode(&bt->hdrnd) == -1) - goto fail; - - map = bt->map + HFS_MAP1SZ; - mapsz = bt->mapsz - HFS_MAP1SZ; - - nnum = bt->hdrnd.nd.ndFLink; - - while (mapsz) - { - node n; - - if (nnum == 0) - ERROR(EIO, "truncated b*-tree map"); - - if (bt_getnode(&n, bt, nnum) == -1) - goto fail; - - if (n.nd.ndType != ndMapNode || - n.nd.ndNRecs != 1 || - n.roff[0] != 0x00e || - n.roff[1] != 0x1fa) - ERROR(EIO, "malformed b*-tree map node"); - - memcpy(HFS_NODEREC(n, 0), map, HFS_MAPXSZ); - - if (bt_putnode(&n) == -1) - goto fail; - - map += HFS_MAPXSZ; - mapsz -= HFS_MAPXSZ; - - nnum = n.nd.ndFLink; - } - - bt->flags &= ~HFS_BT_UPDATE_HDR; - - return 0; - -fail: - return -1; -} - -/* High-Level B*-Tree Routines ============================================= */ - -/* - * NAME: btree->space() - * DESCRIPTION: assert space for new records, or extend the file - */ -int bt_space(btree *bt, unsigned int nrecs) -{ - unsigned int nnodes; - long space; - - nnodes = nrecs * (bt->hdr.bthDepth + 1); - - if (nnodes <= bt->hdr.bthFree) - goto done; - - /* make sure the extents tree has room too */ - - if (bt != &bt->f.vol->ext) - { - if (bt_space(&bt->f.vol->ext, 1) == -1) - goto fail; - } - - space = f_alloc(&bt->f); - if (space == -1) - goto fail; - - nnodes = space * (bt->f.vol->mdb.drAlBlkSiz / bt->hdr.bthNodeSize); - - bt->hdr.bthNNodes += nnodes; - bt->hdr.bthFree += nnodes; - - bt->flags |= HFS_BT_UPDATE_HDR; - - bt->f.vol->flags |= HFS_VOL_UPDATE_ALTMDB; - - while (bt->hdr.bthNNodes > bt->mapsz * 8) - { - byte *newmap; - node mapnd; - - /* extend tree map */ - - newmap = REALLOC(bt->map, byte, bt->mapsz + HFS_MAPXSZ); - if (newmap == 0) - ERROR(ENOMEM, 0); - - memset(newmap + bt->mapsz, 0, HFS_MAPXSZ); - - bt->map = newmap; - bt->mapsz += HFS_MAPXSZ; - - n_init(&mapnd, bt, ndMapNode, 0); - if (n_new(&mapnd) == -1) - goto fail; - - mapnd.nd.ndNRecs = 1; - mapnd.roff[1] = 0x1fa; - - /* link the new map node */ - - if (bt->hdrnd.nd.ndFLink == 0) - { - bt->hdrnd.nd.ndFLink = mapnd.nnum; - mapnd.nd.ndBLink = 0; - } - else - { - node n; - unsigned long nnum; - - nnum = bt->hdrnd.nd.ndFLink; - - while (1) - { - if (bt_getnode(&n, bt, nnum) == -1) - goto fail; - - if (n.nd.ndFLink == 0) - break; - - nnum = n.nd.ndFLink; - } - - n.nd.ndFLink = mapnd.nnum; - mapnd.nd.ndBLink = n.nnum; - - if (bt_putnode(&n) == -1) - goto fail; - } - - if (bt_putnode(&mapnd) == -1) - goto fail; - } - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: insertx() - * DESCRIPTION: recursively locate a node and insert a record - */ -static -int insertx(node *np, byte *record, int *reclen) -{ - node child; - byte *rec; - int result = 0; - - if (n_search(np, record)) - ERROR(EIO, "b*-tree record already exists"); - - switch (np->nd.ndType) - { - case ndIndxNode: - if (np->rnum == -1) - rec = HFS_NODEREC(*np, 0); - else - rec = HFS_NODEREC(*np, np->rnum); - - if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 || - insertx(&child, record, reclen) == -1) - goto fail; - - if (np->rnum == -1) - { - n_index(&child, rec, 0); - if (*reclen == 0) - { - result = bt_putnode(np); - goto done; - } - } - - if (*reclen) - result = n_insert(np, record, reclen); - - break; - - case ndLeafNode: - result = n_insert(np, record, reclen); - break; - - default: - ERROR(EIO, "unexpected b*-tree node"); - } - -done: - return result; - -fail: - return -1; -} - -/* - * NAME: btree->insert() - * DESCRIPTION: insert a new node record into a tree - */ -int bt_insert(btree *bt, const byte *record, unsigned int reclen) -{ - node root; - byte newrec[HFS_MAX_RECLEN]; - - if (bt->hdr.bthRoot == 0) - { - /* create root node */ - - n_init(&root, bt, ndLeafNode, 1); - if (n_new(&root) == -1 || - bt_putnode(&root) == -1) - goto fail; - - bt->hdr.bthDepth = 1; - bt->hdr.bthRoot = root.nnum; - bt->hdr.bthFNode = root.nnum; - bt->hdr.bthLNode = root.nnum; - - bt->flags |= HFS_BT_UPDATE_HDR; - } - else if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1) - goto fail; - - memcpy(newrec, record, reclen); - - if (insertx(&root, newrec, &reclen) == -1) - goto fail; - - if (reclen) - { - byte oroot[HFS_MAX_RECLEN]; - unsigned int orootlen; - - /* root node was split; create a new root */ - - n_index(&root, oroot, &orootlen); - - n_init(&root, bt, ndIndxNode, root.nd.ndNHeight + 1); - if (n_new(&root) == -1) - goto fail; - - ++bt->hdr.bthDepth; - bt->hdr.bthRoot = root.nnum; - - bt->flags |= HFS_BT_UPDATE_HDR; - - /* insert index records for new root */ - - n_search(&root, oroot); - n_insertx(&root, oroot, orootlen); - - n_search(&root, newrec); - n_insertx(&root, newrec, reclen); - - if (bt_putnode(&root) == -1) - goto fail; - } - - ++bt->hdr.bthNRecs; - bt->flags |= HFS_BT_UPDATE_HDR; - - return 0; - -fail: - return -1; -} - -/* - * NAME: deletex() - * DESCRIPTION: recursively locate a node and delete a record - */ -static -int deletex(node *np, const byte *key, byte *record, int *flag) -{ - node child; - byte *rec; - int found, result = 0; - - found = n_search(np, key); - - switch (np->nd.ndType) - { - case ndIndxNode: - if (np->rnum == -1) - ERROR(EIO, "b*-tree record not found"); - - rec = HFS_NODEREC(*np, np->rnum); - - if (bt_getnode(&child, np->bt, d_getul(HFS_RECDATA(rec))) == -1 || - deletex(&child, key, rec, flag) == -1) - goto fail; - - if (*flag) - { - *flag = 0; - - if (HFS_RECKEYLEN(rec) == 0) - { - result = n_delete(np, record, flag); - break; - } - - if (np->rnum == 0) - { - /* propagate index record change into parent */ - - n_index(np, record, 0); - *flag = 1; - } - - result = bt_putnode(np); - } - - break; - - case ndLeafNode: - if (found == 0) - ERROR(EIO, "b*-tree record not found"); - - result = n_delete(np, record, flag); - break; - - default: - ERROR(EIO, "unexpected b*-tree node"); - } - - return result; - -fail: - return -1; -} - -/* - * NAME: btree->delete() - * DESCRIPTION: remove a node record from a tree - */ -int bt_delete(btree *bt, const byte *key) -{ - node root; - byte record[HFS_MAX_RECLEN]; - int flag = 0; - - if (bt->hdr.bthRoot == 0) - ERROR(EIO, "empty b*-tree"); - - if (bt_getnode(&root, bt, bt->hdr.bthRoot) == -1 || - deletex(&root, key, record, &flag) == -1) - goto fail; - - if (bt->hdr.bthDepth > 1 && root.nd.ndNRecs == 1) - { - const byte *rec; - - /* root only has one record; eliminate it and decrease the tree depth */ - - rec = HFS_NODEREC(root, 0); - - --bt->hdr.bthDepth; - bt->hdr.bthRoot = d_getul(HFS_RECDATA(rec)); - - if (n_free(&root) == -1) - goto fail; - } - else if (bt->hdr.bthDepth == 1 && root.nd.ndNRecs == 0) - { - /* root node was deleted */ - - bt->hdr.bthDepth = 0; - bt->hdr.bthRoot = 0; - } - - --bt->hdr.bthNRecs; - bt->flags |= HFS_BT_UPDATE_HDR; - - return 0; - -fail: - return -1; -} - -/* - * NAME: btree->search() - * DESCRIPTION: locate a data record given a search key - */ -int bt_search(btree *bt, const byte *key, node *np) -{ - int found = 0; - unsigned long nnum; - - nnum = bt->hdr.bthRoot; - - if (nnum == 0) - ERROR(ENOENT, 0); - - while (1) - { - const byte *rec; - - if (bt_getnode(np, bt, nnum) == -1) - { - found = -1; - goto fail; - } - - found = n_search(np, key); - - switch (np->nd.ndType) - { - case ndIndxNode: - if (np->rnum == -1) - ERROR(ENOENT, 0); - - rec = HFS_NODEREC(*np, np->rnum); - nnum = d_getul(HFS_RECDATA(rec)); - - break; - - case ndLeafNode: - if (! found) - ERROR(ENOENT, 0); - - goto done; - - default: - found = -1; - ERROR(EIO, "unexpected b*-tree node"); - } - } - -done: -fail: - return found; -} diff --git a/ciderpress/diskimg/libhfs/btree.h b/ciderpress/diskimg/libhfs/btree.h deleted file mode 100644 index cdf9427..0000000 --- a/ciderpress/diskimg/libhfs/btree.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -int bt_getnode(node *, btree *, unsigned long); -int bt_putnode(node *); - -int bt_readhdr(btree *); -int bt_writehdr(btree *); - -int bt_space(btree *, unsigned int); - -int bt_insert(btree *, const byte *, unsigned int); -int bt_delete(btree *, const byte *); - -int bt_search(btree *, const byte *, node *); diff --git a/ciderpress/diskimg/libhfs/config.h b/ciderpress/diskimg/libhfs/config.h deleted file mode 100644 index fd76a54..0000000 --- a/ciderpress/diskimg/libhfs/config.h +++ /dev/null @@ -1,60 +0,0 @@ -/* config.h. Generated automatically by configure. */ -/* config.h.in. Generated automatically from configure.in by autoheader. */ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -/***************************************************************************** - * Definitions selected automatically by `configure' * - *****************************************************************************/ - -/* Define to empty if the keyword does not work. */ -/* #undef const */ - -/* Define to `unsigned' if doesn't define. */ -/* #undef size_t */ - -/* Define if you have the ANSI C header files. */ -#define STDC_HEADERS 1 - -/* Define if your declares struct tm. */ -/* #undef TM_IN_SYS_TIME */ - -/* Define if you want to enable diagnostic debugging support. */ -/* #undef DEBUG */ - -/* Define if you have the mktime function. */ -#define HAVE_MKTIME 1 - -/* Define if you have the header file. */ -#define HAVE_FCNTL_H 1 - -/* Define if you have the header file. */ -#ifndef _WIN32 -# define HAVE_UNISTD_H 1 -#endif - -/***************************************************************************** - * End of automatically configured definitions * - *****************************************************************************/ - -# ifdef DEBUG -# include -# endif diff --git a/ciderpress/diskimg/libhfs/data.c b/ciderpress/diskimg/libhfs/data.c deleted file mode 100644 index 5cf763c..0000000 --- a/ciderpress/diskimg/libhfs/data.c +++ /dev/null @@ -1,485 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include - -# ifdef TM_IN_SYS_TIME -# include -# endif - -# include "data.h" - -# define TIMEDIFF 2082844800UL - -static -time_t tzdiff = -1; - -const -unsigned char hfs_charorder[256] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - - 0x20, 0x22, 0x23, 0x28, 0x29, 0x2a, 0x2b, 0x2c, - 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, - 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, - 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, - - 0x47, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, - 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, - 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, - 0xa3, 0xa5, 0xa8, 0xaa, 0xab, 0xac, 0xad, 0xae, - - 0x54, 0x48, 0x58, 0x5a, 0x5e, 0x60, 0x67, 0x69, - 0x6b, 0x6d, 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7f, - 0x8d, 0x8f, 0x91, 0x93, 0x96, 0x98, 0x9f, 0xa1, - 0xa3, 0xa5, 0xa8, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, - - 0x4c, 0x50, 0x5c, 0x62, 0x7d, 0x81, 0x9a, 0x55, - 0x4a, 0x56, 0x4c, 0x4e, 0x50, 0x5c, 0x62, 0x64, - 0x65, 0x66, 0x6f, 0x70, 0x71, 0x72, 0x7d, 0x89, - 0x8a, 0x8b, 0x81, 0x83, 0x9c, 0x9d, 0x9e, 0x9a, - - 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0x95, - 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0x52, 0x85, - 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, - 0xc9, 0xca, 0xcb, 0x57, 0x8c, 0xcc, 0x52, 0x85, - - 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0x26, - 0x27, 0xd4, 0x20, 0x4a, 0x4e, 0x83, 0x87, 0x87, - 0xd5, 0xd6, 0x24, 0x25, 0x2d, 0x2e, 0xd7, 0xd8, - 0xa7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, - - 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, - 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, - 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff -}; - -/* - * NAME: data->getsb() - * DESCRIPTION: marshal 1 signed byte into local host format - */ -signed char d_getsb(register const unsigned char *ptr) -{ - return ptr[0]; -} - -/* - * NAME: data->getub() - * DESCRIPTION: marshal 1 unsigned byte into local host format - */ -unsigned char d_getub(register const unsigned char *ptr) -{ - return ptr[0]; -} - -/* - * NAME: data->getsw() - * DESCRIPTION: marshal 2 signed bytes into local host format - */ -signed short d_getsw(register const unsigned char *ptr) -{ - return - ((( signed short) ptr[0] << 8) | - ((unsigned short) ptr[1] << 0)); -} - -/* - * NAME: data->getuw() - * DESCRIPTION: marshal 2 unsigned bytes into local host format - */ -unsigned short d_getuw(register const unsigned char *ptr) -{ - return - (((unsigned short) ptr[0] << 8) | - ((unsigned short) ptr[1] << 0)); -} - -/* - * NAME: data->getsl() - * DESCRIPTION: marshal 4 signed bytes into local host format - */ -signed long d_getsl(register const unsigned char *ptr) -{ - return - ((( signed long) ptr[0] << 24) | - ((unsigned long) ptr[1] << 16) | - ((unsigned long) ptr[2] << 8) | - ((unsigned long) ptr[3] << 0)); -} - -/* - * NAME: data->getul() - * DESCRIPTION: marshal 4 unsigned bytes into local host format - */ -unsigned long d_getul(register const unsigned char *ptr) -{ - return - (((unsigned long) ptr[0] << 24) | - ((unsigned long) ptr[1] << 16) | - ((unsigned long) ptr[2] << 8) | - ((unsigned long) ptr[3] << 0)); -} - -/* - * NAME: data->putsb() - * DESCRIPTION: marshal 1 signed byte out in big-endian format - */ -void d_putsb(register unsigned char *ptr, - register signed char data) -{ - *ptr = data; -} - -/* - * NAME: data->putub() - * DESCRIPTION: marshal 1 unsigned byte out in big-endian format - */ -void d_putub(register unsigned char *ptr, - register unsigned char data) -{ - *ptr = data; -} - -/* - * NAME: data->putsw() - * DESCRIPTION: marshal 2 signed bytes out in big-endian format - */ -void d_putsw(register unsigned char *ptr, - register signed short data) -{ - *ptr++ = ((unsigned short) data & 0xff00) >> 8; - *ptr = ((unsigned short) data & 0x00ff) >> 0; -} - -/* - * NAME: data->putuw() - * DESCRIPTION: marshal 2 unsigned bytes out in big-endian format - */ -void d_putuw(register unsigned char *ptr, - register unsigned short data) -{ - *ptr++ = (data & 0xff00) >> 8; - *ptr = (data & 0x00ff) >> 0; -} - -/* - * NAME: data->putsl() - * DESCRIPTION: marshal 4 signed bytes out in big-endian format - */ -void d_putsl(register unsigned char *ptr, - register signed long data) -{ - *ptr++ = ((unsigned long) data & 0xff000000UL) >> 24; - *ptr++ = ((unsigned long) data & 0x00ff0000UL) >> 16; - *ptr++ = ((unsigned long) data & 0x0000ff00UL) >> 8; - *ptr = ((unsigned long) data & 0x000000ffUL) >> 0; -} - -/* - * NAME: data->putul() - * DESCRIPTION: marshal 4 unsigned bytes out in big-endian format - */ -void d_putul(register unsigned char *ptr, - register unsigned long data) -{ - *ptr++ = (data & 0xff000000UL) >> 24; - *ptr++ = (data & 0x00ff0000UL) >> 16; - *ptr++ = (data & 0x0000ff00UL) >> 8; - *ptr = (data & 0x000000ffUL) >> 0; -} - -/* - * NAME: data->fetchsb() - * DESCRIPTION: incrementally retrieve a signed byte of data - */ -void d_fetchsb(register const unsigned char **ptr, - register signed char *dest) -{ - *dest = *(*ptr)++; -} - -/* - * NAME: data->fetchub() - * DESCRIPTION: incrementally retrieve an unsigned byte of data - */ -void d_fetchub(register const unsigned char **ptr, - register unsigned char *dest) -{ - *dest = *(*ptr)++; -} - -/* - * NAME: data->fetchsw() - * DESCRIPTION: incrementally retrieve a signed word of data - */ -void d_fetchsw(register const unsigned char **ptr, - register signed short *dest) -{ - *dest = - ((( signed short) (*ptr)[0] << 8) | - ((unsigned short) (*ptr)[1] << 0)); - *ptr += 2; -} - -/* - * NAME: data->fetchuw() - * DESCRIPTION: incrementally retrieve an unsigned word of data - */ -void d_fetchuw(register const unsigned char **ptr, - register unsigned short *dest) -{ - *dest = - (((unsigned short) (*ptr)[0] << 8) | - ((unsigned short) (*ptr)[1] << 0)); - *ptr += 2; -} - -/* - * NAME: data->fetchsl() - * DESCRIPTION: incrementally retrieve a signed long word of data - */ -void d_fetchsl(register const unsigned char **ptr, - register signed long *dest) -{ - *dest = - ((( signed long) (*ptr)[0] << 24) | - ((unsigned long) (*ptr)[1] << 16) | - ((unsigned long) (*ptr)[2] << 8) | - ((unsigned long) (*ptr)[3] << 0)); - *ptr += 4; -} - -/* - * NAME: data->fetchul() - * DESCRIPTION: incrementally retrieve an unsigned long word of data - */ -void d_fetchul(register const unsigned char **ptr, - register unsigned long *dest) -{ - *dest = - (((unsigned long) (*ptr)[0] << 24) | - ((unsigned long) (*ptr)[1] << 16) | - ((unsigned long) (*ptr)[2] << 8) | - ((unsigned long) (*ptr)[3] << 0)); - *ptr += 4; -} - -/* - * NAME: data->storesb() - * DESCRIPTION: incrementally store a signed byte of data - */ -void d_storesb(register unsigned char **ptr, - register signed char data) -{ - *(*ptr)++ = data; -} - -/* - * NAME: data->storeub() - * DESCRIPTION: incrementally store an unsigned byte of data - */ -void d_storeub(register unsigned char **ptr, - register unsigned char data) -{ - *(*ptr)++ = data; -} - -/* - * NAME: data->storesw() - * DESCRIPTION: incrementally store a signed word of data - */ -void d_storesw(register unsigned char **ptr, - register signed short data) -{ - *(*ptr)++ = ((unsigned short) data & 0xff00) >> 8; - *(*ptr)++ = ((unsigned short) data & 0x00ff) >> 0; -} - -/* - * NAME: data->storeuw() - * DESCRIPTION: incrementally store an unsigned word of data - */ -void d_storeuw(register unsigned char **ptr, - register unsigned short data) -{ - *(*ptr)++ = (data & 0xff00) >> 8; - *(*ptr)++ = (data & 0x00ff) >> 0; -} - -/* - * NAME: data->storesl() - * DESCRIPTION: incrementally store a signed long word of data - */ -void d_storesl(register unsigned char **ptr, - register signed long data) -{ - *(*ptr)++ = ((unsigned long) data & 0xff000000UL) >> 24; - *(*ptr)++ = ((unsigned long) data & 0x00ff0000UL) >> 16; - *(*ptr)++ = ((unsigned long) data & 0x0000ff00UL) >> 8; - *(*ptr)++ = ((unsigned long) data & 0x000000ffUL) >> 0; -} - -/* - * NAME: data->storeul() - * DESCRIPTION: incrementally store an unsigned long word of data - */ -void d_storeul(register unsigned char **ptr, - register unsigned long data) -{ - *(*ptr)++ = (data & 0xff000000UL) >> 24; - *(*ptr)++ = (data & 0x00ff0000UL) >> 16; - *(*ptr)++ = (data & 0x0000ff00UL) >> 8; - *(*ptr)++ = (data & 0x000000ffUL) >> 0; -} - -/* - * NAME: data->fetchstr() - * DESCRIPTION: incrementally retrieve a string - */ -void d_fetchstr(const unsigned char **ptr, char *dest, unsigned size) -{ - unsigned len; - - len = d_getub(*ptr); - - if (len > 0 && len < size) - memcpy(dest, *ptr + 1, len); - else - len = 0; - - dest[len] = 0; - - *ptr += size; -} - -/* - * NAME: data->storestr() - * DESCRIPTION: incrementally store a string - */ -void d_storestr(unsigned char **ptr, const char *src, unsigned size) -{ - unsigned len; - - len = strlen(src); - if (len > --size) - len = 0; - - d_storeub(ptr, (unsigned char) len); - - memcpy(*ptr, src, len); - memset(*ptr + len, 0, size - len); - - *ptr += size; -} - -/* - * NAME: data->relstring() - * DESCRIPTION: compare two strings as per MacOS for HFS - */ -int d_relstring(const char *str1, const char *str2) -{ - register int diff; - - while (*str1 && *str2) - { - diff = hfs_charorder[(unsigned char) *str1] - - hfs_charorder[(unsigned char) *str2]; - - if (diff) - return diff; - - ++str1, ++str2; - } - - if (! *str1 && *str2) - return -1; - else if (*str1 && ! *str2) - return 1; - - return 0; -} - -/* - * NAME: calctzdiff() - * DESCRIPTION: calculate the timezone difference between local time and UTC - */ -static -void calctzdiff(void) -{ -# ifdef HAVE_MKTIME - - time_t t; - int isdst; - struct tm tm; - const struct tm *tmp; - - time(&t); - isdst = localtime(&t)->tm_isdst; - - tmp = gmtime(&t); - if (tmp) - { - tm = *tmp; - tm.tm_isdst = isdst; - - tzdiff = t - mktime(&tm); - } - else - tzdiff = 0; - -# else - - tzdiff = 0; - -# endif -} - -/* - * NAME: data->ltime() - * DESCRIPTION: convert MacOS time to local time - */ -time_t d_ltime(unsigned long mtime) -{ - if (tzdiff == -1) - calctzdiff(); - - return (time_t) (mtime - TIMEDIFF) - tzdiff; -} - -/* - * NAME: data->mtime() - * DESCRIPTION: convert local time to MacOS time - */ -unsigned long d_mtime(time_t ltime) -{ - if (tzdiff == -1) - calctzdiff(); - - return (unsigned long) (ltime + tzdiff) + TIMEDIFF; -} diff --git a/ciderpress/diskimg/libhfs/data.h b/ciderpress/diskimg/libhfs/data.h deleted file mode 100644 index 8cddf00..0000000 --- a/ciderpress/diskimg/libhfs/data.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -extern const unsigned char hfs_charorder[]; - - signed char d_getsb(register const unsigned char *); -unsigned char d_getub(register const unsigned char *); - signed short d_getsw(register const unsigned char *); -unsigned short d_getuw(register const unsigned char *); - signed long d_getsl(register const unsigned char *); -unsigned long d_getul(register const unsigned char *); - -void d_putsb(register unsigned char *, register signed char); -void d_putub(register unsigned char *, register unsigned char); -void d_putsw(register unsigned char *, register signed short); -void d_putuw(register unsigned char *, register unsigned short); -void d_putsl(register unsigned char *, register signed long); -void d_putul(register unsigned char *, register unsigned long); - -void d_fetchsb(register const unsigned char **, register signed char *); -void d_fetchub(register const unsigned char **, register unsigned char *); -void d_fetchsw(register const unsigned char **, register signed short *); -void d_fetchuw(register const unsigned char **, register unsigned short *); -void d_fetchsl(register const unsigned char **, register signed long *); -void d_fetchul(register const unsigned char **, register unsigned long *); - -void d_storesb(register unsigned char **, register signed char); -void d_storeub(register unsigned char **, register unsigned char); -void d_storesw(register unsigned char **, register signed short); -void d_storeuw(register unsigned char **, register unsigned short); -void d_storesl(register unsigned char **, register signed long); -void d_storeul(register unsigned char **, register unsigned long); - -void d_fetchstr(const unsigned char **, char *, unsigned); -void d_storestr(unsigned char **, const char *, unsigned); - -int d_relstring(const char *, const char *); - -time_t d_ltime(unsigned long); -unsigned long d_mtime(time_t); diff --git a/ciderpress/diskimg/libhfs/file.c b/ciderpress/diskimg/libhfs/file.c deleted file mode 100644 index 551a399..0000000 --- a/ciderpress/diskimg/libhfs/file.c +++ /dev/null @@ -1,520 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include - -# include "libhfs.h" -# include "file.h" -# include "btree.h" -# include "record.h" -# include "volume.h" - -/* - * NAME: file->init() - * DESCRIPTION: initialize file structure - */ -void f_init(hfsfile *file, hfsvol *vol, long cnid, const char *name) -{ - int i; - - file->vol = vol; - file->parid = 0; - - strcpy(file->name, name); - - file->cat.cdrType = cdrFilRec; - file->cat.cdrResrv2 = 0; - - file->cat.u.fil.filFlags = 0; - file->cat.u.fil.filTyp = 0; - - file->cat.u.fil.filUsrWds.fdType = 0; - file->cat.u.fil.filUsrWds.fdCreator = 0; - file->cat.u.fil.filUsrWds.fdFlags = 0; - file->cat.u.fil.filUsrWds.fdLocation.v = 0; - file->cat.u.fil.filUsrWds.fdLocation.h = 0; - file->cat.u.fil.filUsrWds.fdFldr = 0; - - file->cat.u.fil.filFlNum = cnid; - file->cat.u.fil.filStBlk = 0; - file->cat.u.fil.filLgLen = 0; - file->cat.u.fil.filPyLen = 0; - file->cat.u.fil.filRStBlk = 0; - file->cat.u.fil.filRLgLen = 0; - file->cat.u.fil.filRPyLen = 0; - file->cat.u.fil.filCrDat = 0; - file->cat.u.fil.filMdDat = 0; - file->cat.u.fil.filBkDat = 0; - - file->cat.u.fil.filFndrInfo.fdIconID = 0; - for (i = 0; i < 4; ++i) - file->cat.u.fil.filFndrInfo.fdUnused[i] = 0; - file->cat.u.fil.filFndrInfo.fdComment = 0; - file->cat.u.fil.filFndrInfo.fdPutAway = 0; - - file->cat.u.fil.filClpSize = 0; - - for (i = 0; i < 3; ++i) - { - file->cat.u.fil.filExtRec[i].xdrStABN = 0; - file->cat.u.fil.filExtRec[i].xdrNumABlks = 0; - - file->cat.u.fil.filRExtRec[i].xdrStABN = 0; - file->cat.u.fil.filRExtRec[i].xdrNumABlks = 0; - } - - file->cat.u.fil.filResrv = 0; - - f_selectfork(file, fkData); - - file->flags = 0; - - file->prev = 0; - file->next = 0; -} - -/* - * NAME: file->selectfork() - * DESCRIPTION: choose a fork for file operations - */ -void f_selectfork(hfsfile *file, int fork) -{ - file->fork = fork; - - memcpy(&file->ext, fork == fkData ? - &file->cat.u.fil.filExtRec : &file->cat.u.fil.filRExtRec, - sizeof(ExtDataRec)); - - file->fabn = 0; - file->pos = 0; -} - -/* - * NAME: file->getptrs() - * DESCRIPTION: make pointers to the current fork's lengths and extents - */ -void f_getptrs(hfsfile *file, ExtDataRec **extrec, - unsigned long **lglen, unsigned long **pylen) -{ - if (file->fork == fkData) - { - if (extrec) - *extrec = &file->cat.u.fil.filExtRec; - if (lglen) - *lglen = &file->cat.u.fil.filLgLen; - if (pylen) - *pylen = &file->cat.u.fil.filPyLen; - } - else - { - if (extrec) - *extrec = &file->cat.u.fil.filRExtRec; - if (lglen) - *lglen = &file->cat.u.fil.filRLgLen; - if (pylen) - *pylen = &file->cat.u.fil.filRPyLen; - } -} - -/* - * NAME: file->doblock() - * DESCRIPTION: read or write a numbered block from a file - */ -int f_doblock(hfsfile *file, unsigned long num, block *bp, - int (*func)(hfsvol *, unsigned int, unsigned int, block *)) -{ - unsigned int abnum; - unsigned int blnum; - unsigned int fabn; - int i; - - abnum = num / file->vol->lpa; - blnum = num % file->vol->lpa; - - /* locate the appropriate extent record */ - - fabn = file->fabn; - - if (abnum < fabn) - { - ExtDataRec *extrec; - - f_getptrs(file, &extrec, 0, 0); - - fabn = file->fabn = 0; - memcpy(&file->ext, extrec, sizeof(ExtDataRec)); - } - else - abnum -= fabn; - - while (1) - { - unsigned int n; - - for (i = 0; i < 3; ++i) - { - n = file->ext[i].xdrNumABlks; - - if (abnum < n) - return func(file->vol, file->ext[i].xdrStABN + abnum, blnum, bp); - - fabn += n; - abnum -= n; - } - - if (v_extsearch(file, fabn, &file->ext, 0) <= 0) - goto fail; - - file->fabn = fabn; - } - -fail: - return -1; -} - -/* - * NAME: file->addextent() - * DESCRIPTION: add an extent to a file - */ -int f_addextent(hfsfile *file, ExtDescriptor *blocks) -{ - hfsvol *vol = file->vol; - ExtDataRec *extrec; - unsigned long *pylen; - unsigned int start, end; - node n; - int i; - - f_getptrs(file, &extrec, 0, &pylen); - - start = file->fabn; - end = *pylen / vol->mdb.drAlBlkSiz; - - n.nnum = 0; - i = -1; - - while (start < end) - { - for (i = 0; i < 3; ++i) - { - unsigned int num; - - num = file->ext[i].xdrNumABlks; - start += num; - - if (start == end) - break; - else if (start > end) - ERROR(EIO, "file extents exceed file physical length"); - else if (num == 0) - ERROR(EIO, "empty file extent"); - } - - if (start == end) - break; - - if (v_extsearch(file, start, &file->ext, &n) <= 0) - goto fail; - - file->fabn = start; - } - - if (i >= 0 && - file->ext[i].xdrStABN + file->ext[i].xdrNumABlks == blocks->xdrStABN) - file->ext[i].xdrNumABlks += blocks->xdrNumABlks; - else - { - /* create a new extent descriptor */ - - if (++i < 3) - file->ext[i] = *blocks; - else - { - ExtKeyRec key; - byte record[HFS_MAX_EXTRECLEN]; - unsigned int reclen; - - /* record is full; create a new one */ - - file->ext[0] = *blocks; - - for (i = 1; i < 3; ++i) - { - file->ext[i].xdrStABN = 0; - file->ext[i].xdrNumABlks = 0; - } - - file->fabn = start; - - r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, end); - r_packextrec(&key, &file->ext, record, &reclen); - - if (bt_insert(&vol->ext, record, reclen) == -1) - goto fail; - - i = -1; - } - } - - if (i >= 0) - { - /* store the modified extent record */ - - if (file->fabn) - { - if ((n.nnum == 0 && - v_extsearch(file, file->fabn, 0, &n) <= 0) || - v_putextrec(&file->ext, &n) == -1) - goto fail; - } - else - memcpy(extrec, &file->ext, sizeof(ExtDataRec)); - } - - *pylen += blocks->xdrNumABlks * vol->mdb.drAlBlkSiz; - - file->flags |= HFS_FILE_UPDATE_CATREC; - - return 0; - -fail: - return -1; -} - -/* - * NAME: file->alloc() - * DESCRIPTION: reserve allocation blocks for a file - */ -long f_alloc(hfsfile *file) -{ - hfsvol *vol = file->vol; - unsigned long clumpsz; - ExtDescriptor blocks; - - clumpsz = file->cat.u.fil.filClpSize; - if (clumpsz == 0) - { - if (file == &vol->ext.f) - clumpsz = vol->mdb.drXTClpSiz; - else if (file == &vol->cat.f) - clumpsz = vol->mdb.drCTClpSiz; - else - clumpsz = vol->mdb.drClpSiz; - } - - blocks.xdrNumABlks = clumpsz / vol->mdb.drAlBlkSiz; - - if (v_allocblocks(vol, &blocks) == -1) - goto fail; - - if (f_addextent(file, &blocks) == -1) - { - v_freeblocks(vol, &blocks); - goto fail; - } - - return blocks.xdrNumABlks; - -fail: - return -1; -} - -/* - * NAME: file->trunc() - * DESCRIPTION: release allocation blocks unneeded by a file - */ -int f_trunc(hfsfile *file) -{ - hfsvol *vol = file->vol; - ExtDataRec *extrec; - unsigned long *lglen, *pylen, alblksz, newpylen; - unsigned int dlen, start, end; - node n; - int i; - - if (vol->flags & HFS_VOL_READONLY) - goto done; - - f_getptrs(file, &extrec, &lglen, &pylen); - - alblksz = vol->mdb.drAlBlkSiz; - newpylen = (*lglen / alblksz + (*lglen % alblksz != 0)) * alblksz; - - if (newpylen > *pylen) - ERROR(EIO, "file size exceeds physical length"); - else if (newpylen == *pylen) - goto done; - - dlen = (*pylen - newpylen) / alblksz; - - start = file->fabn; - end = newpylen / alblksz; - - if (start >= end) - { - start = file->fabn = 0; - memcpy(&file->ext, extrec, sizeof(ExtDataRec)); - } - - n.nnum = 0; - i = -1; - - while (start < end) - { - for (i = 0; i < 3; ++i) - { - unsigned int num; - - num = file->ext[i].xdrNumABlks; - start += num; - - if (start >= end) - break; - else if (num == 0) - ERROR(EIO, "empty file extent"); - } - - if (start >= end) - break; - - if (v_extsearch(file, start, &file->ext, &n) <= 0) - goto fail; - - file->fabn = start; - } - - if (start > end) - { - ExtDescriptor blocks; - - file->ext[i].xdrNumABlks -= start - end; - dlen -= start - end; - - blocks.xdrStABN = file->ext[i].xdrStABN + file->ext[i].xdrNumABlks; - blocks.xdrNumABlks = start - end; - - if (v_freeblocks(vol, &blocks) == -1) - goto fail; - } - - *pylen = newpylen; - - file->flags |= HFS_FILE_UPDATE_CATREC; - - do - { - while (dlen && ++i < 3) - { - unsigned int num; - - num = file->ext[i].xdrNumABlks; - start += num; - - if (num == 0) - ERROR(EIO, "empty file extent"); - else if (num > dlen) - ERROR(EIO, "file extents exceed physical size"); - - dlen -= num; - - if (v_freeblocks(vol, &file->ext[i]) == -1) - goto fail; - - file->ext[i].xdrStABN = 0; - file->ext[i].xdrNumABlks = 0; - } - - if (file->fabn) - { - if (n.nnum == 0 && - v_extsearch(file, file->fabn, 0, &n) <= 0) - goto fail; - - if (file->ext[0].xdrNumABlks) - { - if (v_putextrec(&file->ext, &n) == -1) - goto fail; - } - else - { - if (bt_delete(&vol->ext, HFS_NODEREC(n, n.rnum)) == -1) - goto fail; - - n.nnum = 0; - } - } - else - memcpy(extrec, &file->ext, sizeof(ExtDataRec)); - - if (dlen) - { - if (v_extsearch(file, start, &file->ext, &n) <= 0) - goto fail; - - file->fabn = start; - i = -1; - } - } - while (dlen); - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: file->flush() - * DESCRIPTION: flush all pending changes to an open file - */ -int f_flush(hfsfile *file) -{ - hfsvol *vol = file->vol; - - if (vol->flags & HFS_VOL_READONLY) - goto done; - - if (file->flags & HFS_FILE_UPDATE_CATREC) - { - node n; - - file->cat.u.fil.filStBlk = file->cat.u.fil.filExtRec[0].xdrStABN; - file->cat.u.fil.filRStBlk = file->cat.u.fil.filRExtRec[0].xdrStABN; - - if (v_catsearch(vol, file->parid, file->name, 0, 0, &n) <= 0 || - v_putcatrec(&file->cat, &n) == -1) - goto fail; - - file->flags &= ~HFS_FILE_UPDATE_CATREC; - } - -done: - return 0; - -fail: - return -1; -} diff --git a/ciderpress/diskimg/libhfs/file.h b/ciderpress/diskimg/libhfs/file.h deleted file mode 100644 index b4a9501..0000000 --- a/ciderpress/diskimg/libhfs/file.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -enum { - fkData = 0x00, - fkRsrc = 0xff -}; - -void f_init(hfsfile *, hfsvol *, long, const char *); -void f_selectfork(hfsfile *, int); -void f_getptrs(hfsfile *, ExtDataRec **, unsigned long **, unsigned long **); - -int f_doblock(hfsfile *, unsigned long, block *, - int (*)(hfsvol *, unsigned int, unsigned int, block *)); - -# define f_getblock(file, num, bp) \ - f_doblock((file), (num), (bp), b_readab) -# define f_putblock(file, num, bp) \ - f_doblock((file), (num), (bp), \ - (int (*)(hfsvol *, unsigned int, unsigned int, block *)) \ - b_writeab) - -int f_addextent(hfsfile *, ExtDescriptor *); -long f_alloc(hfsfile *); - -int f_trunc(hfsfile *); -int f_flush(hfsfile *); diff --git a/ciderpress/diskimg/libhfs/hfs.c b/ciderpress/diskimg/libhfs/hfs.c deleted file mode 100644 index 0e995b0..0000000 --- a/ciderpress/diskimg/libhfs/hfs.c +++ /dev/null @@ -1,1991 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include -# include -# include -# include /* debug */ - -# include "libhfs.h" -# include "data.h" -# include "block.h" -# include "medium.h" -# include "file.h" -# include "btree.h" -# include "node.h" -# include "record.h" -# include "volume.h" - -/* should be CP_NO_STATIC, but it's harmless (albeit useless with MT) */ -const char *hfs_error = "no error"; /* static error string */ - -#ifdef CP_NO_STATIC -hfsvol *hfs_mounts; /* linked list of mounted volumes */ - -static -hfsvol *curvol; /* current volume */ -#endif - -/* - * NAME: validvname() - * DESCRIPTION: return true if parameter is a valid volume name - */ -static -int validvname(const char *name) -{ - int len; - - len = strlen(name); - if (len < 1) - ERROR(EINVAL, "volume name cannot be empty"); - else if (len > HFS_MAX_VLEN) - ERROR(ENAMETOOLONG, - "volume name can be at most " STR(HFS_MAX_VLEN) " chars"); - - if (strchr(name, ':')) - ERROR(EINVAL, "volume name may not contain colons"); - - return 1; - -fail: - return 0; -} - -/* - * NAME: getvol() - * DESCRIPTION: validate a volume reference - */ -static -int getvol(hfsvol **vol) -{ -#ifdef CP_NO_STATIC - if (*vol == 0) - { - if (curvol == 0) - ERROR(EINVAL, "no volume is current"); - - *vol = curvol; - } -#else - if (*vol == 0) - ERROR(EINVAL, "no volume is current"); -#endif - - return 0; - -fail: - return -1; -} - -/* High-Level Volume Routines ============================================== */ - -#ifdef CP_NO_STATIC -/* - * NAME: hfs->mount() - * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) - */ -hfsvol *hfs_mount(const char *path, int pnum, int mode) -{ - hfsvol *vol, *check; - - /* see if the volume is already mounted */ - - for (check = hfs_mounts; check; check = check->next) - { - if (check->pnum == pnum && v_same(check, path) == 1) - { - /* verify compatible read/write mode */ - - if (((check->flags & HFS_VOL_READONLY) && - ! (mode & HFS_MODE_RDWR)) || - (! (check->flags & HFS_VOL_READONLY) && - (mode & (HFS_MODE_RDWR | HFS_MODE_ANY)))) - { - vol = check; - goto done; - } - } - } - - vol = ALLOC(hfsvol, 1); - if (vol == 0) - ERROR(ENOMEM, 0); - - v_init(vol, mode); - - /* open the medium */ - - switch (mode & HFS_MODE_MASK) - { - case HFS_MODE_RDWR: - case HFS_MODE_ANY: - if (v_open(vol, path, HFS_MODE_RDWR) != -1) - break; - - if ((mode & HFS_MODE_MASK) == HFS_MODE_RDWR) - goto fail; - - case HFS_MODE_RDONLY: - default: - vol->flags |= HFS_VOL_READONLY; - - if (v_open(vol, path, HFS_MODE_RDONLY) == -1) - goto fail; - } - - /* mount the volume */ - - if (v_geometry(vol, pnum) == -1 || - v_mount(vol) == -1) - goto fail; - - /* add to linked list of volumes */ - - vol->prev = 0; - vol->next = hfs_mounts; - - if (hfs_mounts) - hfs_mounts->prev = vol; - - hfs_mounts = vol; - -done: - ++vol->refs; - curvol = vol; - - return vol; - -fail: - if (vol) - { - v_close(vol); - FREE(vol); - } - - return 0; -} -#endif - -/* - * NAME: hfs_callback_open() - * DESCRIPTION: open an HFS volume; return volume descriptor or 0 (error) - */ -hfsvol* hfs_callback_open(oscallback func, void* cookie, int mode) -{ - hfsvol *vol; - - vol = ALLOC(hfsvol, 1); - if (vol == 0) - ERROR(ENOMEM, 0); - - v_init(vol, mode); - - /* open the medium */ - - switch (mode & HFS_MODE_MASK) - { - case HFS_MODE_RDWR: - case HFS_MODE_ANY: - break; - - case HFS_MODE_RDONLY: - default: - vol->flags |= HFS_VOL_READONLY; - } - - /* set up vol->priv */ - v_callback_open(vol, func, cookie); - - - /* mount the volume */ - - if (v_geometry(vol, 0 /*we don't see partition map*/) == -1 || - v_mount(vol) == -1) - goto fail; - - assert(func != 0); - assert(cookie != 0); - -/*done*/ - ++vol->refs; - - return vol; - -fail: - if (vol) - { - v_close(vol); - FREE(vol); - } - - return 0; -} - -/* - * NAME: hfs->callback_close() - * DESCRIPTION: close an HFS volume - */ -int hfs_callback_close(hfsvol *vol) -{ - int result = 0; - - if (getvol(&vol) == -1) - goto fail; - - if (--vol->refs) - { - result = v_flush(vol); - goto done; - } - - /* close all open files and directories */ - - while (vol->files) - { - if (hfs_close(vol->files) == -1) - result = -1; - } - - while (vol->dirs) - { - if (hfs_closedir(vol->dirs) == -1) - result = -1; - } - - /* close medium */ - - if (v_close(vol) == -1) - result = -1; - - FREE(vol); - -done: - return result; - -fail: - return -1; -} - - -/* - * NAME: hfs->flush() - * DESCRIPTION: flush all pending changes to an HFS volume - */ -int hfs_flush(hfsvol *vol) -{ - hfsfile *file; - - if (getvol(&vol) == -1) - goto fail; - - for (file = vol->files; file; file = file->next) - { - if (f_flush(file) == -1) - goto fail; - } - - if (v_flush(vol) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -#ifdef CP_NO_STATIC -/* - * NAME: hfs->flushall() - * DESCRIPTION: flush all pending changes to all mounted HFS volumes - */ -void hfs_flushall(void) -{ - hfsvol *vol; - - for (vol = hfs_mounts; vol; vol = vol->next) - hfs_flush(vol); -} - -/* - * NAME: hfs->umount() - * DESCRIPTION: close an HFS volume - */ -int hfs_umount(hfsvol *vol) -{ - int result = 0; - - if (getvol(&vol) == -1) - goto fail; - - if (--vol->refs) - { - result = v_flush(vol); - goto done; - } - - /* close all open files and directories */ - - while (vol->files) - { - if (hfs_close(vol->files) == -1) - result = -1; - } - - while (vol->dirs) - { - if (hfs_closedir(vol->dirs) == -1) - result = -1; - } - - /* close medium */ - - if (v_close(vol) == -1) - result = -1; - - /* remove from linked list of volumes */ - - if (vol->prev) - vol->prev->next = vol->next; - if (vol->next) - vol->next->prev = vol->prev; - - if (vol == hfs_mounts) - hfs_mounts = vol->next; - if (vol == curvol) - curvol = 0; - - FREE(vol); - -done: - return result; - -fail: - return -1; -} - -/* - * NAME: hfs->umountall() - * DESCRIPTION: unmount all mounted volumes - */ -void hfs_umountall(void) -{ - while (hfs_mounts) - hfs_umount(hfs_mounts); -} - -/* - * NAME: hfs->getvol() - * DESCRIPTION: return a pointer to a mounted volume - */ -hfsvol *hfs_getvol(const char *name) -{ - hfsvol *vol; - - if (name == 0) - return curvol; - - for (vol = hfs_mounts; vol; vol = vol->next) - { - if (d_relstring(name, vol->mdb.drVN) == 0) - return vol; - } - - return 0; -} - -/* - * NAME: hfs->setvol() - * DESCRIPTION: change the current volume - */ -void hfs_setvol(hfsvol *vol) -{ - curvol = vol; -} -#endif - -/* - * NAME: hfs->vstat() - * DESCRIPTION: return volume statistics - */ -int hfs_vstat(hfsvol *vol, hfsvolent *ent) -{ - if (getvol(&vol) == -1) - goto fail; - - strcpy(ent->name, vol->mdb.drVN); - - ent->flags = (vol->flags & HFS_VOL_READONLY) ? HFS_ISLOCKED : 0; - - ent->totbytes = vol->mdb.drNmAlBlks * vol->mdb.drAlBlkSiz; - ent->freebytes = vol->mdb.drFreeBks * vol->mdb.drAlBlkSiz; - - ent->alblocksz = vol->mdb.drAlBlkSiz; - ent->clumpsz = vol->mdb.drClpSiz; - - ent->numfiles = vol->mdb.drFilCnt; - ent->numdirs = vol->mdb.drDirCnt; - - ent->crdate = d_ltime(vol->mdb.drCrDate); - ent->mddate = d_ltime(vol->mdb.drLsMod); - ent->bkdate = d_ltime(vol->mdb.drVolBkUp); - - ent->blessed = vol->mdb.drFndrInfo[0]; - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->vsetattr() - * DESCRIPTION: change volume attributes - */ -int hfs_vsetattr(hfsvol *vol, hfsvolent *ent) -{ - if (getvol(&vol) == -1) - goto fail; - - if (ent->clumpsz % vol->mdb.drAlBlkSiz != 0) - ERROR(EINVAL, "illegal clump size"); - - /* make sure "blessed" folder exists */ - - if (ent->blessed && - v_getdthread(vol, ent->blessed, 0, 0) <= 0) - ERROR(EINVAL, "illegal blessed folder"); - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - vol->mdb.drClpSiz = ent->clumpsz; - - vol->mdb.drCrDate = d_mtime(ent->crdate); - vol->mdb.drLsMod = d_mtime(ent->mddate); - vol->mdb.drVolBkUp = d_mtime(ent->bkdate); - - vol->mdb.drFndrInfo[0] = ent->blessed; - - vol->flags |= HFS_VOL_UPDATE_MDB; - - return 0; - -fail: - return -1; -} - -/* High-Level Directory Routines =========================================== */ - -/* - * NAME: hfs->chdir() - * DESCRIPTION: change current HFS directory - */ -int hfs_chdir(hfsvol *vol, const char *path) -{ - CatDataRec data; - - if (getvol(&vol) == -1 || - v_resolve(&vol, path, &data, 0, 0, 0) <= 0) - goto fail; - - if (data.cdrType != cdrDirRec) - ERROR(ENOTDIR, 0); - - vol->cwd = data.u.dir.dirDirID; - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->getcwd() - * DESCRIPTION: return the current working directory ID - */ -unsigned long hfs_getcwd(hfsvol *vol) -{ - if (getvol(&vol) == -1) - return 0; - - return vol->cwd; -} - -/* - * NAME: hfs->setcwd() - * DESCRIPTION: set the current working directory ID - */ -int hfs_setcwd(hfsvol *vol, unsigned long id) -{ - if (getvol(&vol) == -1) - goto fail; - - if (id == vol->cwd) - goto done; - - /* make sure the directory exists */ - - if (v_getdthread(vol, id, 0, 0) <= 0) - goto fail; - - vol->cwd = id; - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->dirinfo() - * DESCRIPTION: given a directory ID, return its (name and) parent ID - */ -int hfs_dirinfo(hfsvol *vol, unsigned long *id, char *name) -{ - CatDataRec thread; - - if (getvol(&vol) == -1 || - v_getdthread(vol, *id, &thread, 0) <= 0) - goto fail; - - *id = thread.u.dthd.thdParID; - - if (name) - strcpy(name, thread.u.dthd.thdCName); - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->opendir() - * DESCRIPTION: prepare to read the contents of a directory - */ -hfsdir *hfs_opendir(hfsvol *vol, const char *path) -{ - hfsdir *dir = 0; - CatKeyRec key; - CatDataRec data; - byte pkey[HFS_CATKEYLEN]; - - if (getvol(&vol) == -1) - goto fail; - - dir = ALLOC(hfsdir, 1); - if (dir == 0) - ERROR(ENOMEM, 0); - - dir->vol = vol; - - if (*path == 0) - { -#ifdef CP_NO_STATIC - /* meta-directory containing root dirs from all mounted volumes */ - - dir->dirid = 0; - dir->vptr = hfs_mounts; -#else - assert(0); -#endif - } - else - { - if (v_resolve(&vol, path, &data, 0, 0, 0) <= 0) - goto fail; - - if (data.cdrType != cdrDirRec) - ERROR(ENOTDIR, 0); - - dir->dirid = data.u.dir.dirDirID; - dir->vptr = 0; - - r_makecatkey(&key, dir->dirid, ""); - r_packcatkey(&key, pkey, 0); - - if (bt_search(&vol->cat, pkey, &dir->n) <= 0) - goto fail; - } - - dir->prev = 0; - dir->next = vol->dirs; - - if (vol->dirs) - vol->dirs->prev = dir; - - vol->dirs = dir; - - return dir; - -fail: - FREE(dir); - return 0; -} - -/* - * NAME: hfs->readdir() - * DESCRIPTION: return the next entry in the directory - */ -int hfs_readdir(hfsdir *dir, hfsdirent *ent) -{ - CatKeyRec key; - CatDataRec data; - const byte *ptr; - - if (dir->dirid == 0) - { -#ifdef CP_NO_STATIC - hfsvol *vol; - char cname[HFS_MAX_FLEN + 1]; - - for (vol = hfs_mounts; vol; vol = vol->next) - { - if (vol == dir->vptr) - break; - } - - if (vol == 0) - ERROR(ENOENT, "no more entries"); - - if (v_getdthread(vol, HFS_CNID_ROOTDIR, &data, 0) <= 0 || - v_catsearch(vol, HFS_CNID_ROOTPAR, data.u.dthd.thdCName, - &data, cname, 0) <= 0) - goto fail; - - r_unpackdirent(HFS_CNID_ROOTPAR, cname, &data, ent); - - dir->vptr = vol->next; - - goto done; -#else - assert(0); -#endif - } - - if (dir->n.rnum == -1) - ERROR(ENOENT, "no more entries"); - - while (1) - { - ++dir->n.rnum; - - while (dir->n.rnum >= dir->n.nd.ndNRecs) - { - if (dir->n.nd.ndFLink == 0) - { - dir->n.rnum = -1; - ERROR(ENOENT, "no more entries"); - } - - if (bt_getnode(&dir->n, dir->n.bt, dir->n.nd.ndFLink) == -1) - { - dir->n.rnum = -1; - goto fail; - } - - dir->n.rnum = 0; - } - - ptr = HFS_NODEREC(dir->n, dir->n.rnum); - - r_unpackcatkey(ptr, &key); - - if (key.ckrParID != dir->dirid) - { - dir->n.rnum = -1; - ERROR(ENOENT, "no more entries"); - } - - r_unpackcatdata(HFS_RECDATA(ptr), &data); - - switch (data.cdrType) - { - case cdrDirRec: - case cdrFilRec: - r_unpackdirent(key.ckrParID, key.ckrCName, &data, ent); - goto done; - - case cdrThdRec: - case cdrFThdRec: - break; - - default: - dir->n.rnum = -1; - ERROR(EIO, "unexpected directory entry found"); - } - } - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->closedir() - * DESCRIPTION: stop reading a directory - */ -int hfs_closedir(hfsdir *dir) -{ - hfsvol *vol = dir->vol; - - if (dir->prev) - dir->prev->next = dir->next; - if (dir->next) - dir->next->prev = dir->prev; - if (dir == vol->dirs) - vol->dirs = dir->next; - - FREE(dir); - - return 0; -} - -/* High-Level File Routines ================================================ */ - -/* - * NAME: hfs->create() - * DESCRIPTION: create and open a new file - */ -hfsfile *hfs_create(hfsvol *vol, const char *path, - const char *type, const char *creator) -{ - hfsfile *file = 0; - unsigned long parid; - char name[HFS_MAX_FLEN + 1]; - CatKeyRec key; - byte record[HFS_MAX_CATRECLEN]; - unsigned reclen; - int found; - - if (getvol(&vol) == -1) - goto fail; - - file = ALLOC(hfsfile, 1); - if (file == 0) - ERROR(ENOMEM, 0); - - found = v_resolve(&vol, path, &file->cat, &parid, name, 0); - if (found == -1 || parid == 0) - goto fail; - - if (found) - ERROR(EEXIST, 0); - - if (parid == HFS_CNID_ROOTPAR) - ERROR(EINVAL, 0); - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - /* create file `name' in parent `parid' */ - - if (bt_space(&vol->cat, 1) == -1) - goto fail; - - f_init(file, vol, vol->mdb.drNxtCNID++, name); - vol->flags |= HFS_VOL_UPDATE_MDB; - - file->parid = parid; - - /* create catalog record */ - - file->cat.u.fil.filUsrWds.fdType = - d_getsl((const unsigned char *) type); - file->cat.u.fil.filUsrWds.fdCreator = - d_getsl((const unsigned char *) creator); - - file->cat.u.fil.filCrDat = d_mtime(time(0)); - file->cat.u.fil.filMdDat = file->cat.u.fil.filCrDat; - - r_makecatkey(&key, file->parid, file->name); - r_packcatrec(&key, &file->cat, record, &reclen); - - if (bt_insert(&vol->cat, record, reclen) == -1 || - v_adjvalence(vol, file->parid, 0, 1) == -1) - goto fail; - - /* package file handle for user */ - - file->next = vol->files; - - if (vol->files) - vol->files->prev = file; - - vol->files = file; - - return file; - -fail: - FREE(file); - return 0; -} - -/* - * NAME: hfs->open() - * DESCRIPTION: prepare a file for I/O - */ -hfsfile *hfs_open(hfsvol *vol, const char *path) -{ - hfsfile *file = 0; - - if (getvol(&vol) == -1) - goto fail; - - file = ALLOC(hfsfile, 1); - if (file == 0) - ERROR(ENOMEM, 0); - - if (v_resolve(&vol, path, &file->cat, &file->parid, file->name, 0) <= 0) - goto fail; - - if (file->cat.cdrType != cdrFilRec) - ERROR(EISDIR, 0); - - /* package file handle for user */ - - file->vol = vol; - file->flags = 0; - - f_selectfork(file, fkData); - - file->prev = 0; - file->next = vol->files; - - if (vol->files) - vol->files->prev = file; - - vol->files = file; - - return file; - -fail: - FREE(file); - return 0; -} - -/* - * NAME: hfs->setfork() - * DESCRIPTION: select file fork for I/O operations - */ -int hfs_setfork(hfsfile *file, int fork) -{ - int result = 0; - - if (f_trunc(file) == -1) - result = -1; - - f_selectfork(file, fork ? fkRsrc : fkData); - - return result; -} - -/* - * NAME: hfs->getfork() - * DESCRIPTION: return the current fork for I/O operations - */ -int hfs_getfork(hfsfile *file) -{ - return file->fork != fkData; -} - -/* - * NAME: hfs->read() - * DESCRIPTION: read from an open file - */ -unsigned long hfs_read(hfsfile *file, void *buf, unsigned long len) -{ - unsigned long *lglen, count; - byte *ptr = buf; - - f_getptrs(file, 0, &lglen, 0); - - if (file->pos + len > *lglen) - len = *lglen - file->pos; - - count = len; - while (count) - { - unsigned long bnum, offs, chunk; - - bnum = file->pos >> HFS_BLOCKSZ_BITS; - offs = file->pos & (HFS_BLOCKSZ - 1); - - chunk = HFS_BLOCKSZ - offs; - if (chunk > count) - chunk = count; - - if (offs == 0 && chunk == HFS_BLOCKSZ) - { - if (f_getblock(file, bnum, (block *) ptr) == -1) - goto fail; - } - else - { - block b; - - if (f_getblock(file, bnum, &b) == -1) - goto fail; - - memcpy(ptr, b + offs, chunk); - } - - ptr += chunk; - - file->pos += chunk; - count -= chunk; - } - - return len; - -fail: - return -1; -} - -/* - * NAME: hfs->write() - * DESCRIPTION: write to an open file - */ -unsigned long hfs_write(hfsfile *file, const void *buf, unsigned long len) -{ - unsigned long *lglen, *pylen, count; - const byte *ptr = buf; - - if (file->vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - f_getptrs(file, 0, &lglen, &pylen); - - count = len; - - /* set flag to update (at least) the modification time */ - - if (count) - { - file->cat.u.fil.filMdDat = d_mtime(time(0)); - file->flags |= HFS_FILE_UPDATE_CATREC; - } - - while (count) - { - unsigned long bnum, offs, chunk; - - bnum = file->pos >> HFS_BLOCKSZ_BITS; - offs = file->pos & (HFS_BLOCKSZ - 1); - - chunk = HFS_BLOCKSZ - offs; - if (chunk > count) - chunk = count; - - if (file->pos + chunk > *pylen) - { - if (bt_space(&file->vol->ext, 1) == -1 || - f_alloc(file) == -1) - goto fail; - } - - if (offs == 0 && chunk == HFS_BLOCKSZ) - { - if (f_putblock(file, bnum, (block *) ptr) == -1) - goto fail; - } - else - { - block b; - - if (f_getblock(file, bnum, &b) == -1) - goto fail; - - memcpy(b + offs, ptr, chunk); - - if (f_putblock(file, bnum, &b) == -1) - goto fail; - } - - ptr += chunk; - - file->pos += chunk; - count -= chunk; - - if (file->pos > *lglen) - *lglen = file->pos; - } - - return len; - -fail: - return -1; -} - -/* - * NAME: hfs->truncate() - * DESCRIPTION: truncate an open file - */ -int hfs_truncate(hfsfile *file, unsigned long len) -{ - unsigned long *lglen; - - f_getptrs(file, 0, &lglen, 0); - - if (*lglen > len) - { - if (file->vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - *lglen = len; - - file->cat.u.fil.filMdDat = d_mtime(time(0)); - file->flags |= HFS_FILE_UPDATE_CATREC; - - if (file->pos > len) - file->pos = len; - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->seek() - * DESCRIPTION: change file seek pointer - */ -unsigned long hfs_seek(hfsfile *file, long offset, int from) -{ - unsigned long *lglen, newpos; - - f_getptrs(file, 0, &lglen, 0); - - switch (from) - { - case HFS_SEEK_SET: - newpos = (offset < 0) ? 0 : offset; - break; - - case HFS_SEEK_CUR: - if (offset < 0 && (unsigned long) -offset > file->pos) - newpos = 0; - else - newpos = file->pos + offset; - break; - - case HFS_SEEK_END: - if (offset < 0 && (unsigned long) -offset > *lglen) - newpos = 0; - else - newpos = *lglen + offset; - break; - - default: - ERROR(EINVAL, 0); - } - - if (newpos > *lglen) - newpos = *lglen; - - file->pos = newpos; - - return newpos; - -fail: - return -1; -} - -/* - * NAME: hfs->close() - * DESCRIPTION: close a file - */ -int hfs_close(hfsfile *file) -{ - hfsvol *vol = file->vol; - int result = 0; - - if (f_trunc(file) == -1 || - f_flush(file) == -1) - result = -1; - - if (file->prev) - file->prev->next = file->next; - if (file->next) - file->next->prev = file->prev; - if (file == vol->files) - vol->files = file->next; - - FREE(file); - - return result; -} - -/* High-Level Catalog Routines ============================================= */ - -/* - * NAME: hfs->stat() - * DESCRIPTION: return catalog information for an arbitrary path - */ -int hfs_stat(hfsvol *vol, const char *path, hfsdirent *ent) -{ - CatDataRec data; - unsigned long parid; - char name[HFS_MAX_FLEN + 1]; - - if (getvol(&vol) == -1 || - v_resolve(&vol, path, &data, &parid, name, 0) <= 0) - goto fail; - - r_unpackdirent(parid, name, &data, ent); - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->fstat() - * DESCRIPTION: return catalog information for an open file - */ -int hfs_fstat(hfsfile *file, hfsdirent *ent) -{ - r_unpackdirent(file->parid, file->name, &file->cat, ent); - - return 0; -} - -/* - * NAME: hfs->setattr() - * DESCRIPTION: change a file's attributes - */ -int hfs_setattr(hfsvol *vol, const char *path, const hfsdirent *ent) -{ - CatDataRec data; - node n; - - if (getvol(&vol) == -1 || - v_resolve(&vol, path, &data, 0, 0, &n) <= 0) - goto fail; - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - r_packdirent(&data, ent); - - return v_putcatrec(&data, &n); - -fail: - return -1; -} - -/* - * NAME: hfs->fsetattr() - * DESCRIPTION: change an open file's attributes - */ -int hfs_fsetattr(hfsfile *file, const hfsdirent *ent) -{ - if (file->vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - r_packdirent(&file->cat, ent); - - file->flags |= HFS_FILE_UPDATE_CATREC; - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->mkdir() - * DESCRIPTION: create a new directory - */ -int hfs_mkdir(hfsvol *vol, const char *path) -{ - CatDataRec data; - unsigned long parid; - char name[HFS_MAX_FLEN + 1]; - int found; - - if (getvol(&vol) == -1) - goto fail; - - found = v_resolve(&vol, path, &data, &parid, name, 0); - if (found == -1 || parid == 0) - goto fail; - - if (found) - ERROR(EEXIST, 0); - - if (parid == HFS_CNID_ROOTPAR) - ERROR(EINVAL, 0); - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - return v_mkdir(vol, parid, name); - -fail: - return -1; -} - -/* - * NAME: hfs->rmdir() - * DESCRIPTION: delete an empty directory - */ -int hfs_rmdir(hfsvol *vol, const char *path) -{ - CatKeyRec key; - CatDataRec data; - unsigned long parid; - char name[HFS_MAX_FLEN + 1]; - byte pkey[HFS_CATKEYLEN]; - - if (getvol(&vol) == -1 || - v_resolve(&vol, path, &data, &parid, name, 0) <= 0) - goto fail; - - if (data.cdrType != cdrDirRec) - ERROR(ENOTDIR, 0); - - if (data.u.dir.dirVal != 0) - ERROR(ENOTEMPTY, 0); - - if (parid == HFS_CNID_ROOTPAR) - ERROR(EINVAL, 0); - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - /* delete directory record */ - - r_makecatkey(&key, parid, name); - r_packcatkey(&key, pkey, 0); - - if (bt_delete(&vol->cat, pkey) == -1) - goto fail; - - /* delete thread record */ - - r_makecatkey(&key, data.u.dir.dirDirID, ""); - r_packcatkey(&key, pkey, 0); - - if (bt_delete(&vol->cat, pkey) == -1 || - v_adjvalence(vol, parid, 1, -1) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->delete() - * DESCRIPTION: remove both forks of a file - */ -int hfs_delete(hfsvol *vol, const char *path) -{ - hfsfile file; - CatKeyRec key; - byte pkey[HFS_CATKEYLEN]; - int found; - - if (getvol(&vol) == -1 || - v_resolve(&vol, path, &file.cat, &file.parid, file.name, 0) <= 0) - goto fail; - - if (file.cat.cdrType != cdrFilRec) - ERROR(EISDIR, 0); - - if (file.parid == HFS_CNID_ROOTPAR) - ERROR(EINVAL, 0); - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - /* free allocation blocks */ - - file.vol = vol; - file.flags = 0; - - file.cat.u.fil.filLgLen = 0; - file.cat.u.fil.filRLgLen = 0; - - f_selectfork(&file, fkData); - if (f_trunc(&file) == -1) - goto fail; - - f_selectfork(&file, fkRsrc); - if (f_trunc(&file) == -1) - goto fail; - - /* delete file record */ - - r_makecatkey(&key, file.parid, file.name); - r_packcatkey(&key, pkey, 0); - - if (bt_delete(&vol->cat, pkey) == -1 || - v_adjvalence(vol, file.parid, 0, -1) == -1) - goto fail; - - /* delete file thread, if any */ - - found = v_getfthread(vol, file.cat.u.fil.filFlNum, 0, 0); - if (found == -1) - goto fail; - - if (found) - { - r_makecatkey(&key, file.cat.u.fil.filFlNum, ""); - r_packcatkey(&key, pkey, 0); - - if (bt_delete(&vol->cat, pkey) == -1) - goto fail; - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: hfs->rename() - * DESCRIPTION: change the name of and/or move a file or directory - */ -int hfs_rename(hfsvol *vol, const char *srcpath, const char *dstpath) -{ - hfsvol *srcvol; - CatDataRec src, dst; - unsigned long srcid, dstid; - CatKeyRec key; - char srcname[HFS_MAX_FLEN + 1], dstname[HFS_MAX_FLEN + 1]; - byte record[HFS_MAX_CATRECLEN]; - unsigned int reclen; - int found, isdir, moving; - node n; - - if (getvol(&vol) == -1 || - v_resolve(&vol, srcpath, &src, &srcid, srcname, 0) <= 0) - goto fail; - - isdir = (src.cdrType == cdrDirRec); - srcvol = vol; - - found = v_resolve(&vol, dstpath, &dst, &dstid, dstname, 0); - if (found == -1) - goto fail; - - if (vol != srcvol) - ERROR(EINVAL, "can't move across volumes"); - - if (dstid == 0) - ERROR(ENOENT, "bad destination path"); - - if (found && - dst.cdrType == cdrDirRec && - dst.u.dir.dirDirID != src.u.dir.dirDirID) - { - dstid = dst.u.dir.dirDirID; - strcpy(dstname, srcname); - - found = v_catsearch(vol, dstid, dstname, 0, 0, 0); - if (found == -1) - goto fail; - } - - moving = (srcid != dstid); - - if (found) - { - const char *ptr; - - ptr = strrchr(dstpath, ':'); - if (ptr == 0) - ptr = dstpath; - else - ++ptr; - - if (*ptr) - strcpy(dstname, ptr); - - if (! moving && strcmp(srcname, dstname) == 0) - goto done; /* source and destination are identical */ - - if (moving || d_relstring(srcname, dstname)) - ERROR(EEXIST, "can't use destination name"); - } - - /* can't move anything into the root directory's parent */ - - if (moving && dstid == HFS_CNID_ROOTPAR) - ERROR(EINVAL, "can't move above root directory"); - - if (moving && isdir) - { - unsigned long id; - - /* can't move root directory anywhere */ - - if (src.u.dir.dirDirID == HFS_CNID_ROOTDIR) - ERROR(EINVAL, "can't move root directory"); - - /* make sure we aren't trying to move a directory inside itself */ - - for (id = dstid; id != HFS_CNID_ROOTDIR; id = dst.u.dthd.thdParID) - { - if (id == src.u.dir.dirDirID) - ERROR(EINVAL, "can't move directory inside itself"); - - if (v_getdthread(vol, id, &dst, 0) <= 0) - goto fail; - } - } - - if (vol->flags & HFS_VOL_READONLY) - ERROR(EROFS, 0); - - /* change volume name */ - - if (dstid == HFS_CNID_ROOTPAR) - { - if (! validvname(dstname)) - goto fail; - - strcpy(vol->mdb.drVN, dstname); - vol->flags |= HFS_VOL_UPDATE_MDB; - } - - /* remove source record */ - - r_makecatkey(&key, srcid, srcname); - r_packcatkey(&key, record, 0); - - if (bt_delete(&vol->cat, record) == -1) - goto fail; - - /* insert destination record */ - - r_makecatkey(&key, dstid, dstname); - r_packcatrec(&key, &src, record, &reclen); - - if (bt_insert(&vol->cat, record, reclen) == -1) - goto fail; - - /* update thread record */ - - if (isdir) - { - if (v_getdthread(vol, src.u.dir.dirDirID, &dst, &n) <= 0) - goto fail; - - dst.u.dthd.thdParID = dstid; - strcpy(dst.u.dthd.thdCName, dstname); - - if (v_putcatrec(&dst, &n) == -1) - goto fail; - } - else - { - found = v_getfthread(vol, src.u.fil.filFlNum, &dst, &n); - if (found == -1) - goto fail; - - if (found) - { - dst.u.fthd.fthdParID = dstid; - strcpy(dst.u.fthd.fthdCName, dstname); - - if (v_putcatrec(&dst, &n) == -1) - goto fail; - } - } - - /* update directory valences */ - - if (moving) - { - if (v_adjvalence(vol, srcid, isdir, -1) == -1 || - v_adjvalence(vol, dstid, isdir, 1) == -1) - goto fail; - } - -done: - return 0; - -fail: - return -1; -} - -/* High-Level Media Routines =============================================== */ - -#ifdef CP_NOT_USED -/* - * NAME: hfs->zero() - * DESCRIPTION: initialize medium with new/empty DDR and partition map - */ -int hfs_zero(const char *path, unsigned int maxparts, unsigned long *blocks) -{ - hfsvol vol; - - v_init(&vol, HFS_OPT_NOCACHE); - - if (maxparts < 1) - ERROR(EINVAL, "must allow at least 1 partition"); - - if (v_open(&vol, path, HFS_MODE_RDWR) == -1 || - v_geometry(&vol, 0) == -1) - goto fail; - - if (m_zeroddr(&vol) == -1 || - m_zeropm(&vol, 1 + maxparts) == -1) - goto fail; - - if (blocks) - { - Partition map; - int found; - - found = m_findpmentry(&vol, "Apple_Free", &map, 0); - if (found == -1) - goto fail; - - if (! found) - ERROR(EIO, "unable to determine free partition space"); - - *blocks = map.pmPartBlkCnt; - } - - if (v_close(&vol) == -1) - goto fail; - - return 0; - -fail: - v_close(&vol); - return -1; -} -#endif - -#ifdef CP_NOT_USED -/* - * NAME: hfs->mkpart() - * DESCRIPTION: create a new HFS partition - */ -int hfs_mkpart(const char *path, unsigned long len) -{ - hfsvol vol; - - v_init(&vol, HFS_OPT_NOCACHE); - - if (v_open(&vol, path, HFS_MODE_RDWR) == -1) - goto fail; - - if (m_mkpart(&vol, "MacOS", "Apple_HFS", len) == -1) - goto fail; - - if (v_close(&vol) == -1) - goto fail; - - return 0; - -fail: - v_close(&vol); - return -1; -} - -/* - * NAME: hfs->nparts() - * DESCRIPTION: return the number of HFS partitions in the medium - */ -int hfs_nparts(const char *path) -{ - hfsvol vol; - int nparts, found; - Partition map; - unsigned long bnum = 0; - - v_init(&vol, HFS_OPT_NOCACHE); - - if (v_open(&vol, path, HFS_MODE_RDONLY) == -1) - goto fail; - - nparts = 0; - while (1) - { - found = m_findpmentry(&vol, "Apple_HFS", &map, &bnum); - if (found == -1) - goto fail; - - if (! found) - break; - - ++nparts; - } - - if (v_close(&vol) == -1) - goto fail; - - return nparts; - -fail: - v_close(&vol); - return -1; -} -#endif - -/* - * NAME: compare() - * DESCRIPTION: comparison function for qsort of blocks to be spared - */ -//static -//int compare(const unsigned int *n1, const unsigned int *n2) -//{ -// return *n1 - *n2; -//} - -/* - * NAME: hfs->format() - * DESCRIPTION: write a new filesystem - */ -#ifdef CP_NOT_USED -int hfs_format(const char *path, int pnum, int mode, const char *vname, - unsigned int nbadblocks, const unsigned long badblocks[]) -#else -int hfs_callback_format(oscallback func, void* cookie, int mode, - const char* vname) -#endif -{ - hfsvol vol; - btree *ext = &vol.ext; - btree *cat = &vol.cat; - unsigned int i, *badalloc = 0; - - v_init(&vol, mode); - - if (! validvname(vname)) - goto fail; - -#ifdef CP_NOT_USED - if (v_open(&vol, path, HFS_MODE_RDWR) == -1 || - v_geometry(&vol, pnum) == -1) - goto fail; -#else - if (v_callback_open(&vol, func, cookie) != 0 || - v_geometry(&vol, 0) != 0) - goto fail; -#endif - - /* initialize volume geometry */ - - vol.lpa = 1 + ((vol.vlen - 6) >> 16); - - if (vol.flags & HFS_OPT_2048) - vol.lpa = (vol.lpa + 3) & ~3; - - vol.vbmsz = (vol.vlen / vol.lpa + 0x0fff) >> 12; - - vol.mdb.drSigWord = HFS_SIGWORD; - vol.mdb.drCrDate = d_mtime(time(0)); - vol.mdb.drLsMod = vol.mdb.drCrDate; - vol.mdb.drAtrb = 0; - vol.mdb.drNmFls = 0; - vol.mdb.drVBMSt = 3; - vol.mdb.drAllocPtr = 0; - - vol.mdb.drAlBlkSiz = vol.lpa << HFS_BLOCKSZ_BITS; - vol.mdb.drClpSiz = vol.mdb.drAlBlkSiz << 2; - vol.mdb.drAlBlSt = vol.mdb.drVBMSt + vol.vbmsz; - - if (vol.flags & HFS_OPT_2048) - vol.mdb.drAlBlSt = ((vol.vstart & 3) + vol.mdb.drAlBlSt + 3) & ~3; - - vol.mdb.drNmAlBlks = (vol.vlen - 2 - vol.mdb.drAlBlSt) / vol.lpa; - - vol.mdb.drNxtCNID = HFS_CNID_ROOTDIR; /* modified later */ - vol.mdb.drFreeBks = vol.mdb.drNmAlBlks; - - strcpy(vol.mdb.drVN, vname); - - vol.mdb.drVolBkUp = 0; - vol.mdb.drVSeqNum = 0; - vol.mdb.drWrCnt = 0; - - vol.mdb.drXTClpSiz = vol.mdb.drNmAlBlks / 128 * vol.mdb.drAlBlkSiz; - vol.mdb.drCTClpSiz = vol.mdb.drXTClpSiz; - - vol.mdb.drNmRtDirs = 0; - vol.mdb.drFilCnt = 0; - vol.mdb.drDirCnt = -1; /* incremented when root directory is created */ - - for (i = 0; i < 8; ++i) - vol.mdb.drFndrInfo[i] = 0; - - vol.mdb.drEmbedSigWord = 0x0000; - vol.mdb.drEmbedExtent.xdrStABN = 0; - vol.mdb.drEmbedExtent.xdrNumABlks = 0; - - /* vol.mdb.drXTFlSize */ - /* vol.mdb.drCTFlSize */ - - /* vol.mdb.drXTExtRec[0..2] */ - /* vol.mdb.drCTExtRec[0..2] */ - - vol.flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB; - - /* initialize volume bitmap */ - - vol.vbm = ALLOC(block, vol.vbmsz); - if (vol.vbm == 0) - ERROR(ENOMEM, 0); - - memset(vol.vbm, 0, vol.vbmsz << HFS_BLOCKSZ_BITS); - - vol.flags |= HFS_VOL_UPDATE_VBM; - - /* perform initial bad block sparing */ - -#ifdef CP_NOT_USED - if (nbadblocks > 0) - { - if (nbadblocks * 4 > vol.vlen) - ERROR(EINVAL, "volume contains too many bad blocks"); - - badalloc = ALLOC(unsigned int, nbadblocks); - if (badalloc == 0) - ERROR(ENOMEM, 0); - - if (vol.mdb.drNmAlBlks == 1594) - vol.mdb.drFreeBks = --vol.mdb.drNmAlBlks; - - for (i = 0; i < nbadblocks; ++i) - { - unsigned long bnum; - unsigned int anum; - - bnum = badblocks[i]; - - if (bnum < vol.mdb.drAlBlSt || bnum == vol.vlen - 2) - ERROR(EINVAL, "can't spare critical bad block"); - else if (bnum >= vol.vlen) - ERROR(EINVAL, "bad block not in volume"); - - anum = (bnum - vol.mdb.drAlBlSt) / vol.lpa; - - if (anum < vol.mdb.drNmAlBlks) - BMSET(vol.vbm, anum); - - badalloc[i] = anum; - } - - vol.mdb.drAtrb |= HFS_ATRB_BBSPARED; - } -#endif - - /* create extents overflow file */ - - n_init(&ext->hdrnd, ext, ndHdrNode, 0); - - ext->hdrnd.nnum = 0; - ext->hdrnd.nd.ndNRecs = 3; - ext->hdrnd.roff[1] = 0x078; - ext->hdrnd.roff[2] = 0x0f8; - ext->hdrnd.roff[3] = 0x1f8; - - memset(HFS_NODEREC(ext->hdrnd, 1), 0, 128); - - ext->hdr.bthDepth = 0; - ext->hdr.bthRoot = 0; - ext->hdr.bthNRecs = 0; - ext->hdr.bthFNode = 0; - ext->hdr.bthLNode = 0; - ext->hdr.bthNodeSize = HFS_BLOCKSZ; - ext->hdr.bthKeyLen = 0x07; - ext->hdr.bthNNodes = 0; - ext->hdr.bthFree = 0; - for (i = 0; i < 76; ++i) - ext->hdr.bthResv[i] = 0; - - ext->map = ALLOC(byte, HFS_MAP1SZ); - if (ext->map == 0) - ERROR(ENOMEM, 0); - - memset(ext->map, 0, HFS_MAP1SZ); - BMSET(ext->map, 0); - - ext->mapsz = HFS_MAP1SZ; - ext->flags = HFS_BT_UPDATE_HDR; - - /* create catalog file */ - - n_init(&cat->hdrnd, cat, ndHdrNode, 0); - - cat->hdrnd.nnum = 0; - cat->hdrnd.nd.ndNRecs = 3; - cat->hdrnd.roff[1] = 0x078; - cat->hdrnd.roff[2] = 0x0f8; - cat->hdrnd.roff[3] = 0x1f8; - - memset(HFS_NODEREC(cat->hdrnd, 1), 0, 128); - - cat->hdr.bthDepth = 0; - cat->hdr.bthRoot = 0; - cat->hdr.bthNRecs = 0; - cat->hdr.bthFNode = 0; - cat->hdr.bthLNode = 0; - cat->hdr.bthNodeSize = HFS_BLOCKSZ; - cat->hdr.bthKeyLen = 0x25; - cat->hdr.bthNNodes = 0; - cat->hdr.bthFree = 0; - for (i = 0; i < 76; ++i) - cat->hdr.bthResv[i] = 0; - - cat->map = ALLOC(byte, HFS_MAP1SZ); - if (cat->map == 0) - ERROR(ENOMEM, 0); - - memset(cat->map, 0, HFS_MAP1SZ); - BMSET(cat->map, 0); - - cat->mapsz = HFS_MAP1SZ; - cat->flags = HFS_BT_UPDATE_HDR; - - /* allocate space for header nodes (and initial extents) */ - - if (bt_space(ext, 1) == -1 || - bt_space(cat, 1) == -1) - goto fail; - - --ext->hdr.bthFree; - --cat->hdr.bthFree; - - /* create extent records for bad blocks */ - -#ifdef CP_NOT_USED - if (nbadblocks > 0) - { - hfsfile bbfile; - ExtDescriptor extent; - ExtDataRec *extrec; - ExtKeyRec key; - byte record[HFS_MAX_EXTRECLEN]; - unsigned int reclen; - - f_init(&bbfile, &vol, HFS_CNID_BADALLOC, "bad blocks"); - - qsort(badalloc, nbadblocks, sizeof(*badalloc), - (int (*)(const void *, const void *)) compare); - - for (i = 0; i < nbadblocks; ++i) - { - if (i == 0 || badalloc[i] != extent.xdrStABN) - { - extent.xdrStABN = badalloc[i]; - extent.xdrNumABlks = 1; - - if (extent.xdrStABN < vol.mdb.drNmAlBlks && - f_addextent(&bbfile, &extent) == -1) - goto fail; - } - } - - /* flush local extents into extents overflow file */ - - f_getptrs(&bbfile, &extrec, 0, 0); - - r_makeextkey(&key, bbfile.fork, bbfile.cat.u.fil.filFlNum, 0); - r_packextrec(&key, extrec, record, &reclen); - - if (bt_insert(&vol.ext, record, reclen) == -1) - goto fail; - } -#endif - - vol.flags |= HFS_VOL_MOUNTED; - - /* create root directory */ - - if (v_mkdir(&vol, HFS_CNID_ROOTPAR, vname) == -1) - goto fail; - - vol.mdb.drNxtCNID = 16; /* first CNID not reserved by Apple */ - - /* write boot blocks */ - - if (m_zerobb(&vol) == -1) - goto fail; - - /* zero other unused space, if requested */ - - if (vol.flags & HFS_OPT_ZERO) - { - block b; - unsigned long bnum; - - memset(&b, 0, sizeof(b)); - - /* between MDB and VBM (never) */ - - for (bnum = 3; bnum < vol.mdb.drVBMSt; ++bnum) - b_writelb(&vol, bnum, &b); - - /* between VBM and first allocation block (sometimes if HFS_OPT_2048) */ - - for (bnum = vol.mdb.drVBMSt + vol.vbmsz; bnum < vol.mdb.drAlBlSt; ++bnum) - b_writelb(&vol, bnum, &b); - - /* between last allocation block and alternate MDB (sometimes) */ - - for (bnum = vol.mdb.drAlBlSt + vol.mdb.drNmAlBlks * vol.lpa; - bnum < vol.vlen - 2; ++bnum) - b_writelb(&vol, bnum, &b); - - /* final block (always) */ - - b_writelb(&vol, vol.vlen - 1, &b); - } - - /* flush remaining state and close volume */ - - if (v_close(&vol) == -1) - goto fail; - - FREE(badalloc); - - return 0; - -fail: - v_close(&vol); - - FREE(badalloc); - - return -1; -} - diff --git a/ciderpress/diskimg/libhfs/hfs.h b/ciderpress/diskimg/libhfs/hfs.h deleted file mode 100644 index e0b9499..0000000 --- a/ciderpress/diskimg/libhfs/hfs.h +++ /dev/null @@ -1,201 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -#ifdef __cplusplus -extern "C" { -#endif - -# include - -# define HFS_BLOCKSZ 512 -# define HFS_BLOCKSZ_BITS 9 - -# define HFS_MAX_FLEN 31 -# define HFS_MAX_VLEN 27 - -typedef struct _hfsvol_ hfsvol; -typedef struct _hfsfile_ hfsfile; -typedef struct _hfsdir_ hfsdir; - -typedef struct { - char name[HFS_MAX_VLEN + 1]; /* name of volume (MacOS Standard Roman) */ - int flags; /* volume flags */ - - unsigned long totbytes; /* total bytes on volume */ - unsigned long freebytes; /* free bytes on volume */ - - unsigned long alblocksz; /* volume allocation block size */ - unsigned long clumpsz; /* default file clump size */ - - unsigned long numfiles; /* number of files in volume */ - unsigned long numdirs; /* number of directories in volume */ - - time_t crdate; /* volume creation date */ - time_t mddate; /* last volume modification date */ - time_t bkdate; /* last volume backup date */ - - unsigned long blessed; /* CNID of MacOS System Folder */ -} hfsvolent; - -typedef struct { - char name[HFS_MAX_FLEN + 1]; /* catalog name (MacOS Standard Roman) */ - int flags; /* bit flags */ - unsigned long cnid; /* catalog node id (CNID) */ - unsigned long parid; /* CNID of parent directory */ - - time_t crdate; /* date of creation */ - time_t mddate; /* date of last modification */ - time_t bkdate; /* date of last backup */ - - short fdflags; /* Macintosh Finder flags */ - - struct { - signed short v; /* Finder icon vertical coordinate */ - signed short h; /* horizontal coordinate */ - } fdlocation; - - union { - struct { - unsigned long dsize; /* size of data fork */ - unsigned long rsize; /* size of resource fork */ - - char type[5]; /* file type code (plus null) */ - char creator[5]; /* file creator code (plus null) */ - } file; - - struct { - unsigned short valence; /* number of items in directory */ - - struct { - signed short top; /* top edge of folder's rectangle */ - signed short left; /* left edge */ - signed short bottom; /* bottom edge */ - signed short right; /* right edge */ - } rect; - } dir; - } u; -} hfsdirent; - -# define HFS_ISDIR 0x0001 -# define HFS_ISLOCKED 0x0002 - -# define HFS_CNID_ROOTPAR 1 -# define HFS_CNID_ROOTDIR 2 -# define HFS_CNID_EXT 3 -# define HFS_CNID_CAT 4 -# define HFS_CNID_BADALLOC 5 - -# define HFS_FNDR_ISONDESK (1 << 0) -# define HFS_FNDR_COLOR 0x0e -# define HFS_FNDR_COLORRESERVED (1 << 4) -# define HFS_FNDR_REQUIRESSWITCHLAUNCH (1 << 5) -# define HFS_FNDR_ISSHARED (1 << 6) -# define HFS_FNDR_HASNOINITS (1 << 7) -# define HFS_FNDR_HASBEENINITED (1 << 8) -# define HFS_FNDR_RESERVED (1 << 9) -# define HFS_FNDR_HASCUSTOMICON (1 << 10) -# define HFS_FNDR_ISSTATIONERY (1 << 11) -# define HFS_FNDR_NAMELOCKED (1 << 12) -# define HFS_FNDR_HASBUNDLE (1 << 13) -# define HFS_FNDR_ISINVISIBLE (1 << 14) -# define HFS_FNDR_ISALIAS (1 << 15) - -extern const char *hfs_error; -extern const unsigned char hfs_charorder[]; - -# define HFS_MODE_RDONLY 0 -# define HFS_MODE_RDWR 1 -# define HFS_MODE_ANY 2 - -# define HFS_MODE_MASK 0x0003 - -# define HFS_OPT_NOCACHE 0x0100 -# define HFS_OPT_2048 0x0200 -# define HFS_OPT_ZERO 0x0400 - -# define HFS_SEEK_SET 0 -# define HFS_SEEK_CUR 1 -# define HFS_SEEK_END 2 - -#ifdef CP_NO_STATIC -hfsvol *hfs_mount(const char *, int, int); -#endif -int hfs_flush(hfsvol *); -#ifdef CP_NO_STATIC -void hfs_flushall(void); -int hfs_umount(hfsvol *); -void hfs_umountall(void); -hfsvol *hfs_getvol(const char *); -void hfs_setvol(hfsvol *); -#endif - -int hfs_vstat(hfsvol *, hfsvolent *); -int hfs_vsetattr(hfsvol *, hfsvolent *); - -int hfs_chdir(hfsvol *, const char *); -unsigned long hfs_getcwd(hfsvol *); -int hfs_setcwd(hfsvol *, unsigned long); -int hfs_dirinfo(hfsvol *, unsigned long *, char *); - -hfsdir *hfs_opendir(hfsvol *, const char *); -int hfs_readdir(hfsdir *, hfsdirent *); -int hfs_closedir(hfsdir *); - -hfsfile *hfs_create(hfsvol *, const char *, const char *, const char *); -hfsfile *hfs_open(hfsvol *, const char *); -int hfs_setfork(hfsfile *, int); -int hfs_getfork(hfsfile *); -unsigned long hfs_read(hfsfile *, void *, unsigned long); -unsigned long hfs_write(hfsfile *, const void *, unsigned long); -int hfs_truncate(hfsfile *, unsigned long); -unsigned long hfs_seek(hfsfile *, long, int); -int hfs_close(hfsfile *); - -int hfs_stat(hfsvol *, const char *, hfsdirent *); -int hfs_fstat(hfsfile *, hfsdirent *); -int hfs_setattr(hfsvol *, const char *, const hfsdirent *); -int hfs_fsetattr(hfsfile *, const hfsdirent *); - -int hfs_mkdir(hfsvol *, const char *); -int hfs_rmdir(hfsvol *, const char *); - -int hfs_delete(hfsvol *, const char *); -int hfs_rename(hfsvol *, const char *, const char *); - -int hfs_zero(const char *, unsigned int, unsigned long *); -int hfs_mkpart(const char *, unsigned long); -int hfs_nparts(const char *); - -int hfs_format(const char *, int, int, - const char *, unsigned int, const unsigned long []); - -/* CiderPress callback interface */ -enum { HFS_CB_VOLSIZE, HFS_CB_READ, HFS_CB_WRITE, HFS_CB_SEEK }; -typedef unsigned long (*oscallback)(void* cookie, int op, unsigned long arg1, - void* arg2); -hfsvol* hfs_callback_open(oscallback func, void* cookie, int mode); -int hfs_callback_close(hfsvol* vol); -int hfs_callback_format(oscallback func, void* cookie, int mode, - const char* vname); - -#ifdef __cplusplus -}; -#endif diff --git a/ciderpress/diskimg/libhfs/libhfs.h b/ciderpress/diskimg/libhfs/libhfs.h deleted file mode 100644 index b9131a1..0000000 --- a/ciderpress/diskimg/libhfs/libhfs.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# include "hfs.h" -# include "apple.h" - -//extern int errno; -# include - -# define ERROR(code, str) \ - do { hfs_error = (str), errno = (code); goto fail; } while (0) - -# ifdef DEBUG -# define ASSERT(cond) do { if (! (cond)) abort(); } while (0) -# else -# define ASSERT(cond) /* nothing */ -# endif - -# define SIZE(type, n) ((size_t) (sizeof(type) * (n))) -# define ALLOC(type, n) ((type *) malloc(SIZE(type, n))) -# define ALLOCX(type, n) ((n) ? ALLOC(type, n) : (type *) 0) -# define FREE(ptr) ((ptr) ? (void) free((void *) ptr) : (void) 0) - -# define REALLOC(ptr, type, n) \ - ((type *) ((ptr) ? realloc(ptr, SIZE(type, n)) : malloc(SIZE(type, n)))) -# define REALLOCX(ptr, type, n) \ - ((n) ? REALLOC(ptr, type, n) : (FREE(ptr), (type *) 0)) - -# define BMTST(bm, num) \ - (((const byte *) (bm))[(num) >> 3] & (0x80 >> ((num) & 0x07))) -# define BMSET(bm, num) \ - (((byte *) (bm))[(num) >> 3] |= (0x80 >> ((num) & 0x07))) -# define BMCLR(bm, num) \ - (((byte *) (bm))[(num) >> 3] &= ~(0x80 >> ((num) & 0x07))) - -# define STRINGIZE(x) #x -# define STR(x) STRINGIZE(x) - -typedef unsigned char byte; -typedef byte block[HFS_BLOCKSZ]; - -typedef struct _bucket_ { - int flags; /* bit flags */ - unsigned int count; /* number of times this block is requested */ - - unsigned long bnum; /* logical block number */ - block *data; /* pointer to block contents */ - - struct _bucket_ *cnext; /* next bucket in cache chain */ - struct _bucket_ *cprev; /* previous bucket in cache chain */ - - struct _bucket_ *hnext; /* next bucket in hash chain */ - struct _bucket_ **hprev; /* previous bucket's pointer to this bucket */ -} bucket; - -# define HFS_BUCKET_INUSE 0x01 -# define HFS_BUCKET_DIRTY 0x02 - -# define HFS_CACHESZ 128 -# define HFS_HASHSZ 32 -# define HFS_BLOCKBUFSZ 16 - -typedef struct { - struct _hfsvol_ *vol; /* volume to which cache belongs */ - bucket *tail; /* end of bucket chain */ - - unsigned int hits; /* number of cache hits */ - unsigned int misses; /* number of cache misses */ - - bucket chain[HFS_CACHESZ]; /* cache bucket chain */ - bucket *hash[HFS_HASHSZ]; /* hash table for bucket chain */ - - block pool[HFS_CACHESZ]; /* physical blocks in cache */ -} bcache; - -# define HFS_MAP1SZ 256 -# define HFS_MAPXSZ 492 - -# define HFS_NODEREC(nd, rnum) ((nd).data + (nd).roff[rnum]) -# define HFS_RECLEN(nd, rnum) ((nd).roff[(rnum) + 1] - (nd).roff[rnum]) - -# define HFS_RECKEYLEN(ptr) (*(const byte *) (ptr)) -# define HFS_RECKEYSKIP(ptr) ((size_t) ((1 + HFS_RECKEYLEN(ptr) + 1) & ~1)) -# define HFS_RECDATA(ptr) ((ptr) + HFS_RECKEYSKIP(ptr)) - -# define HFS_SETKEYLEN(ptr, x) (*(byte *) (ptr) = (x)) - -# define HFS_CATDATALEN sizeof(CatDataRec) -# define HFS_EXTDATALEN sizeof(ExtDataRec) -# define HFS_MAX_DATALEN (HFS_CATDATALEN > HFS_EXTDATALEN ? \ - HFS_CATDATALEN : HFS_EXTDATALEN) - -# define HFS_CATKEYLEN sizeof(CatKeyRec) -# define HFS_EXTKEYLEN sizeof(ExtKeyRec) -# define HFS_MAX_KEYLEN (HFS_CATKEYLEN > HFS_EXTKEYLEN ? \ - HFS_CATKEYLEN : HFS_EXTKEYLEN) - -# define HFS_MAX_CATRECLEN (HFS_CATKEYLEN + HFS_CATDATALEN) -# define HFS_MAX_EXTRECLEN (HFS_EXTKEYLEN + HFS_EXTDATALEN) -# define HFS_MAX_RECLEN (HFS_MAX_KEYLEN + HFS_MAX_DATALEN) - -# define HFS_SIGWORD 0x4244 -# define HFS_SIGWORD_MFS ((Integer) 0xd2d7) - -# define HFS_ATRB_BUSY (1 << 6) -# define HFS_ATRB_HLOCKED (1 << 7) -# define HFS_ATRB_UMOUNTED (1 << 8) -# define HFS_ATRB_BBSPARED (1 << 9) -# define HFS_ATRB_BVINCONSIS (1 << 11) -# define HFS_ATRB_COPYPROT (1 << 14) -# define HFS_ATRB_SLOCKED (1 << 15) - -struct _hfsfile_ { - struct _hfsvol_ *vol; /* pointer to volume descriptor */ - unsigned long parid; /* parent directory ID of this file */ - char name[HFS_MAX_FLEN + 1]; /* catalog name of this file */ - CatDataRec cat; /* catalog information */ - ExtDataRec ext; /* current extent record */ - unsigned int fabn; /* starting file allocation block number */ - int fork; /* current selected fork for I/O */ - unsigned long pos; /* current file seek pointer */ - int flags; /* bit flags */ - - struct _hfsfile_ *prev; - struct _hfsfile_ *next; -}; - -# define HFS_FILE_UPDATE_CATREC 0x01 - -# define HFS_MAX_NRECS 35 /* maximum based on minimum record size */ - -typedef struct _node_ { - struct _btree_ *bt; /* btree to which this node belongs */ - unsigned long nnum; /* node index */ - NodeDescriptor nd; /* node descriptor */ - int rnum; /* current record index */ - UInteger roff[HFS_MAX_NRECS + 1]; - /* record offsets */ - block data; /* raw contents of node */ -} node; - -struct _hfsdir_ { - struct _hfsvol_ *vol; /* associated volume */ - unsigned long dirid; /* directory ID of interest (or 0) */ - - node n; /* current B*-tree node */ - struct _hfsvol_ *vptr; /* current volume pointer */ - - struct _hfsdir_ *prev; - struct _hfsdir_ *next; -}; - -typedef void (*keyunpackfunc)(const byte *, void *); -typedef int (*keycomparefunc)(const void *, const void *); - -typedef struct _btree_ { - hfsfile f; /* subset file information */ - node hdrnd; /* header node */ - BTHdrRec hdr; /* header record */ - byte *map; /* usage bitmap */ - unsigned long mapsz; /* number of bytes in bitmap */ - int flags; /* bit flags */ - - keyunpackfunc keyunpack; /* key unpacking function */ - keycomparefunc keycompare; /* key comparison function */ -} btree; - -# define HFS_BT_UPDATE_HDR 0x01 - -struct _hfsvol_ { - void *priv; /* OS-dependent private descriptor data */ - int flags; /* bit flags */ - - int pnum; /* ordinal HFS partition number */ - unsigned long vstart; /* logical block offset to start of volume */ - unsigned long vlen; /* number of logical blocks in volume */ - unsigned int lpa; /* number of logical blocks per allocation block */ - - bcache *cache; /* cache of recently used blocks */ - - MDB mdb; /* master directory block */ - block *vbm; /* volume bitmap */ - unsigned short vbmsz; /* number of blocks in bitmap */ - - btree ext; /* B*-tree control block for extents overflow file */ - btree cat; /* B*-tree control block for catalog file */ - - unsigned long cwd; /* directory id of current working directory */ - - int refs; /* number of external references to this volume */ - hfsfile *files; /* list of open files */ - hfsdir *dirs; /* list of open directories */ - - struct _hfsvol_ *prev; - struct _hfsvol_ *next; -}; - -# define HFS_VOL_OPEN 0x0001 -# define HFS_VOL_MOUNTED 0x0002 -# define HFS_VOL_READONLY 0x0004 -# define HFS_VOL_USINGCACHE 0x0008 - -# define HFS_VOL_UPDATE_MDB 0x0010 -# define HFS_VOL_UPDATE_ALTMDB 0x0020 -# define HFS_VOL_UPDATE_VBM 0x0040 - -# define HFS_VOL_OPT_MASK 0xff00 - -extern hfsvol *hfs_mounts; diff --git a/ciderpress/diskimg/libhfs/low.c b/ciderpress/diskimg/libhfs/low.c deleted file mode 100644 index a5c2727..0000000 --- a/ciderpress/diskimg/libhfs/low.c +++ /dev/null @@ -1,470 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include - -# include "libhfs.h" -# include "low.h" -# include "data.h" -# include "block.h" -# include "file.h" - -/* - * NAME: low->getddr() - * DESCRIPTION: read a driver descriptor record - */ -int l_getddr(hfsvol *vol, Block0 *ddr) -{ - block b; - const byte *ptr = b; - int i; - - if (b_readpb(vol, 0, &b, 1) == -1) - goto fail; - - d_fetchsw(&ptr, &ddr->sbSig); - d_fetchsw(&ptr, &ddr->sbBlkSize); - d_fetchsl(&ptr, &ddr->sbBlkCount); - d_fetchsw(&ptr, &ddr->sbDevType); - d_fetchsw(&ptr, &ddr->sbDevId); - d_fetchsl(&ptr, &ddr->sbData); - d_fetchsw(&ptr, &ddr->sbDrvrCount); - d_fetchsl(&ptr, &ddr->ddBlock); - d_fetchsw(&ptr, &ddr->ddSize); - d_fetchsw(&ptr, &ddr->ddType); - - for (i = 0; i < 243; ++i) - d_fetchsw(&ptr, &ddr->ddPad[i]); - - ASSERT(ptr - b == HFS_BLOCKSZ); - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->putddr() - * DESCRIPTION: write a driver descriptor record - */ -int l_putddr(hfsvol *vol, const Block0 *ddr) -{ - block b; - byte *ptr = b; - int i; - - d_storesw(&ptr, ddr->sbSig); - d_storesw(&ptr, ddr->sbBlkSize); - d_storesl(&ptr, ddr->sbBlkCount); - d_storesw(&ptr, ddr->sbDevType); - d_storesw(&ptr, ddr->sbDevId); - d_storesl(&ptr, ddr->sbData); - d_storesw(&ptr, ddr->sbDrvrCount); - d_storesl(&ptr, ddr->ddBlock); - d_storesw(&ptr, ddr->ddSize); - d_storesw(&ptr, ddr->ddType); - - for (i = 0; i < 243; ++i) - d_storesw(&ptr, ddr->ddPad[i]); - - ASSERT(ptr - b == HFS_BLOCKSZ); - - if (b_writepb(vol, 0, &b, 1) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->getpmentry() - * DESCRIPTION: read a partition map entry - */ -int l_getpmentry(hfsvol *vol, Partition *map, unsigned long bnum) -{ - block b; - const byte *ptr = b; - int i; - - if (b_readpb(vol, bnum, &b, 1) == -1) - goto fail; - - d_fetchsw(&ptr, &map->pmSig); - d_fetchsw(&ptr, &map->pmSigPad); - d_fetchsl(&ptr, &map->pmMapBlkCnt); - d_fetchsl(&ptr, &map->pmPyPartStart); - d_fetchsl(&ptr, &map->pmPartBlkCnt); - - strncpy((char *) map->pmPartName, (const char *) ptr, 32); - map->pmPartName[32] = 0; - ptr += 32; - - strncpy((char *) map->pmParType, (const char *) ptr, 32); - map->pmParType[32] = 0; - ptr += 32; - - d_fetchsl(&ptr, &map->pmLgDataStart); - d_fetchsl(&ptr, &map->pmDataCnt); - d_fetchsl(&ptr, &map->pmPartStatus); - d_fetchsl(&ptr, &map->pmLgBootStart); - d_fetchsl(&ptr, &map->pmBootSize); - d_fetchsl(&ptr, &map->pmBootAddr); - d_fetchsl(&ptr, &map->pmBootAddr2); - d_fetchsl(&ptr, &map->pmBootEntry); - d_fetchsl(&ptr, &map->pmBootEntry2); - d_fetchsl(&ptr, &map->pmBootCksum); - - strncpy((char *) map->pmProcessor, (const char *) ptr, 16); - map->pmProcessor[16] = 0; - ptr += 16; - - for (i = 0; i < 188; ++i) - d_fetchsw(&ptr, &map->pmPad[i]); - - ASSERT(ptr - b == HFS_BLOCKSZ); - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->putpmentry() - * DESCRIPTION: write a partition map entry - */ -int l_putpmentry(hfsvol *vol, const Partition *map, unsigned long bnum) -{ - block b; - byte *ptr = b; - int i; - - d_storesw(&ptr, map->pmSig); - d_storesw(&ptr, map->pmSigPad); - d_storesl(&ptr, map->pmMapBlkCnt); - d_storesl(&ptr, map->pmPyPartStart); - d_storesl(&ptr, map->pmPartBlkCnt); - - memset(ptr, 0, 32); - strncpy((char *) ptr, (const char *) map->pmPartName, 32); - ptr += 32; - - memset(ptr, 0, 32); - strncpy((char *) ptr, (const char *) map->pmParType, 32); - ptr += 32; - - d_storesl(&ptr, map->pmLgDataStart); - d_storesl(&ptr, map->pmDataCnt); - d_storesl(&ptr, map->pmPartStatus); - d_storesl(&ptr, map->pmLgBootStart); - d_storesl(&ptr, map->pmBootSize); - d_storesl(&ptr, map->pmBootAddr); - d_storesl(&ptr, map->pmBootAddr2); - d_storesl(&ptr, map->pmBootEntry); - d_storesl(&ptr, map->pmBootEntry2); - d_storesl(&ptr, map->pmBootCksum); - - memset(ptr, 0, 16); - strncpy((char *) ptr, (const char *) map->pmProcessor, 16); - ptr += 16; - - for (i = 0; i < 188; ++i) - d_storesw(&ptr, map->pmPad[i]); - - ASSERT(ptr - b == HFS_BLOCKSZ); - - if (b_writepb(vol, bnum, &b, 1) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->getbb() - * DESCRIPTION: read a volume's boot blocks - */ -int l_getbb(hfsvol *vol, BootBlkHdr *bb, byte *bootcode) -{ - block b; - const byte *ptr = b; - - if (b_readlb(vol, 0, &b) == -1) - goto fail; - - d_fetchsw(&ptr, &bb->bbID); - d_fetchsl(&ptr, &bb->bbEntry); - d_fetchsw(&ptr, &bb->bbVersion); - d_fetchsw(&ptr, &bb->bbPageFlags); - - d_fetchstr(&ptr, bb->bbSysName, sizeof(bb->bbSysName)); - d_fetchstr(&ptr, bb->bbShellName, sizeof(bb->bbShellName)); - d_fetchstr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name)); - d_fetchstr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name)); - d_fetchstr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName)); - d_fetchstr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName)); - d_fetchstr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName)); - - d_fetchsw(&ptr, &bb->bbCntFCBs); - d_fetchsw(&ptr, &bb->bbCntEvts); - d_fetchsl(&ptr, &bb->bb128KSHeap); - d_fetchsl(&ptr, &bb->bb256KSHeap); - d_fetchsl(&ptr, &bb->bbSysHeapSize); - d_fetchsw(&ptr, &bb->filler); - d_fetchsl(&ptr, &bb->bbSysHeapExtra); - d_fetchsl(&ptr, &bb->bbSysHeapFract); - - ASSERT(ptr - b == 148); - - if (bootcode) - { - memcpy(bootcode, ptr, HFS_BOOTCODE1LEN); - - if (b_readlb(vol, 1, &b) == -1) - goto fail; - - memcpy(bootcode + HFS_BOOTCODE1LEN, b, HFS_BOOTCODE2LEN); - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->putbb() - * DESCRIPTION: write a volume's boot blocks - */ -int l_putbb(hfsvol *vol, const BootBlkHdr *bb, const byte *bootcode) -{ - block b; - byte *ptr = b; - - d_storesw(&ptr, bb->bbID); - d_storesl(&ptr, bb->bbEntry); - d_storesw(&ptr, bb->bbVersion); - d_storesw(&ptr, bb->bbPageFlags); - - d_storestr(&ptr, bb->bbSysName, sizeof(bb->bbSysName)); - d_storestr(&ptr, bb->bbShellName, sizeof(bb->bbShellName)); - d_storestr(&ptr, bb->bbDbg1Name, sizeof(bb->bbDbg1Name)); - d_storestr(&ptr, bb->bbDbg2Name, sizeof(bb->bbDbg2Name)); - d_storestr(&ptr, bb->bbScreenName, sizeof(bb->bbScreenName)); - d_storestr(&ptr, bb->bbHelloName, sizeof(bb->bbHelloName)); - d_storestr(&ptr, bb->bbScrapName, sizeof(bb->bbScrapName)); - - d_storesw(&ptr, bb->bbCntFCBs); - d_storesw(&ptr, bb->bbCntEvts); - d_storesl(&ptr, bb->bb128KSHeap); - d_storesl(&ptr, bb->bb256KSHeap); - d_storesl(&ptr, bb->bbSysHeapSize); - d_storesw(&ptr, bb->filler); - d_storesl(&ptr, bb->bbSysHeapExtra); - d_storesl(&ptr, bb->bbSysHeapFract); - - ASSERT(ptr - b == 148); - - if (bootcode) - memcpy(ptr, bootcode, HFS_BOOTCODE1LEN); - else - memset(ptr, 0, HFS_BOOTCODE1LEN); - - if (b_writelb(vol, 0, &b) == -1) - goto fail; - - if (bootcode) - memcpy(&b, bootcode + HFS_BOOTCODE1LEN, HFS_BOOTCODE2LEN); - else - memset(&b, 0, HFS_BOOTCODE2LEN); - - if (b_writelb(vol, 1, &b) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->getmdb() - * DESCRIPTION: read a master directory block - */ -int l_getmdb(hfsvol *vol, MDB *mdb, int backup) -{ - block b; - const byte *ptr = b; - int i; - - if (b_readlb(vol, backup ? vol->vlen - 2 : 2, &b) == -1) - goto fail; - - d_fetchsw(&ptr, &mdb->drSigWord); - d_fetchsl(&ptr, &mdb->drCrDate); - d_fetchsl(&ptr, &mdb->drLsMod); - d_fetchsw(&ptr, &mdb->drAtrb); - d_fetchuw(&ptr, &mdb->drNmFls); - d_fetchuw(&ptr, &mdb->drVBMSt); - d_fetchuw(&ptr, &mdb->drAllocPtr); - d_fetchuw(&ptr, &mdb->drNmAlBlks); - d_fetchul(&ptr, &mdb->drAlBlkSiz); - d_fetchul(&ptr, &mdb->drClpSiz); - d_fetchuw(&ptr, &mdb->drAlBlSt); - d_fetchsl(&ptr, &mdb->drNxtCNID); - d_fetchuw(&ptr, &mdb->drFreeBks); - - d_fetchstr(&ptr, mdb->drVN, sizeof(mdb->drVN)); - - ASSERT(ptr - b == 64); - - d_fetchsl(&ptr, &mdb->drVolBkUp); - d_fetchsw(&ptr, &mdb->drVSeqNum); - d_fetchul(&ptr, &mdb->drWrCnt); - d_fetchul(&ptr, &mdb->drXTClpSiz); - d_fetchul(&ptr, &mdb->drCTClpSiz); - d_fetchuw(&ptr, &mdb->drNmRtDirs); - d_fetchul(&ptr, &mdb->drFilCnt); - d_fetchul(&ptr, &mdb->drDirCnt); - - for (i = 0; i < 8; ++i) - d_fetchsl(&ptr, &mdb->drFndrInfo[i]); - - ASSERT(ptr - b == 124); - - d_fetchuw(&ptr, &mdb->drEmbedSigWord); - d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrStABN); - d_fetchuw(&ptr, &mdb->drEmbedExtent.xdrNumABlks); - - d_fetchul(&ptr, &mdb->drXTFlSize); - - for (i = 0; i < 3; ++i) - { - d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrStABN); - d_fetchuw(&ptr, &mdb->drXTExtRec[i].xdrNumABlks); - } - - ASSERT(ptr - b == 146); - - d_fetchul(&ptr, &mdb->drCTFlSize); - - for (i = 0; i < 3; ++i) - { - d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrStABN); - d_fetchuw(&ptr, &mdb->drCTExtRec[i].xdrNumABlks); - } - - ASSERT(ptr - b == 162); - - return 0; - -fail: - return -1; -} - -/* - * NAME: low->putmdb() - * DESCRIPTION: write master directory block(s) - */ -int l_putmdb(hfsvol *vol, const MDB *mdb, int backup) -{ - block b; - byte *ptr = b; - int i; - - d_storesw(&ptr, mdb->drSigWord); - d_storesl(&ptr, mdb->drCrDate); - d_storesl(&ptr, mdb->drLsMod); - d_storesw(&ptr, mdb->drAtrb); - d_storeuw(&ptr, mdb->drNmFls); - d_storeuw(&ptr, mdb->drVBMSt); - d_storeuw(&ptr, mdb->drAllocPtr); - d_storeuw(&ptr, mdb->drNmAlBlks); - d_storeul(&ptr, mdb->drAlBlkSiz); - d_storeul(&ptr, mdb->drClpSiz); - d_storeuw(&ptr, mdb->drAlBlSt); - d_storesl(&ptr, mdb->drNxtCNID); - d_storeuw(&ptr, mdb->drFreeBks); - - d_storestr(&ptr, mdb->drVN, sizeof(mdb->drVN)); - - ASSERT(ptr - b == 64); - - d_storesl(&ptr, mdb->drVolBkUp); - d_storesw(&ptr, mdb->drVSeqNum); - d_storeul(&ptr, mdb->drWrCnt); - d_storeul(&ptr, mdb->drXTClpSiz); - d_storeul(&ptr, mdb->drCTClpSiz); - d_storeuw(&ptr, mdb->drNmRtDirs); - d_storeul(&ptr, mdb->drFilCnt); - d_storeul(&ptr, mdb->drDirCnt); - - for (i = 0; i < 8; ++i) - d_storesl(&ptr, mdb->drFndrInfo[i]); - - ASSERT(ptr - b == 124); - - d_storeuw(&ptr, mdb->drEmbedSigWord); - d_storeuw(&ptr, mdb->drEmbedExtent.xdrStABN); - d_storeuw(&ptr, mdb->drEmbedExtent.xdrNumABlks); - - d_storeul(&ptr, mdb->drXTFlSize); - - for (i = 0; i < 3; ++i) - { - d_storeuw(&ptr, mdb->drXTExtRec[i].xdrStABN); - d_storeuw(&ptr, mdb->drXTExtRec[i].xdrNumABlks); - } - - ASSERT(ptr - b == 146); - - d_storeul(&ptr, mdb->drCTFlSize); - - for (i = 0; i < 3; ++i) - { - d_storeuw(&ptr, mdb->drCTExtRec[i].xdrStABN); - d_storeuw(&ptr, mdb->drCTExtRec[i].xdrNumABlks); - } - - ASSERT(ptr - b == 162); - - memset(ptr, 0, HFS_BLOCKSZ - (ptr - b)); - - if (b_writelb(vol, 2, &b) == -1 || - (backup && b_writelb(vol, vol->vlen - 2, &b) == -1)) - goto fail; - - return 0; - -fail: - return -1; -} diff --git a/ciderpress/diskimg/libhfs/low.h b/ciderpress/diskimg/libhfs/low.h deleted file mode 100644 index 0e59d83..0000000 --- a/ciderpress/diskimg/libhfs/low.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# define HFS_DDR_SIGWORD 0x4552 - -# define HFS_PM_SIGWORD 0x504d -# define HFS_PM_SIGWORD_OLD 0x5453 - -# define HFS_BB_SIGWORD 0x4c4b - -# define HFS_BOOTCODE1LEN (HFS_BLOCKSZ - 148) -# define HFS_BOOTCODE2LEN HFS_BLOCKSZ - -# define HFS_BOOTCODELEN (HFS_BOOTCODE1LEN + HFS_BOOTCODE2LEN) - -int l_getddr(hfsvol *, Block0 *); -int l_putddr(hfsvol *, const Block0 *); - -int l_getpmentry(hfsvol *, Partition *, unsigned long); -int l_putpmentry(hfsvol *, const Partition *, unsigned long); - -int l_getbb(hfsvol *, BootBlkHdr *, byte *); -int l_putbb(hfsvol *, const BootBlkHdr *, const byte *); - -int l_getmdb(hfsvol *, MDB *, int); -int l_putmdb(hfsvol *, const MDB *, int); diff --git a/ciderpress/diskimg/libhfs/medium.c b/ciderpress/diskimg/libhfs/medium.c deleted file mode 100644 index bcd070d..0000000 --- a/ciderpress/diskimg/libhfs/medium.c +++ /dev/null @@ -1,318 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include - -# include "libhfs.h" -# include "block.h" -# include "low.h" -# include "medium.h" - -/* Driver Descriptor Record Routines ======================================= */ - -/* - * NAME: medium->zeroddr() - * DESCRIPTION: write a new/empty driver descriptor record - */ -int m_zeroddr(hfsvol *vol) -{ - Block0 ddr; - int i; - - ASSERT(vol->pnum == 0 && vol->vlen != 0); - - ddr.sbSig = HFS_DDR_SIGWORD; - ddr.sbBlkSize = HFS_BLOCKSZ; - ddr.sbBlkCount = vol->vlen; - - ddr.sbDevType = 0; - ddr.sbDevId = 0; - ddr.sbData = 0; - - ddr.sbDrvrCount = 0; - - ddr.ddBlock = 0; - ddr.ddSize = 0; - ddr.ddType = 0; - - for (i = 0; i < 243; ++i) - ddr.ddPad[i] = 0; - - return l_putddr(vol, &ddr); -} - -/* Partition Map Routines ================================================== */ - -/* - * NAME: medium->zeropm() - * DESCRIPTION: write new/empty partition map - */ -int m_zeropm(hfsvol *vol, unsigned int maxparts) -{ - Partition map; - unsigned int i; - - ASSERT(vol->pnum == 0 && vol->vlen != 0); - - if (maxparts < 2) - ERROR(EINVAL, "must allow at least 2 partitions"); - - /* first entry: partition map itself */ - - map.pmSig = HFS_PM_SIGWORD; - map.pmSigPad = 0; - map.pmMapBlkCnt = 2; - - map.pmPyPartStart = 1; - map.pmPartBlkCnt = maxparts; - - strcpy((char *) map.pmPartName, "Apple"); - strcpy((char *) map.pmParType, "Apple_partition_map"); - - map.pmLgDataStart = 0; - map.pmDataCnt = map.pmPartBlkCnt; - - map.pmPartStatus = 0; - - map.pmLgBootStart = 0; - map.pmBootSize = 0; - map.pmBootAddr = 0; - map.pmBootAddr2 = 0; - map.pmBootEntry = 0; - map.pmBootEntry2 = 0; - map.pmBootCksum = 0; - - strcpy((char *) map.pmProcessor, ""); - - for (i = 0; i < 188; ++i) - map.pmPad[i] = 0; - - if (l_putpmentry(vol, &map, 1) == -1) - goto fail; - - /* second entry: rest of medium */ - - map.pmPyPartStart = 1 + maxparts; - map.pmPartBlkCnt = vol->vlen - 1 - maxparts; - - strcpy((char *) map.pmPartName, "Extra"); - strcpy((char *) map.pmParType, "Apple_Free"); - - map.pmDataCnt = map.pmPartBlkCnt; - - if (l_putpmentry(vol, &map, 2) == -1) - goto fail; - - /* zero rest of partition map's partition */ - - if (maxparts > 2) - { - block b; - - memset(&b, 0, sizeof(b)); - - for (i = 3; i <= maxparts; ++i) - { - if (b_writepb(vol, i, &b, 1) == -1) - goto fail; - } - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: medium->findpmentry() - * DESCRIPTION: locate a partition map entry - */ -int m_findpmentry(hfsvol *vol, const char *type, - Partition *map, unsigned long *start) -{ - unsigned long bnum; - int found = 0; - - if (start && *start > 0) - { - bnum = *start; - - if (bnum++ >= (unsigned long) map->pmMapBlkCnt) - ERROR(EINVAL, "partition not found"); - } - else - bnum = 1; - - while (1) - { - if (l_getpmentry(vol, map, bnum) == -1) - { - found = -1; - goto fail; - } - - if (map->pmSig != HFS_PM_SIGWORD) - { - found = -1; - - if (map->pmSig == HFS_PM_SIGWORD_OLD) - ERROR(EINVAL, "old partition map format not supported"); - else - ERROR(EINVAL, "invalid partition map"); - } - - if (strcmp((char *) map->pmParType, type) == 0) - { - found = 1; - goto done; - } - - if (bnum++ >= (unsigned long) map->pmMapBlkCnt) - ERROR(EINVAL, "partition not found"); - } - -done: - if (start) - *start = bnum; - -fail: - return found; -} - -/* - * NAME: medium->mkpart() - * DESCRIPTION: create a new partition from available free space - */ -int m_mkpart(hfsvol *vol, - const char *name, const char *type, unsigned long len) -{ - Partition map; - unsigned int nparts, maxparts; - unsigned long bnum, start, remain; - int found; - - if (strlen(name) > 32 || - strlen(type) > 32) - ERROR(EINVAL, "partition name/type can each be at most 32 chars"); - - if (len == 0) - ERROR(EINVAL, "partition length must be > 0"); - - found = m_findpmentry(vol, "Apple_partition_map", &map, 0); - if (found == -1) - goto fail; - - if (! found) - ERROR(EIO, "cannot find partition map's partition"); - - nparts = map.pmMapBlkCnt; - maxparts = map.pmPartBlkCnt; - - bnum = 0; - do - { - found = m_findpmentry(vol, "Apple_Free", &map, &bnum); - if (found == -1) - goto fail; - - if (! found) - ERROR(ENOSPC, "no available partitions"); - } - while (len > (unsigned long) map.pmPartBlkCnt); - - start = (unsigned long) map.pmPyPartStart + len; - remain = (unsigned long) map.pmPartBlkCnt - len; - - if (remain && nparts >= maxparts) - ERROR(EINVAL, "must allocate all blocks in free space"); - - map.pmPartBlkCnt = len; - - strcpy((char *) map.pmPartName, name); - strcpy((char *) map.pmParType, type); - - map.pmLgDataStart = 0; - map.pmDataCnt = len; - - map.pmPartStatus = 0; - - if (l_putpmentry(vol, &map, bnum) == -1) - goto fail; - - if (remain) - { - map.pmPyPartStart = start; - map.pmPartBlkCnt = remain; - - strcpy((char *) map.pmPartName, "Extra"); - strcpy((char *) map.pmParType, "Apple_Free"); - - map.pmDataCnt = remain; - - if (l_putpmentry(vol, &map, ++nparts) == -1) - goto fail; - - for (bnum = 1; bnum <= nparts; ++bnum) - { - if (l_getpmentry(vol, &map, bnum) == -1) - goto fail; - - map.pmMapBlkCnt = nparts; - - if (l_putpmentry(vol, &map, bnum) == -1) - goto fail; - } - } - - return 0; - -fail: - return -1; -} - -/* Boot Blocks Routines ==================================================== */ - -/* - * NAME: medium->zerobb() - * DESCRIPTION: write new/empty volume boot blocks - */ -int m_zerobb(hfsvol *vol) -{ - block b; - - memset(&b, 0, sizeof(b)); - - if (b_writelb(vol, 0, &b) == -1 || - b_writelb(vol, 1, &b) == -1) - goto fail; - - return 0; - -fail: - return -1; -} diff --git a/ciderpress/diskimg/libhfs/medium.h b/ciderpress/diskimg/libhfs/medium.h deleted file mode 100644 index 912f40c..0000000 --- a/ciderpress/diskimg/libhfs/medium.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -/* - * Partition Types: - * - * "Apple_partition_map" partition map - * "Apple_Driver" device driver - * "Apple_Driver43" SCSI Manager 4.3 device driver - * "Apple_MFS" Macintosh 64K ROM filesystem - * "Apple_HFS" Macintosh hierarchical filesystem - * "Apple_Unix_SVR2" Unix filesystem - * "Apple_PRODOS" ProDOS filesystem - * "Apple_Free" unused - * "Apple_Scratch" empty - */ - -int m_zeroddr(hfsvol *); - -int m_zeropm(hfsvol *, unsigned int); -int m_findpmentry(hfsvol *, const char *, Partition *, unsigned long *); -int m_mkpart(hfsvol *, const char *, const char *, unsigned long); - -int m_zerobb(hfsvol *); diff --git a/ciderpress/diskimg/libhfs/memcmp.c b/ciderpress/diskimg/libhfs/memcmp.c deleted file mode 100644 index 037b5b2..0000000 --- a/ciderpress/diskimg/libhfs/memcmp.c +++ /dev/null @@ -1,50 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include - -/* - * NAME: memcmp() - * DESCRIPTION: compare memory areas - */ -int memcmp(const void *s1, const void *s2, size_t n) -{ - register const unsigned char *c1, *c2; - - c1 = s1; - c2 = s2; - - while (n--) - { - register int diff; - - diff = *c1++ - *c2++; - - if (diff) - return diff; - } - - return 0; -} diff --git a/ciderpress/diskimg/libhfs/node.c b/ciderpress/diskimg/libhfs/node.c deleted file mode 100644 index 835bbc0..0000000 --- a/ciderpress/diskimg/libhfs/node.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include - -# include "libhfs.h" -# include "node.h" -# include "data.h" -# include "btree.h" - -/* total bytes used by records (NOT including record offsets) */ - -# define NODEUSED(n) \ - ((size_t) ((n).roff[(n).nd.ndNRecs] - (n).roff[0])) - -/* total bytes available for new records (INCLUDING record offsets) */ - -# define NODEFREE(n) \ - ((size_t) (HFS_BLOCKSZ - (n).roff[(n).nd.ndNRecs] - \ - 2 * ((n).nd.ndNRecs + 1))) - -/* - * NAME: node->init() - * DESCRIPTION: construct an empty node - */ -void n_init(node *np, btree *bt, int type, int height) -{ - np->bt = bt; - np->nnum = (unsigned long) -1; - - np->nd.ndFLink = 0; - np->nd.ndBLink = 0; - np->nd.ndType = type; - np->nd.ndNHeight = height; - np->nd.ndNRecs = 0; - np->nd.ndResv2 = 0; - - np->rnum = -1; - np->roff[0] = 0x00e; - - memset(&np->data, 0, sizeof(np->data)); -} - -/* - * NAME: node->new() - * DESCRIPTION: allocate a new b*-tree node - */ -int n_new(node *np) -{ - btree *bt = np->bt; - unsigned long num; - - if (bt->hdr.bthFree == 0) - ERROR(EIO, "b*-tree full"); - - num = 0; - while (num < bt->hdr.bthNNodes && BMTST(bt->map, num)) - ++num; - - if (num == bt->hdr.bthNNodes) - ERROR(EIO, "free b*-tree node not found"); - - np->nnum = num; - - BMSET(bt->map, num); - --bt->hdr.bthFree; - - bt->flags |= HFS_BT_UPDATE_HDR; - - return 0; - -fail: - return -1; -} - -/* - * NAME: node->free() - * DESCRIPTION: deallocate and remove a b*-tree node - */ -int n_free(node *np) -{ - btree *bt = np->bt; - node sib; - - if (bt->hdr.bthFNode == np->nnum) - bt->hdr.bthFNode = np->nd.ndFLink; - - if (bt->hdr.bthLNode == np->nnum) - bt->hdr.bthLNode = np->nd.ndBLink; - - if (np->nd.ndFLink > 0) - { - if (bt_getnode(&sib, bt, np->nd.ndFLink) == -1) - goto fail; - - sib.nd.ndBLink = np->nd.ndBLink; - - if (bt_putnode(&sib) == -1) - goto fail; - } - - if (np->nd.ndBLink > 0) - { - if (bt_getnode(&sib, bt, np->nd.ndBLink) == -1) - goto fail; - - sib.nd.ndFLink = np->nd.ndFLink; - - if (bt_putnode(&sib) == -1) - goto fail; - } - - BMCLR(bt->map, np->nnum); - ++bt->hdr.bthFree; - - bt->flags |= HFS_BT_UPDATE_HDR; - - return 0; - -fail: - return -1; -} - -/* - * NAME: compact() - * DESCRIPTION: clean up a node, removing deleted records - */ -static -void compact(node *np) -{ - byte *ptr; - int offset, nrecs, i; - - offset = 0x00e; - ptr = np->data + offset; - nrecs = 0; - - for (i = 0; i < np->nd.ndNRecs; ++i) - { - const byte *rec; - int reclen; - - rec = HFS_NODEREC(*np, i); - reclen = HFS_RECLEN(*np, i); - - if (HFS_RECKEYLEN(rec) > 0) - { - np->roff[nrecs++] = offset; - offset += reclen; - - if (ptr == rec) - ptr += reclen; - else - { - while (reclen--) - *ptr++ = *rec++; - } - } - } - - np->roff[nrecs] = offset; - np->nd.ndNRecs = nrecs; -} - -/* - * NAME: node->search() - * DESCRIPTION: locate a record in a node, or the record it should follow - */ -int n_search(node *np, const byte *pkey) -{ - const btree *bt = np->bt; - byte key1[HFS_MAX_KEYLEN], key2[HFS_MAX_KEYLEN]; - int i, comp = -1; - - bt->keyunpack(pkey, key2); - - for (i = np->nd.ndNRecs; i--; ) - { - const byte *rec; - - rec = HFS_NODEREC(*np, i); - - if (HFS_RECKEYLEN(rec) == 0) - continue; /* deleted record */ - - bt->keyunpack(rec, key1); - comp = bt->keycompare(key1, key2); - - if (comp <= 0) - break; - } - - np->rnum = i; - - return comp == 0; -} - -/* - * NAME: node->index() - * DESCRIPTION: create an index record from a key and node pointer - */ -void n_index(const node *np, byte *record, unsigned int *reclen) -{ - const byte *key = HFS_NODEREC(*np, 0); - - if (np->bt == &np->bt->f.vol->cat) - { - /* force the key length to be 0x25 */ - - HFS_SETKEYLEN(record, 0x25); - memset(record + 1, 0, 0x25); - memcpy(record + 1, key + 1, HFS_RECKEYLEN(key)); - } - else - memcpy(record, key, HFS_RECKEYSKIP(key)); - - d_putul(HFS_RECDATA(record), np->nnum); - - if (reclen) - *reclen = HFS_RECKEYSKIP(record) + 4; -} - -/* - * NAME: split() - * DESCRIPTION: divide a node into two and insert a record - */ -static -int split(node *left, byte *record, unsigned int *reclen) -{ - btree *bt = left->bt; - node n, *right = &n, *side = 0; - int mark, i; - - /* create a second node by cloning the first */ - - *right = *left; - - if (n_new(right) == -1) - goto fail; - - left->nd.ndFLink = right->nnum; - right->nd.ndBLink = left->nnum; - - /* divide all records evenly between the two nodes */ - - mark = (NODEUSED(*left) + 2 * left->nd.ndNRecs + *reclen + 2) >> 1; - - if (left->rnum == -1) - { - side = left; - mark -= *reclen + 2; - } - - for (i = 0; i < left->nd.ndNRecs; ++i) - { - node *np; - byte *rec; - - np = (mark > 0) ? right : left; - rec = HFS_NODEREC(*np, i); - - mark -= HFS_RECLEN(*np, i) + 2; - - HFS_SETKEYLEN(rec, 0); - - if (left->rnum == i) - { - side = (mark > 0) ? left : right; - mark -= *reclen + 2; - } - } - - compact(left); - compact(right); - - /* insert the new record and store the modified nodes */ - - ASSERT(side); - - n_search(side, record); - n_insertx(side, record, *reclen); - - if (bt_putnode(left) == -1 || - bt_putnode(right) == -1) - goto fail; - - /* create an index record in the parent for the new node */ - - n_index(right, record, reclen); - - /* update link pointers */ - - if (bt->hdr.bthLNode == left->nnum) - { - bt->hdr.bthLNode = right->nnum; - bt->flags |= HFS_BT_UPDATE_HDR; - } - - if (right->nd.ndFLink > 0) - { - node sib; - - if (bt_getnode(&sib, right->bt, right->nd.ndFLink) == -1) - goto fail; - - sib.nd.ndBLink = right->nnum; - - if (bt_putnode(&sib) == -1) - goto fail; - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: node->insertx() - * DESCRIPTION: insert a record into a node (which must already have room) - */ -void n_insertx(node *np, const byte *record, unsigned int reclen) -{ - int rnum, i; - byte *ptr; - - rnum = np->rnum + 1; - - /* push other records down to make room */ - - for (ptr = HFS_NODEREC(*np, np->nd.ndNRecs) + reclen; - ptr > HFS_NODEREC(*np, rnum) + reclen; --ptr) - *(ptr - 1) = *(ptr - 1 - reclen); - - ++np->nd.ndNRecs; - - for (i = np->nd.ndNRecs; i > rnum; --i) - np->roff[i] = np->roff[i - 1] + reclen; - - /* write the new record */ - - memcpy(HFS_NODEREC(*np, rnum), record, reclen); -} - -/* - * NAME: node->insert() - * DESCRIPTION: insert a new record into a node; return a record for parent - */ -int n_insert(node *np, byte *record, unsigned int *reclen) -{ - /* check for free space */ - - if (np->nd.ndNRecs >= HFS_MAX_NRECS || - *reclen + 2 > NODEFREE(*np)) - return split(np, record, reclen); - - n_insertx(np, record, *reclen); - *reclen = 0; - - return bt_putnode(np); -} - -/* - * NAME: join() - * DESCRIPTION: combine two nodes into a single node - */ -static -int join(node *left, node *right, byte *record, int *flag) -{ - int i, offset; - - /* copy records and offsets */ - - memcpy(HFS_NODEREC(*left, left->nd.ndNRecs), - HFS_NODEREC(*right, 0), NODEUSED(*right)); - - offset = left->roff[left->nd.ndNRecs] - right->roff[0]; - - for (i = 1; i <= right->nd.ndNRecs; ++i) - left->roff[++left->nd.ndNRecs] = offset + right->roff[i]; - - if (bt_putnode(left) == -1) - goto fail; - - /* eliminate node and update link pointers */ - - if (n_free(right) == -1) - goto fail; - - HFS_SETKEYLEN(record, 0); - *flag = 1; - - return 0; - -fail: - return -1; -} - -/* - * NAME: node->delete() - * DESCRIPTION: remove a record from a node - */ -int n_delete(node *np, byte *record, int *flag) -{ - byte *rec; - - rec = HFS_NODEREC(*np, np->rnum); - - HFS_SETKEYLEN(rec, 0); - compact(np); - - if (np->nd.ndNRecs == 0) - { - if (n_free(np) == -1) - goto fail; - - HFS_SETKEYLEN(record, 0); - *flag = 1; - - return 0; - } - - /* see if we can join with our left sibling */ - - if (np->nd.ndBLink > 0) - { - node left; - - if (bt_getnode(&left, np->bt, np->nd.ndBLink) == -1) - goto fail; - - if (np->nd.ndNRecs + left.nd.ndNRecs <= HFS_MAX_NRECS && - NODEUSED(*np) + 2 * np->nd.ndNRecs <= NODEFREE(left)) - return join(&left, np, record, flag); - } - - if (np->rnum == 0) - { - /* special case: first record changed; update parent record key */ - - n_index(np, record, 0); - *flag = 1; - } - - return bt_putnode(np); - -fail: - return -1; -} diff --git a/ciderpress/diskimg/libhfs/node.h b/ciderpress/diskimg/libhfs/node.h deleted file mode 100644 index 12272ce..0000000 --- a/ciderpress/diskimg/libhfs/node.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -void n_init(node *, btree *, int, int); - -int n_new(node *); -int n_free(node *); - -int n_search(node *, const byte *); - -void n_index(const node *, byte *, unsigned int *); - -void n_insertx(node *, const byte *, unsigned int); -int n_insert(node *, byte *, unsigned int *); - -int n_delete(node *, byte *, int *); diff --git a/ciderpress/diskimg/libhfs/os.c b/ciderpress/diskimg/libhfs/os.c deleted file mode 100644 index 22b8d3a..0000000 --- a/ciderpress/diskimg/libhfs/os.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# ifdef HAVE_FCNTL_H -# include -# else -int open(const char *, int, ...); -int fcntl(int, int, ...); -# endif - -# ifdef HAVE_UNISTD_H -# include -# else -#ifndef _WIN32 -int close(int); -off_t lseek(int, off_t, int); -ssize_t read(int, void *, size_t); -ssize_t write(int, const char *, size_t); -int stat(const char *, struct stat *); -int fstat(int, struct stat *); -#endif -# endif - -# include -# include -# include -# include /* debug */ - -# include "libhfs.h" -# include "os.h" - -typedef struct cp_private { - oscallback func; /* function to call */ - void* cookie; /* magic cookie to pass in */ - long cur_block; /* current seek offset */ -} cp_private; - -/* - * NAME: os->callback_open() - * DESCRIPTION: open and lock a new descriptor from the given path and mode - */ -int os_callback_open(void **priv, oscallback func, void* cookie) -{ - cp_private* mypriv; - - mypriv = malloc(sizeof(*mypriv)); - mypriv->func = func; - mypriv->cookie = cookie; - mypriv->cur_block = 0; - - *priv = mypriv; - //fprintf(stderr, "ALLOC %p->%p\n", priv, *priv); - - return 0; -} - -/* - * NAME: os->close() - * DESCRIPTION: close an open descriptor - */ -int os_close(void **priv) -{ - //fprintf(stderr, "FREEING %p->%p\n", priv, *priv); - free(*priv); - *priv = 0; - - return 0; -} - -#ifdef CP_NOT_USED -/* - * NAME: os->same() - * DESCRIPTION: return 1 iff path is same as the open descriptor - */ -int os_same(void **priv, const char *path) -{ - return 0; - - int fd = (int) *priv; - struct stat fdev, dev; - - if (fstat(fd, &fdev) == -1 || - stat(path, &dev) == -1) - ERROR(errno, "can't get path information"); - - return fdev.st_dev == dev.st_dev && - fdev.st_ino == dev.st_ino; - -fail: - return -1; -} -#endif - -/* - * NAME: os->seek() - * DESCRIPTION: set a descriptor's seek pointer (offset in blocks) - */ -unsigned long os_seek(void **priv, unsigned long offset) -{ - cp_private* mypriv = (cp_private*) *priv; - unsigned long result; - - if (offset == (unsigned long) -1) { - result = (*mypriv->func)(mypriv->cookie, HFS_CB_VOLSIZE, 0, 0); - } else { - result = (*mypriv->func)(mypriv->cookie, HFS_CB_SEEK, offset, 0); - if (result != -1) - mypriv->cur_block = offset; - } - - return result; -} - -/* - * NAME: os->read() - * DESCRIPTION: read blocks from an open descriptor - */ -unsigned long os_read(void **priv, void *buf, unsigned long len) -{ - cp_private* mypriv = (cp_private*) *priv; - unsigned long result; - unsigned long success = 0; - - while (len--) { - result = (*mypriv->func)(mypriv->cookie, HFS_CB_READ, - mypriv->cur_block, buf); - if (result == -1) - break; - - mypriv->cur_block++; - buf = ((unsigned char*) buf) + HFS_BLOCKSZ; - success++; - } - - return success; -} - -/* - * NAME: os->write() - * DESCRIPTION: write blocks to an open descriptor - */ -unsigned long os_write(void **priv, const void *buf, unsigned long len) -{ - cp_private* mypriv = (cp_private*) *priv; - unsigned long result; - unsigned long success = 0; - - while (len--) { - result = (*mypriv->func)(mypriv->cookie, HFS_CB_WRITE, - mypriv->cur_block, (void*)buf); - if (result == -1) - break; - - mypriv->cur_block++; - buf = ((unsigned char*) buf) + HFS_BLOCKSZ; - success++; - } - - return success; -} diff --git a/ciderpress/diskimg/libhfs/os.h b/ciderpress/diskimg/libhfs/os.h deleted file mode 100644 index a40c74f..0000000 --- a/ciderpress/diskimg/libhfs/os.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -#ifdef CP_NOT_USED -int os_open(void **, const char *, int); -#endif -int os_callback_open(void **priv, oscallback func, void* cookie); -int os_close(void **); - -#ifdef CP_NOT_USED -int os_same(void **, const char *); -#endif - -unsigned long os_seek(void **, unsigned long); -unsigned long os_read(void **, void *, unsigned long); -unsigned long os_write(void **, const void *, unsigned long); diff --git a/ciderpress/diskimg/libhfs/record.c b/ciderpress/diskimg/libhfs/record.c deleted file mode 100644 index fe269a3..0000000 --- a/ciderpress/diskimg/libhfs/record.c +++ /dev/null @@ -1,557 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include - -# include "libhfs.h" -# include "record.h" -# include "data.h" - -/* - * NAME: record->packcatkey() - * DESCRIPTION: pack a catalog record key - */ -void r_packcatkey(const CatKeyRec *key, byte *pkey, unsigned int *len) -{ - const byte *start = pkey; - - d_storesb(&pkey, key->ckrKeyLen); - d_storesb(&pkey, key->ckrResrv1); - d_storeul(&pkey, key->ckrParID); - - d_storestr(&pkey, key->ckrCName, sizeof(key->ckrCName)); - - if (len) - *len = HFS_RECKEYSKIP(start); -} - -/* - * NAME: record->unpackcatkey() - * DESCRIPTION: unpack a catalog record key - */ -void r_unpackcatkey(const byte *pkey, CatKeyRec *key) -{ - d_fetchsb(&pkey, &key->ckrKeyLen); - d_fetchsb(&pkey, &key->ckrResrv1); - d_fetchul(&pkey, &key->ckrParID); - - d_fetchstr(&pkey, key->ckrCName, sizeof(key->ckrCName)); -} - -/* - * NAME: record->packextkey() - * DESCRIPTION: pack an extents record key - */ -void r_packextkey(const ExtKeyRec *key, byte *pkey, unsigned int *len) -{ - const byte *start = pkey; - - d_storesb(&pkey, key->xkrKeyLen); - d_storesb(&pkey, key->xkrFkType); - d_storeul(&pkey, key->xkrFNum); - d_storeuw(&pkey, key->xkrFABN); - - if (len) - *len = HFS_RECKEYSKIP(start); -} - -/* - * NAME: record->unpackextkey() - * DESCRIPTION: unpack an extents record key - */ -void r_unpackextkey(const byte *pkey, ExtKeyRec *key) -{ - d_fetchsb(&pkey, &key->xkrKeyLen); - d_fetchsb(&pkey, &key->xkrFkType); - d_fetchul(&pkey, &key->xkrFNum); - d_fetchuw(&pkey, &key->xkrFABN); -} - -/* - * NAME: record->comparecatkeys() - * DESCRIPTION: compare two (packed) catalog record keys - */ -int r_comparecatkeys(const CatKeyRec *key1, const CatKeyRec *key2) -{ - int diff; - - diff = key1->ckrParID - key2->ckrParID; - if (diff) - return diff; - - return d_relstring(key1->ckrCName, key2->ckrCName); -} - -/* - * NAME: record->compareextkeys() - * DESCRIPTION: compare two (packed) extents record keys - */ -int r_compareextkeys(const ExtKeyRec *key1, const ExtKeyRec *key2) -{ - int diff; - - diff = key1->xkrFNum - key2->xkrFNum; - if (diff) - return diff; - - diff = (unsigned char) key1->xkrFkType - - (unsigned char) key2->xkrFkType; - if (diff) - return diff; - - return key1->xkrFABN - key2->xkrFABN; -} - -/* - * NAME: record->packcatdata() - * DESCRIPTION: pack catalog record data - */ -void r_packcatdata(const CatDataRec *data, byte *pdata, unsigned int *len) -{ - const byte *start = pdata; - int i; - - d_storesb(&pdata, data->cdrType); - d_storesb(&pdata, data->cdrResrv2); - - switch (data->cdrType) - { - case cdrDirRec: - d_storesw(&pdata, data->u.dir.dirFlags); - d_storeuw(&pdata, data->u.dir.dirVal); - d_storeul(&pdata, data->u.dir.dirDirID); - d_storesl(&pdata, data->u.dir.dirCrDat); - d_storesl(&pdata, data->u.dir.dirMdDat); - d_storesl(&pdata, data->u.dir.dirBkDat); - - d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.top); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.left); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.bottom); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frRect.right); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frFlags); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.v); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frLocation.h); - d_storesw(&pdata, data->u.dir.dirUsrInfo.frView); - - d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.v); - d_storesw(&pdata, data->u.dir.dirFndrInfo.frScroll.h); - d_storesl(&pdata, data->u.dir.dirFndrInfo.frOpenChain); - d_storesw(&pdata, data->u.dir.dirFndrInfo.frUnused); - d_storesw(&pdata, data->u.dir.dirFndrInfo.frComment); - d_storesl(&pdata, data->u.dir.dirFndrInfo.frPutAway); - - for (i = 0; i < 4; ++i) - d_storesl(&pdata, data->u.dir.dirResrv[i]); - - break; - - case cdrFilRec: - d_storesb(&pdata, data->u.fil.filFlags); - d_storesb(&pdata, data->u.fil.filTyp); - - d_storesl(&pdata, data->u.fil.filUsrWds.fdType); - d_storesl(&pdata, data->u.fil.filUsrWds.fdCreator); - d_storesw(&pdata, data->u.fil.filUsrWds.fdFlags); - d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.v); - d_storesw(&pdata, data->u.fil.filUsrWds.fdLocation.h); - d_storesw(&pdata, data->u.fil.filUsrWds.fdFldr); - - d_storeul(&pdata, data->u.fil.filFlNum); - - d_storeuw(&pdata, data->u.fil.filStBlk); - d_storeul(&pdata, data->u.fil.filLgLen); - d_storeul(&pdata, data->u.fil.filPyLen); - - d_storeuw(&pdata, data->u.fil.filRStBlk); - d_storeul(&pdata, data->u.fil.filRLgLen); - d_storeul(&pdata, data->u.fil.filRPyLen); - - d_storesl(&pdata, data->u.fil.filCrDat); - d_storesl(&pdata, data->u.fil.filMdDat); - d_storesl(&pdata, data->u.fil.filBkDat); - - d_storesw(&pdata, data->u.fil.filFndrInfo.fdIconID); - for (i = 0; i < 4; ++i) - d_storesw(&pdata, data->u.fil.filFndrInfo.fdUnused[i]); - d_storesw(&pdata, data->u.fil.filFndrInfo.fdComment); - d_storesl(&pdata, data->u.fil.filFndrInfo.fdPutAway); - - d_storeuw(&pdata, data->u.fil.filClpSize); - - for (i = 0; i < 3; ++i) - { - d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrStABN); - d_storeuw(&pdata, data->u.fil.filExtRec[i].xdrNumABlks); - } - - for (i = 0; i < 3; ++i) - { - d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrStABN); - d_storeuw(&pdata, data->u.fil.filRExtRec[i].xdrNumABlks); - } - - d_storesl(&pdata, data->u.fil.filResrv); - - break; - - case cdrThdRec: - for (i = 0; i < 2; ++i) - d_storesl(&pdata, data->u.dthd.thdResrv[i]); - - d_storeul(&pdata, data->u.dthd.thdParID); - - d_storestr(&pdata, data->u.dthd.thdCName, - sizeof(data->u.dthd.thdCName)); - - break; - - case cdrFThdRec: - for (i = 0; i < 2; ++i) - d_storesl(&pdata, data->u.fthd.fthdResrv[i]); - - d_storeul(&pdata, data->u.fthd.fthdParID); - - d_storestr(&pdata, data->u.fthd.fthdCName, - sizeof(data->u.fthd.fthdCName)); - - break; - - default: - ASSERT(0); - } - - if (len) - *len += pdata - start; -} - -/* - * NAME: record->unpackcatdata() - * DESCRIPTION: unpack catalog record data - */ -void r_unpackcatdata(const byte *pdata, CatDataRec *data) -{ - int i; - - d_fetchsb(&pdata, &data->cdrType); - d_fetchsb(&pdata, &data->cdrResrv2); - - switch (data->cdrType) - { - case cdrDirRec: - d_fetchsw(&pdata, &data->u.dir.dirFlags); - d_fetchuw(&pdata, &data->u.dir.dirVal); - d_fetchul(&pdata, &data->u.dir.dirDirID); - d_fetchsl(&pdata, &data->u.dir.dirCrDat); - d_fetchsl(&pdata, &data->u.dir.dirMdDat); - d_fetchsl(&pdata, &data->u.dir.dirBkDat); - - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.top); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.left); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.bottom); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frRect.right); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frFlags); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.v); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frLocation.h); - d_fetchsw(&pdata, &data->u.dir.dirUsrInfo.frView); - - d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.v); - d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frScroll.h); - d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frOpenChain); - d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frUnused); - d_fetchsw(&pdata, &data->u.dir.dirFndrInfo.frComment); - d_fetchsl(&pdata, &data->u.dir.dirFndrInfo.frPutAway); - - for (i = 0; i < 4; ++i) - d_fetchsl(&pdata, &data->u.dir.dirResrv[i]); - - break; - - case cdrFilRec: - d_fetchsb(&pdata, &data->u.fil.filFlags); - d_fetchsb(&pdata, &data->u.fil.filTyp); - - d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdType); - d_fetchsl(&pdata, &data->u.fil.filUsrWds.fdCreator); - d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFlags); - d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.v); - d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdLocation.h); - d_fetchsw(&pdata, &data->u.fil.filUsrWds.fdFldr); - - d_fetchul(&pdata, &data->u.fil.filFlNum); - - d_fetchuw(&pdata, &data->u.fil.filStBlk); - d_fetchul(&pdata, &data->u.fil.filLgLen); - d_fetchul(&pdata, &data->u.fil.filPyLen); - - d_fetchuw(&pdata, &data->u.fil.filRStBlk); - d_fetchul(&pdata, &data->u.fil.filRLgLen); - d_fetchul(&pdata, &data->u.fil.filRPyLen); - - d_fetchsl(&pdata, &data->u.fil.filCrDat); - d_fetchsl(&pdata, &data->u.fil.filMdDat); - d_fetchsl(&pdata, &data->u.fil.filBkDat); - - d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdIconID); - for (i = 0; i < 4; ++i) - d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdUnused[i]); - d_fetchsw(&pdata, &data->u.fil.filFndrInfo.fdComment); - d_fetchsl(&pdata, &data->u.fil.filFndrInfo.fdPutAway); - - d_fetchuw(&pdata, &data->u.fil.filClpSize); - - for (i = 0; i < 3; ++i) - { - d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrStABN); - d_fetchuw(&pdata, &data->u.fil.filExtRec[i].xdrNumABlks); - } - - for (i = 0; i < 3; ++i) - { - d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrStABN); - d_fetchuw(&pdata, &data->u.fil.filRExtRec[i].xdrNumABlks); - } - - d_fetchsl(&pdata, &data->u.fil.filResrv); - - break; - - case cdrThdRec: - for (i = 0; i < 2; ++i) - d_fetchsl(&pdata, &data->u.dthd.thdResrv[i]); - - d_fetchul(&pdata, &data->u.dthd.thdParID); - - d_fetchstr(&pdata, data->u.dthd.thdCName, - sizeof(data->u.dthd.thdCName)); - - break; - - case cdrFThdRec: - for (i = 0; i < 2; ++i) - d_fetchsl(&pdata, &data->u.fthd.fthdResrv[i]); - - d_fetchul(&pdata, &data->u.fthd.fthdParID); - - d_fetchstr(&pdata, data->u.fthd.fthdCName, - sizeof(data->u.fthd.fthdCName)); - - break; - - default: - ASSERT(0); - } -} - -/* - * NAME: record->packextdata() - * DESCRIPTION: pack extent record data - */ -void r_packextdata(const ExtDataRec *data, byte *pdata, unsigned int *len) -{ - const byte *start = pdata; - int i; - - for (i = 0; i < 3; ++i) - { - d_storeuw(&pdata, (*data)[i].xdrStABN); - d_storeuw(&pdata, (*data)[i].xdrNumABlks); - } - - if (len) - *len += pdata - start; -} - -/* - * NAME: record->unpackextdata() - * DESCRIPTION: unpack extent record data - */ -void r_unpackextdata(const byte *pdata, ExtDataRec *data) -{ - int i; - - for (i = 0; i < 3; ++i) - { - d_fetchuw(&pdata, &(*data)[i].xdrStABN); - d_fetchuw(&pdata, &(*data)[i].xdrNumABlks); - } -} - -/* - * NAME: record->makecatkey() - * DESCRIPTION: construct a catalog record key - */ -void r_makecatkey(CatKeyRec *key, unsigned long parid, const char *name) -{ - int len; - - len = strlen(name) + 1; - - key->ckrKeyLen = 0x05 + len + (len & 1); - key->ckrResrv1 = 0; - key->ckrParID = parid; - - strcpy(key->ckrCName, name); -} - -/* - * NAME: record->makeextkey() - * DESCRIPTION: construct an extents record key - */ -void r_makeextkey(ExtKeyRec *key, - int fork, unsigned long fnum, unsigned int fabn) -{ - key->xkrKeyLen = 0x07; - key->xkrFkType = fork; - key->xkrFNum = fnum; - key->xkrFABN = fabn; -} - -/* - * NAME: record->packcatrec() - * DESCRIPTION: create a packed catalog record - */ -void r_packcatrec(const CatKeyRec *key, const CatDataRec *data, - byte *precord, unsigned int *len) -{ - r_packcatkey(key, precord, len); - r_packcatdata(data, HFS_RECDATA(precord), len); -} - -/* - * NAME: record->packextrec() - * DESCRIPTION: create a packed extents record - */ -void r_packextrec(const ExtKeyRec *key, const ExtDataRec *data, - byte *precord, unsigned int *len) -{ - r_packextkey(key, precord, len); - r_packextdata(data, HFS_RECDATA(precord), len); -} - -/* - * NAME: record->packdirent() - * DESCRIPTION: make changes to a catalog record - */ -void r_packdirent(CatDataRec *data, const hfsdirent *ent) -{ - switch (data->cdrType) - { - case cdrDirRec: - data->u.dir.dirCrDat = d_mtime(ent->crdate); - data->u.dir.dirMdDat = d_mtime(ent->mddate); - data->u.dir.dirBkDat = d_mtime(ent->bkdate); - - data->u.dir.dirUsrInfo.frFlags = ent->fdflags; - data->u.dir.dirUsrInfo.frLocation.v = ent->fdlocation.v; - data->u.dir.dirUsrInfo.frLocation.h = ent->fdlocation.h; - - data->u.dir.dirUsrInfo.frRect.top = ent->u.dir.rect.top; - data->u.dir.dirUsrInfo.frRect.left = ent->u.dir.rect.left; - data->u.dir.dirUsrInfo.frRect.bottom = ent->u.dir.rect.bottom; - data->u.dir.dirUsrInfo.frRect.right = ent->u.dir.rect.right; - - break; - - case cdrFilRec: - if (ent->flags & HFS_ISLOCKED) - data->u.fil.filFlags |= (1 << 0); - else - data->u.fil.filFlags &= ~(1 << 0); - - data->u.fil.filCrDat = d_mtime(ent->crdate); - data->u.fil.filMdDat = d_mtime(ent->mddate); - data->u.fil.filBkDat = d_mtime(ent->bkdate); - - data->u.fil.filUsrWds.fdFlags = ent->fdflags; - data->u.fil.filUsrWds.fdLocation.v = ent->fdlocation.v; - data->u.fil.filUsrWds.fdLocation.h = ent->fdlocation.h; - - data->u.fil.filUsrWds.fdType = - d_getsl((const unsigned char *) ent->u.file.type); - data->u.fil.filUsrWds.fdCreator = - d_getsl((const unsigned char *) ent->u.file.creator); - - break; - } -} - -/* - * NAME: record->unpackdirent() - * DESCRIPTION: unpack catalog information into hfsdirent structure - */ -void r_unpackdirent(unsigned long parid, const char *name, - const CatDataRec *data, hfsdirent *ent) -{ - strcpy(ent->name, name); - ent->parid = parid; - - switch (data->cdrType) - { - case cdrDirRec: - ent->flags = HFS_ISDIR; - ent->cnid = data->u.dir.dirDirID; - - ent->crdate = d_ltime(data->u.dir.dirCrDat); - ent->mddate = d_ltime(data->u.dir.dirMdDat); - ent->bkdate = d_ltime(data->u.dir.dirBkDat); - - ent->fdflags = data->u.dir.dirUsrInfo.frFlags; - ent->fdlocation.v = data->u.dir.dirUsrInfo.frLocation.v; - ent->fdlocation.h = data->u.dir.dirUsrInfo.frLocation.h; - - ent->u.dir.valence = data->u.dir.dirVal; - - ent->u.dir.rect.top = data->u.dir.dirUsrInfo.frRect.top; - ent->u.dir.rect.left = data->u.dir.dirUsrInfo.frRect.left; - ent->u.dir.rect.bottom = data->u.dir.dirUsrInfo.frRect.bottom; - ent->u.dir.rect.right = data->u.dir.dirUsrInfo.frRect.right; - - break; - - case cdrFilRec: - ent->flags = (data->u.fil.filFlags & (1 << 0)) ? HFS_ISLOCKED : 0; - ent->cnid = data->u.fil.filFlNum; - - ent->crdate = d_ltime(data->u.fil.filCrDat); - ent->mddate = d_ltime(data->u.fil.filMdDat); - ent->bkdate = d_ltime(data->u.fil.filBkDat); - - ent->fdflags = data->u.fil.filUsrWds.fdFlags; - ent->fdlocation.v = data->u.fil.filUsrWds.fdLocation.v; - ent->fdlocation.h = data->u.fil.filUsrWds.fdLocation.h; - - ent->u.file.dsize = data->u.fil.filLgLen; - ent->u.file.rsize = data->u.fil.filRLgLen; - - d_putsl((unsigned char *) ent->u.file.type, - data->u.fil.filUsrWds.fdType); - d_putsl((unsigned char *) ent->u.file.creator, - data->u.fil.filUsrWds.fdCreator); - - ent->u.file.type[4] = ent->u.file.creator[4] = 0; - - break; - } -} diff --git a/ciderpress/diskimg/libhfs/record.h b/ciderpress/diskimg/libhfs/record.h deleted file mode 100644 index 74b3067..0000000 --- a/ciderpress/diskimg/libhfs/record.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -void r_packcatkey(const CatKeyRec *, byte *, unsigned int *); -void r_unpackcatkey(const byte *, CatKeyRec *); - -void r_packextkey(const ExtKeyRec *, byte *, unsigned int *); -void r_unpackextkey(const byte *, ExtKeyRec *); - -int r_comparecatkeys(const CatKeyRec *, const CatKeyRec *); -int r_compareextkeys(const ExtKeyRec *, const ExtKeyRec *); - -void r_packcatdata(const CatDataRec *, byte *, unsigned int *); -void r_unpackcatdata(const byte *, CatDataRec *); - -void r_packextdata(const ExtDataRec *, byte *, unsigned int *); -void r_unpackextdata(const byte *, ExtDataRec *); - -void r_makecatkey(CatKeyRec *, unsigned long, const char *); -void r_makeextkey(ExtKeyRec *, int, unsigned long, unsigned int); - -void r_packcatrec(const CatKeyRec *, const CatDataRec *, - byte *, unsigned int *); -void r_packextrec(const ExtKeyRec *, const ExtDataRec *, - byte *, unsigned int *); - -void r_packdirent(CatDataRec *, const hfsdirent *); -void r_unpackdirent(unsigned long, const char *, - const CatDataRec *, hfsdirent *); diff --git a/ciderpress/diskimg/libhfs/version.c b/ciderpress/diskimg/libhfs/version.c deleted file mode 100644 index e88e560..0000000 --- a/ciderpress/diskimg/libhfs/version.c +++ /dev/null @@ -1,29 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# include "version.h" - -const char libhfs_rcsid[] = - "$Id$"; - -const char libhfs_version[] = "libhfs version 3.2.6"; -const char libhfs_copyright[] = "Copyright (C) 1996-1998 Robert Leslie"; -const char libhfs_author[] = "Robert Leslie "; diff --git a/ciderpress/diskimg/libhfs/version.h b/ciderpress/diskimg/libhfs/version.h deleted file mode 100644 index 8717e07..0000000 --- a/ciderpress/diskimg/libhfs/version.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -extern const char libhfs_rcsid[]; - -extern const char libhfs_version[]; -extern const char libhfs_copyright[]; -extern const char libhfs_author[]; diff --git a/ciderpress/diskimg/libhfs/volume.c b/ciderpress/diskimg/libhfs/volume.c deleted file mode 100644 index 477d0fa..0000000 --- a/ciderpress/diskimg/libhfs/volume.c +++ /dev/null @@ -1,1250 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -# ifdef HAVE_CONFIG_H -# include "config.h" -# endif - -# include -# include -# include -# include -# include -# include /* debug */ - -# include "libhfs.h" -# include "volume.h" -# include "data.h" -# include "block.h" -# include "low.h" -# include "medium.h" -# include "file.h" -# include "btree.h" -# include "record.h" -# include "os.h" - -/* - * NAME: vol->init() - * DESCRIPTION: initialize volume structure - */ -void v_init(hfsvol *vol, int flags) -{ - btree *ext = &vol->ext; - btree *cat = &vol->cat; - - vol->priv = 0; - vol->flags = flags & HFS_VOL_OPT_MASK; - - vol->pnum = -1; - vol->vstart = 0; - vol->vlen = 0; - vol->lpa = 0; - - vol->cache = 0; - - vol->vbm = 0; - vol->vbmsz = 0; - - f_init(&ext->f, vol, HFS_CNID_EXT, "extents overflow"); - - ext->map = 0; - ext->mapsz = 0; - ext->flags = 0; - - ext->keyunpack = (keyunpackfunc) r_unpackextkey; - ext->keycompare = (keycomparefunc) r_compareextkeys; - - f_init(&cat->f, vol, HFS_CNID_CAT, "catalog"); - - cat->map = 0; - cat->mapsz = 0; - cat->flags = 0; - - cat->keyunpack = (keyunpackfunc) r_unpackcatkey; - cat->keycompare = (keycomparefunc) r_comparecatkeys; - - vol->cwd = HFS_CNID_ROOTDIR; - - vol->refs = 0; - vol->files = 0; - vol->dirs = 0; - - vol->prev = 0; - vol->next = 0; -} - -#ifdef CP_NOT_USED -/* - * NAME: vol->open() - * DESCRIPTION: open volume source and lock against concurrent updates - */ -int v_open(hfsvol *vol, const char *path, int mode) -{ - if (vol->flags & HFS_VOL_OPEN) - ERROR(EINVAL, "volume already open"); - - if (os_open(&vol->priv, path, mode) == -1) - goto fail; - - vol->flags |= HFS_VOL_OPEN; - - /* initialize volume block cache (OK to fail) */ - - if (! (vol->flags & HFS_OPT_NOCACHE) && - b_init(vol) != -1) - vol->flags |= HFS_VOL_USINGCACHE; - - return 0; - -fail: - return -1; -} -#endif - -/* - * NAME: vol->opencallback() - * DESCRIPTION: open volume source and lock against concurrent updates - */ -int v_callback_open(hfsvol* vol, oscallback func, void* cookie) -{ - if (vol->flags & HFS_VOL_OPEN) - ERROR(EINVAL, "volume already open"); - - if (os_callback_open(&vol->priv, func, cookie) == -1) - goto fail; - - vol->flags |= HFS_VOL_OPEN; - - /* initialize volume block cache (OK to fail) */ - - if (! (vol->flags & HFS_OPT_NOCACHE) && - b_init(vol) != -1) - vol->flags |= HFS_VOL_USINGCACHE; - - return 0; - -fail: - return -1; -} - -/* - * NAME: flushvol() - * DESCRIPTION: flush all pending changes (B*-tree, MDB, VBM) to volume - */ -static -int flushvol(hfsvol *vol, int umount) -{ - if (vol->flags & HFS_VOL_READONLY) - goto done; - - if ((vol->ext.flags & HFS_BT_UPDATE_HDR) && - bt_writehdr(&vol->ext) == -1) - goto fail; - - if ((vol->cat.flags & HFS_BT_UPDATE_HDR) && - bt_writehdr(&vol->cat) == -1) - goto fail; - - if ((vol->flags & HFS_VOL_UPDATE_VBM) && - v_writevbm(vol) == -1) - goto fail; - - /* - * CiderPress note: this causes the MDB to be written when we are - * unmounting the volume if changes have been made at any point. - * This means we ALWAYS write something when we're closing the disk - * if we touched something. That's not great for us, since we - * might be using removable media. We can "fix" this by removing - * the part that marks the volume as mounted (earlier). - */ - if (umount && ! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED)) - { - vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; - vol->flags |= HFS_VOL_UPDATE_MDB; - } - - if ((vol->flags & (HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB)) && - v_writemdb(vol) == -1) - goto fail; - -done: - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->flush() - * DESCRIPTION: commit all pending changes to volume device - */ -int v_flush(hfsvol *vol) -{ - if (flushvol(vol, 0) == -1) - goto fail; - - if ((vol->flags & HFS_VOL_USINGCACHE) && - b_flush(vol) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->close() - * DESCRIPTION: close access path to volume source - */ -int v_close(hfsvol *vol) -{ - int result = 0; - - if (! (vol->flags & HFS_VOL_OPEN)) - goto done; - - if ((vol->flags & HFS_VOL_MOUNTED) && - flushvol(vol, 1) == -1) - result = -1; - - if ((vol->flags & HFS_VOL_USINGCACHE) && - b_finish(vol) == -1) - result = -1; - - if (os_close(&vol->priv) == -1) - result = -1; - - vol->flags &= ~(HFS_VOL_OPEN | HFS_VOL_MOUNTED | HFS_VOL_USINGCACHE); - - /* free dynamically allocated structures */ - - FREE(vol->vbm); - - vol->vbm = 0; - vol->vbmsz = 0; - - FREE(vol->ext.map); - FREE(vol->cat.map); - - vol->ext.map = 0; - vol->cat.map = 0; - -done: - return result; -} - -#ifdef CP_NOT_USED -/* - * NAME: vol->same() - * DESCRIPTION: return 1 iff path is same as open volume - */ -int v_same(hfsvol *vol, const char *path) -{ - return os_same(&vol->priv, path); -} -#endif - -/* - * NAME: vol->geometry() - * DESCRIPTION: determine volume location and size (possibly in a partition) - */ -int v_geometry(hfsvol *vol, int pnum) -{ - Partition map; - unsigned long bnum = 0; - int found; - - vol->pnum = pnum; - - if (pnum == 0) - { - vol->vstart = 0; - vol->vlen = b_size(vol); - - if (vol->vlen == 0) - goto fail; - } - else - { - while (pnum--) - { - found = m_findpmentry(vol, "Apple_HFS", &map, &bnum); - if (found == -1 || ! found) - goto fail; - } - - vol->vstart = map.pmPyPartStart; - vol->vlen = map.pmPartBlkCnt; - - if (map.pmDataCnt) - { - if ((unsigned long) map.pmLgDataStart + - (unsigned long) map.pmDataCnt > vol->vlen) - ERROR(EINVAL, "partition data overflows partition"); - - vol->vstart += (unsigned long) map.pmLgDataStart; - vol->vlen = map.pmDataCnt; - } - - if (vol->vlen == 0) - ERROR(EINVAL, "volume partition is empty"); - } - - if (vol->vlen < 800 * (1024 >> HFS_BLOCKSZ_BITS)) - ERROR(EINVAL, "volume is smaller than 800K"); - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->readmdb() - * DESCRIPTION: load Master Directory Block into memory - */ -int v_readmdb(hfsvol *vol) -{ - if (l_getmdb(vol, &vol->mdb, 0) == -1) - goto fail; - - if (vol->mdb.drSigWord != HFS_SIGWORD) - { - if (vol->mdb.drSigWord == HFS_SIGWORD_MFS) - ERROR(EINVAL, "MFS volume format not supported"); - else - ERROR(EINVAL, "not a Macintosh HFS volume"); - } - - if (vol->mdb.drAlBlkSiz % HFS_BLOCKSZ != 0) - ERROR(EINVAL, "bad volume allocation block size"); - - vol->lpa = vol->mdb.drAlBlkSiz >> HFS_BLOCKSZ_BITS; - - /* extents pseudo-file structs */ - - vol->ext.f.cat.u.fil.filStBlk = vol->mdb.drXTExtRec[0].xdrStABN; - vol->ext.f.cat.u.fil.filLgLen = vol->mdb.drXTFlSize; - vol->ext.f.cat.u.fil.filPyLen = vol->mdb.drXTFlSize; - - vol->ext.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; - vol->ext.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; - - memcpy(&vol->ext.f.cat.u.fil.filExtRec, - &vol->mdb.drXTExtRec, sizeof(ExtDataRec)); - - f_selectfork(&vol->ext.f, fkData); - - /* catalog pseudo-file structs */ - - vol->cat.f.cat.u.fil.filStBlk = vol->mdb.drCTExtRec[0].xdrStABN; - vol->cat.f.cat.u.fil.filLgLen = vol->mdb.drCTFlSize; - vol->cat.f.cat.u.fil.filPyLen = vol->mdb.drCTFlSize; - - vol->cat.f.cat.u.fil.filCrDat = vol->mdb.drCrDate; - vol->cat.f.cat.u.fil.filMdDat = vol->mdb.drLsMod; - - memcpy(&vol->cat.f.cat.u.fil.filExtRec, - &vol->mdb.drCTExtRec, sizeof(ExtDataRec)); - - f_selectfork(&vol->cat.f, fkData); - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->writemdb() - * DESCRIPTION: flush Master Directory Block to medium - */ -int v_writemdb(hfsvol *vol) -{ - vol->mdb.drLsMod = d_mtime(time(0)); - - vol->mdb.drXTFlSize = vol->ext.f.cat.u.fil.filPyLen; - memcpy(&vol->mdb.drXTExtRec, - &vol->ext.f.cat.u.fil.filExtRec, sizeof(ExtDataRec)); - - vol->mdb.drCTFlSize = vol->cat.f.cat.u.fil.filPyLen; - memcpy(&vol->mdb.drCTExtRec, - &vol->cat.f.cat.u.fil.filExtRec, sizeof(ExtDataRec)); - - if (l_putmdb(vol, &vol->mdb, vol->flags & HFS_VOL_UPDATE_ALTMDB) == -1) - goto fail; - - vol->flags &= ~(HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_ALTMDB); - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->readvbm() - * DESCRIPTION: read volume bitmap into memory - */ -int v_readvbm(hfsvol *vol) -{ - unsigned int vbmst = vol->mdb.drVBMSt; - unsigned int vbmsz = (vol->mdb.drNmAlBlks + 0x0fff) >> 12; - block *bp; - - ASSERT(vol->vbm == 0); - - if (vol->mdb.drAlBlSt - vbmst < vbmsz) - ERROR(EIO, "volume bitmap collides with volume data"); - - vol->vbm = ALLOC(block, vbmsz); - if (vol->vbm == 0) - ERROR(ENOMEM, 0); - - vol->vbmsz = vbmsz; - - for (bp = vol->vbm; vbmsz--; ++bp) - { - if (b_readlb(vol, vbmst++, bp) == -1) - goto fail; - } - - return 0; - -fail: - FREE(vol->vbm); - - vol->vbm = 0; - vol->vbmsz = 0; - - return -1; -} - -/* - * NAME: vol->writevbm() - * DESCRIPTION: flush volume bitmap to medium - */ -int v_writevbm(hfsvol *vol) -{ - unsigned int vbmst = vol->mdb.drVBMSt; - unsigned int vbmsz = vol->vbmsz; - const block *bp; - - for (bp = vol->vbm; vbmsz--; ++bp) - { - if (b_writelb(vol, vbmst++, bp) == -1) - goto fail; - } - - vol->flags &= ~HFS_VOL_UPDATE_VBM; - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->mount() - * DESCRIPTION: load volume information into memory - */ -int v_mount(hfsvol *vol) -{ - /* read the MDB, volume bitmap, and extents/catalog B*-tree headers */ - - if (v_readmdb(vol) == -1 || - v_readvbm(vol) == -1 || - bt_readhdr(&vol->ext) == -1 || - bt_readhdr(&vol->cat) == -1) - goto fail; - - if (! (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) && - v_scavenge(vol) == -1) - goto fail; - - if (vol->mdb.drAtrb & HFS_ATRB_SLOCKED) - vol->flags |= HFS_VOL_READONLY; - else if (vol->flags & HFS_VOL_READONLY) - vol->mdb.drAtrb |= HFS_ATRB_HLOCKED; - else - vol->mdb.drAtrb &= ~HFS_ATRB_HLOCKED; - - vol->flags |= HFS_VOL_MOUNTED; - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->dirty() - * DESCRIPTION: ensure the volume is marked "in use" before we make changes - */ -int v_dirty(hfsvol *vol) -{ -#ifdef NOT_FOR_CP // see notes in flushvol() - if (vol->mdb.drAtrb & HFS_ATRB_UMOUNTED) - { - vol->mdb.drAtrb &= ~HFS_ATRB_UMOUNTED; - ++vol->mdb.drWrCnt; - - if (v_writemdb(vol) == -1) - goto fail; - - if ((vol->flags & HFS_VOL_USINGCACHE) && - b_flush(vol) == -1) - goto fail; - } -#endif - - return 0; - -//fail: - // return -1; -} - -/* - * NAME: vol->catsearch() - * DESCRIPTION: search catalog tree - */ -int v_catsearch(hfsvol *vol, unsigned long parid, const char *name, - CatDataRec *data, char *cname, node *np) -{ - CatKeyRec key; - byte pkey[HFS_CATKEYLEN]; - const byte *ptr; - node n; - int found; - - if (np == 0) - np = &n; - - r_makecatkey(&key, parid, name); - r_packcatkey(&key, pkey, 0); - - found = bt_search(&vol->cat, pkey, np); - if (found <= 0) - return found; - - ptr = HFS_NODEREC(*np, np->rnum); - - if (cname) - { - r_unpackcatkey(ptr, &key); - strcpy(cname, key.ckrCName); - } - - if (data) - r_unpackcatdata(HFS_RECDATA(ptr), data); - - return 1; -} - -/* - * NAME: vol->extsearch() - * DESCRIPTION: search extents tree - */ -int v_extsearch(hfsfile *file, unsigned int fabn, - ExtDataRec *data, node *np) -{ - ExtKeyRec key; - ExtDataRec extsave; - unsigned int fabnsave; - byte pkey[HFS_EXTKEYLEN]; - const byte *ptr; - node n; - int found; - - if (np == 0) - np = &n; - - r_makeextkey(&key, file->fork, file->cat.u.fil.filFlNum, fabn); - r_packextkey(&key, pkey, 0); - - /* in case bt_search() clobbers these */ - - memcpy(&extsave, &file->ext, sizeof(ExtDataRec)); - fabnsave = file->fabn; - - found = bt_search(&file->vol->ext, pkey, np); - - memcpy(&file->ext, &extsave, sizeof(ExtDataRec)); - file->fabn = fabnsave; - - if (found <= 0) - return found; - - if (data) - { - ptr = HFS_NODEREC(*np, np->rnum); - r_unpackextdata(HFS_RECDATA(ptr), data); - } - - return 1; -} - -/* - * NAME: vol->getthread() - * DESCRIPTION: retrieve catalog thread information for a file or directory - */ -int v_getthread(hfsvol *vol, unsigned long id, - CatDataRec *thread, node *np, int type) -{ - CatDataRec rec; - int found; - - if (thread == 0) - thread = &rec; - - found = v_catsearch(vol, id, "", thread, 0, np); - if (found == 1 && thread->cdrType != type) - ERROR(EIO, "bad thread record"); - - return found; - -fail: - return -1; -} - -/* - * NAME: vol->putcatrec() - * DESCRIPTION: store catalog information - */ -int v_putcatrec(const CatDataRec *data, node *np) -{ - byte pdata[HFS_CATDATALEN], *ptr; - unsigned int len = 0; - - r_packcatdata(data, pdata, &len); - - ptr = HFS_NODEREC(*np, np->rnum); - memcpy(HFS_RECDATA(ptr), pdata, len); - - return bt_putnode(np); -} - -/* - * NAME: vol->putextrec() - * DESCRIPTION: store extent information - */ -int v_putextrec(const ExtDataRec *data, node *np) -{ - byte pdata[HFS_EXTDATALEN], *ptr; - unsigned int len = 0; - - r_packextdata(data, pdata, &len); - - ptr = HFS_NODEREC(*np, np->rnum); - memcpy(HFS_RECDATA(ptr), pdata, len); - - return bt_putnode(np); -} - -/* - * NAME: vol->allocblocks() - * DESCRIPTION: allocate a contiguous range of blocks - */ -int v_allocblocks(hfsvol *vol, ExtDescriptor *blocks) -{ - unsigned int request, found, foundat, start, end; - register unsigned int pt; - block *vbm; - int wrap = 0; - - if (vol->mdb.drFreeBks == 0) - ERROR(ENOSPC, "volume full"); - - request = blocks->xdrNumABlks; - found = 0; - foundat = 0; - start = vol->mdb.drAllocPtr; - end = vol->mdb.drNmAlBlks; - vbm = vol->vbm; - - ASSERT(request > 0); - - /* backtrack the start pointer to recover unused space */ - - if (! BMTST(vbm, start)) - { - while (start > 0 && ! BMTST(vbm, start - 1)) - --start; - } - - /* find largest unused block which satisfies request */ - - pt = start; - - while (1) - { - unsigned int mark; - - /* skip blocks in use */ - - while (pt < end && BMTST(vbm, pt)) - ++pt; - - if (wrap && pt >= start) - break; - - /* count blocks not in use */ - - mark = pt; - while (pt < end && pt - mark < request && ! BMTST(vbm, pt)) - ++pt; - - if (pt - mark > found) - { - found = pt - mark; - foundat = mark; - } - - if (wrap && pt >= start) - break; - - if (pt == end) - pt = 0, wrap = 1; - - if (found == request) - break; - } - - if (found == 0 || found > vol->mdb.drFreeBks) - ERROR(EIO, "bad volume bitmap or free block count"); - - blocks->xdrStABN = foundat; - blocks->xdrNumABlks = found; - - if (v_dirty(vol) == -1) - goto fail; - - vol->mdb.drAllocPtr = pt; - vol->mdb.drFreeBks -= found; - - for (pt = foundat; pt < foundat + found; ++pt) - BMSET(vbm, pt); - - vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM; - - if (vol->flags & HFS_OPT_ZERO) - { - block b; - unsigned int i; - - memset(&b, 0, sizeof(b)); - - for (pt = foundat; pt < foundat + found; ++pt) - { - for (i = 0; i < vol->lpa; ++i) - b_writeab(vol, pt, i, &b); - } - } - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->freeblocks() - * DESCRIPTION: deallocate a contiguous range of blocks - */ -int v_freeblocks(hfsvol *vol, const ExtDescriptor *blocks) -{ - unsigned int start, len, pt; - block *vbm; - - start = blocks->xdrStABN; - len = blocks->xdrNumABlks; - vbm = vol->vbm; - - if (v_dirty(vol) == -1) - goto fail; - - vol->mdb.drFreeBks += len; - - for (pt = start; pt < start + len; ++pt) - BMCLR(vbm, pt); - - vol->flags |= HFS_VOL_UPDATE_MDB | HFS_VOL_UPDATE_VBM; - - return 0; - -fail: - return -1; -} - -/* - * NAME: vol->resolve() - * DESCRIPTION: translate a pathname; return catalog information - */ -int v_resolve(hfsvol **vol, const char *path, - CatDataRec *data, long *parid, char *fname, node *np) -{ - unsigned long dirid; - char name[HFS_MAX_FLEN + 1], *nptr; - int found = 0; - - if (*path == 0) - ERROR(ENOENT, "empty path"); - - if (parid) - *parid = 0; - - nptr = strchr(path, ':'); - - if (*path == ':' || nptr == 0) - { - dirid = (*vol)->cwd; /* relative path */ - - if (*path == ':') - ++path; - - if (*path == 0) - { - found = v_getdthread(*vol, dirid, data, 0); - if (found == -1) - goto fail; - - if (found) - { - if (parid) - *parid = data->u.dthd.thdParID; - - found = v_catsearch(*vol, data->u.dthd.thdParID, - data->u.dthd.thdCName, data, fname, np); - if (found == -1) - goto fail; - } - - goto done; - } - } - else - { -#ifdef CP_NO_STATIC - hfsvol *check; -#endif - - dirid = HFS_CNID_ROOTPAR; /* absolute path */ - - if (nptr - path > HFS_MAX_VLEN) - ERROR(ENAMETOOLONG, 0); - - strncpy(name, path, nptr - path); - name[nptr - path] = 0; - -#ifdef CP_NO_STATIC - for (check = hfs_mounts; check; check = check->next) - { - if (d_relstring(check->mdb.drVN, name) == 0) - { - *vol = check; - break; - } - } -#else - assert(*vol != 0); -#endif - } - - while (1) - { - while (*path == ':') - { - ++path; - - found = v_getdthread(*vol, dirid, data, 0); - if (found == -1) - goto fail; - else if (! found) - goto done; - - dirid = data->u.dthd.thdParID; - } - - if (*path == 0) - { - found = v_getdthread(*vol, dirid, data, 0); - if (found == -1) - goto fail; - - if (found) - { - if (parid) - *parid = data->u.dthd.thdParID; - - found = v_catsearch(*vol, data->u.dthd.thdParID, - data->u.dthd.thdCName, data, fname, np); - if (found == -1) - goto fail; - } - - goto done; - } - - nptr = name; - while (nptr < name + sizeof(name) - 1 && *path && *path != ':') - *nptr++ = *path++; - - if (*path && *path != ':') - ERROR(ENAMETOOLONG, 0); - - *nptr = 0; - if (*path == ':') - ++path; - - if (parid) - *parid = dirid; - - found = v_catsearch(*vol, dirid, name, data, fname, np); - if (found == -1) - goto fail; - - if (! found) - { - if (*path && parid) - *parid = 0; - - if (*path == 0 && fname) - strcpy(fname, name); - - goto done; - } - - switch (data->cdrType) - { - case cdrDirRec: - if (*path == 0) - goto done; - - dirid = data->u.dir.dirDirID; - break; - - case cdrFilRec: - if (*path == 0) - goto done; - - ERROR(ENOTDIR, "invalid pathname"); - - default: - ERROR(EIO, "unexpected catalog record"); - } - } - -done: - return found; - -fail: - return -1; -} - -/* - * NAME: vol->adjvalence() - * DESCRIPTION: update a volume's valence counts - */ -int v_adjvalence(hfsvol *vol, unsigned long parid, int isdir, int adj) -{ - node n; - CatDataRec data; - int result = 0; - - if (isdir) - vol->mdb.drDirCnt += adj; - else - vol->mdb.drFilCnt += adj; - - vol->flags |= HFS_VOL_UPDATE_MDB; - - if (parid == HFS_CNID_ROOTDIR) - { - if (isdir) - vol->mdb.drNmRtDirs += adj; - else - vol->mdb.drNmFls += adj; - } - else if (parid == HFS_CNID_ROOTPAR) - goto done; - - if (v_getdthread(vol, parid, &data, 0) <= 0 || - v_catsearch(vol, data.u.dthd.thdParID, data.u.dthd.thdCName, - &data, 0, &n) <= 0 || - data.cdrType != cdrDirRec) - ERROR(EIO, "can't find parent directory"); - - data.u.dir.dirVal += adj; - data.u.dir.dirMdDat = d_mtime(time(0)); - - result = v_putcatrec(&data, &n); - -done: - return result; - -fail: - return -1; -} - -/* - * NAME: vol->mkdir() - * DESCRIPTION: create a new HFS directory - */ -int v_mkdir(hfsvol *vol, unsigned long parid, const char *name) -{ - CatKeyRec key; - CatDataRec data; - unsigned long id; - byte record[HFS_MAX_CATRECLEN]; - unsigned int reclen; - int i; - - if (bt_space(&vol->cat, 2) == -1) - goto fail; - - id = vol->mdb.drNxtCNID++; - vol->flags |= HFS_VOL_UPDATE_MDB; - - /* create directory record */ - - data.cdrType = cdrDirRec; - data.cdrResrv2 = 0; - - data.u.dir.dirFlags = 0; - data.u.dir.dirVal = 0; - data.u.dir.dirDirID = id; - data.u.dir.dirCrDat = d_mtime(time(0)); - data.u.dir.dirMdDat = data.u.dir.dirCrDat; - data.u.dir.dirBkDat = 0; - - memset(&data.u.dir.dirUsrInfo, 0, sizeof(data.u.dir.dirUsrInfo)); - memset(&data.u.dir.dirFndrInfo, 0, sizeof(data.u.dir.dirFndrInfo)); - for (i = 0; i < 4; ++i) - data.u.dir.dirResrv[i] = 0; - - r_makecatkey(&key, parid, name); - r_packcatrec(&key, &data, record, &reclen); - - if (bt_insert(&vol->cat, record, reclen) == -1) - goto fail; - - /* create thread record */ - - data.cdrType = cdrThdRec; - data.cdrResrv2 = 0; - - data.u.dthd.thdResrv[0] = 0; - data.u.dthd.thdResrv[1] = 0; - data.u.dthd.thdParID = parid; - strcpy(data.u.dthd.thdCName, name); - - r_makecatkey(&key, id, ""); - r_packcatrec(&key, &data, record, &reclen); - - if (bt_insert(&vol->cat, record, reclen) == -1 || - v_adjvalence(vol, parid, 1, 1) == -1) - goto fail; - - return 0; - -fail: - return -1; -} - -/* - * NAME: markexts() - * DESCRIPTION: set bits from an extent record in the volume bitmap - */ -static -void markexts(block *vbm, const ExtDataRec *exts) -{ - int i; - unsigned int pt, len; - - for (i = 0; i < 3; ++i) - { - for ( pt = (*exts)[i].xdrStABN, - len = (*exts)[i].xdrNumABlks; len--; ++pt) - BMSET(vbm, pt); - } -} - -/* - * NAME: vol->scavenge() - * DESCRIPTION: safeguard blocks in the volume bitmap - */ -int v_scavenge(hfsvol *vol) -{ - block *vbm = vol->vbm; - node n; - unsigned int pt, blks; - unsigned long lastcnid = 15; - -# ifdef DEBUG - fprintf(stderr, "VOL: \"%s\" not cleanly unmounted\n", - vol->mdb.drVN); -# endif - - if (vol->flags & HFS_VOL_READONLY) - goto done; - -# ifdef DEBUG - fprintf(stderr, "VOL: scavenging...\n"); -# endif - - /* reset MDB by marking it dirty again */ - - vol->mdb.drAtrb |= HFS_ATRB_UMOUNTED; - if (v_dirty(vol) == -1) - goto fail; - - /* begin by marking extents in MDB */ - - markexts(vbm, &vol->mdb.drXTExtRec); - markexts(vbm, &vol->mdb.drCTExtRec); - - vol->flags |= HFS_VOL_UPDATE_VBM; - - /* scavenge the extents overflow file */ - - if (vol->ext.hdr.bthFNode > 0) - { - if (bt_getnode(&n, &vol->ext, vol->ext.hdr.bthFNode) == -1) - goto fail; - - n.rnum = 0; - - while (1) - { - ExtDataRec data; - const byte *ptr; - - while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0) - { - if (bt_getnode(&n, &vol->ext, n.nd.ndFLink) == -1) - goto fail; - - n.rnum = 0; - } - - if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0) - break; - - ptr = HFS_NODEREC(n, n.rnum); - r_unpackextdata(HFS_RECDATA(ptr), &data); - - markexts(vbm, &data); - - ++n.rnum; - } - } - - /* scavenge the catalog file */ - - if (vol->cat.hdr.bthFNode > 0) - { - if (bt_getnode(&n, &vol->cat, vol->cat.hdr.bthFNode) == -1) - goto fail; - - n.rnum = 0; - - while (1) - { - CatDataRec data; - const byte *ptr; - - while (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink > 0) - { - if (bt_getnode(&n, &vol->cat, n.nd.ndFLink) == -1) - goto fail; - - n.rnum = 0; - } - - if (n.rnum >= n.nd.ndNRecs && n.nd.ndFLink == 0) - break; - - ptr = HFS_NODEREC(n, n.rnum); - r_unpackcatdata(HFS_RECDATA(ptr), &data); - - switch (data.cdrType) - { - case cdrFilRec: - markexts(vbm, &data.u.fil.filExtRec); - markexts(vbm, &data.u.fil.filRExtRec); - - if (data.u.fil.filFlNum > lastcnid) - lastcnid = data.u.fil.filFlNum; - break; - - case cdrDirRec: - if (data.u.dir.dirDirID > lastcnid) - lastcnid = data.u.dir.dirDirID; - break; - } - - ++n.rnum; - } - } - - /* count free blocks */ - - for (blks = 0, pt = vol->mdb.drNmAlBlks; pt--; ) - { - if (! BMTST(vbm, pt)) - ++blks; - } - - if (vol->mdb.drFreeBks != blks) - { -# ifdef DEBUG - fprintf(stderr, "VOL: updating free blocks from %u to %u\n", - vol->mdb.drFreeBks, blks); -# endif - - vol->mdb.drFreeBks = blks; - vol->flags |= HFS_VOL_UPDATE_MDB; - } - - /* ensure next CNID is sane */ - - if ((unsigned long) vol->mdb.drNxtCNID <= lastcnid) - { -# ifdef DEBUG - fprintf(stderr, "VOL: updating next CNID from %lu to %lu\n", - vol->mdb.drNxtCNID, lastcnid + 1); -# endif - - vol->mdb.drNxtCNID = lastcnid + 1; - vol->flags |= HFS_VOL_UPDATE_MDB; - } - -# ifdef DEBUG - fprintf(stderr, "VOL: scavenging complete\n"); -# endif - -done: - return 0; - -fail: - return -1; -} diff --git a/ciderpress/diskimg/libhfs/volume.h b/ciderpress/diskimg/libhfs/volume.h deleted file mode 100644 index c5ded74..0000000 --- a/ciderpress/diskimg/libhfs/volume.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * libhfs - library for reading and writing Macintosh HFS volumes - * Copyright (C) 1996-1998 Robert Leslie - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ - */ - -void v_init(hfsvol *, int); - -#ifdef CP_NOT_USED -int v_open(hfsvol *, const char *, int); -#endif -int v_callback_open(hfsvol *, oscallback, void*); -int v_flush(hfsvol *); -int v_close(hfsvol *); - -#ifdef CP_NOT_USED -int v_same(hfsvol *, const char *); -#endif -int v_geometry(hfsvol *, int); - -int v_readmdb(hfsvol *); -int v_writemdb(hfsvol *); - -int v_readvbm(hfsvol *); -int v_writevbm(hfsvol *); - -int v_mount(hfsvol *); -int v_dirty(hfsvol *); - -int v_catsearch(hfsvol *, unsigned long, const char *, - CatDataRec *, char *, node *); -int v_extsearch(hfsfile *, unsigned int, ExtDataRec *, node *); - -int v_getthread(hfsvol *, unsigned long, CatDataRec *, node *, int); - -# define v_getdthread(vol, id, thread, np) \ - v_getthread(vol, id, thread, np, cdrThdRec) -# define v_getfthread(vol, id, thread, np) \ - v_getthread(vol, id, thread, np, cdrFThdRec) - -int v_putcatrec(const CatDataRec *, node *); -int v_putextrec(const ExtDataRec *, node *); - -int v_allocblocks(hfsvol *, ExtDescriptor *); -int v_freeblocks(hfsvol *, const ExtDescriptor *); - -int v_resolve(hfsvol **, const char *, CatDataRec *, long *, char *, node *); - -int v_adjvalence(hfsvol *, unsigned long, int, int); -int v_mkdir(hfsvol *, unsigned long, const char *); - -int v_scavenge(hfsvol *); diff --git a/ciderpress/linux/.gitignore b/ciderpress/linux/.gitignore deleted file mode 100644 index af101e1..0000000 --- a/ciderpress/linux/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -getfile -iconv -makedisk -mdc -packddd -sstasm diff --git a/ciderpress/linux/Convert.cpp b/ciderpress/linux/Convert.cpp deleted file mode 100644 index 81d5941..0000000 --- a/ciderpress/linux/Convert.cpp +++ /dev/null @@ -1,491 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Convert from one image format to another. - */ -#include -#include -#include -#include -#include -#include "../diskimg/DiskImg.h" -#include "../nufxlib/NufxLib.h" - -using namespace DiskImgLib; - -#define nil NULL -#define ASSERT assert -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) - -FILE* gLog = nil; -pid_t gPid = getpid(); - - -/* - * Handle a debug message from the DiskImg library. - */ -/*static*/ void -MsgHandler(const char* file, int line, const char* msg) -{ - ASSERT(file != nil); - ASSERT(msg != nil); - - fprintf(gLog, "%05u %s", gPid, msg); -} -/* - * Handle a global error message from the NufxLib library by shoving it - * through the DiskImgLib message function. - */ -NuResult -NufxErrorMsgHandler(NuArchive* /*pArchive*/, void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - if (pErrorMessage->isDebug) { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - " [D] %s\n", pErrorMessage->message); - } else { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - " %s\n", pErrorMessage->message); - } - - return kNuOK; -} - -/* - * Convert one disk image to another. - */ -DIError -Convert(const char* infile, const char* outfile) -{ - DIError dierr = kDIErrNone; - DiskImg srcImg, dstImg; - const char* storageName = nil; - - printf("Converting in='%s' out='%s'\n", infile, outfile); - - /* - * Prepare the source image. - */ - dierr = srcImg.OpenImage(infile, '/', true); - if (dierr != kDIErrNone) { - fprintf(stderr, "Unable to open disk image: %s.\n", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - dierr = srcImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "Unable to determine source image format.\n"); - goto bail; - } - - if (!srcImg.GetHasBlocks() && !srcImg.GetHasSectors()) { - /* add nibble tracks someday */ - fprintf(stderr, - "Sorry, only block- or sector-addressable images allowed.\n"); - dierr = kDIErrUnsupportedPhysicalFmt; - goto bail; - } - if (srcImg.GetHasBlocks()) { - assert(srcImg.GetNumBlocks() > 0); - } else { - assert(srcImg.GetNumTracks() > 0); - } - - if (srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - fprintf(stderr, "(QUERY) don't know sector order\n"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - storageName = "MyHappyDisk"; - - /* force the access to be ProDOS-ordered */ - dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), - DiskImg::kFormatGenericProDOSOrd, srcImg.GetSectorOrder()); - if (dierr != kDIErrNone) { - fprintf(stderr, "Couldn't switch to generic ProDOS: %s.\n", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* transfer the DOS volume num, if one was set */ - printf("DOS volume number set to %d\n", srcImg.GetDOSVolumeNum()); - dstImg.SetDOSVolumeNum(srcImg.GetDOSVolumeNum()); - - const DiskImg::NibbleDescr* pNibbleDescr; - pNibbleDescr = nil; - - /* - * Prepare the destination image. - * - * We *always* use DiskImg::kFormatGenericProDOSOrd here, because it - * must match up with what we selected above. - * - * We could enable "skipFormat" on all of these but the nibble images, - * but we go ahead and set it to "false" on all of them just for fun. - */ - switch (18) { - case 0: - /* 16-sector nibble image, by blocks */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatNib525_6656, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 1: - /* 16-sector nibble image, by tracks/sectors */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatNib525_6656, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - 35, 16, - false); - break; - case 2: - /* 16-sector NB2 nibble image, by tracks/sectors */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatNib525_6384, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - 35, 16, - false); - break; - case 3: - /* 13-sector nibble image, by tracks/sectors */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS32Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatNib525_6656, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - 35, 13, - false); - break; - case 4: - /* 16-sector nb2 image, by tracks/sectors */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatNib525_6384, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - 35, 16, - false); - break; - case 5: - /* sector image, by blocks, ProDOS order */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 6: - /* sector image, by blocks, DOS order */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 7: - /* sector image, by blocks, ProDOS order, Sim2e */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatSim2eHDV, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 8: - /* odd-length HUGE sector image, by blocks */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - 65535, - false); - break; - case 9: - /* sector image, by blocks, physical order, with gzip */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatGzip, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 10: - /* sector image, by blocks, ProDOS order, with gzip */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatGzip, - DiskImg::kFileFormatSim2eHDV, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 11: - /* sector image, by blocks, ProDOS order, 2MG */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormat2MG, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 12: - /* 16-sector nibble image, by tracks/sectors, 2MG */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormat2MG, - DiskImg::kPhysicalFormatNib525_6656, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - 35, 16, - false); - break; - case 13: - /* 16-sector nibble image, by tracks/sectors, 2MG, gzip */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatGzip, - DiskImg::kFileFormat2MG, - DiskImg::kPhysicalFormatNib525_6656, - pNibbleDescr, - DiskImg::kSectorOrderPhysical, - DiskImg::kFormatGenericProDOSOrd, - 35, 16, - false); - break; - case 14: - /* sector image, by blocks, for DC42 (800K only) */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatDiskCopy42, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 15: - /* sector image, by blocks, for NuFX */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatNuFX, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 16: - /* sector image, by blocks, for DDD */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatDDD, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 17: - /* sector image, by blocks, ProDOS order, stored in ZIP (.po.zip) */ - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatZip, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - srcImg.GetNumBlocks(), - false); - break; - case 18: - /* 13-sector nibble image, by tracks/sectors */ - pNibbleDescr= DiskImg::GetStdNibbleDescr(DiskImg::kNibbleDescrDOS33Std); - dierr = dstImg.CreateImage(outfile, storageName, - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - pNibbleDescr, - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - 35, 13, - false); - break; - default: - fprintf(stderr, "UNEXPECTED NUMBER\n"); - abort(); - } - if (dierr != kDIErrNone) { - fprintf(stderr, "Couldn't create new image file '%s': %s.\n", - outfile, DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* - * Copy blocks or sectors from source to destination. - */ - if (srcImg.GetHasBlocks()) { - int numBlocks; - numBlocks = srcImg.GetNumBlocks(); - if (dstImg.GetNumBlocks() < srcImg.GetNumBlocks()) - numBlocks = dstImg.GetNumBlocks(); - printf("Copying %d blocks\n", numBlocks); - - unsigned char blkBuf[512]; - for (int block = 0; block < numBlocks; block++) { - dierr = srcImg.ReadBlock(block, blkBuf); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: ReadBlock failed (err=%d)\n", dierr); - goto bail; - } - dierr = dstImg.WriteBlock(block, blkBuf); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: WriteBlock failed (err=%d)\n", dierr); - goto bail; - } - } - } else { - int numTracks, numSectPerTrack; - numTracks = srcImg.GetNumTracks(); - numSectPerTrack = srcImg.GetNumSectPerTrack(); - if (dstImg.GetNumTracks() < srcImg.GetNumTracks()) - numTracks = dstImg.GetNumTracks(); - if (dstImg.GetNumSectPerTrack() < srcImg.GetNumSectPerTrack()) - numSectPerTrack = dstImg.GetNumSectPerTrack(); - printf("Copying %d tracks of %d sectors\n", numTracks, numSectPerTrack); - - unsigned char sctBuf[256]; - for (int track = 0; track < numTracks; track++) { - for (int sector = 0; sector < numSectPerTrack; sector++) { - dierr = srcImg.ReadTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) { - fprintf(stderr, - "WARNING: ReadTrackSector failed on T=%d S=%d (err=%d)\n", - track, sector, dierr); - dierr = kDIErrNone; // allow bad blocks - memset(sctBuf, 0, sizeof(sctBuf)); - } - dierr = dstImg.WriteTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) { - fprintf(stderr, - "ERROR: WriteBlock failed on T=%d S=%d (err=%d)\n", - track, sector, dierr); - goto bail; - } - } - } - } - - dierr = srcImg.CloseImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: srcImg close failed?!\n"); - goto bail; - } - - dierr = dstImg.CloseImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: dstImg close failed (err=%d)\n", dierr); - goto bail; - } - - assert(dierr == kDIErrNone); -bail: - return dierr; -} - -/* - * Process every argument. - */ -int -main(int argc, char** argv) -{ - const char* kLogFile = "iconv-log.txt"; - - if (argc != 3) { - fprintf(stderr, "%s: infile outfile\n", argv[0]); - exit(2); - } - - gLog = fopen(kLogFile, "w"); - if (gLog == nil) { - fprintf(stderr, "ERROR: unable to open log file\n"); - exit(1); - } - - printf("Image Converter for Linux v1.0\n"); - printf("Copyright (C) 2014 by faddenSoft. All rights reserved.\n"); - int32_t major, minor, bug; - Global::GetVersion(&major, &minor, &bug); - printf("Linked against DiskImg library v%d.%d.%d\n", - major, minor, bug); - printf("Log file is '%s'\n", kLogFile); - printf("\n"); - - Global::SetDebugMsgHandler(MsgHandler); - Global::AppInit(); - - NuSetGlobalErrorMessageHandler(NufxErrorMsgHandler); - - Convert(argv[1], argv[2]); - - Global::AppCleanup(); - fclose(gLog); - - exit(0); -} - diff --git a/ciderpress/linux/GetFile.cpp b/ciderpress/linux/GetFile.cpp deleted file mode 100644 index c3ca570..0000000 --- a/ciderpress/linux/GetFile.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Get a file from a disk image. - */ -#include -#include -#include -#include -#include -#include -#include -#include "../diskimg/DiskImg.h" - -using namespace DiskImgLib; - -#define nil NULL -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) - -/* - * Globals. - */ -FILE* gLog = nil; -pid_t gPid = getpid(); - -/* - * Show usage info. - */ -void -Usage(const char* argv0) -{ - fprintf(stderr, "Usage: %s image-filename file\n", argv0); - - fprintf(stderr, "\n"); - fprintf(stderr, "The file will be written to stdout.\n"); -} - -/* - * Copy a file from "src" to "dst". - */ -int -CopyFile(A2FileDescr* src, FILE* dst) -{ - DIError dierr; - size_t actual; - char buf[4096]; - - while (1) { - dierr = src->Read(buf, sizeof(buf), &actual); - if (dierr != kDIErrNone) { - fprintf(stderr, "Error: read failed: %s\n", DIStrError(dierr)); - return -1; - } - - if (actual == 0) // EOF hit - break; - - fwrite(buf, 1, actual, dst); - } - - return 0; -} - -/* - * Extract the named file from the specified image. - */ -int -Process(const char* imageName, const char* wantedFileName) -{ - DIError dierr; - DiskImg diskImg; - DiskFS* pDiskFS = nil; - A2File* pFile = nil; - A2FileDescr* pDescr = nil; - int result = -1; - - /* open read-only */ - dierr = diskImg.OpenImage(imageName, '/', true); - if (dierr != kDIErrNone) { - fprintf(stderr, "Unable to open '%s': %s\n", imageName, - DIStrError(dierr)); - goto bail; - } - - /* figure out the format */ - dierr = diskImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "Analysis of '%s' failed: %s\n", imageName, - DIStrError(dierr)); - goto bail; - } - - /* recognized? */ - if (diskImg.GetFSFormat() == DiskImg::kFormatUnknown || - diskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - fprintf(stderr, "Unable to identify filesystem on '%s'\n", imageName); - goto bail; - } - - /* create an appropriate DiskFS object */ - pDiskFS = diskImg.OpenAppropriateDiskFS(); - if (pDiskFS == nil) { - /* unknown FS should've been caught above! */ - assert(false); - fprintf(stderr, "Format of '%s' not recognized.\n", imageName); - goto bail; - } - - /* go ahead and load up volumes mounted inside volumes */ - pDiskFS->SetScanForSubVolumes(DiskFS::kScanSubEnabled); - - /* do a full scan */ - dierr = pDiskFS->Initialize(&diskImg, DiskFS::kInitFull); - if (dierr != kDIErrNone) { - fprintf(stderr, "Error reading list of files from disk: %s\n", - DIStrError(dierr)); - goto bail; - } - - /* - * Find the file. This comes out of a list of entries, so don't - * delete "pFile" when we're done. - */ - pFile = pDiskFS->GetFileByName(wantedFileName); - if (pFile == nil) { - fprintf(stderr, "File '%s' not found in '%s'\n", wantedFileName, - imageName); - goto bail; - } - - /* - * Open the file read-only. - */ - dierr = pFile->Open(&pDescr, true); - if (dierr != kDIErrNone) { - fprintf(stderr, "Error opening '%s': %s\n", wantedFileName, - DIStrError(dierr)); - goto bail; - } - - /* - * Copy the file to stdout. - */ - result = CopyFile(pDescr, stdout); - -bail: - if (pDescr != nil) { - pDescr->Close(); - //delete pDescr; -- don't do this (double free) - } - delete pDiskFS; - return result; -} - -/* - * Handle a debug message from the DiskImg library. - */ -/*static*/ void -MsgHandler(const char* file, int line, const char* msg) -{ - assert(file != nil); - assert(msg != nil); - -#ifdef _DEBUG - fprintf(gLog, "%05u %s", gPid, msg); -#endif -} - -/* - * Process args. - */ -int -main(int argc, char** argv) -{ -#ifdef _DEBUG - const char* kLogFile = "makedisk-log.txt"; - gLog = fopen(kLogFile, "w"); - if (gLog == nil) { - fprintf(stderr, "ERROR: unable to open log file\n"); - exit(1); - } -#endif - -#ifdef _DEBUG - fprintf(stderr, "Log file is '%s'\n", kLogFile); -#endif - - Global::SetDebugMsgHandler(MsgHandler); - Global::AppInit(); - - if (argc != 3) { - Usage(argv[0]); - exit(2); - } - - const char* imageName; - const char* getFileName; - - argv++; - imageName = *argv++; - getFileName = *argv++; - argc -= 2; - - if (Process(imageName, getFileName) == 0) - fprintf(stderr, "Success!\n"); - else - fprintf(stderr, "Failed.\n"); - - Global::AppCleanup(); -#ifdef _DEBUG - fclose(gLog); -#endif - - exit(0); -} - diff --git a/ciderpress/linux/MDC.cpp b/ciderpress/linux/MDC.cpp deleted file mode 100644 index ce62b9d..0000000 --- a/ciderpress/linux/MDC.cpp +++ /dev/null @@ -1,894 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Drive diskimglib. Similar to MDC. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "zlib.h" -#include "../diskimg/DiskImg.h" -#include "../nufxlib/NufxLib.h" -#include "StringArray.h" - -using namespace DiskImgLib; - -#define nil NULL -#define ASSERT assert -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) -typedef const char* LPCTSTR; - -#define UNIX_LIKE -#define HAVE_DIRENT_H // linux -#define MAX_PATH_LEN 1024 - -/* get a grip on this opendir/readdir stuff */ -#if defined(UNIX_LIKE) -# if defined(HAVE_DIRENT_H) -# include -# define DIR_NAME_LEN(dirent) ((int)strlen((dirent)->d_name)) - typedef struct dirent DIR_TYPE; -# elif defined(HAVE_SYS_DIR_H) -# include -# define DIR_NAME_LEN(direct) ((direct)->d_namlen) - typedef struct direct DIR_TYPE; -# elif defined(HAVE_NDIR_H) -# include -# define DIR_NAME_LEN(direct) ((direct)->d_namlen) - typedef struct direct DIR_TYPE; -# else -# error "Port this?" -# endif -#endif - -/* - * Globals. - */ -FILE* gLog = nil; -pid_t gPid = getpid(); - -struct Stats { - long numFiles; - long numDirectories; - long goodDiskImages; -} gStats = { 0 }; - -typedef struct ScanOpts { - FILE* outfp; -} ScanOpts; - -typedef enum RecordKind { - kRecordKindUnknown = 0, - kRecordKindDisk, - kRecordKindFile, - kRecordKindForkedFile, - kRecordKindDirectory, - kRecordKindVolumeDirectory, -} RecordKind; - - -//#define kFilenameExtDelim '.' /* separates extension from filename */ - -/* time_t values for bad dates */ -#define kDateNone ((time_t) -2) -#define kDateInvalid ((time_t) -1) // should match return from mktime() - -/* - * "buf" must hold at least 64 chars. - */ -void -FormatDate(time_t when, char* buf) -{ - if (when == kDateNone) { - strcpy(buf, "[No Date]"); - } else if (when == kDateInvalid) { - strcpy(buf, ""); - } else { - struct tm* ptm; - - ptm = localtime(&when); - strftime(buf, 64, "%d-%b-%y %H:%M", ptm); - } -} - -#if 0 -/* - * Find the filename component of a local pathname. Uses the fssep passed - * in. If the fssep is '\0' (as is the case for DOS 3.3), then the entire - * pathname is returned. - * - * Always returns a pointer to a string; never returns nil. - */ -const char* -FilenameOnly(const char* pathname, char fssep) -{ - const char* retstr; - const char* pSlash; - char* tmpStr = nil; - - ASSERT(pathname != nil); - if (fssep == '\0') { - retstr = pathname; - goto bail; - } - - pSlash = strrchr(pathname, fssep); - if (pSlash == nil) { - retstr = pathname; /* whole thing is the filename */ - goto bail; - } - - pSlash++; - if (*pSlash == '\0') { - if (strlen(pathname) < 2) { - retstr = pathname; /* the pathname is just "/"? Whatever */ - goto bail; - } - - /* some bonehead put an fssep on the very end; back up before it */ - /* (not efficient, but this should be rare, and I'm feeling lazy) */ - tmpStr = strdup(pathname); - tmpStr[strlen(pathname)-1] = '\0'; - pSlash = strrchr(tmpStr, fssep); - - if (pSlash == nil) { - retstr = pathname; /* just a filename with a '/' after it */ - goto bail; - } - - pSlash++; - if (*pSlash == '\0') { - retstr = pathname; /* I give up! */ - goto bail; - } - - retstr = pathname + (pSlash - tmpStr); - - } else { - retstr = pSlash; - } - -bail: - free(tmpStr); - return retstr; -} - -/* - * Return the filename extension found in a full pathname. - * - * An extension is the stuff following the last '.' in the filename. If - * there is nothing following the last '.', then there is no extension. - * - * Returns a pointer to the '.' preceding the extension, or nil if no - * extension was found. - * - * We guarantee that there is at least one character after the '.'. - */ -const char* -FindExtension(const char* pathname, char fssep) -{ - const char* pFilename; - const char* pExt; - - /* - * We have to isolate the filename so that we don't get excited - * about "/foo.bar/file". - */ - pFilename = FilenameOnly(pathname, fssep); - ASSERT(pFilename != nil); - pExt = strrchr(pFilename, kFilenameExtDelim); - - /* also check for "/blah/foo.", which doesn't count */ - if (pExt != nil && *(pExt+1) != '\0') - return pExt; - - return nil; -} -#endif - - -/* - * Analyze a file's characteristics. - */ -void -AnalyzeFile(const A2File* pFile, RecordKind* pRecordKind, - unsigned long* pTotalLen, unsigned long* pTotalCompLen) -{ - if (pFile->IsVolumeDirectory()) { - /* volume directory entry */ - ASSERT(pFile->GetRsrcLength() < 0); - *pRecordKind = kRecordKindVolumeDirectory; - *pTotalLen = pFile->GetDataLength(); - *pTotalCompLen = pFile->GetDataLength(); - } else if (pFile->IsDirectory()) { - /* directory entry */ - ASSERT(pFile->GetRsrcLength() < 0); - *pRecordKind = kRecordKindDirectory; - *pTotalLen = pFile->GetDataLength(); - *pTotalCompLen = pFile->GetDataLength(); - } else if (pFile->GetRsrcLength() >= 0) { - /* has resource fork */ - *pRecordKind = kRecordKindForkedFile; - *pTotalLen = pFile->GetDataLength() + pFile->GetRsrcLength(); - *pTotalCompLen = - pFile->GetDataSparseLength() + pFile->GetRsrcSparseLength(); - } else { - /* just data fork */ - *pRecordKind = kRecordKindFile; - *pTotalLen = pFile->GetDataLength(); - *pTotalCompLen = pFile->GetDataSparseLength(); - } -} - -/* - * Determine whether the access bits on the record make it a read-only - * file or not. - * - * Uses a simplified view of the access flags. - */ -bool -IsRecordReadOnly(int accessBits) -{ - if (accessBits == 0x21L || accessBits == 0x01L) - return true; - else - return false; -} - -/* ProDOS file type names; must be entirely in upper case */ -static const char gFileTypeNames[256][4] = { - "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", - "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", - "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", - "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", - "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", - "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", - "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", - "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", - "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", - "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", - "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", - "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", - "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", - "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", - "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", - "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", - "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", - "$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F", - "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", - "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", - "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", - "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", - "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", - "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", - "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", - "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", - "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", - "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", - "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", - "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", - "CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", - "$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS" -}; - -/* - * Return a pointer to the three-letter representation of the file type name. - * - * Note to self: code down below tests first char for '?'. - */ -/*static*/ const char* -GetFileTypeString(unsigned long fileType) -{ - if (fileType < NELEM(gFileTypeNames)) - return gFileTypeNames[fileType]; - else - return "???"; -} - -/* - * Sanitize a string. The Mac likes to stick control characters into - * things, e.g. ^C and ^M. - */ -static void -MacSanitize(char* str) -{ - while (*str != '\0') { - if (*str < 0x20 || *str >= 0x7f) - *str = '?'; - str++; - } -} - - -/* - * Load the contents of a DiskFS. - * - * Recursively handle sub-volumes. - */ -int -LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, - ScanOpts* pScanOpts) -{ - static const char* kBlankFileName = ""; - DiskFS::SubVolume* pSubVol = nil; - A2File* pFile; - - ASSERT(pDiskFS != nil); - pFile = pDiskFS->GetNextFile(nil); - while (pFile != nil) { - char subVolName[128] = ""; - char dispName[128] = ""; - //CString subVolName, dispName; - RecordKind recordKind; - unsigned long totalLen, totalCompLen; - char tmpbuf[16]; - - AnalyzeFile(pFile, &recordKind, &totalLen, &totalCompLen); - if (recordKind == kRecordKindVolumeDirectory) { - /* skip these */ - pFile = pDiskFS->GetNextFile(pFile); - continue; - } - - /* prepend volName for sub-volumes; must be valid Win32 dirname */ - if (volName[0] != '\0') - snprintf(subVolName, sizeof(subVolName), "_%s", volName); - - const char* ccp = pFile->GetPathName(); - ASSERT(ccp != nil); - if (strlen(ccp) == 0) - ccp = kBlankFileName; - - if (subVolName[0] == '\0') { - strncpy(dispName, ccp, sizeof(dispName)); - dispName[sizeof(dispName) - 1] = '\0'; - } else { - snprintf(dispName, sizeof(dispName), "%s:%s", subVolName, ccp); - //dispName = subVolName; - //dispName += ':'; - //dispName += ccp; - } - ccp = dispName; - - int len = strlen(ccp); - if (len <= 32) { - fprintf(pScanOpts->outfp, "%c%-32.32s ", - IsRecordReadOnly(pFile->GetAccess()) ? '*' : ' ', - ccp); - } else { - fprintf(pScanOpts->outfp, "%c..%-30.30s ", - IsRecordReadOnly(pFile->GetAccess()) ? '*' : ' ', - ccp + len - 30); - } - switch (recordKind) { - case kRecordKindUnknown: - fprintf(pScanOpts->outfp, "%s- $%04X ", - GetFileTypeString(pFile->GetFileType()), - pFile->GetAuxType()); - break; - case kRecordKindDisk: - snprintf(tmpbuf, sizeof(tmpbuf), "%ldk", totalLen / 1024); - fprintf(pScanOpts->outfp, "Disk %-6s ", tmpbuf); - break; - case kRecordKindFile: - case kRecordKindForkedFile: - case kRecordKindDirectory: - if (pDiskFS->GetDiskImg()->GetFSFormat() == DiskImg::kFormatMacHFS) - { - if (recordKind != kRecordKindDirectory && - pFile->GetFileType() >= 0 && pFile->GetFileType() <= 0xff && - pFile->GetAuxType() >= 0 && pFile->GetAuxType() <= 0xffff) - { - /* ProDOS type embedded in HFS */ - fprintf(pScanOpts->outfp, "%s%c $%04X ", - GetFileTypeString(pFile->GetFileType()), - recordKind == kRecordKindForkedFile ? '+' : ' ', - pFile->GetAuxType()); - } else { - char typeStr[5]; - char creatorStr[5]; - unsigned long val; - - val = pFile->GetAuxType(); - creatorStr[0] = (unsigned char) (val >> 24); - creatorStr[1] = (unsigned char) (val >> 16); - creatorStr[2] = (unsigned char) (val >> 8); - creatorStr[3] = (unsigned char) val; - creatorStr[4] = '\0'; - - val = pFile->GetFileType(); - typeStr[0] = (unsigned char) (val >> 24); - typeStr[1] = (unsigned char) (val >> 16); - typeStr[2] = (unsigned char) (val >> 8); - typeStr[3] = (unsigned char) val; - typeStr[4] = '\0'; - - MacSanitize(creatorStr); - MacSanitize(typeStr); - - if (recordKind == kRecordKindDirectory) { - fprintf(pScanOpts->outfp, "DIR %-4s ", creatorStr); - } else { - fprintf(pScanOpts->outfp, "%-4s%c %-4s ", - typeStr, - pFile->GetRsrcLength() > 0 ? '+' : ' ', - creatorStr); - } - } - } else { - fprintf(pScanOpts->outfp, "%s%c $%04X ", - GetFileTypeString(pFile->GetFileType()), - recordKind == kRecordKindForkedFile ? '+' : ' ', - pFile->GetAuxType()); - } - break; - default: - ASSERT(0); - fprintf(pScanOpts->outfp, "ERROR "); - break; - } - - char date[64]; - if (pFile->GetModWhen() == 0) - FormatDate(kDateNone, date); - else - FormatDate(pFile->GetModWhen(), date); - fprintf(pScanOpts->outfp, "%-15s ", (LPCTSTR) date); - - const char* fmtStr; - switch (pFile->GetFSFormat()) { - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - case DiskImg::kFormatUNIDOS: - fmtStr = "DOS "; - break; - case DiskImg::kFormatProDOS: - fmtStr = "ProDOS"; - break; - case DiskImg::kFormatPascal: - fmtStr = "Pascal"; - break; - case DiskImg::kFormatMacHFS: - fmtStr = "HFS "; - break; - case DiskImg::kFormatCPM: - fmtStr = "CP/M "; - break; - case DiskImg::kFormatMSDOS: - fmtStr = "MS-DOS"; - break; - case DiskImg::kFormatRDOS33: - case DiskImg::kFormatRDOS32: - case DiskImg::kFormatRDOS3: - fmtStr = "RDOS "; - break; - case DiskImg::kFormatGutenberg: - fmtStr = "Gutenb"; - break; - default: - fmtStr = "??? "; - break; - } - if (pFile->GetQuality() == A2File::kQualityDamaged) - fmtStr = "BROKEN"; - - fprintf(pScanOpts->outfp, "%s ", fmtStr); - - -#if 0 - /* compute the percent size */ - if ((!totalLen && totalCompLen) || (totalLen && !totalCompLen)) - fprintf(pScanOpts->outfp, "--- "); /* weird */ - else if (totalLen < totalCompLen) - fprintf(pScanOpts->outfp, ">100%% "); /* compression failed? */ - else { - sprintf(tmpbuf, "%02d%%", ComputePercent(totalCompLen, totalLen)); - fprintf(pScanOpts->outfp, "%4s ", tmpbuf); - } -#endif - - if (!totalLen && totalCompLen) - fprintf(pScanOpts->outfp, " ????"); /* weird */ - else - fprintf(pScanOpts->outfp, "%8ld", totalLen); - - fprintf(pScanOpts->outfp, "\n"); - - pFile = pDiskFS->GetNextFile(pFile); - } - - /* - * Load all sub-volumes. - */ - pSubVol = pDiskFS->GetNextSubVolume(nil); - while (pSubVol != nil) { - const char* subVolName; - int ret; - - subVolName = pSubVol->GetDiskFS()->GetVolumeName(); - if (subVolName == nil) - subVolName = "+++"; // could probably do better than this - - ret = LoadDiskFSContents(pSubVol->GetDiskFS(), subVolName, pScanOpts); - if (ret != 0) - return ret; - pSubVol = pDiskFS->GetNextSubVolume(pSubVol); - } - - return 0; -} - -/* - * Open a disk image and dump the contents. - * - * Returns 0 on success, nonzero on failure. - */ -int -ScanDiskImage(const char* pathName, ScanOpts* pScanOpts) -{ - ASSERT(pathName != nil); - ASSERT(pScanOpts != nil); - ASSERT(pScanOpts->outfp != nil); - - DIError dierr; - char errMsg[256] = ""; - DiskImg diskImg; - DiskFS* pDiskFS = nil; - - dierr = diskImg.OpenImage(pathName, '/', true); - if (dierr != kDIErrNone) { - snprintf(errMsg, sizeof(errMsg), "Unable to open '%s': %s", - pathName, DIStrError(dierr)); - goto bail; - } - - dierr = diskImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - snprintf(errMsg, sizeof(errMsg), "Analysis of '%s' failed: %s", - pathName, DIStrError(dierr)); - goto bail; - } - - if (diskImg.GetFSFormat() == DiskImg::kFormatUnknown || - diskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - snprintf(errMsg, sizeof(errMsg), "Unable to identify filesystem on '%s'", - pathName); - goto bail; - } - - /* create an appropriate DiskFS object */ - pDiskFS = diskImg.OpenAppropriateDiskFS(); - if (pDiskFS == nil) { - /* unknown FS should've been caught above! */ - ASSERT(false); - snprintf(errMsg, sizeof(errMsg), "Format of '%s' not recognized.", - pathName); - goto bail; - } - - pDiskFS->SetScanForSubVolumes(DiskFS::kScanSubEnabled); - - /* object created; prep it */ - dierr = pDiskFS->Initialize(&diskImg, DiskFS::kInitFull); - if (dierr != kDIErrNone) { - snprintf(errMsg, sizeof(errMsg), - "Error reading list of files from disk: %s", DIStrError(dierr)); - goto bail; - } - - fprintf(pScanOpts->outfp, "File: %s\n", pathName); - - int kbytes; - if (pDiskFS->GetDiskImg()->GetHasBlocks()) - kbytes = pDiskFS->GetDiskImg()->GetNumBlocks() / 2; - else if (pDiskFS->GetDiskImg()->GetHasSectors()) - kbytes = (pDiskFS->GetDiskImg()->GetNumTracks() * - pDiskFS->GetDiskImg()->GetNumSectPerTrack()) / 4; - else - kbytes = 0; - fprintf(pScanOpts->outfp, "Disk: %s%s (%dKB)\n", pDiskFS->GetVolumeID(), - pDiskFS->GetFSDamaged() ? " [*]" : "", kbytes); - - fprintf(pScanOpts->outfp, - " Name Type Auxtyp Modified" - " Format Length\n"); - fprintf(pScanOpts->outfp, - "------------------------------------------------------" - "------------------------\n"); - if (LoadDiskFSContents(pDiskFS, "", pScanOpts) != 0) { - snprintf(errMsg, sizeof(errMsg), - "Failed while loading contents of '%s'.", pathName); - goto bail; - } - fprintf(pScanOpts->outfp, - "------------------------------------------------------" - "------------------------\n\n"); - - gStats.goodDiskImages++; - -bail: - delete pDiskFS; - - if (errMsg[0] != '\0') { - fprintf(pScanOpts->outfp, "Unable to process '%s'\n", pathName); - fprintf(pScanOpts->outfp, " %s\n\n", (LPCTSTR) errMsg); - return -1; - } else { - return 0; - } -} - - -/* - * Check a file's status. - * - * [ Someday we may want to modify this to handle symbolic links. ] - */ -int -CheckFileStatus(const char* pathname, struct stat* psb, bool* pExists, - bool* pIsReadable, bool* pIsDir) -{ - int result = 0; - int cc; - - assert(pathname != nil); - assert(psb != nil); - assert(pExists != nil); - assert(pIsReadable != nil); - assert(pIsDir != nil); - - *pExists = true; - *pIsReadable = true; - *pIsDir = false; - - cc = stat(pathname, psb); - if (cc) { - if (errno == ENOENT) - *pExists = false; - else - result = -1; // stat failed - goto bail; - } - - if (S_ISDIR(psb->st_mode)) - *pIsDir = true; - - /* - * Test if we can read this file. How do we do that? The easy but slow - * way is to call access(2), the harder way is to figure out - * what user/group we are and compare the appropriate file mode. - */ - if (access(pathname, R_OK) < 0) - *pIsReadable = false; - -bail: - return result; -} - - -/* forward decl */ -int ProcessFile(const char* pathname, ScanOpts* pScanOpts); - -/* - * UNIX-style recursive directory descent. Scan the contents of a directory. - * If a subdirectory is found, follow it; otherwise, call ProcessFile to - * handle the file. - */ -int -ProcessDirectory(const char* dirName, ScanOpts* pScanOpts) -{ - StringArray strArray; - int result = -1; - DIR* dirp = nil; - DIR_TYPE* entry; - char nbuf[MAX_PATH_LEN]; /* malloc might be better; this soaks stack */ - char fssep; - int len; - - assert(pScanOpts != nil); - assert(dirName != nil); - -#ifdef _DEBUG - fprintf(gLog, "+++ Processing directory '%s'\n", dirName); -#endif - - dirp = opendir(dirName); - if (dirp == nil) { - //err = errno ? errno : -1; - goto bail; - } - - fssep = '/'; - - /* could use readdir_r, but we don't care about reentrancy here */ - while ((entry = readdir(dirp)) != nil) { - /* skip the dotsies */ - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) - continue; - - len = strlen(dirName); - if (len + DIR_NAME_LEN(entry) +2 > MAX_PATH_LEN) { - fprintf(stderr, "ERROR: Filename exceeds %d bytes: %s%c%s\n", - MAX_PATH_LEN, dirName, fssep, entry->d_name); - goto bail; - } - - /* form the new name, inserting an fssep if needed */ - strcpy(nbuf, dirName); - if (dirName[len-1] != fssep) - nbuf[len++] = fssep; - strcpy(nbuf+len, entry->d_name); - - strArray.Add(nbuf); - } - - /* sort the list, then process the files */ - strArray.Sort(StringArray::CmpAscendingAlpha); - for (int i = 0; i < strArray.GetCount(); i++) - (void) ProcessFile(strArray.GetEntry(i), pScanOpts); - - result = 0; - -bail: - if (dirp != nil) - (void)closedir(dirp); - return result; -} - -/* - * Process a file. - * - * Returns with an error if the file doesn't exist or isn't readable. - */ -int -ProcessFile(const char* pathname, ScanOpts* pScanOpts) -{ - int result = -1; - bool exists, isDir, isReadable; - struct stat sb; - - assert(pathname != nil); - assert(pScanOpts != nil); - -#ifdef _DEBUG - fprintf(gLog, "+++ Processing file or dir '%s'\n", pathname); -#endif - - if (CheckFileStatus(pathname, &sb, &exists, &isReadable, &isDir) != 0) { - fprintf(stderr, "ERROR: unexpected error while examining '%s'\n", - pathname); - goto bail; - } - - if (!exists) { - fprintf(stderr, "ERROR: couldn't find '%s'\n", pathname); - goto bail; - } - if (!isReadable) { - fprintf(stderr, "ERROR: file '%s' isn't readable\n", pathname); - goto bail; - } - - if (isDir) { - result = ProcessDirectory(pathname, pScanOpts); - gStats.numDirectories++; - } else { - result = ScanDiskImage(pathname, pScanOpts); - gStats.numFiles++; - } - -bail: - return result; -} - - -/* - * Handle a debug message from the DiskImg library. - */ -/*static*/ void -MsgHandler(const char* file, int line, const char* msg) -{ - ASSERT(file != nil); - ASSERT(msg != nil); - -#ifdef _DEBUG - fprintf(gLog, "%05u %s", gPid, msg); -#endif -} -/* - * Handle a global error message from the NufxLib library by shoving it - * through the DiskImgLib message function. - */ -NuResult -NufxErrorMsgHandler(NuArchive* /*pArchive*/, void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - if (pErrorMessage->isDebug) { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - " [D] %s\n", pErrorMessage->message); - } else { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - " %s\n", pErrorMessage->message); - } - - return kNuOK; -} - -/* - * Process every argument. - */ -int -main(int argc, char** argv) -{ - ScanOpts scanOpts; - scanOpts.outfp = stdout; - -#ifdef _DEBUG - const char* kLogFile = "mdc-log.txt"; - gLog = fopen(kLogFile, "w"); - if (gLog == nil) { - fprintf(stderr, "ERROR: unable to open log file\n"); - exit(1); - } -#endif - - int32_t major, minor, bug; - Global::GetVersion(&major, &minor, &bug); - - printf("MDC for Linux v3.0.0 (DiskImg library v%d.%d.%d)\n", - major, minor, bug); - printf("Copyright (C) 2006 by faddenSoft, LLC. All rights reserved.\n"); - printf("MDC is part of CiderPress, available from http://www.faddensoft.com/.\n"); - NuGetVersion(&major, &minor, &bug, nil, nil); - printf("Linked against NufxLib v%d.%d.%d and zlib version %s.\n", - major, minor, bug, zlibVersion()); - - if (argc == 1) { - fprintf(stderr, "\nUsage: mdc file ...\n"); - goto done; - } - -#ifdef _DEBUG - printf("Log file is '%s'\n", kLogFile); -#endif - printf("\n"); - - Global::SetDebugMsgHandler(MsgHandler); - Global::AppInit(); - - NuSetGlobalErrorMessageHandler(NufxErrorMsgHandler); - - time_t start; - start = time(NULL); - printf("Run started at %.24s\n\n", ctime(&start)); - - while (--argc) { - ProcessFile(*++argv, &scanOpts); - } - - printf("Scan completed in %ld seconds:\n", time(NULL) - start); - printf(" Directories : %ld\n", gStats.numDirectories); - printf(" Files : %ld (%ld good disk images)\n", gStats.numFiles, - gStats.goodDiskImages); - - Global::AppCleanup(); - -done: -#ifdef _DEBUG - fclose(gLog); -#endif - - return 0; -} - diff --git a/ciderpress/linux/MakeDisk.cpp b/ciderpress/linux/MakeDisk.cpp deleted file mode 100644 index 38cb505..0000000 --- a/ciderpress/linux/MakeDisk.cpp +++ /dev/null @@ -1,388 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Create a blank disk image, format it, and copy some files onto it. - */ -#include -#include -#include -#include -#include -#include -#include -#include "../diskimg/DiskImg.h" - -using namespace DiskImgLib; - -#define nil NULL -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) - -/* - * Globals. - */ -FILE* gLog = nil; -pid_t gPid = getpid(); - -/* - * Show usage info. - */ -void -Usage(const char* argv0) -{ - fprintf(stderr, - "Usage: %s {dos|prodos|pascal} size image-filename.po input-file1 ...\n", - argv0); - - fprintf(stderr, "\n"); - fprintf(stderr, "Example: makedisk prodos 800k foo.po file1.txt file2.txt\n"); -} - - -/* - * Create a ProDOS-ordered disk image. - * - * Returns a DiskImg pointer on success, or nil on failure. - */ -DiskImg* -CreateDisk(const char* fileName, long blockCount) -{ - DIError dierr; - DiskImg* pDiskImg = nil; - - pDiskImg = new DiskImg; - dierr = pDiskImg->CreateImage( - fileName, - nil, // storageName - DiskImg::kOuterFormatNone, - DiskImg::kFileFormatUnadorned, - DiskImg::kPhysicalFormatSectors, - nil, // pNibbleDescr - DiskImg::kSectorOrderProDOS, - DiskImg::kFormatGenericProDOSOrd, - blockCount, - true); // no need to format the image - - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: CreateImage failed: %s\n", - DIStrError(dierr)); - delete pDiskImg; - pDiskImg = nil; - } - - return pDiskImg; -} - -/* - * Copy files to the disk. - */ -int -CopyFiles(DiskFS* pDiskFS, int argc, char** argv) -{ - DIError dierr; - DiskFS::CreateParms parms; - A2File* pNewFile; - - struct CreateParms { - const char* pathName; // full pathname - char fssep; - int storageType; // determines normal, subdir, or forked - long fileType; - long auxType; - int access; - time_t createWhen; - time_t modWhen; - }; - - - while (argc--) { - printf("+++ Adding '%s'\n", *argv); - - /* - * Use external pathname as internal pathname. This isn't quite - * right, since things like "../" will end up getting converted - * to something we don't want, but it'll do for now. - */ - parms.pathName = *argv; - parms.fssep = '/'; // UNIX fssep - parms.storageType = DiskFS::kStorageSeedling; // not forked, not dir - parms.fileType = 0; // NON - parms.auxType = 0; // $0000 - parms.access = DiskFS::kFileAccessUnlocked; - parms.createWhen = time(nil); - parms.modWhen = time(nil); - - /* - * Create a new, empty file. The "pNewFile" pointer does not belong - * to us, so we should not delete it later, or try to access it - * after the underlying file is deleted. - */ - dierr = pDiskFS->CreateFile(&parms, &pNewFile); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: unable to create '%s': %s\n", - *argv, DIStrError(dierr)); - return -1; - } - - /* - * Load the input file into memory. - */ - FILE* fp; - char* buf; - long len; - - fp = fopen(*argv, "r"); - if (fp == nil) { - fprintf(stderr, "ERROR: unable to open input file '%s': %s\n", - *argv, strerror(errno)); - return -1; - } - - if (fseek(fp, 0, SEEK_END) != 0) { - fprintf(stderr, "ERROR: unable to seek input file '%s': %s\n", - *argv, strerror(errno)); - fclose(fp); - return -1; - } - - len = ftell(fp); - rewind(fp); - - buf = new char[len]; - if (buf == nil) { - fprintf(stderr, "ERROR: unable to alloc %ld bytes\n", len); - fclose(fp); - return -1; - } - - if (fread(buf, len, 1, fp) != 1) { - fprintf(stderr, "ERROR: fread of %ld bytes from '%s' failed: %s\n", - len, *argv, strerror(errno)); - fclose(fp); - delete[] buf; - return -1; - } - fclose(fp); - - /* - * Write the buffer to the disk image. - * - * The A2FileDescr object is created by "Open" and deleted by - * "Close". - */ - A2FileDescr* pFD; - - dierr = pNewFile->Open(&pFD, true); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: unable to open new file '%s': %s\n", - pNewFile->GetPathName(), DIStrError(dierr)); - delete[] buf; - return -1; - } - - dierr = pFD->Write(buf, len); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: failed writing to '%s': %s\n", - pNewFile->GetPathName(), DIStrError(dierr)); - pFD->Close(); - pDiskFS->DeleteFile(pNewFile); - delete[] buf; - return -1; - } - delete[] buf; - - dierr = pFD->Close(); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: failed while closing '%s': %s\n", - pNewFile->GetPathName(), DIStrError(dierr)); - return -1; - } - - /* - * On to the next file. - */ - argv++; - } - - return 0; -} - -/* - * Process the request. - * - * Returns 0 on success, -1 on failure. - */ -int -Process(const char* formatName, const char* sizeStr, - const char* outputFileName, int argc, char** argv) -{ - DiskImg::FSFormat format; - long blockCount; - - if (strcasecmp(formatName, "dos") == 0) - format = DiskImg::kFormatDOS33; - else if (strcasecmp(formatName, "prodos") == 0) - format = DiskImg::kFormatProDOS; - else if (strcasecmp(formatName, "pascal") == 0) - format = DiskImg::kFormatPascal; - else { - fprintf(stderr, "ERROR: invalid format '%s'\n", formatName); - return -1; - } - - if (strcasecmp(sizeStr, "140k") == 0) - blockCount = 280; - else if (strcasecmp(sizeStr, "800k") == 0) - blockCount = 1600; - else { - blockCount = atoi(sizeStr); - if (blockCount <= 0 || blockCount > 65536) { - fprintf(stderr, "ERROR: invalid size '%s'\n", sizeStr); - return -1; - } - } - - if (access(outputFileName, F_OK) == 0) { - fprintf(stderr, "ERROR: output file '%s' already exists\n", - outputFileName); - return -1; - } - - assert(argc >= 1); - assert(*argv != nil); - - - const char* volName; - DiskImg* pDiskImg; - DiskFS* pDiskFS; - DIError dierr; - - /* - * Prepare the disk image file. - */ - pDiskImg = CreateDisk(outputFileName, blockCount); - if (pDiskImg == nil) - return -1; - - if (format == DiskImg::kFormatDOS33) - volName = "DOS"; // put DOS 3.3 in tracks 0-2 - else - volName = "TEST"; - dierr = pDiskImg->FormatImage(format, volName); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: unable to format disk: %s\n", - DIStrError(dierr)); - delete pDiskImg; - return -1; - } - - /* - * Prepare to access the image as a filesystem. - */ - pDiskFS = pDiskImg->OpenAppropriateDiskFS(false); - if (pDiskFS == nil) { - fprintf(stderr, "ERROR: unable to open appropriate DiskFS\n"); - delete pDiskImg; - return -1; - } - - dierr = pDiskFS->Initialize(pDiskImg, DiskFS::kInitFull); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: unable to initialize DiskFS: %s\n", - DIStrError(dierr)); - delete pDiskFS; - delete pDiskImg; - return -1; - } - - /* - * Copy the files over. - */ - if (CopyFiles(pDiskFS, argc, argv) != 0) { - delete pDiskFS; - delete pDiskImg; - return -1; - } - - /* - * Clean up. Note "CloseImage" isn't strictly necessary, but it gives - * us an opportunity to detect failures. - */ - delete pDiskFS; - - if (pDiskImg->CloseImage() != 0) { - fprintf(stderr, "WARNING: CloseImage failed: %s\n", - DIStrError(dierr)); - } - - delete pDiskImg; - return 0; -} - - -/* - * Handle a debug message from the DiskImg library. - */ -/*static*/ void -MsgHandler(const char* file, int line, const char* msg) -{ - assert(file != nil); - assert(msg != nil); - -#ifdef _DEBUG - fprintf(gLog, "%05u %s", gPid, msg); -#endif -} - -/* - * Process args. - */ -int -main(int argc, char** argv) -{ -#ifdef _DEBUG - const char* kLogFile = "makedisk-log.txt"; - gLog = fopen(kLogFile, "w"); - if (gLog == nil) { - fprintf(stderr, "ERROR: unable to open log file\n"); - exit(1); - } -#endif - -#ifdef _DEBUG - printf("Log file is '%s'\n", kLogFile); -#endif - - Global::SetDebugMsgHandler(MsgHandler); - Global::AppInit(); - - if (argc < 5) { - Usage(argv[0]); - exit(2); - } - - const char* formatName; - const char* sizeStr; - const char* outputFileName; - - argv++; - formatName = *argv++; - sizeStr = *argv++; - outputFileName = *argv++; - argc -= 4; - - if (Process(formatName, sizeStr, outputFileName, argc, argv) == 0) - fprintf(stderr, "Success!\n"); - else - fprintf(stderr, "Failed.\n"); - - Global::AppCleanup(); -#ifdef _DEBUG - fclose(gLog); -#endif - - exit(0); -} - diff --git a/ciderpress/linux/Makefile b/ciderpress/linux/Makefile deleted file mode 100644 index 9a70caa..0000000 --- a/ciderpress/linux/Makefile +++ /dev/null @@ -1,80 +0,0 @@ -# -# CiderPress -# Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. -# See the file LICENSE for distribution terms. -# -# DiskImg makefile for Linux. -# -SHELL = /bin/sh -CC = gcc -CXX = g++ -#OPT = -g -D_DEBUG -OPT = -g -O2 -GCC_FLAGS = -Wall -Wwrite-strings -Wpointer-arith -Wshadow -CXXFLAGS = $(OPT) $(GCC_FLAGS) -D_FILE_OFFSET_BITS=64 - -SRCS1 = MDC.cpp -SRCS2 = Convert.cpp -SRCS3 = SSTAsm.cpp -SRCS4 = PackDDD.cpp -SRCS5 = MakeDisk.cpp -SRCS5 = GetFile.cpp - -OBJS1 = MDC.o -OBJS2 = Convert.o -OBJS3 = SSTAsm.o -OBJS4 = PackDDD.o -OBJS5 = MakeDisk.o -OBJS6 = GetFile.o - -PRODUCT1 = mdc -PRODUCT2 = iconv -PRODUCT3 = sstasm -PRODUCT4 = packddd -PRODUCT5 = makedisk -PRODUCT6 = getfile - -DISKIMGLIB = ../diskimg/libdiskimg.a ../diskimg/libhfs/libhfs.a -NUFXLIB = ../nufxlib/libnufx.a - -all: $(PRODUCT1) $(PRODUCT2) $(PRODUCT3) $(PRODUCT4) $(PRODUCT5) $(PRODUCT6) - @true - -$(PRODUCT1): $(OBJS1) $(DISKIMGLIB) - $(CXX) -o $@ $(OBJS1) $(DISKIMGLIB) $(NUFXLIB) -lz - -$(PRODUCT2): $(OBJS2) $(DISKIMGLIB) - $(CXX) -o $@ $(OBJS2) $(DISKIMGLIB) $(NUFXLIB) -lz - -$(PRODUCT3): $(OBJS3) $(DISKIMGLIB) - $(CXX) -o $@ $(OBJS3) $(DISKIMGLIB) $(NUFXLIB) -lz - -$(PRODUCT4): $(OBJS4) $(DISKIMGLIB) - $(CXX) -o $@ $(OBJS4) $(DISKIMGLIB) $(NUFXLIB) -lz - -$(PRODUCT5): $(OBJS5) $(DISKIMGLIB) - $(CXX) -o $@ $(OBJS5) $(DISKIMGLIB) $(NUFXLIB) -lz - -$(PRODUCT6): $(OBJS6) $(DISKIMGLIB) - $(CXX) -o $@ $(OBJS6) $(DISKIMGLIB) $(NUFXLIB) -lz - -../diskimg/libdiskimg.a: - (cd ../diskimg ; make) - -../diskimg/libhfs/libhfs.a: - (cd ../diskimg/libhfs ; make) - -clean: - -rm -f *.o core - -rm -f $(PRODUCT1) $(PRODUCT2) $(PRODUCT3) $(PRODUCT4) $(PRODUCT5) - -rm -f $(PRODUCT6) - -rm -f Makefile.bak tags - -rm -f mdc-log.txt iconv-log.txt makedisk-log.txt - -tags:: - @ctags -R --totals * - -depend: - makedepend -- $(CFLAGS) -- $(SRCS1) $(SRCS2) $(SRCS3) $(SRCS4) $(SRCS5) $(SRCS6) - -# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/ciderpress/linux/PackDDD.cpp b/ciderpress/linux/PackDDD.cpp deleted file mode 100644 index b56da53..0000000 --- a/ciderpress/linux/PackDDD.cpp +++ /dev/null @@ -1,766 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Pack DDD. - -The trouble with unpacking DOS DDD 2.x files: - -The files are stored as binary files with no length. DDD v2.0 stored -a copy of the length in sectors in the filename (e.g. "<397>"). This -means that, when CiderPress goes to extract or view the file, it just -sees an empty binary file. - -CiderPress could make an exception and assume that any binary file with -zero length and more than one sector allocated has a length equal to the -number of sectors times 256. This could cause problems for other things, -but it's probably pretty safe. However, we still don't have an accurate -idea of where the end of the file is. - -Knowing where the file ends is important because there is no identifying -information or checksum in a DDD file. The only way to know that it's a -DDD compressed disk is to try to unpack it and see if you end up at exactly -140K at the same time that you run out of input. Without knowing where the -file really ends, this test is much less certain. - -The only safe way to make this work would be to skip the automatic format -detection and tell CiderPress that the file is definitely DDD format. -There's currently no easy way to do that without complicating the user -interface. Filename extensions might be useful, but they're rare under -DOS 3.3, and I don't think the "<397>" convention is common to all versions -of DDD. - -Complicating the matter is that, if a DOS DDD file (type 'B') is converted -to ProDOS, the first 4 bytes will be stripped off. Without unpacking -the file and knowing to within a byte where it ends, there's no way to -automatically tell whether to start at byte 0 or byte 4. (DDD Pro files -have four bytes of garbage at the very start, probably in an attempt to -retain compatibility with the DOS version. Because it uses REL files the -4 bytes of extra DOS stuff aren't added when the files are copied around, -so this was a reasonably smart thing to do, but it complicates matters -for CiderPress because a file extracted from DOS and a file extracted -from ProDOS will come out differently due to the 4 bytes of type 'B' -gunk getting stripped. This can be avoided if the DOS file uses the 'R' -or 'S' file type.) - -All this would have been much easier if the DOS files had a length word. - -To unpack a file created by DOS DDD v2.x: - - Copy the file to a ProDOS disk, using something that guesses at the - actual length when one isn't provided (Copy ][+ 9.0 may work). - - Reduce the length to within a byte or two of the actual end of file. - Removing all but the last couple of trailing zero bytes usually does - the trick. - - Insert 4 bytes of garbage at the front of the file. My copy of DDD - Pro 1.1 seems to like 03 c9 bf d0. -Probably not worth the effort. - */ -#include -#include -#include -#include -#include -#include "../diskimg/DiskImg.h" -#include "../nufxlib/NufxLib.h" - -using namespace DiskImgLib; - -#define nil NULL -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) - -FILE* gLog = nil; -pid_t gPid = getpid(); - -const int kTrackLen = 4096; -const int kNumSymbols = 256; -const int kNumFavorites = 20; -const int kRLEDelim = 0x97; // value MUST have high bit set -const int kNumTracks = 35; - -/* I suspect this is random garbage, but it's consistent for me */ -const unsigned long kDDDProSignature = 0xd0bfc903; - - -/* - * Class for getting and putting bits to and from a file. - */ -class BitBuffer { -public: - BitBuffer(void) : fFp(nil), fBits(0), fBitCount(0) {} - ~BitBuffer(void) {} - - void SetFile(FILE* fp) { fFp = fp; } - void PutBits(unsigned char bits, int numBits); - //void FlushBits(void); - unsigned char GetBits(int numBits); - - static unsigned char Reverse(unsigned char val); - -private: - FILE* fFp; - unsigned char fBits; - int fBitCount; -}; - -/* - * Add bits to the buffer. - * - * We roll the low bits out of "bits" and shift them to the left (in the - * reverse order in which they were passed in). As soon as we get 8 bits - * we flush. - */ -void -BitBuffer::PutBits(unsigned char bits, int numBits) -{ - assert(fBitCount >= 0 && fBitCount < 8); - assert(numBits > 0 && numBits <= 8); - assert(fFp != nil); - - while (numBits--) { - fBits = (fBits << 1) | (bits & 0x01); - fBitCount++; - - if (fBitCount == 8) { - putc(fBits, fFp); - fBitCount = 0; - } - - bits >>= 1; - } -} - -/* - * Get bits from the buffer. - * - * These come out in the order in which they appear in the file, which - * means that in some cases they will have to be reversed. - */ -unsigned char -BitBuffer::GetBits(int numBits) -{ - assert(fBitCount >= 0 && fBitCount < 8); - assert(numBits > 0 && numBits <= 8); - assert(fFp != nil); - - unsigned char retVal; - - if (fBitCount == 0) { - /* have no bits */ - fBits = getc(fFp); - fBitCount = 8; - } - - if (numBits <= fBitCount) { - /* just serve up what we've already got */ - retVal = fBits >> (8 - numBits); - fBits <<= numBits; - fBitCount -= numBits; - } else { - /* some old, some new; load what we have right-aligned */ - retVal = fBits >> (8 - fBitCount); - numBits -= fBitCount; - - fBits = getc(fFp); - fBitCount = 8; - - /* make room for the rest (also zeroes out the low bits) */ - retVal <<= numBits; - - /* add the high bits from the new byte */ - retVal |= fBits >> (8 - numBits); - fBits <<= numBits; - fBitCount -= numBits; - } - - return retVal; -} - -/* - * Utility function to reverse the order of bits in a byte. - */ -/*static*/ unsigned char -BitBuffer::Reverse(unsigned char val) -{ - int i; - unsigned char result = 0; // init to make compiler happy - - for (i = 0; i < 8; i++) { - result = (result << 1) + (val & 0x01); - val >>= 1; - } - - return result; -} - -#if 0 -/* - * Flush any remaining bits out. Call this at the very end. - */ -void -BitBuffer::FlushBits(void) -{ - if (fBitCount) { - fBits <<= 8 - fBitCount; - putc(fBits, fFp); - - fBitCount = 0; - } -} -#endif - - -/* - * Compute the #of times each byte appears in trackBuf. Runs of four - * bytes or longer are completely ignored. - * - * "trackBuf" holds kTrackLen bytes of data, and "freqCounts" holds - * kNumSymbols (256) unsigned shorts. - */ -void -ComputeFreqCounts(const unsigned char* trackBuf, unsigned short* freqCounts) -{ - const unsigned char* ucp; - int i; - - memset(freqCounts, 0, 256 * sizeof(unsigned short)); - - ucp = trackBuf; - for (i = 0; i < kTrackLen; i++, ucp++) { - if (i < (kTrackLen-3) && - *ucp == *(ucp+1) && - *ucp == *(ucp+2) && - *ucp == *(ucp+3)) - { - int runLen = 4; // DEBUG only - i += 3; - ucp += 3; - - while (*ucp == *(ucp+1) && i < kTrackLen) { - runLen++; - ucp++; - i++; - - if (runLen == 256) { - runLen = 0; - break; - } - } - - //printf("Found run of %d of 0x%02x\n", runLen, *ucp); - } else { - /* not a run, just update stats */ - freqCounts[*ucp]++; - } - } -} - -/* - * Find the 20 most frequently occurring symbols, in order. - * - * Modifies "freqCounts". - */ -void -ComputeFavorites(unsigned short* freqCounts, unsigned char* favorites) -{ - int i, fav; - - for (fav = 0; fav < kNumFavorites; fav++) { - unsigned short bestCount = 0; - unsigned char bestSym = 0; - - for (i = 0; i < kNumSymbols; i++) { - if (freqCounts[i] >= bestCount) { - bestSym = (unsigned char) i; - bestCount = freqCounts[i]; - } - } - - favorites[fav] = bestSym; - freqCounts[bestSym] = 0; - } - - //printf("FAVORITES: "); - //for (fav = 0; fav < kNumFavorites; fav++) - // printf("%02x ", favorites[fav]); - //printf("\n"); -} - -/* - * These are all odd, which when they're written in reverse order means - * they all have their hi bits set. - */ -static const unsigned char kFavoriteBitEnc[kNumFavorites] = { - 0x03, 0x09, 0x1f, 0x0f, 0x07, 0x1b, 0x0b, 0x0d, 0x15, 0x37, - 0x3d, 0x25, 0x05, 0xb1, 0x11, 0x21, 0x01, 0x57, 0x5d, 0x1d -}; -static const int kFavoriteBitEncLen[kNumFavorites] = { - 4, 4, 5, 5, 5, 5, 5, 5, 5, 6, - 6, 6, 6, 6, 6, 6, 6, 7, 7, 7 -}; - - -/* - * Compress a track full of data. - */ -void -CompressTrack(const unsigned char* trackBuf, BitBuffer* pBitBuf) -{ - unsigned short freqCounts[kNumSymbols]; - unsigned char favorites[kNumFavorites]; - int i, fav; - - ComputeFreqCounts(trackBuf, freqCounts); - ComputeFavorites(freqCounts, favorites); - - /* write favorites */ - for (fav = 0; fav < kNumFavorites; fav++) - pBitBuf->PutBits(favorites[fav], 8); - - /* - * Compress track data. Store runs as { 0x97 char count }, where - * a count of zero means 256. - */ - const unsigned char* ucp = trackBuf; - for (i = 0; i < kTrackLen; i++, ucp++) { - if (i < (kTrackLen-3) && - *ucp == *(ucp+1) && - *ucp == *(ucp+2) && - *ucp == *(ucp+3)) - { - int runLen = 4; - i += 3; - ucp += 3; - - while (*ucp == *(ucp+1) && i < kTrackLen) { - runLen++; - ucp++; - i++; - - if (runLen == 256) { - runLen = 0; - break; - } - } - - pBitBuf->PutBits(kRLEDelim, 8); // note kRLEDelim has hi bit set - pBitBuf->PutBits(*ucp, 8); - pBitBuf->PutBits(runLen, 8); - - } else { - /* - * Not a run, see if it's one of our favorites. - */ - for (fav = 0; fav < kNumFavorites; fav++) { - if (*ucp == favorites[fav]) - break; - } - if (fav == kNumFavorites) { - /* just a plain byte */ - pBitBuf->PutBits(0x00, 1); - pBitBuf->PutBits(*ucp, 8); - } else { - /* found a favorite; leading hi bit is implied */ - pBitBuf->PutBits(kFavoriteBitEnc[fav], kFavoriteBitEncLen[fav]); - } - } - } -} - - - -/* - * Handle a debug message from the DiskImg library. - */ -/*static*/ void -MsgHandler(const char* file, int line, const char* msg) -{ - assert(file != nil); - assert(msg != nil); - - fprintf(gLog, "%05u %s", gPid, msg); -} -/* - * Handle a global error message from the NufxLib library by shoving it - * through the DiskImgLib message function. - */ -NuResult -NufxErrorMsgHandler(NuArchive* /*pArchive*/, void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - if (pErrorMessage->isDebug) { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - " [D] %s\n", pErrorMessage->message); - } else { - Global::PrintDebugMsg(pErrorMessage->file, pErrorMessage->line, - " %s\n", pErrorMessage->message); - } - - return kNuOK; -} - -/* - * Pack a disk image with DDD. - */ -DIError -Pack(const char* infile, const char* outfile) -{ - DIError dierr = kDIErrNone; - DiskImg srcImg; - FILE* outfp = nil; - BitBuffer bitBuffer; - - printf("Packing in='%s' out='%s'\n", infile, outfile); - - /* - * Prepare the source image. - */ - dierr = srcImg.OpenImage(infile, '/', true); - if (dierr != kDIErrNone) { - fprintf(stderr, "Unable to open disk image: %s.\n", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - dierr = srcImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "Unable to determine source image format.\n"); - goto bail; - } - - if (!srcImg.GetHasSectors()) { - fprintf(stderr, "Sorry, only sector-addressable images allowed.\n"); - dierr = kDIErrUnsupportedPhysicalFmt; - goto bail; - } - assert(srcImg.GetNumSectPerTrack() > 0); - - if (srcImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - fprintf(stderr, "(QUERY) don't know sector order\n"); - dierr = kDIErrFilesystemNotFound; - goto bail; - } - - /* force the access to be DOS-ordered */ - dierr = srcImg.OverrideFormat(srcImg.GetPhysicalFormat(), - DiskImg::kFormatGenericDOSOrd, srcImg.GetSectorOrder()); - if (dierr != kDIErrNone) { - fprintf(stderr, "Couldn't switch to generic ProDOS: %s.\n", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - /* transfer the DOS volume num, if one was set */ - printf("DOS volume number set to %d\n", srcImg.GetDOSVolumeNum()); - if (srcImg.GetDOSVolumeNum() == DiskImg::kVolumeNumNotSet) - srcImg.SetDOSVolumeNum(kDefaultNibbleVolumeNum); - - /* - * Open the output file. - */ - outfp = fopen(outfile, "w"); - if (outfp == nil) { - perror("unable to open output file"); - dierr = kDIErrGeneric; - goto bail; - } - - /* write four zeroes to replace the DOS addr/len bytes */ - /* (let's write the apparent DDD Pro v1.1 signature instead) */ - putc(kDDDProSignature, outfp); - putc(kDDDProSignature >> 8, outfp); - putc(kDDDProSignature >> 16, outfp); - putc(kDDDProSignature >> 24, outfp); - - bitBuffer.SetFile(outfp); - - bitBuffer.PutBits(0x00, 3); - bitBuffer.PutBits(srcImg.GetDOSVolumeNum(), 8); - - /* - * Process all tracks. - */ - for (int track = 0; track < srcImg.GetNumTracks(); track++) { - unsigned char trackBuf[kTrackLen]; - - /* - * Read the track. - */ - for (int sector = 0; sector < srcImg.GetNumSectPerTrack(); sector++) { - dierr = srcImg.ReadTrackSector(track, sector, - trackBuf + sector * 256); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: ReadBlock failed (err=%d)\n", dierr); - goto bail; - } - } - - //printf("Got track %d (0x%02x %02x %02x %02x %02x %02x ...)\n", - // track, trackBuf[0], trackBuf[1], trackBuf[2], trackBuf[3], - // trackBuf[4], trackBuf[5]); - CompressTrack(trackBuf, &bitBuffer); - } - - /* write 8 bits of zeroes to flush remaining data out of buffer */ - bitBuffer.PutBits(0x00, 8); - - /* write another zero byte because that's what DDD Pro v1.1 does */ - long zero; - zero = 0; - fwrite(&zero, 1, 1, outfp); - - dierr = srcImg.CloseImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: srcImg close failed?!\n"); - goto bail; - } - - assert(dierr == kDIErrNone); -bail: - return dierr; -} - -/* - * This is the reverse of the kFavoriteBitEnc table. The bits are - * reversed and lack the high bit. - */ -static const unsigned char kFavoriteBitDec[kNumFavorites] = { - 0x04, 0x01, 0x0f, 0x0e, 0x0c, 0x0b, 0x0a, 0x06, 0x05, 0x1b, - 0x0f, 0x09, 0x08, 0x03, 0x02, 0x01, 0x00, 0x35, 0x1d, 0x1c -}; - -/* - * Unpack a single track. - * - * Returns "true" if all went well, "false" if something failed. - */ -bool -UnpackTrack(BitBuffer* pBitBuffer, unsigned char* trackBuf) -{ - unsigned char favorites[kNumFavorites]; - unsigned char val; - unsigned char* trackPtr; - int fav; - - /* - * Start by pulling our favorites out, in reverse order. - */ - for (fav = 0; fav < kNumFavorites; fav++) { - val = pBitBuffer->GetBits(8); - val = pBitBuffer->Reverse(val); - favorites[fav] = val; - } - - trackPtr = trackBuf; - - /* - * Keep pulling data out until the track is full. - */ - while (trackPtr < trackBuf + kTrackLen) { - val = pBitBuffer->GetBits(1); - if (!val) { - /* simple byte */ - val = pBitBuffer->GetBits(8); - val = pBitBuffer->Reverse(val); - *trackPtr++ = val; - } else { - /* try for a prefix match */ - int extraBits; - - val = pBitBuffer->GetBits(2); - - for (extraBits = 0; extraBits < 4; extraBits++) { - val = (val << 1) | pBitBuffer->GetBits(1); - int start, end; - - if (extraBits == 0) { - start = 0; - end = 2; - } else if (extraBits == 1) { - start = 2; - end = 9; - } else if (extraBits == 2) { - start = 9; - end = 17; - } else { - start = 17; - end = 20; - } - - while (start < end) { - if (val == kFavoriteBitDec[start]) { - /* winner! */ - *trackPtr++ = favorites[start]; - break; - } - start++; - } - if (start != end) - break; // we got it, break out of for loop - } - if (extraBits == 4) { - /* we didn't get it, this must be RLE */ - unsigned char rleChar; - int rleCount; - - (void) pBitBuffer->GetBits(1); // get last bit of 0x97 - val = pBitBuffer->GetBits(8); - rleChar = pBitBuffer->Reverse(val); - val = pBitBuffer->GetBits(8); - rleCount = pBitBuffer->Reverse(val); - printf("Found run of %d of 0x%02x\n", rleCount, rleChar); - - if (rleCount == 0) - rleCount = 256; - - /* make sure we won't overrun */ - if (trackPtr + rleCount > trackBuf + kTrackLen) { - printf("Overrun in RLE\n"); - return false; - } - while (rleCount--) - *trackPtr++ = rleChar; - } - } - } - - return true; -} - -/* - * Unpack a disk image compressed with DDD. - * - * The result is an unadorned DOS-ordered image. - */ -DIError -Unpack(const char* infile, const char* outfile) -{ - DIError dierr = kDIErrNone; - FILE* infp = nil; - FILE* outfp = nil; - BitBuffer bitBuffer; - unsigned char val; - - printf("Unpacking in='%s' out='%s'\n", infile, outfile); - - /* - * Open the input file. - */ - infp = fopen(infile, "r"); - if (infp == nil) { - perror("unable to open input file"); - dierr = kDIErrGeneric; - goto bail; - } - - /* - * Open the output file. - */ - outfp = fopen(outfile, "w"); - if (outfp == nil) { - perror("unable to open output file"); - dierr = kDIErrGeneric; - goto bail; - } - - /* read four zeroes to skip the DOS addr/len bytes */ - (void) getc(infp); - (void) getc(infp); - (void) getc(infp); - (void) getc(infp); - - bitBuffer.SetFile(infp); - - val = bitBuffer.GetBits(3); - if (val != 0) { - printf("HEY: this isn't a DDD II file (%d)\n", val); - dierr = kDIErrGeneric; - goto bail; - } - val = bitBuffer.GetBits(8); - val = bitBuffer.Reverse(val); - printf("GOT disk volume num = %d\n", val); - - for (int track = 0; track < kNumTracks; track++) { - unsigned char trackBuf[kTrackLen]; - - if (!UnpackTrack(&bitBuffer, trackBuf)) { - fprintf(stderr, "FAILED on track %d\n", track); - dierr = kDIErrBadCompressedData; - goto bail; - } - if (feof(infp) || ferror(infp)) { - fprintf(stderr, "Failure or EOF on input file\n"); - dierr = kDIErrBadCompressedData; - goto bail; - } - fwrite(trackBuf, 1, 4096, outfp); - } - - /* - * We should be within a byte or two of the end of the file. - */ - (void) getc(infp); - (void) getc(infp); - (void) getc(infp); - (void) getc(infp); - if (!feof(infp)) { - fprintf(stderr, "Looks like too much data in input file\n"); - dierr = kDIErrBadCompressedData; - goto bail; - } - - assert(dierr == kDIErrNone); -bail: - return dierr; -} - -/* - * Process every argument. - */ -int -main(int argc, char** argv) -{ -// const char* kLogFile = "iconv-log.txt"; - - if (argc != 3) { - fprintf(stderr, "%s: infile outfile\n", argv[0]); - exit(2); - } - - gLog = stdout; -// gLog = fopen(kLogFile, "w"); -// if (gLog == nil) { -// fprintf(stderr, "ERROR: unable to open log file\n"); -// exit(1); -// } - - printf("DDD Converter for Linux v1.0\n"); - printf("Copyright (C) 2003 by faddenSoft, LLC. All rights reserved.\n"); - int32_t major, minor, bug; - Global::GetVersion(&major, &minor, &bug); - printf("Linked against DiskImg library v%d.%d.%d\n", - major, minor, bug); -// printf("Log file is '%s'\n", kLogFile); - printf("\n"); - - Global::SetDebugMsgHandler(MsgHandler); - Global::AppInit(); - - NuSetGlobalErrorMessageHandler(NufxErrorMsgHandler); - - int len = strlen(argv[2]); - if (len > 3 && strcasecmp(argv[2] + len - 3, ".do") == 0) { - Unpack(argv[1], argv[2]); - } else { - Pack(argv[1], argv[2]); - } - - Global::AppCleanup(); - fclose(gLog); - - exit(0); -} - diff --git a/ciderpress/linux/SSTAsm.cpp b/ciderpress/linux/SSTAsm.cpp deleted file mode 100644 index 0d85eb8..0000000 --- a/ciderpress/linux/SSTAsm.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Reassemble SST disk images into a .NIB file. - */ -#include -#include -#include -#include -#include -#include "../diskimg/DiskImg.h" - -using namespace DiskImgLib; - -#define nil NULL - - -#if 0 -inline int -ConvOddEven(unsigned char val1, unsigned char val2) -{ - return ((val1 & 0x55) << 1) | (val2 & 0x55); -} -#endif - - -const int kSSTNumTracks = 35; -const int kSSTTrackLen = 6656; // or 6384 for .NB2 - -/* - * Compute the destination file offset for a particular source track. The - * track number ranges from 0 to 69 inclusive. Sectors from two adjacent - * "cooked" tracks are combined into a single "raw nibbilized" track. - * - * The data is ordered like this: - * track 1 sector 15 --> track 1 sector 4 (12 sectors) - * track 0 sector 13 --> track 0 sector 0 (14 sectors) - * - * Total of 26 sectors, or $1a00 bytes. - */ -long -GetBufOffset(int track) -{ - assert(track >= 0 && track < kSSTNumTracks*2); - - long offset; - - if (track & 0x01) { - /* odd, use start of data */ - offset = (track / 2) * kSSTTrackLen; - } else { - /* even, start of data plus 12 sectors */ - offset = (track / 2) * kSSTTrackLen + 12 * 256; - } - - assert(offset >= 0 && offset < kSSTTrackLen * kSSTNumTracks); - - return offset; -} - -/* - * Copy 17.5 tracks of data from the SST image to a .NIB image. - * - * Data is stored in all 16 sectors of track 0, followed by the first - * 12 sectors of track 1, then on to track 2. Total of $1a00 bytes. - */ -int -LoadSSTData(DiskImg* pDiskImg, int seqNum, unsigned char* trackBuf) -{ - DIError dierr; - char sctBuf[256]; - int track, sector; - long bufOffset; - - for (track = 0; track < kSSTNumTracks; track++) { - int virtualTrack = track + (seqNum * kSSTNumTracks); - bufOffset = GetBufOffset(virtualTrack); - //fprintf(stderr, "USING offset=%ld (track=%d / %d)\n", - // bufOffset, track, virtualTrack); - - if (virtualTrack & 0x01) { - /* odd-numbered track, sectors 15-4 */ - for (sector = 15; sector >= 4; sector--) { - dierr = pDiskImg->ReadTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: on track=%d sector=%d\n", - track, sector); - return -1; - } - - memcpy(trackBuf + bufOffset, sctBuf, 256); - bufOffset += 256; - } - } else { - for (sector = 13; sector >= 0; sector--) { - dierr = pDiskImg->ReadTrackSector(track, sector, sctBuf); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: on track=%d sector=%d\n", - track, sector); - return -1; - } - - memcpy(trackBuf + bufOffset, sctBuf, 256); - bufOffset += 256; - } - } - } - -#if 0 - int i; - for (i = 0; (size_t) i < sizeof(trackBuf)-10; i++) { - if ((trackBuf[i] | 0x80) == 0xd5 && - (trackBuf[i+1] | 0x80) == 0xaa && - (trackBuf[i+2] | 0x80) == 0x96) - { - fprintf(stderr, "off=%5d vol=%d trk=%d sct=%d chk=%d\n", i, - ConvOddEven(trackBuf[i+3], trackBuf[i+4]), - ConvOddEven(trackBuf[i+5], trackBuf[i+6]), - ConvOddEven(trackBuf[i+7], trackBuf[i+8]), - ConvOddEven(trackBuf[i+9], trackBuf[i+10])); - i += 10; - if ((size_t)i < sizeof(trackBuf)-3) { - fprintf(stderr, " 0x%02x 0x%02x 0x%02x\n", - trackBuf[i+1], trackBuf[i+2], trackBuf[i+3]); - } - } - } -#endif - - return 0; -} - -/* - * Copy sectors from a single image. - */ -int -HandleSSTImage(const char* fileName, int seqNum, unsigned char* trackBuf) -{ - DIError dierr; - DiskImg diskImg; - int result = -1; - - fprintf(stderr, "Handling '%s'\n", fileName); - - dierr = diskImg.OpenImage(fileName, '/', true); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: unable to open '%s'\n", fileName); - goto bail; - } - - dierr = diskImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: image analysis failed\n"); - goto bail; - } - - if (diskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) { - fprintf(stderr, "ERROR: sector order not set\n"); - goto bail; - } - if (diskImg.GetFSFormat() != DiskImg::kFormatUnknown) { - fprintf(stderr, "WARNING: file format *was* recognized!\n"); - goto bail; - } - if (diskImg.GetNumTracks() != kSSTNumTracks || - diskImg.GetNumSectPerTrack() != 16) - { - fprintf(stderr, "ERROR: only 140K floppies can be SST inputs\n"); - goto bail; - } - - dierr = diskImg.OverrideFormat(diskImg.GetPhysicalFormat(), - DiskImg::kFormatGenericDOSOrd, diskImg.GetSectorOrder()); - if (dierr != kDIErrNone) { - fprintf(stderr, "ERROR: format override failed\n"); - goto bail; - } - - /* - * We have the image open successfully, now do something with it. - */ - result = LoadSSTData(&diskImg, seqNum, trackBuf); - -bail: - return result; -} - -/* - * Run through the data, adding 0x80 everywhere and re-aligning the - * tracks so that the big clump of sync bytes is at the end. - */ -int -ProcessTrackData(unsigned char* trackBuf) -{ - unsigned char* trackPtr; - int track; - - for (track = 0, trackPtr = trackBuf; track < kSSTNumTracks; - track++, trackPtr += kSSTTrackLen) - { - bool inRun; - int start = 0; - int longestStart = -1; - int count7f = 0; - int longest = -1; - int i; - - inRun = false; - for (i = 0; i < kSSTTrackLen; i++) { - if (trackPtr[i] == 0x7f) { - if (inRun) { - count7f++; - } else { - count7f = 1; - start = i; - inRun = true; - } - } else { - if (inRun) { - if (count7f > longest) { - longest = count7f; - longestStart = start; - } - inRun = false; - } else { - /* do nothing */ - } - } - - trackPtr[i] |= 0x80; - } - - - if (longest == -1) { - fprintf(stderr, "HEY: couldn't find any 0x7f in track %d\n", - track); - } else { - fprintf(stderr, "Found run of %d at %d in track %d\n", - longest, longestStart, track); - - int bkpt = longestStart + longest; - assert(bkpt < kSSTTrackLen); - - char oneTrack[kSSTTrackLen]; - memcpy(oneTrack, trackPtr, kSSTTrackLen); - - /* copy it back so sync bytes are at end of track */ - memcpy(trackPtr, oneTrack + bkpt, kSSTTrackLen - bkpt); - memcpy(trackPtr + (kSSTTrackLen - bkpt), oneTrack, bkpt); - } - } - - return 0; -} - -/* - * Read sectors from file1 and file2, and write them in the correct - * sequence to outfp. - */ -int -ReassembleSST(const char* file1, const char* file2, FILE* outfp) -{ - unsigned char* trackBuf = nil; - int result; - - trackBuf = new unsigned char[kSSTNumTracks * kSSTTrackLen]; - if (trackBuf == nil) { - fprintf(stderr, "ERROR: malloc failed\n"); - return -1; - } - - result = HandleSSTImage(file1, 0, trackBuf); - if (result != 0) - return result; - - result = HandleSSTImage(file2, 1, trackBuf); - if (result != 0) - return result; - - result = ProcessTrackData(trackBuf); - - fprintf(stderr, "Writing %d bytes\n", kSSTNumTracks * kSSTTrackLen); - fwrite(trackBuf, 1, kSSTNumTracks * kSSTTrackLen, outfp); - - delete[] trackBuf; - return result; -} - - -/* - * Handle a debug message from the DiskImg library. - */ -/*static*/ void -MsgHandler(const char* file, int line, const char* msg) -{ - assert(file != nil); - assert(msg != nil); - - fprintf(stderr, "%s", msg); -} - -/* - * Parse args, go. - */ -int -main(int argc, char** argv) -{ - int result; - - if (argc != 3) { - fprintf(stderr, "Usage: %s file1 file2 > outfile\n", argv[0]); - exit(2); - } - - Global::SetDebugMsgHandler(MsgHandler); - Global::AppInit(); - - result = ReassembleSST(argv[1], argv[2], stdout); - - Global::AppCleanup(); - exit(result != 0); -} - diff --git a/ciderpress/linux/StringArray.h b/ciderpress/linux/StringArray.h deleted file mode 100644 index fdbb477..0000000 --- a/ciderpress/linux/StringArray.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * An expandable array of strings. - */ -#ifndef __STRING_ARRAY__ -#define __STRING_ARRAY__ - -#include -#include -#include - -// -// This is a simple container for an array of strings. You can add strings -// to the list and sort them. -// -class StringArray { -public: - StringArray() - : mMax(0), mCurrent(0), mArray(NULL) - {} - virtual ~StringArray() - { - for (int i = 0; i < mCurrent; i++) - delete[] mArray[i]; - delete[] mArray; - } - - // - // Add a string. A copy of the string is made. - // - bool Add(const char* str) - { - if (mCurrent >= mMax) { - char** tmp; - - if (mMax == 0) - mMax = 16; - else - mMax *= 2; - - tmp = new char*[mMax]; - if (tmp == NULL) - return false; - - memcpy(tmp, mArray, mCurrent * sizeof(char*)); - delete[] mArray; - mArray = tmp; - } - - int len = strlen(str); - mArray[mCurrent] = new char[len+1]; - memcpy(mArray[mCurrent], str, len+1); - mCurrent++; - - return true; - } - - // - // Sort the array. Supply a sort function that takes two strings - // and returns <0, 0, or >0 if the first argument is less than, - // equal to, or greater than the second argument. (strcmp works.) - // - void Sort(int (*compare)(const void*, const void*)) - { - qsort(mArray, mCurrent, sizeof(char*), compare); - } - - // - // Use this as an argument to the sort routine. - // - static int CmpAscendingAlpha(const void* pstr1, const void* pstr2) - { - return strcmp(*(const char**)pstr1, *(const char**)pstr2); - } - - // - // Get the #of items in the array. - // - inline int GetCount(void) const { return mCurrent; } - - // - // Get entry N. - // - const char* GetEntry(int idx) const - { - if (idx < 0 || idx >= mCurrent) - return NULL; - return mArray[idx]; - } - -private: - int mMax; - int mCurrent; - char** mArray; -}; - -#endif /*__STRING_ARRAY__*/ diff --git a/ciderpress/linux/makedisk b/ciderpress/linux/makedisk deleted file mode 100755 index 049f99a..0000000 Binary files a/ciderpress/linux/makedisk and /dev/null differ diff --git a/ciderpress/mdc/AboutDlg.cpp b/ciderpress/mdc/AboutDlg.cpp deleted file mode 100644 index 433407d..0000000 --- a/ciderpress/mdc/AboutDlg.cpp +++ /dev/null @@ -1,56 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// AboutDlg.cpp : implementation file -// - -#include "stdafx.h" -#include "mdc.h" -#include "AboutDlg.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// AboutDlg dialog - - -AboutDlg::AboutDlg(CWnd* pParent /*=NULL*/) - : CDialog(AboutDlg::IDD, pParent) -{ - //{{AFX_DATA_INIT(AboutDlg) - // NOTE: the ClassWizard will add member initialization here - //}}AFX_DATA_INIT -} - - -BEGIN_MESSAGE_MAP(AboutDlg, CDialog) - //{{AFX_MSG_MAP(AboutDlg) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// AboutDlg message handlers - -BOOL AboutDlg::OnInitDialog() -{ - CDialog::OnInitDialog(); - - // TODO: Add extra initialization here - CWnd* pWnd = GetDlgItem(IDC_ABOUT_VERS); - ASSERT(pWnd != NULL); - CString fmt, newText; - - pWnd->GetWindowText(fmt); - newText.Format(fmt, kAppMajorVersion, kAppMinorVersion, kAppBugVersion); - pWnd->SetWindowText(newText); - LOGD("STR is '%ls'", (LPCWSTR) newText); - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} diff --git a/ciderpress/mdc/AboutDlg.h b/ciderpress/mdc/AboutDlg.h deleted file mode 100644 index c020ad1..0000000 --- a/ciderpress/mdc/AboutDlg.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#if !defined(AFX_ABOUTDLG_H__340C7108_C3D5_4F24_98AF_C1C614460F6A__INCLUDED_) -#define AFX_ABOUTDLG_H__340C7108_C3D5_4F24_98AF_C1C614460F6A__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// AboutDlg.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// AboutDlg dialog - -class AboutDlg : public CDialog -{ -// Construction -public: - AboutDlg(CWnd* pParent = NULL); // standard constructor - -// Dialog Data - //{{AFX_DATA(AboutDlg) - enum { IDD = IDD_ABOUTBOX }; - // NOTE: the ClassWizard will add data members here - //}}AFX_DATA - - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(AboutDlg) - //}}AFX_VIRTUAL - -// Implementation -protected: - - // Generated message map functions - //{{AFX_MSG(AboutDlg) - virtual BOOL OnInitDialog(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_ABOUTDLG_H__340C7108_C3D5_4F24_98AF_C1C614460F6A__INCLUDED_) diff --git a/ciderpress/mdc/Main.cpp b/ciderpress/mdc/Main.cpp deleted file mode 100644 index b3c7266..0000000 --- a/ciderpress/mdc/Main.cpp +++ /dev/null @@ -1,913 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Main window management. - */ -#include "stdafx.h" -#include "Main.h" -#include "mdc.h" -#include "AboutDlg.h" -#include "ProgressDlg.h" -#include "resource.h" -#include "../diskimg/DiskImg.h" -#include "../zlib/zlib.h" - -const WCHAR* kWebSiteURL = L"http://www.a2ciderpress.com/"; - - -BEGIN_MESSAGE_MAP(MainWindow, CFrameWnd) - ON_COMMAND(IDM_FILE_SCAN, OnFileScan) - ON_COMMAND(IDM_FILE_EXIT, OnFileExit) - ON_COMMAND(IDM_HELP_WEBSITE, OnHelpWebSite) - ON_COMMAND(IDM_HELP_ABOUT, OnHelpAbout) -END_MESSAGE_MAP() - - -MainWindow::MainWindow() -{ - static const WCHAR* kAppName = L"MDC"; - - CString wndClass = AfxRegisterWndClass( - CS_DBLCLKS /*| CS_HREDRAW | CS_VREDRAW*/, - gMyApp.LoadStandardCursor(IDC_ARROW), - (HBRUSH) (COLOR_WINDOW + 1), - gMyApp.LoadIcon(IDI_MDC) ); - - Create(wndClass, kAppName, WS_OVERLAPPEDWINDOW /*| WS_CLIPCHILDREN*/, - rectDefault, NULL, MAKEINTRESOURCE(IDC_MDC)); - - LoadAccelTable(MAKEINTRESOURCE(IDC_MDC)); - - // initialize some OLE garbage - //AfxOleInit(); - - // required by MFC if Rich Edit controls are used - //AfxInitRichEdit(); - - DiskImgLib::Global::SetDebugMsgHandler(DebugMsgHandler); - DiskImgLib::Global::AppInit(); - - NuSetGlobalErrorMessageHandler(NufxErrorMsgHandler); - - //fTitleAnimation = 0; - fCancelFlag = false; -} - -MainWindow::~MainWindow() -{ - DiskImgLib::Global::AppCleanup(); -} - -void MainWindow::OnFileExit(void) -{ - // Handle Exit item by sending a close request. - SendMessage(WM_CLOSE, 0, 0); -} - -void MainWindow::OnHelpWebSite(void) -{ - // Go to the CiderPress web site. - int err; - - err = (int) ::ShellExecute(m_hWnd, L"open", kWebSiteURL, NULL, NULL, - SW_SHOWNORMAL); - if (err <= 32) { - CString msg; - msg.Format(L"Unable to launch web browser (err=%d).", err); - ShowFailureMsg(this, msg, IDS_FAILED); - } -} - -void MainWindow::OnHelpAbout(void) -{ - int result; - - AboutDlg dlg(this); - - result = dlg.DoModal(); - LOGI("HelpAbout returned %d", result); -} - -void MainWindow::OnFileScan(void) -{ - if (0) { - CString msg; - CheckedLoadString(&msg, IDS_MUST_REGISTER); - ShowFailureMsg(this, msg, IDS_APP_TITLE); - } else { - ScanFiles(); - } -} - -BOOL MainWindow::PeekAndPump(void) -{ - MSG msg; - - while (::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { - if (!AfxGetApp()->PumpMessage()) { - ::PostQuitMessage(0); - return FALSE; - } - } - - LONG lIdle = 0; - while (AfxGetApp()->OnIdle(lIdle++)) - ; - return TRUE; -} - - -/* - * ========================================================================== - * Disk image processing - * ========================================================================== - */ - -/*static*/ void MainWindow::DebugMsgHandler(const char* file, int line, - const char* msg) -{ - ASSERT(file != NULL); - ASSERT(msg != NULL); - - LOG_BASE(DebugLog::LOG_INFO, file, line, " %hs", msg); -} - -/*static*/ NuResult MainWindow::NufxErrorMsgHandler(NuArchive* /*pArchive*/, - void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - LOG_BASE(pErrorMessage->isDebug ? DebugLog::LOG_DEBUG : DebugLog::LOG_WARN, - pErrorMessage->file, pErrorMessage->line, " %hs", - pErrorMessage->message); - - return kNuOK; -} - - -const int kLocalFssep = '\\'; - -struct ScanOpts { - FILE* outfp; - ProgressDlg* pProgress; -}; - -void MainWindow::ScanFiles(void) -{ - WCHAR curDir[MAX_PATH] = L""; - CString errMsg, newDir; - bool doResetDir = false; - ScanOpts scanOpts; - - memset(&scanOpts, 0, sizeof(scanOpts)); - - // choose input files - SelectFilesDialog chooseFiles(L"IDD_CHOOSE_FILES", false, this); - chooseFiles.SetWindowTitle(L"Choose Files..."); - INT_PTR retval = chooseFiles.DoModal(); - if (retval != IDOK) { - return; - } - - // choose output file; use an Explorer-style dialog for consistency - CString outPath; - CFileDialog dlg(FALSE, L"txt", NULL, - OFN_OVERWRITEPROMPT|OFN_NOREADONLYRETURN, - L"Text Files (*.txt)|*.txt|All Files (*.*)|*.*||", this, - 0, FALSE /*disable Vista style*/); - - dlg.m_ofn.lpstrTitle = L"Save Output As..."; - wcscpy(dlg.m_ofn.lpstrFile, L"mdc-out.txt"); - - if (dlg.DoModal() != IDOK) { - goto bail; - } - - outPath = dlg.GetPathName(); - LOGI("NEW FILE '%ls'", (LPCWSTR) outPath); - - scanOpts.outfp = _wfopen(outPath, L"w"); - if (scanOpts.outfp == NULL) { - ShowFailureMsg(this, "Unable to open output file", IDS_FAILED); - goto bail; - } - - int32_t major, minor, bug; - DiskImgLib::Global::GetVersion(&major, &minor, &bug); - fprintf(scanOpts.outfp, "MDC for Windows v%d.%d.%d (DiskImg library v%ld.%ld.%ld)\n", - kAppMajorVersion, kAppMinorVersion, kAppBugVersion, - major, minor, bug); - fprintf(scanOpts.outfp, - "Copyright (C) 2014 by faddenSoft, LLC. All rights reserved.\n"); - fprintf(scanOpts.outfp, - "MDC is part of CiderPress, available from http://www.a2ciderpress.com/.\n"); - NuGetVersion(&major, &minor, &bug, NULL, NULL); - fprintf(scanOpts.outfp, - "Linked against NufxLib v%ld.%ld.%ld and zlib v%hs\n", - major, minor, bug, zlibVersion()); - fprintf(scanOpts.outfp, "\n"); - - /* change to base directory */ - if (GetCurrentDirectory(NELEM(curDir), curDir) == 0) { - errMsg = L"Unable to get current directory."; - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - newDir = chooseFiles.GetDirectory(); - if (SetCurrentDirectory(newDir) == false) { - errMsg.Format(L"Unable to change current directory to '%ls'.", - (LPCWSTR) newDir); - ShowFailureMsg(this, errMsg, IDS_FAILED); - goto bail; - } - doResetDir = true; - - time_t now; - now = time(NULL); - fprintf(scanOpts.outfp, - "Run started at %.24hs in '%ls'\n\n", ctime(&now), (LPCWSTR) newDir); - - /* obstruct input to the main window */ - EnableWindow(FALSE); - - - /* create a modeless dialog with a cancel button */ - scanOpts.pProgress = new ProgressDlg; - if (scanOpts.pProgress == NULL) - goto bail; - scanOpts.pProgress->fpCancelFlag = &fCancelFlag; - fCancelFlag = false; - if (scanOpts.pProgress->Create(this) == FALSE) { - LOGI("WARNING: ProgressDlg init failed"); - ASSERT(false); - } else { - scanOpts.pProgress->CenterWindow(this); - } - - time_t start, end; - start = time(NULL); - - /* start cranking */ - const CStringArray& arr = chooseFiles.GetFileNames(); - for (int i = 0; i < arr.GetCount(); i++) { - const CString& name = arr.GetAt(i); - if (Process(name, &scanOpts, &errMsg) != 0) { - LOGI("Skipping '%ls': %ls.", (LPCWSTR) name, (LPCWSTR) errMsg); - } - - if (fCancelFlag) { - LOGI("Canceled by user"); - MessageBox(L"Canceled!", L"MDC", MB_OK); - goto bail; - } - } - end = time(NULL); - fprintf(scanOpts.outfp, "\nScan completed in %ld seconds.\n", - (long) (end - start)); - - { - SetWindowText(L"MDC Done!"); - - CString doneMsg = L"Processing completed."; - CString appName; - - CheckedLoadString(&appName, IDS_APP_TITLE); - scanOpts.pProgress->MessageBox(doneMsg, appName, MB_OK|MB_ICONINFORMATION); - } - -bail: - if (scanOpts.outfp != NULL) - fclose(scanOpts.outfp); - - if (doResetDir && SetCurrentDirectory(curDir) == false) { - errMsg.Format(L"Unable to reset current directory to '%ls'.\n", curDir); - ShowFailureMsg(this, errMsg, IDS_FAILED); - // bummer - } - - // restore the main window - EnableWindow(TRUE); - - if (scanOpts.pProgress != NULL) - scanOpts.pProgress->DestroyWindow(); - - SetWindowText(L"MDC"); -} - -/* - * Directory structure and functions, modified from zDIR in Info-Zip sources. - */ -typedef struct Win32dirent { - DWORD d_attr; - WCHAR d_name[MAX_PATH]; - int d_first; - HANDLE d_hFindFile; -} Win32dirent; - -static const WCHAR kWildMatchAll[] = L"*.*"; - -Win32dirent* MainWindow::OpenDir(const WCHAR* name) -{ - Win32dirent* dir = NULL; - WCHAR* tmpStr = NULL; - WCHAR* cp; - WIN32_FIND_DATA fnd; - - dir = (Win32dirent*) malloc(sizeof(*dir)); - tmpStr = (WCHAR*) malloc((wcslen(name) + wcslen(kWildMatchAll) + 2) - * sizeof(WCHAR)); - if (dir == NULL || tmpStr == NULL) - goto failed; - - wcscpy(tmpStr, name); - cp = tmpStr + wcslen(tmpStr); - - /* don't end in a colon (e.g. "C:") */ - if ((cp - tmpStr) > 0 && wcsrchr(tmpStr, ':') == (cp - 1)) - *cp++ = '.'; - /* must end in a slash */ - if ((cp - tmpStr) > 0 && - wcsrchr(tmpStr, kLocalFssep) != (cp - 1)) - *cp++ = kLocalFssep; - - wcscpy(cp, kWildMatchAll); - - dir->d_hFindFile = FindFirstFile(tmpStr, &fnd); - if (dir->d_hFindFile == INVALID_HANDLE_VALUE) - goto failed; - - wcscpy(dir->d_name, fnd.cFileName); - dir->d_attr = fnd.dwFileAttributes; - dir->d_first = 1; - -bail: - free(tmpStr); - return dir; - -failed: - free(dir); - dir = NULL; - goto bail; -} - -Win32dirent* MainWindow::ReadDir(Win32dirent* dir) -{ - if (dir->d_first) - dir->d_first = 0; - else { - WIN32_FIND_DATA fnd; - - if (!FindNextFile(dir->d_hFindFile, &fnd)) - return NULL; - wcscpy(dir->d_name, fnd.cFileName); - dir->d_attr = (unsigned char) fnd.dwFileAttributes; - } - - return dir; -} - -void MainWindow::CloseDir(Win32dirent* dir) -{ - if (dir == NULL) - return; - - FindClose(dir->d_hFindFile); - free(dir); -} - -int MainWindow::Process(const WCHAR* pathname, ScanOpts* pScanOpts, - CString* pErrMsg) -{ - bool exists, isDir, isReadable; - struct _stat sb; - int result = -1; - - if (fCancelFlag) - return -1; - - ASSERT(pathname != NULL); - ASSERT(pErrMsg != NULL); - - PathName checkPath(pathname); - int ierr = checkPath.CheckFileStatus(&sb, &exists, &isReadable, &isDir); - if (ierr != 0) { - pErrMsg->Format(L"Unexpected error while examining '%ls': %hs", pathname, - strerror(ierr)); - goto bail; - } - - if (!exists) { - pErrMsg->Format(L"Couldn't find '%ls'", pathname); - goto bail; - } - if (!isReadable) { - pErrMsg->Format(L"File '%ls' isn't readable", pathname); - goto bail; - } - if (isDir) { - result = ProcessDirectory(pathname, pScanOpts, pErrMsg); - goto bail; - } - - (void) ScanDiskImage(pathname, pScanOpts); - - result = 0; - -bail: - if (result != 0 && pErrMsg->IsEmpty()) { - pErrMsg->Format(L"Unable to add file '%ls'", pathname); - } - return result; -} - -int MainWindow::ProcessDirectory(const WCHAR* dirName, ScanOpts* pScanOpts, - CString* pErrMsg) -{ - Win32dirent* dirp = NULL; - Win32dirent* entry; - WCHAR nbuf[MAX_PATH]; /* malloc might be better; this soaks stack */ - WCHAR fssep; - int len; - int result = -1; - - ASSERT(dirName != NULL); - ASSERT(pErrMsg != NULL); - - LOGI("+++ DESCEND: '%ls'", (LPCWSTR) dirName); - - dirp = OpenDir(dirName); - if (dirp == NULL) { - pErrMsg->Format(L"Failed on '%ls': %hs", dirName, strerror(errno)); - goto bail; - } - - fssep = kLocalFssep; - - /* could use readdir_r, but we don't care about reentrancy here */ - while ((entry = ReadDir(dirp)) != NULL) { - /* skip the dotsies */ - if (wcscmp(entry->d_name, L".") == 0 || wcscmp(entry->d_name, L"..") == 0) - continue; - - len = wcslen(dirName); - if (len + wcslen(entry->d_name) + 2 > MAX_PATH) { - LOGE("ERROR: Filename exceeds %d bytes: %ls%c%ls", - MAX_PATH, dirName, fssep, entry->d_name); - goto bail; - } - - /* form the new name, inserting an fssep if needed */ - wcscpy(nbuf, dirName); - if (dirName[len - 1] != fssep) { - nbuf[len++] = fssep; - } - wcscpy(nbuf+len, entry->d_name); - - result = Process(nbuf, pScanOpts, pErrMsg); - if (result != 0) - goto bail; - } - - result = 0; - -bail: - if (dirp != NULL) - (void)CloseDir(dirp); - return result; -} - -int MainWindow::ScanDiskImage(const WCHAR* pathName, ScanOpts* pScanOpts) -{ - ASSERT(pathName != NULL); - ASSERT(pScanOpts != NULL); - ASSERT(pScanOpts->outfp != NULL); - - DIError dierr; - CString errMsg; - DiskImg diskImg; - DiskFS* pDiskFS = NULL; - PathName path(pathName); - CString ext = path.GetExtension(); - - /* first, some housekeeping */ - PeekAndPump(); - pScanOpts->pProgress->SetCurrentFile(pathName); - if (fCancelFlag) - return -1; - - CString title; - title = L"MDC "; - title += PathName::FilenameOnly(pathName, '\\'); - SetWindowText(title); - - fprintf(pScanOpts->outfp, "File: %ls\n", pathName); - fflush(pScanOpts->outfp); // in case we crash - - if (!ext.IsEmpty()) { - /* delete the leading '.' */ - ext.Delete(0, 1); - } - - CStringA pathNameA(pathName); - dierr = diskImg.OpenImage(pathNameA, '\\', true); - if (dierr != kDIErrNone) { - errMsg.Format(L"Unable to open '%ls': %hs", pathName, - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - dierr = diskImg.AnalyzeImage(); - if (dierr != kDIErrNone) { - errMsg.Format(L"Analysis of '%ls' failed: %hs", pathName, - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - if (diskImg.GetFSFormat() == DiskImg::kFormatUnknown || - diskImg.GetSectorOrder() == DiskImg::kSectorOrderUnknown) - { - errMsg.Format(L"Unable to identify filesystem on '%ls'", pathName); - goto bail; - } - - /* create an appropriate DiskFS object */ - pDiskFS = diskImg.OpenAppropriateDiskFS(); - if (pDiskFS == NULL) { - /* unknown FS should've been caught above! */ - ASSERT(false); - errMsg.Format(L"Format of '%ls' not recognized.", pathName); - goto bail; - } - - pDiskFS->SetScanForSubVolumes(DiskFS::kScanSubEnabled); - - /* object created; prep it */ - dierr = pDiskFS->Initialize(&diskImg, DiskFS::kInitFull); - if (dierr != kDIErrNone) { - errMsg.Format(L"Error reading list of files from disk: %hs", - DiskImgLib::DIStrError(dierr)); - goto bail; - } - - int kbytes; - if (pDiskFS->GetDiskImg()->GetHasBlocks()) - kbytes = pDiskFS->GetDiskImg()->GetNumBlocks() / 2; - else if (pDiskFS->GetDiskImg()->GetHasSectors()) - kbytes = (pDiskFS->GetDiskImg()->GetNumTracks() * - pDiskFS->GetDiskImg()->GetNumSectPerTrack()) / 4; - else - kbytes = 0; - fprintf(pScanOpts->outfp, "Disk: %hs%hs (%dKB)\n", pDiskFS->GetVolumeID(), - pDiskFS->GetFSDamaged() ? " [*]" : "", kbytes); - fprintf(pScanOpts->outfp, - " Name Type Auxtyp Modified" - " Format Length\n"); - fprintf(pScanOpts->outfp, - "------------------------------------------------------" - "------------------------\n"); - if (LoadDiskFSContents(pDiskFS, "", pScanOpts) != 0) { - errMsg.Format(L"Failed while loading contents of '%ls'.", pathName); - goto bail; - } - fprintf(pScanOpts->outfp, - "------------------------------------------------------" - "------------------------\n\n"); - -bail: - delete pDiskFS; - - //PeekAndPump(); - - if (!errMsg.IsEmpty()) { - fprintf(pScanOpts->outfp, "Failed: %ls\n\n", (LPCWSTR) errMsg); - return -1; - } else { - return 0; - } -} - -void MainWindow::AnalyzeFile(const A2File* pFile, RecordKind* pRecordKind, - LONGLONG* pTotalLen, LONGLONG* pTotalCompLen) -{ - if (pFile->IsVolumeDirectory()) { - /* volume dir entry */ - ASSERT(pFile->GetRsrcLength() < 0); - *pRecordKind = kRecordKindVolumeDir; - *pTotalLen = pFile->GetDataLength(); - *pTotalCompLen = pFile->GetDataLength(); - } else if (pFile->IsDirectory()) { - /* directory entry */ - ASSERT(pFile->GetRsrcLength() < 0); - *pRecordKind = kRecordKindDirectory; - *pTotalLen = pFile->GetDataLength(); - *pTotalCompLen = pFile->GetDataLength(); - } else if (pFile->GetRsrcLength() >= 0) { - /* has resource fork */ - *pRecordKind = kRecordKindForkedFile; - *pTotalLen = pFile->GetDataLength() + pFile->GetRsrcLength(); - *pTotalCompLen = - pFile->GetDataSparseLength() + pFile->GetRsrcSparseLength(); - } else { - /* just data fork */ - *pRecordKind = kRecordKindFile; - *pTotalLen = pFile->GetDataLength(); - *pTotalCompLen = pFile->GetDataSparseLength(); - } -} - -bool MainWindow::IsRecordReadOnly(int access) -{ - if (access == 0x21L || access == 0x01L) - return true; - else - return false; -} - -/* ProDOS file type names; must be entirely in upper case */ -static const char gFileTypeNames[256][4] = { - "NON", "BAD", "PCD", "PTX", "TXT", "PDA", "BIN", "FNT", - "FOT", "BA3", "DA3", "WPF", "SOS", "$0D", "$0E", "DIR", - "RPD", "RPI", "AFD", "AFM", "AFR", "SCL", "PFS", "$17", - "$18", "ADB", "AWP", "ASP", "$1C", "$1D", "$1E", "$1F", - "TDM", "$21", "$22", "$23", "$24", "$25", "$26", "$27", - "$28", "$29", "8SC", "8OB", "8IC", "8LD", "P8C", "$2F", - "$30", "$31", "$32", "$33", "$34", "$35", "$36", "$37", - "$38", "$39", "$3A", "$3B", "$3C", "$3D", "$3E", "$3F", - "DIC", "OCR", "FTD", "$43", "$44", "$45", "$46", "$47", - "$48", "$49", "$4A", "$4B", "$4C", "$4D", "$4E", "$4F", - "GWP", "GSS", "GDB", "DRW", "GDP", "HMD", "EDU", "STN", - "HLP", "COM", "CFG", "ANM", "MUM", "ENT", "DVU", "FIN", - "$60", "$61", "$62", "$63", "$64", "$65", "$66", "$67", - "$68", "$69", "$6A", "BIO", "$6C", "TDR", "PRE", "HDV", - "$70", "$71", "$72", "$73", "$74", "$75", "$76", "$77", - "$78", "$79", "$7A", "$7B", "$7C", "$7D", "$7E", "$7F", - "$80", "$81", "$82", "$83", "$84", "$85", "$86", "$87", - "$88", "$89", "$8A", "$8B", "$8C", "$8D", "$8E", "$8F", - "$90", "$91", "$92", "$93", "$94", "$95", "$96", "$97", - "$98", "$99", "$9A", "$9B", "$9C", "$9D", "$9E", "$9F", - "WP ", "$A1", "$A2", "$A3", "$A4", "$A5", "$A6", "$A7", - "$A8", "$A9", "$AA", "GSB", "TDF", "BDF", "$AE", "$AF", - "SRC", "OBJ", "LIB", "S16", "RTL", "EXE", "PIF", "TIF", - "NDA", "CDA", "TOL", "DVR", "LDF", "FST", "$BE", "DOC", - "PNT", "PIC", "ANI", "PAL", "$C4", "OOG", "SCR", "CDV", - "FON", "FND", "ICN", "$CB", "$CC", "$CD", "$CE", "$CF", - "$D0", "$D1", "$D2", "$D3", "$D4", "MUS", "INS", "MDI", - "SND", "$D9", "$DA", "DBM", "$DC", "DDD", "$DE", "$DF", - "LBR", "$E1", "ATK", "$E3", "$E4", "$E5", "$E6", "$E7", - "$E8", "$E9", "$EA", "$EB", "$EC", "$ED", "R16", "PAS", - "CMD", "$F1", "$F2", "$F3", "$F4", "$F5", "$F6", "$F7", - "$F8", "OS ", "INT", "IVR", "BAS", "VAR", "REL", "SYS" -}; - -/*static*/ const char* MainWindow::GetFileTypeString(unsigned long fileType) -{ - if (fileType < NELEM(gFileTypeNames)) - return gFileTypeNames[fileType]; - else - return "???"; -} - -/* - * Sanitize a string. The Mac likes to stick control characters into - * things, e.g. ^C and ^M, and uses high ASCII for special characters. - * - * TODO(Unicode): we could do a Mac OS Roman to Unicode conversion, but - * we'd probably want to output UTF-8 (which Windows accessories like - * Notepad *are* able to read). - */ -static void MacSanitize(char* str) -{ - while (*str != '\0') { - *str = DiskImg::MacToASCII(*str); - str++; - } -} - -int MainWindow::LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, - ScanOpts* pScanOpts) -{ - static const char* kBlankFileNameMOR = ""; - DiskFS::SubVolume* pSubVol = NULL; - A2File* pFile; - - ASSERT(pDiskFS != NULL); - pFile = pDiskFS->GetNextFile(NULL); - for ( ; pFile != NULL; pFile = pDiskFS->GetNextFile(pFile)) { - CStringA subVolName, dispName; - RecordKind recordKind; - LONGLONG totalLen, totalCompLen; - char tmpbuf[16]; - - AnalyzeFile(pFile, &recordKind, &totalLen, &totalCompLen); - - if (recordKind == kRecordKindVolumeDir) { - /* this is a volume directory */ - LOGI("Not displaying volume dir '%hs'", pFile->GetPathName()); - continue; - } - - /* prepend volName for sub-volumes; must be valid Win32 dirname */ - if (volName[0] != '\0') - subVolName.Format("_%hs", volName); - - const char* ccp = pFile->GetPathName(); - ASSERT(ccp != NULL); - if (strlen(ccp) == 0) - ccp = kBlankFileNameMOR; - - CStringA path(ccp); - if (DiskImg::UsesDOSFileStructure(pFile->GetFSFormat()) && 0) { - InjectLowercase(&path); - } - - if (subVolName.IsEmpty()) - dispName = path; - else { - dispName = subVolName; - dispName += ':'; - dispName += path; - } - - /* strip out ctrl chars and high ASCII in HFS names */ - // TODO: consider having a Unicode output mode - MacSanitize(dispName.GetBuffer(0)); - dispName.ReleaseBuffer(); - - ccp = dispName; - - int len = strlen(ccp); - if (len <= 32) { - fprintf(pScanOpts->outfp, "%c%-32.32s ", - IsRecordReadOnly(pFile->GetAccess()) ? '*' : ' ', - ccp); - } else { - fprintf(pScanOpts->outfp, "%c..%-30.30s ", - IsRecordReadOnly(pFile->GetAccess()) ? '*' : ' ', - ccp + len - 30); - } - switch (recordKind) { - case kRecordKindUnknown: - fprintf(pScanOpts->outfp, "%hs- $%04lX ", - GetFileTypeString(pFile->GetFileType()), - pFile->GetAuxType()); - break; - case kRecordKindDisk: - sprintf(tmpbuf, "%I64dk", totalLen / 1024); - fprintf(pScanOpts->outfp, "Disk %-6hs ", tmpbuf); - break; - case kRecordKindFile: - case kRecordKindForkedFile: - case kRecordKindDirectory: - if (pDiskFS->GetDiskImg()->GetFSFormat() == DiskImg::kFormatMacHFS) - { - if (recordKind != kRecordKindDirectory && - pFile->GetFileType() >= 0 && pFile->GetFileType() <= 0xff && - pFile->GetAuxType() >= 0 && pFile->GetAuxType() <= 0xffff) - { - /* ProDOS type embedded in HFS */ - fprintf(pScanOpts->outfp, "%hs%c $%04lX ", - GetFileTypeString(pFile->GetFileType()), - recordKind == kRecordKindForkedFile ? '+' : ' ', - pFile->GetAuxType()); - } else { - char typeStr[5]; - char creatorStr[5]; - unsigned long val; - - val = pFile->GetAuxType(); - creatorStr[0] = (unsigned char) (val >> 24); - creatorStr[1] = (unsigned char) (val >> 16); - creatorStr[2] = (unsigned char) (val >> 8); - creatorStr[3] = (unsigned char) val; - creatorStr[4] = '\0'; - - val = pFile->GetFileType(); - typeStr[0] = (unsigned char) (val >> 24); - typeStr[1] = (unsigned char) (val >> 16); - typeStr[2] = (unsigned char) (val >> 8); - typeStr[3] = (unsigned char) val; - typeStr[4] = '\0'; - - MacSanitize(creatorStr); - MacSanitize(typeStr); - - if (recordKind == kRecordKindDirectory) { - fprintf(pScanOpts->outfp, "DIR %-4s ", creatorStr); - } else { - fprintf(pScanOpts->outfp, "%-4s%c %-4s ", - typeStr, - pFile->GetRsrcLength() > 0 ? '+' : ' ', - creatorStr); - } - } - } else { - fprintf(pScanOpts->outfp, "%hs%c $%04lX ", - GetFileTypeString(pFile->GetFileType()), - recordKind == kRecordKindForkedFile ? '+' : ' ', - pFile->GetAuxType()); - } - break; - case kRecordKindVolumeDir: - /* should've trapped this earlier */ - ASSERT(0); - fprintf(pScanOpts->outfp, "ERROR "); - break; - default: - ASSERT(0); - fprintf(pScanOpts->outfp, "ERROR "); - break; - } - - CString date; - if (pFile->GetModWhen() == 0) - FormatDate(kDateNone, &date); - else - FormatDate(pFile->GetModWhen(), &date); - fprintf(pScanOpts->outfp, "%-15ls ", (LPCWSTR) date); - - const char* fmtStr; - switch (pFile->GetFSFormat()) { - case DiskImg::kFormatDOS33: - case DiskImg::kFormatDOS32: - case DiskImg::kFormatUNIDOS: - case DiskImg::kFormatOzDOS: - fmtStr = "DOS "; - break; - case DiskImg::kFormatProDOS: - fmtStr = "ProDOS"; - break; - case DiskImg::kFormatPascal: - fmtStr = "Pascal"; - break; - case DiskImg::kFormatCPM: - fmtStr = "CP/M "; - break; - case DiskImg::kFormatRDOS33: - case DiskImg::kFormatRDOS32: - case DiskImg::kFormatRDOS3: - fmtStr = "RDOS "; - break; - case DiskImg::kFormatMacHFS: - fmtStr = "HFS "; - break; - case DiskImg::kFormatMSDOS: - fmtStr = "MS-DOS"; - break; - case DiskImg::kFormatGutenberg: - fmtStr = "Gutenb"; - break; - default: - fmtStr = "??? "; - break; - } - if (pFile->GetQuality() == A2File::kQualityDamaged) - fmtStr = "BROKEN"; - else if (pFile->GetQuality() == A2File::kQualitySuspicious) - fmtStr = "BAD? "; - - fprintf(pScanOpts->outfp, "%hs ", fmtStr); - - -#if 0 - /* compute the percent size */ - if ((!totalLen && totalCompLen) || (totalLen && !totalCompLen)) - fprintf(pScanOpts->outfp, "--- "); /* weird */ - else if (totalLen < totalCompLen) - fprintf(pScanOpts->outfp, ">100%% "); /* compression failed? */ - else { - sprintf(tmpbuf, "%02d%%", ComputePercent(totalCompLen, totalLen)); - fprintf(pScanOpts->outfp, "%4s ", tmpbuf); - } -#endif - - if (!totalLen && totalCompLen) - fprintf(pScanOpts->outfp, " ????"); /* weird */ - else - fprintf(pScanOpts->outfp, "%8I64d", totalLen); - - fprintf(pScanOpts->outfp, "\n"); - } - - /* - * Load all sub-volumes. - */ - pSubVol = pDiskFS->GetNextSubVolume(NULL); - while (pSubVol != NULL) { - const char* subVolName; - int ret; - - subVolName = pSubVol->GetDiskFS()->GetVolumeName(); - if (subVolName == NULL) - subVolName = "+++"; // could probably do better than this - - ret = LoadDiskFSContents(pSubVol->GetDiskFS(), subVolName, pScanOpts); - if (ret != 0) - return ret; - pSubVol = pDiskFS->GetNextSubVolume(pSubVol); - } - - return 0; -} diff --git a/ciderpress/mdc/Main.h b/ciderpress/mdc/Main.h deleted file mode 100644 index 5b8f138..0000000 --- a/ciderpress/mdc/Main.h +++ /dev/null @@ -1,158 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Main frame window declarations. - */ -#ifndef MDC_MAIN_H -#define MDC_MAIN_H - -#include "../diskimg/DiskImg.h" -#include "../nufxlib/NufxLib.h" -using namespace DiskImgLib; - -struct Win32dirent; -struct ScanOpts; - -typedef enum RecordKind { - kRecordKindUnknown = 0, - kRecordKindDisk, - kRecordKindFile, - kRecordKindForkedFile, - kRecordKindDirectory, - kRecordKindVolumeDir, -} RecordKind; - - -/* - * The main UI window. - */ -class MainWindow : public CFrameWnd { -public: - /* - * MainWindow constructor. Creates the main window and sets - * its properties. - */ - MainWindow(void); - - /* - * MainWindow destructor. Close the archive if one is open, but don't try - * to shut down any controls in child windows. By this point, Windows has - * already snuffed them. - */ - ~MainWindow(void); - -private: - afx_msg void OnFileScan(void); - afx_msg void OnFileExit(void); - afx_msg void OnHelpWebSite(void); - afx_msg void OnHelpAbout(void); - - /* - * Allow events to flow through the message queue whenever the - * progress meter gets updated. This will allow us to redraw with - * reasonable frequency. - * - * Calling this can result in other code being called, such as Windows - * message handlers, which can lead to reentrancy problems. Make sure - * you're adequately semaphored before calling here. - * - * Returns TRUE if all is well, FALSE if we're trying to quit. - */ - BOOL PeekAndPump(void); - - /* - * Handle a debug message from the DiskImg library. - */ - static void DebugMsgHandler(const char* file, int line, - const char* msg); - static NuResult NufxErrorMsgHandler(NuArchive* /*pArchive*/, - void* vErrorMessage); - - /* - * Prompts the user to select the input set and output file, then starts - * the scan. - */ - void ScanFiles(void); - - /* - * Prepare a directory for reading. - * - * Allocates a Win32dirent struct that must be freed by the caller. - */ - Win32dirent* OpenDir(const WCHAR* name); - - /* - * Get an entry from an open directory. - * - * Returns a NULL pointer after the last entry has been read. - */ - Win32dirent* ReadDir(Win32dirent* dir); - - /* - * Close a directory. - */ - void CloseDir(Win32dirent* dir); - - /* - * Process a file or directory. These are expected to be names of files in - * the current directory. - * - * Returns 0 on success, nonzero on error with a message in "*pErrMsg". - */ - int Process(const WCHAR* pathname, ScanOpts* pScanOpts, - CString* pErrMsg); - - /* - * Win32 recursive directory descent. Scan the contents of a directory. - * If a subdirectory is found, follow it; otherwise, call Win32AddFile to - * add the file. - */ - int ProcessDirectory(const WCHAR* dirName, ScanOpts* pScanOpts, - CString* pErrMsg); - - /* - * Open a disk image and dump the contents. - * - * Returns 0 on success, nonzero on failure. - */ - int ScanDiskImage(const WCHAR* pathName, ScanOpts* pScanOpts); - - /* - * Analyze a file's characteristics. - */ - void AnalyzeFile(const A2File* pFile, RecordKind* pRecordKind, - LONGLONG* pTotalLen, LONGLONG* pTotalCompLen); - - /* - * Determine whether the access bits on the record make it a read-only - * file or not. - * - * Uses a simplified view of the access flags. - */ - bool IsRecordReadOnly(int access); - - /* - * Return a pointer to the three-letter representation of the file type name. - * - * Note to self: code down below tests first char for '?'. - */ - static const char* GetFileTypeString(unsigned long fileType); - - /* - * Load the contents of a DiskFS. - * - * Recursively handle sub-volumes. - */ - int LoadDiskFSContents(DiskFS* pDiskFS, const char* volName, - ScanOpts* pScanOpts); - - bool fCancelFlag; - //int fTitleAnimation; - - DECLARE_MESSAGE_MAP() -}; - -#endif /*MDC_MAIN_H*/ diff --git a/ciderpress/mdc/ProgressDlg.cpp b/ciderpress/mdc/ProgressDlg.cpp deleted file mode 100644 index abdd9b1..0000000 --- a/ciderpress/mdc/ProgressDlg.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// ProgressDlg.cpp : implementation file -// - -#include "stdafx.h" -#include "mdc.h" -#include "ProgressDlg.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// ProgressDlg dialog - -#if 0 -ProgressDlg::ProgressDlg(CWnd* pParent /*=NULL*/) - : CDialog(ProgressDlg::IDD, pParent) -{ - //{{AFX_DATA_INIT(ProgressDlg) - // NOTE: the ClassWizard will add member initialization here - //}}AFX_DATA_INIT - - fpCancelFlag = NULL; -} -#endif - - -BEGIN_MESSAGE_MAP(ProgressDlg, CDialog) - //{{AFX_MSG_MAP(ProgressDlg) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// ProgressDlg message handlers - -BOOL ProgressDlg::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) -{ - // TODO: Add your specialized code here and/or call the base class - - return CDialog::Create(IDD, pParentWnd); -} - -void ProgressDlg::PostNcDestroy() -{ - // TODO: Add your specialized code here and/or call the base class - delete this; - - //CDialog::PostNcDestroy(); -} - -/* - * Update the progress display with the name of the file we're currently - * working on. - */ -void -ProgressDlg::SetCurrentFile(const WCHAR* fileName) -{ - CWnd* pWnd = GetDlgItem(IDC_PROGRESS_FILENAME); - ASSERT(pWnd != NULL); - pWnd->SetWindowText(fileName); -} - -void ProgressDlg::OnCancel() -{ - // TODO: Add extra cleanup here - LOGI("Cancel button pushed"); - ASSERT(fpCancelFlag != NULL); - *fpCancelFlag = true; - - //CDialog::OnCancel(); -} diff --git a/ciderpress/mdc/ProgressDlg.h b/ciderpress/mdc/ProgressDlg.h deleted file mode 100644 index f12a697..0000000 --- a/ciderpress/mdc/ProgressDlg.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#if !defined(AFX_PROGRESSDLG_H__94B5552B_1337_4FA4_A336_32FA9544ADAC__INCLUDED_) -#define AFX_PROGRESSDLG_H__94B5552B_1337_4FA4_A336_32FA9544ADAC__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// ProgressDlg.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// ProgressDlg dialog - -class ProgressDlg : public CDialog -{ -// Construction -public: - /*ProgressDlg(CWnd* pParent = NULL); // standard constructor*/ - -// Dialog Data - //{{AFX_DATA(ProgressDlg) - enum { IDD = IDD_PROGRESS }; - // NOTE: the ClassWizard will add data members here - //}}AFX_DATA - - bool* fpCancelFlag; - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(ProgressDlg) - public: - virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); - protected: - virtual void PostNcDestroy(); - //}}AFX_VIRTUAL - -public: - BOOL Create(CWnd* pParentWnd = NULL) { - return CDialog::Create(IDD_PROGRESS, pParentWnd); - } - void SetCurrentFile(const WCHAR* fileName); - -// Implementation -protected: - // Generated message map functions - //{{AFX_MSG(ProgressDlg) - virtual void OnCancel(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_PROGRESSDLG_H__94B5552B_1337_4FA4_A336_32FA9544ADAC__INCLUDED_) diff --git a/ciderpress/mdc/StdAfx.cpp b/ciderpress/mdc/StdAfx.cpp deleted file mode 100644 index dcc6f56..0000000 --- a/ciderpress/mdc/StdAfx.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// stdafx.cpp : source file that includes just the standard includes -// mdc.pch will be the pre-compiled header -// stdafx.obj will contain the pre-compiled type information - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file diff --git a/ciderpress/mdc/StdAfx.h b/ciderpress/mdc/StdAfx.h deleted file mode 100644 index 5c9578a..0000000 --- a/ciderpress/mdc/StdAfx.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -// stdafx.h : include file for standard system include files, -// or project specific include files that are used frequently, but -// are changed infrequently -// - -#if !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) -#define AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers - -#include "../app/targetver.h" - -// Windows Header Files: -//#include - -// C RunTime Header Files -#include -#include -#include -#include -#include -#include -#include -#include - -//#include -#include -#include -#include -#include -#include -#include -#include "..\util\UtilLib.h" - -// Local Header Files - -// TODO: reference additional headers your program requires here - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_STDAFX_H__A9DB83DB_A9FD_11D0_BFD1_444553540000__INCLUDED_) diff --git a/ciderpress/mdc/mdc.ICO b/ciderpress/mdc/mdc.ICO deleted file mode 100644 index 0004723..0000000 Binary files a/ciderpress/mdc/mdc.ICO and /dev/null differ diff --git a/ciderpress/mdc/mdc.clw b/ciderpress/mdc/mdc.clw deleted file mode 100644 index aed7367..0000000 --- a/ciderpress/mdc/mdc.clw +++ /dev/null @@ -1,82 +0,0 @@ -; CLW file contains information for the MFC ClassWizard - -[General Info] -Version=1 -LastClass=AboutDlg -LastTemplate=CDialog -NewFileInclude1=#include "stdafx.h" -NewFileInclude2=#include "mdc.h" -LastPage=0 - -ClassCount=2 - -ResourceCount=4 -Resource1=IDD_ABOUTBOX -Class1=AboutDlg -Resource2="IDD_CHOOSE_FILES" -Resource3=IDC_MDC -Class2=ProgressDlg -Resource4=IDD_PROGRESS - -[DLG:IDD_ABOUTBOX] -Type=1 -Class=AboutDlg -ControlCount=6 -Control1=IDC_MYICON,static,1342177283 -Control2=IDC_ABOUT_VERS,static,1342308480 -Control3=IDC_STATIC,static,1342308352 -Control4=IDOK,button,1342242817 -Control5=IDC_STATIC,static,1342308352 -Control6=IDC_STATIC,static,1342308352 - -[MNU:IDC_MDC] -Type=1 -Class=? -Command1=IDM_FILE_SCAN -Command2=IDM_FILE_EXIT -Command3=IDM_HELP_WEBSITE -Command4=IDM_HELP_ABOUT -CommandCount=4 - -[ACL:IDC_MDC] -Type=1 -Class=? -Command1=IDM_ABOUT -Command2=IDM_ABOUT -CommandCount=2 - -[CLS:AboutDlg] -Type=0 -HeaderFile=AboutDlg.h -ImplementationFile=AboutDlg.cpp -BaseClass=CDialog -Filter=D -VirtualFilter=dWC -LastObject=AboutDlg - -[DLG:"IDD_CHOOSE_FILES"] -Type=1 -Class=? -ControlCount=4 -Control1=1119,static,1342177280 -Control2=IDC_SELECT_ACCEPT,button,1342242816 -Control3=IDCANCEL,button,1342242816 -Control4=IDC_CHOOSEFILES_STATIC1,static,1342308352 - -[DLG:IDD_PROGRESS] -Type=1 -Class=ProgressDlg -ControlCount=3 -Control1=IDCANCEL,button,1342242816 -Control2=IDC_STATIC,static,1342308352 -Control3=IDC_PROGRESS_FILENAME,static,1342308352 - -[CLS:ProgressDlg] -Type=0 -HeaderFile=ProgressDlg.h -ImplementationFile=ProgressDlg.cpp -BaseClass=CDialog -Filter=C -LastObject=IDCANCEL -VirtualFilter=dWC - diff --git a/ciderpress/mdc/mdc.cpp b/ciderpress/mdc/mdc.cpp deleted file mode 100644 index 43d8702..0000000 --- a/ciderpress/mdc/mdc.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * The application object. - */ -#include "stdafx.h" -#include "mdc.h" -#include "Main.h" -#include - -/* magic global that MFC finds (or that finds MFC) */ -MyApp gMyApp; - -DebugLog* gDebugLog; - -/* - * Constructor. This is the closest thing to "main" that we have, but we - * should wait for InitInstance for most things. - */ -MyApp::MyApp(LPCTSTR lpszAppName) : CWinApp(lpszAppName) -{ - // TODO: make the log file configurable - gDebugLog = new DebugLog(L"C:\\src\\mdclog.txt"); - - time_t now = time(NULL); - LOGI("MDC v%d.%d.%d started at %.24hs", - kAppMajorVersion, kAppMinorVersion, kAppBugVersion, - ctime(&now)); - - int tmpDbgFlag; - // enable memory leak detection - tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF; - _CrtSetDbgFlag(tmpDbgFlag); - LOGI("Leak detection enabled"); -} - -/* - * This is the last point of control we have. - */ -MyApp::~MyApp(void) -{ - LOGI("MDC shutting down"); - delete gDebugLog; -} - - -/* - * It all begins here. - * - * Create a main window. - */ -BOOL -MyApp::InitInstance(void) -{ - m_pMainWnd = new MainWindow; - m_pMainWnd->ShowWindow(m_nCmdShow); - m_pMainWnd->UpdateWindow(); - - return TRUE; -} diff --git a/ciderpress/mdc/mdc.h b/ciderpress/mdc/mdc.h deleted file mode 100644 index 31239a5..0000000 --- a/ciderpress/mdc/mdc.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -#if !defined(AFX_MDC_H__15BEB7EF_BB49_4E23_BDD1_7F7B0220F3DB__INCLUDED_) -#define AFX_MDC_H__15BEB7EF_BB49_4E23_BDD1_7F7B0220F3DB__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 - -#include "resource.h" - -/* MDC version numbers */ -#define kAppMajorVersion 3 -#define kAppMinorVersion 0 -#define kAppBugVersion 0 - -/* - * Windows application object. - */ -class MyApp : public CWinApp { -public: - MyApp(LPCTSTR lpszAppName = NULL); - virtual ~MyApp(void); - - // Overridden functions - virtual BOOL InitInstance(void); - //virtual BOOL OnIdle(LONG lCount); -}; - -extern MyApp gMyApp; - -#endif // !defined(AFX_MDC_H__15BEB7EF_BB49_4E23_BDD1_7F7B0220F3DB__INCLUDED_) diff --git a/ciderpress/mdc/mdc.rc b/ciderpress/mdc/mdc.rc deleted file mode 100644 index a622051..0000000 --- a/ciderpress/mdc/mdc.rc +++ /dev/null @@ -1,200 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#define APSTUDIO_HIDDEN_SYMBOLS -#include "afxres.h" -#undef APSTUDIO_HIDDEN_SYMBOLS -#include "resource.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US -#pragma code_page(1252) - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_MDC ICON "mdc.ICO" - -///////////////////////////////////////////////////////////////////////////// -// -// Menu -// - -IDC_MDC MENU -BEGIN - POPUP "&File" - BEGIN - MENUITEM "&Scan disk images...", IDM_FILE_SCAN - MENUITEM "E&xit", IDM_FILE_EXIT - END - POPUP "&Help" - BEGIN - MENUITEM "Visit CiderPress &web site", IDM_HELP_WEBSITE - MENUITEM "&About ...", IDM_HELP_ABOUT - END -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Accelerator -// - -IDC_MDC ACCELERATORS -BEGIN - "?", IDM_ABOUT, ASCII, ALT - "/", IDM_ABOUT, ASCII, ALT -END - - -///////////////////////////////////////////////////////////////////////////// -// -// Dialog -// - -IDD_ABOUTBOX DIALOGEX 22, 17, 178, 111 -STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CAPTION | WS_SYSMENU -CAPTION "About" -FONT 10, "MS Shell Dlg", 400, 0, 0x0 -BEGIN - ICON IDI_MDC,IDC_MYICON,7,7,18,16 - LTEXT "MDC version %d.%d.%d",IDC_ABOUT_VERS,33,7,138,8,SS_NOPREFIX - LTEXT "Copyright © 2015 by CiderPress project authors.\rAll Rights Reserved.",IDC_STATIC,7,31,164,19 - DEFPUSHBUTTON "OK",IDOK,64,90,50,14 - LTEXT "This utility scans Apple II disk images and creates formatted listings of the files in them.",IDC_STATIC,7,58,164,26 - LTEXT "Multi-Disk Catalog",IDC_STATIC,33,15,138,8 -END - -IDD_CHOOSE_FILES DIALOGEX 0, 0, 270, 112 -STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN -FONT 8, "MS Shell Dlg", 0, 0, 0x1 -BEGIN - LTEXT "",1119,0,0,270,87,NOT WS_GROUP - LTEXT "Select the disk images to scan. If you select a folder, all files in that folder will be processed. Use Ctrl-click and Shift-click to select multiple items.",IDC_CHOOSEFILES_STATIC1,7,87,263,17 -END - -IDD_PROGRESS DIALOGEX 0, 0, 250, 66 -STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION -CAPTION "Progress..." -FONT 8, "MS Shell Dlg", 0, 0, 0x0 -BEGIN - PUSHBUTTON "Cancel",IDCANCEL,99,45,50,14 - LTEXT "Now scanning:",IDC_STATIC,7,7,96,8 - LTEXT ">filename<",IDC_PROGRESS_FILENAME,7,20,236,8 -END - - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -2 TEXTINCLUDE -BEGIN - "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""afxres.h""\r\n" - "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" - "#include ""resource.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// DESIGNINFO -// - -#ifdef APSTUDIO_INVOKED -GUIDELINES DESIGNINFO -BEGIN - IDD_ABOUTBOX, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 171 - VERTGUIDE, 33 - TOPMARGIN, 7 - BOTTOMMARGIN, 104 - END - - "IDD_CHOOSE_FILES", DIALOG - BEGIN - VERTGUIDE, 7 - BOTTOMMARGIN, 104 - HORZGUIDE, 87 - END - - IDD_PROGRESS, DIALOG - BEGIN - LEFTMARGIN, 7 - RIGHTMARGIN, 243 - TOPMARGIN, 7 - BOTTOMMARGIN, 59 - END -END -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// String Table -// - -STRINGTABLE -BEGIN - IDS_APP_TITLE "MDC" - IDC_MDC "MDC" - IDS_MUST_REGISTER "You must be a registered owner of CiderPress to use MDC.\r\n\r\nVisit http://www.faddensoft.com/." - IDS_FAILED "Failed" -END - -STRINGTABLE -BEGIN - IDM_FILE_SCAN "Select disk images to scan" - IDM_HELP_WEBSITE "Visit the CiderPress web site\nGo to web site" -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED - diff --git a/ciderpress/mdc/mdc.vcxproj b/ciderpress/mdc/mdc.vcxproj deleted file mode 100644 index 3724c8e..0000000 --- a/ciderpress/mdc/mdc.vcxproj +++ /dev/null @@ -1,197 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - MFCProj - {7DF41D71-C8DC-48AA-B372-4613210310A4} - 8.1 - - - - Application - v120_xp - Dynamic - Unicode - - - Application - v120_xp - Dynamic - Unicode - - - - - - - - - - - - - <_ProjectFileVersion>12.0.30501.0 - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - false - - - $(SolutionDir)$(Configuration)\ - $(Configuration)\ - true - - - - MaxSpeed - OnlyExplicitInline - WIN32;NDEBUGX;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - true - MultiThreadedDLL - true - Use - stdafx.h - $(IntDir)$(TargetName).pch - $(IntDir) - $(IntDir) - $(IntDir)vc$(PlatformToolsetVersion).pdb - Level3 - true - true - - - $(OutDir)$(TargetName)$(TargetExt) - false - Release/mdc.pdb - true - Release/mdc.map - Windows - MachineX86 - - - - NDEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Release/mdc.tlb - - - - - - - - - - NDEBUG;%(PreprocessorDefinitions) - 0x0409 - - - - - Disabled - WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebugDLL - Use - stdafx.h - $(IntDir)$(TargetName).pch - $(IntDir) - $(IntDir) - $(IntDir)vc$(PlatformToolsetVersion).pdb - true - Level3 - true - EditAndContinue - true - - - $(OutDir)$(TargetName)$(TargetExt) - false - true - Debug/mdc.pdb - Windows - MachineX86 - - - - _DEBUG;%(PreprocessorDefinitions) - true - true - Win32 - .\Debug/mdc.tlb - - - - - - - - - - _DEBUG;%(PreprocessorDefinitions) - 0x0409 - - - - - - - - - - - - - - - - - - - {0cfe6fad-0126-4e99-8625-c807d1d2aaf4} - false - - - {c48ae53b-3dcb-43b1-9207-b7c5b6bb78af} - - - {04bfae2a-7ab3-4b63-b4ab-42ff1d6ad3c5} - false - - - {b66109f4-217b-43c0-86aa-eb55657e5ac0} - false - false - false - true - false - - - - - - - - - Create - Create - - - - - - \ No newline at end of file diff --git a/ciderpress/mdc/mdc.vcxproj.filters b/ciderpress/mdc/mdc.vcxproj.filters deleted file mode 100644 index ed13339..0000000 --- a/ciderpress/mdc/mdc.vcxproj.filters +++ /dev/null @@ -1,64 +0,0 @@ - - - - - {19d28053-27cc-4138-924c-e2bfc781aa40} - cpp;c;cxx;rc;def;r;odl;idl;hpj;bat - - - {493f9868-fdfc-4dc5-9c9a-b75808666a2a} - h;hpp;hxx;hm;inl - - - {39acd0ae-9058-474a-84b3-d4244d95c53c} - ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe - - - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Resource Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/ciderpress/mdc/mdc.vcxproj.user b/ciderpress/mdc/mdc.vcxproj.user deleted file mode 100644 index ef5ff2a..0000000 --- a/ciderpress/mdc/mdc.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/ciderpress/mdc/resource.h b/ciderpress/mdc/resource.h deleted file mode 100644 index 35c17f8..0000000 --- a/ciderpress/mdc/resource.h +++ /dev/null @@ -1,32 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by mdc.rc -// -#define IDC_MYICON 2 -#define IDD_ABOUTBOX 103 -#define IDS_APP_TITLE 103 -#define IDM_ABOUT 104 -#define IDM_FILE_EXIT 105 -#define IDI_MDC 107 -#define IDC_MDC 109 -#define IDS_MUST_REGISTER 110 -#define IDS_FAILED 111 -#define IDD_PROGRESS 131 -#define IDC_CHOOSEFILES_STATIC1 1002 -#define IDC_PROGRESS_FILENAME 1003 -#define IDC_ABOUT_VERS 1004 -#define IDC_SELECT_ACCEPT 1175 -#define IDM_HELP_ABOUT 32771 -#define IDM_FILE_SCAN 32772 -#define IDM_HELP_WEBSITE 32773 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 132 -#define _APS_NEXT_COMMAND_VALUE 32774 -#define _APS_NEXT_CONTROL_VALUE 1005 -#define _APS_NEXT_SYMED_VALUE 110 -#endif -#endif diff --git a/ciderpress/nufxlib/.gitignore b/ciderpress/nufxlib/.gitignore deleted file mode 100644 index 0f68fbd..0000000 --- a/ciderpress/nufxlib/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -# config-generated sources -Makefile -config.h -config.log -config.status - -# generated binaries -libnufx.a -samples/exerciser -samples/imgconv -samples/launder -samples/test-basic -samples/test-extract -samples/test-names -samples/test-simple -samples/test-twirl diff --git a/ciderpress/nufxlib/Archive.c b/ciderpress/nufxlib/Archive.c deleted file mode 100644 index 7c97cb3..0000000 --- a/ciderpress/nufxlib/Archive.c +++ /dev/null @@ -1,1204 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Archive structure creation and manipulation. - */ -#include "NufxLibPriv.h" - -#ifdef HAVE_FCNTL_H -# include -#endif -#ifndef O_BINARY -# define O_BINARY 0 -#endif - -/* master header identification */ -static const uint8_t kNuMasterID[kNufileIDLen] = - { 0x4e, 0xf5, 0x46, 0xe9, 0x6c, 0xe5 }; - -/* other identification; can be no longer than kNufileIDLen */ -static const uint8_t kNuBinary2ID[] = - { 0x0a, 0x47, 0x4c }; -static const uint8_t kNuSHKSEAID[] = - { 0xa2, 0x2e, 0x00 }; - -/* - * Offsets to some interesting places in the wrappers. - */ -#define kNuBNYFileSizeLo 8 /* file size in 512-byte blocks (2B) */ -#define kNuBNYFileSizeHi 114 /* ... (2B) */ -#define kNuBNYEOFLo 20 /* file size in bytes (3B) */ -#define kNuBNYEOFHi 116 /* ... (1B) */ -#define kNuBNYDiskSpace 117 /* total space req'd; equiv FileSize (4B) */ -#define kNuBNYFilesToFollow 127 /* (1B) #of files in rest of BNY file */ -#define kNuSEAFunkySize 11938 /* length of archive + 68 (4B?) */ -#define kNuSEAFunkyAdjust 68 /* ... adjustment to "FunkySize" */ -#define kNuSEALength1 11946 /* length of archive (4B?) */ -#define kNuSEALength2 12001 /* length of archive (4B?) */ - -#define kDefaultJunkSkipMax 1024 /* default junk scan size */ - -static void Nu_CloseAndFree(NuArchive* pArchive); - - -/* - * =========================================================================== - * Archive and MasterHeader utility functions - * =========================================================================== - */ - -/* - * Allocate and initialize a new NuArchive structure. - */ -static NuError Nu_NuArchiveNew(NuArchive** ppArchive) -{ - Assert(ppArchive != NULL); - - /* validate some assumptions we make throughout the code */ - Assert(sizeof(int) >= 2); - Assert(sizeof(void*) >= sizeof(NuArchive*)); - - *ppArchive = Nu_Calloc(NULL, sizeof(**ppArchive)); - if (*ppArchive == NULL) - return kNuErrMalloc; - - (*ppArchive)->structMagic = kNuArchiveStructMagic; - - (*ppArchive)->recordIdxSeed = 1000; /* could be a random number */ - (*ppArchive)->nextRecordIdx = (*ppArchive)->recordIdxSeed; - - /* - * Initialize assorted values to defaults. We don't try to do any - * system-specific values here; it's up to the application to decide - * what is most appropriate for the current system. - */ - (*ppArchive)->valIgnoreCRC = false; - #ifdef ENABLE_LZW - (*ppArchive)->valDataCompression = kNuCompressLZW2; - #else - (*ppArchive)->valDataCompression = kNuCompressNone; - #endif - (*ppArchive)->valDiscardWrapper = false; - (*ppArchive)->valEOL = kNuEOLLF; /* non-UNIX apps must override */ - (*ppArchive)->valConvertExtractedEOL = kNuConvertOff; - (*ppArchive)->valOnlyUpdateOlder = false; - (*ppArchive)->valAllowDuplicates = false; - (*ppArchive)->valHandleExisting = kNuMaybeOverwrite; - (*ppArchive)->valModifyOrig = false; - (*ppArchive)->valMimicSHK = false; - (*ppArchive)->valMaskDataless = false; - (*ppArchive)->valStripHighASCII = false; - /* bug: this can't be set by application! */ - (*ppArchive)->valJunkSkipMax = kDefaultJunkSkipMax; - (*ppArchive)->valIgnoreLZW2Len = false; - (*ppArchive)->valHandleBadMac = false; - - (*ppArchive)->messageHandlerFunc = gNuGlobalErrorMessageHandler; - - return kNuErrNone; -} - -/* - * Free up a NuArchive structure and its contents. - */ -static NuError Nu_NuArchiveFree(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - Assert(pArchive->structMagic == kNuArchiveStructMagic); - - (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->origRecordSet); - pArchive->haveToc = false; - (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); - (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->newRecordSet); - - Nu_Free(NULL, pArchive->archivePathnameUNI); - Nu_Free(NULL, pArchive->tmpPathnameUNI); - Nu_Free(NULL, pArchive->compBuf); - Nu_Free(NULL, pArchive->lzwCompressState); - Nu_Free(NULL, pArchive->lzwExpandState); - - /* mark it as deceased to prevent further use, then free it */ - pArchive->structMagic = kNuArchiveStructMagic ^ 0xffffffff; - Nu_Free(NULL, pArchive); - - return kNuErrNone; -} - - -/* - * Copy a NuMasterHeader struct. - */ -void Nu_MasterHeaderCopy(NuArchive* pArchive, NuMasterHeader* pDstHeader, - const NuMasterHeader* pSrcHeader) -{ - Assert(pArchive != NULL); - Assert(pDstHeader != NULL); - Assert(pSrcHeader != NULL); - - *pDstHeader = *pSrcHeader; -} - -/* - * Get a pointer to the archive master header (this is an API call). - */ -NuError Nu_GetMasterHeader(NuArchive* pArchive, - const NuMasterHeader** ppMasterHeader) -{ - if (ppMasterHeader == NULL) - return kNuErrInvalidArg; - - *ppMasterHeader = &pArchive->masterHeader; - - return kNuErrNone; -} - - -/* - * Allocate the general-purpose compression buffer, if needed. - */ -NuError Nu_AllocCompressionBufferIFN(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - - if (pArchive->compBuf != NULL) - return kNuErrNone; - - pArchive->compBuf = Nu_Malloc(pArchive, kNuGenCompBufSize); - if (pArchive->compBuf == NULL) - return kNuErrMalloc; - - return kNuErrNone; -} - - -/* - * Return a unique value. - */ -NuRecordIdx Nu_GetNextRecordIdx(NuArchive* pArchive) -{ - return pArchive->nextRecordIdx++; -} - -/* - * Return a unique value. - */ -NuThreadIdx Nu_GetNextThreadIdx(NuArchive* pArchive) -{ - return pArchive->nextRecordIdx++; /* just use the record counter */ -} - - -/* - * =========================================================================== - * Wrapper (SEA, BXY, BSE) functions - * =========================================================================== - */ - -/* - * Copy the wrapper from the archive file to the temp file. - */ -NuError Nu_CopyWrapperToTemp(NuArchive* pArchive) -{ - NuError err; - - Assert(pArchive->headerOffset); /* no wrapper to copy?? */ - - err = Nu_FSeek(pArchive->archiveFp, 0, SEEK_SET); - BailError(err); - err = Nu_FSeek(pArchive->tmpFp, 0, SEEK_SET); - BailError(err); - err = Nu_CopyFileSection(pArchive, pArchive->tmpFp, - pArchive->archiveFp, pArchive->headerOffset); - BailError(err); - -bail: - return err; -} - - -/* - * Fix up the wrapper. The SEA and BXY headers have some fields - * set according to file length and archive attributes. - * - * Pass in the file pointer that will be written to. Wrappers are - * assumed to start at offset 0. - * - * Wrappers must appear in this order: - * Leading junk - * Binary II - * ShrinkIt SEA (Self-Extracting Archive) - * - * If they didn't, we wouldn't be this far. - * - * I have a Binary II specification, but don't have one for SEA, so I'm - * making educated guesses based on the differences between archives. I'd - * guess some of the SEA weirdness stems from some far-sighted support - * for multiple archives within a single SEA wrapper. - */ -NuError Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp) -{ - NuError err = kNuErrNone; - Boolean hasBinary2, hasSea; - uint8_t identBuf[kNufileIDLen]; - uint32_t archiveLen, archiveLen512; - - Assert(pArchive->newMasterHeader.isValid); /* need new crc and len */ - - hasBinary2 = hasSea = false; - - switch (pArchive->archiveType) { - case kNuArchiveNuFX: - goto bail; - case kNuArchiveNuFXInBNY: - hasBinary2 = true; - break; - case kNuArchiveNuFXSelfEx: - hasSea = true; - break; - case kNuArchiveNuFXSelfExInBNY: - hasBinary2 = hasSea = true; - break; - default: - if (pArchive->headerOffset != 0 && - pArchive->headerOffset != pArchive->junkOffset) - { - Nu_ReportError(NU_BLOB, kNuErrNone, "Can't fix the wrapper??"); - err = kNuErrInternal; - goto bail; - } else - goto bail; - } - - err = Nu_FSeek(fp, pArchive->junkOffset, SEEK_SET); - BailError(err); - - if (hasBinary2) { - /* sanity check - make sure it's Binary II */ - Nu_ReadBytes(pArchive, fp, identBuf, kNufileIDLen); - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed reading BNY wrapper"); - goto bail; - } - if (memcmp(identBuf, kNuBinary2ID, sizeof(kNuBinary2ID)) != 0) { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, kNuErrNone,"Didn't find Binary II wrapper"); - goto bail; - } - - /* archiveLen includes the SEA wrapper, if any, but excludes junk */ - archiveLen = pArchive->newMasterHeader.mhMasterEOF + - (pArchive->headerOffset - pArchive->junkOffset) - - kNuBinary2BlockSize; - archiveLen512 = (archiveLen + 511) / 512; - - err = Nu_FSeek(fp, kNuBNYFileSizeLo - kNufileIDLen, SEEK_CUR); - BailError(err); - Nu_WriteTwo(pArchive, fp, (uint16_t)(archiveLen512 & 0xffff)); - - err = Nu_FSeek(fp, kNuBNYFileSizeHi - (kNuBNYFileSizeLo+2), SEEK_CUR); - BailError(err); - Nu_WriteTwo(pArchive, fp, (uint16_t)(archiveLen512 >> 16)); - - err = Nu_FSeek(fp, kNuBNYEOFLo - (kNuBNYFileSizeHi+2), SEEK_CUR); - BailError(err); - Nu_WriteTwo(pArchive, fp, (uint16_t)(archiveLen & 0xffff)); - Nu_WriteOne(pArchive, fp, (uint8_t)((archiveLen >> 16) & 0xff)); - - err = Nu_FSeek(fp, kNuBNYEOFHi - (kNuBNYEOFLo+3), SEEK_CUR); - BailError(err); - Nu_WriteOne(pArchive, fp, (uint8_t)(archiveLen >> 24)); - - err = Nu_FSeek(fp, kNuBNYDiskSpace - (kNuBNYEOFHi+1), SEEK_CUR); - BailError(err); - Nu_WriteFour(pArchive, fp, archiveLen512); - - /* probably ought to update "modified when" date/time field */ - - /* seek just past end of BNY wrapper */ - err = Nu_FSeek(fp, kNuBinary2BlockSize - (kNuBNYDiskSpace+4), SEEK_CUR); - BailError(err); - - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed updating Binary II wrapper"); - goto bail; - } - } - - if (hasSea) { - /* sanity check - make sure it's SEA */ - Nu_ReadBytes(pArchive, fp, identBuf, kNufileIDLen); - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed reading SEA wrapper"); - goto bail; - } - if (memcmp(identBuf, kNuSHKSEAID, sizeof(kNuSHKSEAID)) != 0) { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, kNuErrNone, "Didn't find SEA wrapper"); - goto bail; - } - - archiveLen = pArchive->newMasterHeader.mhMasterEOF; - - err = Nu_FSeek(fp, kNuSEAFunkySize - kNufileIDLen, SEEK_CUR); - BailError(err); - Nu_WriteFour(pArchive, fp, archiveLen + kNuSEAFunkyAdjust); - - err = Nu_FSeek(fp, kNuSEALength1 - (kNuSEAFunkySize+4), SEEK_CUR); - BailError(err); - Nu_WriteTwo(pArchive, fp, (uint16_t)archiveLen); - - err = Nu_FSeek(fp, kNuSEALength2 - (kNuSEALength1+2), SEEK_CUR); - BailError(err); - Nu_WriteTwo(pArchive, fp, (uint16_t)archiveLen); - - /* seek past end of SEA wrapper */ - err = Nu_FSeek(fp, kNuSEAOffset - (kNuSEALength2+2), SEEK_CUR); - BailError(err); - - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed updating SEA wrapper"); - goto bail; - } - } - -bail: - return kNuErrNone; -} - - -/* - * Adjust wrapper-induced padding on the archive. - * - * GS/ShrinkIt v1.1 does some peculiar things with SEA (Self-Extracting - * Archive) files. For no apparent reason, it always adds one extra 00 - * byte to the end. When you combine SEA and BXY to make BSE, it will - * leave that extra byte inside the BXY 128-byte padding area, UNLESS - * the archive itself happens to be exactly 128 bytes, in which case - * it throws the pad byte onto the end -- resulting in an archive that - * isn't an exact multiple of 128. - * - * I've chosen to emulate the 1-byte padding "feature" of GSHK, but I'm - * not going to try to emulate the quirky behavior described above. - * - * The SEA pad byte is added first, and then the 128-byte BXY padding - * is considered. In the odd case described above, the file would be - * 127 bytes larger with nufxlib than it is with GSHK. This shouldn't - * require additional disk space to be used, assuming a filesystem block - * size of at least 128 bytes. - */ -NuError Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp) -{ - NuError err = kNuErrNone; - Boolean hasBinary2, hasSea; - - hasBinary2 = hasSea = false; - - switch (pArchive->archiveType) { - case kNuArchiveNuFX: - goto bail; - case kNuArchiveNuFXInBNY: - hasBinary2 = true; - break; - case kNuArchiveNuFXSelfEx: - hasSea = true; - break; - case kNuArchiveNuFXSelfExInBNY: - hasBinary2 = hasSea = true; - break; - default: - if (pArchive->headerOffset != 0 && - pArchive->headerOffset != pArchive->junkOffset) - { - Nu_ReportError(NU_BLOB, kNuErrNone, "Can't check the padding??"); - err = kNuErrInternal; - goto bail; - } else - goto bail; - } - - err = Nu_FSeek(fp, 0, SEEK_END); - BailError(err); - - if (hasSea && pArchive->valMimicSHK) { - /* throw on a single pad byte, for no apparent reason whatsoever */ - Nu_WriteOne(pArchive, fp, 0); - } - - if (hasBinary2) { - /* pad out to the next 128-byte boundary */ - long curOffset; - - err = Nu_FTell(fp, &curOffset); - BailError(err); - curOffset -= pArchive->junkOffset; /* don't factor junk into account */ - - DBUG(("+++ BNY needs %ld bytes of padding\n", curOffset & 0x7f)); - if (curOffset & 0x7f) { - int i; - - for (i = kNuBinary2BlockSize - (curOffset & 0x7f); i > 0; i--) - Nu_WriteOne(pArchive, fp, 0); - } - } - - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed updating wrapper padding"); - goto bail; - } - -bail: - return err; -} - - -/* - * =========================================================================== - * Open an archive - * =========================================================================== - */ - -/* - * Read the master header from the archive file. - * - * This also handles skipping the first 128 bytes of a .BXY file and the - * front part of a self-extracting GSHK archive. - * - * We try to provide helpful messages about things that aren't archives, - * but try to stay silent about files that are other types of archives. - * That way, if the application is trying a series of libraries to find - * one that will accept the file, we don't generate spurious complaints. - * - * Since there's a fair possibility that whoever is opening this file is - * also interested in related formats, we try to return a meaningful error - * code for stuff we recognize (especially Binary II). - * - * If at first we don't succeed, we keep trying further along until we - * find something we recognize. We don't want to just scan for the - * NuFile ID, because that might prevent this from working properly with - * SEA archives which push the NuFX start out about 12K. We also wouldn't - * be able to update the BNY/SEA wrappers correctly. So, we inch our way - * along until we find something we recognize or get bored. - * - * On exit, the stream will be positioned just past the master header. - */ -static NuError Nu_ReadMasterHeader(NuArchive* pArchive) -{ - NuError err; - uint16_t crc; - FILE* fp; - NuMasterHeader* pHeader; - Boolean isBinary2 = false; - Boolean isSea = false; - - Assert(pArchive != NULL); - - fp = pArchive->archiveFp; /* saves typing */ - pHeader = &pArchive->masterHeader; - - pArchive->junkOffset = 0; - -retry: - pArchive->headerOffset = pArchive->junkOffset; - Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); - /* may have read fewer than kNufileIDLen; that's okay */ - - if (memcmp(pHeader->mhNufileID, kNuBinary2ID, sizeof(kNuBinary2ID)) == 0) - { - int count; - - /* looks like a Binary II archive, might be BXY or BSE; seek forward */ - err = Nu_SeekArchive(pArchive, fp, kNuBNYFilesToFollow - kNufileIDLen, - SEEK_CUR); - if (err != kNuErrNone) { - err = kNuErrNotNuFX; - /* probably too short to be BNY, so go ahead and whine */ - Nu_ReportError(NU_BLOB, kNuErrNone, - "Looks like a truncated Binary II archive?"); - goto bail; - } - - /* - * Check "files to follow", so we can be sure this isn't a BNY that - * just happened to have a .SHK as the first file. If it is, then - * any updates to the archive will trash the rest of the BNY files. - */ - count = Nu_ReadOne(pArchive, fp); - if (count != 0) { - err = kNuErrIsBinary2; - /*Nu_ReportError(NU_BLOB, kNuErrNone, - "This is a Binary II archive with %d files in it", count+1);*/ - DBUG(("This is a Binary II archive with %d files in it\n",count+1)); - goto bail; - } - - /* that was last item in BNY header, no need to seek */ - Assert(kNuBNYFilesToFollow == kNuBinary2BlockSize -1); - - isBinary2 = true; - pArchive->headerOffset += kNuBinary2BlockSize; - Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); - } - if (memcmp(pHeader->mhNufileID, kNuSHKSEAID, sizeof(kNuSHKSEAID)) == 0) - { - /* might be GSHK self-extracting; seek forward */ - err = Nu_SeekArchive(pArchive, fp, kNuSEAOffset - kNufileIDLen, - SEEK_CUR); - if (err != kNuErrNone) { - err = kNuErrNotNuFX; - Nu_ReportError(NU_BLOB, kNuErrNone, - "Looks like GS executable, not NuFX"); - goto bail; - } - - isSea = true; - pArchive->headerOffset += kNuSEAOffset; - Nu_ReadBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); - } - - if (memcmp(kNuMasterID, pHeader->mhNufileID, kNufileIDLen) != 0) { - /* - * Doesn't look like a NuFX archive. Scan forward and see if we - * can find the start past some leading junk. MacBinary headers - * and chunks of HTTP seem popular on FTP sites. - */ - if ((pArchive->openMode == kNuOpenRO || - pArchive->openMode == kNuOpenRW) && - pArchive->junkOffset < (long)pArchive->valJunkSkipMax) - { - pArchive->junkOffset++; - DBUG(("+++ scanning from offset %ld\n", pArchive->junkOffset)); - err = Nu_SeekArchive(pArchive, fp, pArchive->junkOffset, SEEK_SET); - BailError(err); - goto retry; - } - - err = kNuErrNotNuFX; - - if (isBinary2) { - err = kNuErrIsBinary2; - /*Nu_ReportError(NU_BLOB, kNuErrNone, - "Looks like Binary II, not NuFX");*/ - DBUG(("Looks like Binary II, not NuFX\n")); - } else if (isSea) - Nu_ReportError(NU_BLOB, kNuErrNone, - "Looks like GS executable, not NuFX"); - else if (Nu_HeaderIOFailed(pArchive, fp) != kNuErrNone) - Nu_ReportError(NU_BLOB, kNuErrNone, - "Couldn't read enough data, not NuFX?"); - else - Nu_ReportError(NU_BLOB, kNuErrNone, - "Not a NuFX archive? Got 0x%02x%02x%02x%02x%02x%02x...", - pHeader->mhNufileID[0], pHeader->mhNufileID[1], - pHeader->mhNufileID[2], pHeader->mhNufileID[3], - pHeader->mhNufileID[4], pHeader->mhNufileID[5]); - goto bail; - } - - if (pArchive->junkOffset != 0) { - DBUG(("+++ found apparent start of archive at offset %ld\n", - pArchive->junkOffset)); - } - - crc = 0; - pHeader->mhMasterCRC = Nu_ReadTwo(pArchive, fp); - pHeader->mhTotalRecords = Nu_ReadFourC(pArchive, fp, &crc); - pHeader->mhArchiveCreateWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); - pHeader->mhArchiveModWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); - pHeader->mhMasterVersion = Nu_ReadTwoC(pArchive, fp, &crc); - Nu_ReadBytesC(pArchive, fp, pHeader->mhReserved1, - kNufileMasterReserved1Len, &crc); - pHeader->mhMasterEOF = Nu_ReadFourC(pArchive, fp, &crc); - Nu_ReadBytesC(pArchive, fp, pHeader->mhReserved2, - kNufileMasterReserved2Len, &crc); - - /* check for errors in any of the above reads */ - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed reading master header"); - goto bail; - } - if (pHeader->mhMasterVersion > kNuMaxMHVersion) { - err = kNuErrBadMHVersion; - Nu_ReportError(NU_BLOB, err, "Bad Master Header version %u", - pHeader->mhMasterVersion); - goto bail; - } - - /* compare the CRC */ - if (!pArchive->valIgnoreCRC && crc != pHeader->mhMasterCRC) { - if (!Nu_ShouldIgnoreBadCRC(pArchive, NULL, kNuErrBadMHCRC)) { - err = kNuErrBadMHCRC; - Nu_ReportError(NU_BLOB, err, "Stored MH CRC=0x%04x, calc=0x%04x", - pHeader->mhMasterCRC, crc); - goto bail; - } - } - - /* - * Check for an unusual condition. GS/ShrinkIt appears to update - * the archive structure in the disk file periodically as it writes, - * so it's possible to get an apparently complete archive (with - * correct CRCs in the master and record headers!) that is actually - * only partially written. I did this by accident when archiving a - * 3.5" disk across a slow AppleTalk network. The only obvious - * indication of brain-damage, until you try to unpack the archive, - * seems to be a bogus MasterEOF==48. - * - * Matthew Fischer found some archives that exhibit MasterEOF==0 - * but are otherwise functional, suggesting that there might be a - * version of ShrinkIt that created these without reporting an error. - * One such archive was a disk image with no filename entry, suggesting - * that it was created by an early version of P8 ShrinkIt. - * - * So, we only fail if the EOF equals 48. - */ - if (pHeader->mhMasterEOF == kNuMasterHeaderSize) { - err = kNuErrNoRecords; - Nu_ReportError(NU_BLOB, err, - "Master EOF is %u, archive is probably truncated", - pHeader->mhMasterEOF); - goto bail; - } - - /* - * Set up a few things in the archive structure on our way out. - */ - if (isBinary2) { - if (isSea) - pArchive->archiveType = kNuArchiveNuFXSelfExInBNY; - else - pArchive->archiveType = kNuArchiveNuFXInBNY; - } else { - if (isSea) - pArchive->archiveType = kNuArchiveNuFXSelfEx; - else - pArchive->archiveType = kNuArchiveNuFX; - } - - if (isSea || isBinary2) { - DBUG(("--- Archive isSea=%d isBinary2=%d type=%d\n", - isSea, isBinary2, pArchive->archiveType)); - } - - /*pArchive->origNumRecords = pHeader->mhTotalRecords;*/ - pArchive->currentOffset = pArchive->headerOffset + kNuMasterHeaderSize; - - /*DBUG(("--- GOT: records=%ld, vers=%d, EOF=%ld, type=%d, hdrOffset=%ld\n", - pHeader->mhTotalRecords, pHeader->mhMasterVersion, - pHeader->mhMasterEOF, pArchive->archiveType, pArchive->headerOffset));*/ - - pHeader->isValid = true; - -bail: - return err; -} - - -/* - * Prepare the NuArchive and NuMasterHeader structures for use with a - * newly-created archive. - */ -static void Nu_InitNewArchive(NuArchive* pArchive) -{ - NuMasterHeader* pHeader; - - Assert(pArchive != NULL); - - pHeader = &pArchive->masterHeader; - - memcpy(pHeader->mhNufileID, kNuMasterID, kNufileIDLen); - /*pHeader->mhMasterCRC*/ - pHeader->mhTotalRecords = 0; - Nu_SetCurrentDateTime(&pHeader->mhArchiveCreateWhen); - /*pHeader->mhArchiveModWhen*/ - pHeader->mhMasterVersion = kNuOurMHVersion; - /*pHeader->mhReserved1*/ - pHeader->mhMasterEOF = kNuMasterHeaderSize; - /*pHeader->mhReserved2*/ - - pHeader->isValid = true; - - /* no need to use a temp file for a newly-created archive */ - pArchive->valModifyOrig = true; -} - - -/* - * Open an archive in streaming read-only mode. - */ -NuError Nu_StreamOpenRO(FILE* infp, NuArchive** ppArchive) -{ - NuError err; - NuArchive* pArchive = NULL; - - Assert(infp != NULL); - Assert(ppArchive != NULL); - - err = Nu_NuArchiveNew(ppArchive); - if (err != kNuErrNone) - goto bail; - pArchive = *ppArchive; - - pArchive->openMode = kNuOpenStreamingRO; - pArchive->archiveFp = infp; - pArchive->archivePathnameUNI = strdup("(stream)"); - - err = Nu_ReadMasterHeader(pArchive); - BailError(err); - -bail: - if (err != kNuErrNone) { - if (pArchive != NULL) - (void) Nu_NuArchiveFree(pArchive); - *ppArchive = NULL; - } - return err; -} - - -/* - * Open an archive in non-streaming read-only mode. - */ -NuError Nu_OpenRO(const UNICHAR* archivePathnameUNI, NuArchive** ppArchive) -{ - NuError err; - NuArchive* pArchive = NULL; - FILE* fp = NULL; - - if (archivePathnameUNI == NULL || !strlen(archivePathnameUNI) || - ppArchive == NULL) - { - return kNuErrInvalidArg; - } - - *ppArchive = NULL; - - fp = fopen(archivePathnameUNI, kNuFileOpenReadOnly); - if (fp == NULL) { - Nu_ReportError(NU_BLOB, errno, "Unable to open '%s'", - archivePathnameUNI); - err = kNuErrFileOpen; - goto bail; - } - - err = Nu_NuArchiveNew(ppArchive); - if (err != kNuErrNone) - goto bail; - pArchive = *ppArchive; - - pArchive->openMode = kNuOpenRO; - pArchive->archiveFp = fp; - fp = NULL; - pArchive->archivePathnameUNI = strdup(archivePathnameUNI); - - err = Nu_ReadMasterHeader(pArchive); - BailError(err); - -bail: - if (err != kNuErrNone) { - if (pArchive != NULL) { - (void) Nu_CloseAndFree(pArchive); - *ppArchive = NULL; - } - if (fp != NULL) - fclose(fp); - } - return err; -} - - -/* - * Open a temp file. If "fileName" contains six Xs ("XXXXXX"), it will - * be treated as a mktemp-style template, and modified before use (so - * pass a copy of the string in). - * - * Thought for the day: consider using Win32 SetFileAttributes() to make - * temp files hidden. We will need to un-hide it before rolling it over. - */ -static NuError Nu_OpenTempFile(UNICHAR* fileNameUNI, FILE** pFp) -{ - NuArchive* pArchive = NULL; /* dummy for NU_BLOB */ - NuError err = kNuErrNone; - int len; - - /* - * If this is a mktemp-style template, use mktemp or mkstemp to fill in - * the blanks. - * - * BUG: not all implementations of mktemp actually generate a unique - * name. We probably need to do probing here. Some BSD variants like - * to complain about mktemp, since it's generally a bad way to do - * things. - */ - len = strlen(fileNameUNI); - if (len > 6 && strcmp(fileNameUNI + len - 6, "XXXXXX") == 0) { -#if defined(HAVE_MKSTEMP) && defined(HAVE_FDOPEN) - int fd; - - DBUG(("+++ Using mkstemp\n")); - - /* this modifies the template *and* opens the file */ - fd = mkstemp(fileNameUNI); - if (fd < 0) { - err = errno ? errno : kNuErrFileOpen; - Nu_ReportError(NU_BLOB, kNuErrNone, "mkstemp failed on '%s'", - fileNameUNI); - goto bail; - } - - DBUG(("--- Fd-opening temp file '%s'\n", fileNameUNI)); - *pFp = fdopen(fd, kNuFileOpenReadWriteCreat); - if (*pFp == NULL) { - close(fd); - err = errno ? errno : kNuErrFileOpen; - goto bail; - } - - /* file is open, we're done */ - goto bail; - -#else - char* result; - - DBUG(("+++ Using mktemp\n")); - result = mktemp(fileNameUNI); - if (result == NULL) { - Nu_ReportError(NU_BLOB, kNuErrNone, "mktemp failed on '%s'", - fileNameUNI); - err = kNuErrInternal; - goto bail; - } - - /* now open the filename as usual */ -#endif - } - - DBUG(("--- Opening temp file '%s'\n", fileNameUNI)); - -#if defined(HAVE_FDOPEN) - { - int fd; - - fd = open(fileNameUNI, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0600); - if (fd < 0) { - err = errno ? errno : kNuErrFileOpen; - goto bail; - } - - *pFp = fdopen(fd, kNuFileOpenReadWriteCreat); - if (*pFp == NULL) { - close(fd); - err = errno ? errno : kNuErrFileOpen; - goto bail; - } - } -#else - if (access(fileNameUNI, F_OK) == 0) { - err = kNuErrFileExists; - goto bail; - } - - *pFp = fopen(fileNameUNI, kNuFileOpenReadWriteCreat); - if (*pFp == NULL) { - err = errno ? errno : kNuErrFileOpen; - goto bail; - } -#endif - - -bail: - return err; -} - -/* - * Open an archive in read-write mode, optionally creating it if it doesn't - * exist. - */ -NuError Nu_OpenRW(const UNICHAR* archivePathnameUNI, - const UNICHAR* tmpPathnameUNI, uint32_t flags, NuArchive** ppArchive) -{ - NuError err; - FILE* fp = NULL; - FILE* tmpFp = NULL; - NuArchive* pArchive = NULL; - char* tmpPathDup = NULL; - Boolean archiveExists; - Boolean newlyCreated; - - if (archivePathnameUNI == NULL || !strlen(archivePathnameUNI) || - tmpPathnameUNI == NULL || !strlen(tmpPathnameUNI) || - ppArchive == NULL || (flags & ~(kNuOpenCreat|kNuOpenExcl)) != 0) - { - return kNuErrInvalidArg; - } - - archiveExists = (access(archivePathnameUNI, F_OK) == 0); - - /* - * Open or create archive file. - */ - if (archiveExists) { - if ((flags & kNuOpenCreat) && (flags & kNuOpenExcl)) { - err = kNuErrFileExists; - Nu_ReportError(NU_BLOB, err, "File '%s' exists", - archivePathnameUNI); - goto bail; - } - fp = fopen(archivePathnameUNI, kNuFileOpenReadWrite); - newlyCreated = false; - } else { - if (!(flags & kNuOpenCreat)) { - err = kNuErrFileNotFound; - Nu_ReportError(NU_BLOB, err, "File '%s' not found", - archivePathnameUNI); - goto bail; - } - fp = fopen(archivePathnameUNI, kNuFileOpenReadWriteCreat); - newlyCreated = true; - } - - if (fp == NULL) { - if (errno == EACCES) - err = kNuErrFileAccessDenied; - else - err = kNuErrFileOpen; - Nu_ReportError(NU_BLOB, errno, "Unable to open '%s'", - archivePathnameUNI); - goto bail; - } - - /* - * Treat zero-length files as newly-created archives. - */ - if (archiveExists && !newlyCreated) { - long length; - - err = Nu_GetFileLength(NULL, fp, &length); - BailError(err); - - if (!length) { - DBUG(("--- treating zero-length file as newly created archive\n")); - newlyCreated = true; - } - } - - /* - * Create a temp file. We don't need one for a newly-created archive, - * at least not right away. It's possible the caller could add some - * files, flush the changes, and then want to delete them without - * closing and reopening the archive. - * - * So, create a temp file whether we think we need one or not. Won't - * do any harm, and might save us some troubles later. - */ - tmpPathDup = strdup(tmpPathnameUNI); - BailNil(tmpPathDup); - err = Nu_OpenTempFile(tmpPathDup, &tmpFp); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed opening temp file '%s'", - tmpPathnameUNI); - goto bail; - } - - err = Nu_NuArchiveNew(ppArchive); - if (err != kNuErrNone) - goto bail; - pArchive = *ppArchive; - - pArchive->openMode = kNuOpenRW; - pArchive->newlyCreated = newlyCreated; - pArchive->archivePathnameUNI = strdup(archivePathnameUNI); - pArchive->archiveFp = fp; - fp = NULL; - pArchive->tmpFp = tmpFp; - tmpFp = NULL; - pArchive->tmpPathnameUNI = tmpPathDup; - tmpPathDup = NULL; - - if (archiveExists && !newlyCreated) { - err = Nu_ReadMasterHeader(pArchive); - BailError(err); - } else { - Nu_InitNewArchive(pArchive); - } - -bail: - if (err != kNuErrNone) { - if (pArchive != NULL) { - (void) Nu_CloseAndFree(pArchive); - *ppArchive = NULL; - } - if (fp != NULL) - fclose(fp); - if (tmpFp != NULL) - fclose(tmpFp); - if (tmpPathDup != NULL) - Nu_Free(pArchive, tmpPathDup); - } - return err; -} - - -/* - * =========================================================================== - * Update an archive - * =========================================================================== - */ - -/* - * Write the NuFX master header at the current offset. - */ -NuError Nu_WriteMasterHeader(NuArchive* pArchive, FILE* fp, - NuMasterHeader* pHeader) -{ - NuError err; - long crcOffset; - uint16_t crc; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pHeader != NULL); - Assert(pHeader->isValid); - Assert(pHeader->mhMasterVersion == kNuOurMHVersion); - - crc = 0; - - Nu_WriteBytes(pArchive, fp, pHeader->mhNufileID, kNufileIDLen); - err = Nu_FTell(fp, &crcOffset); - BailError(err); - Nu_WriteTwo(pArchive, fp, 0); - Nu_WriteFourC(pArchive, fp, pHeader->mhTotalRecords, &crc); - Nu_WriteDateTimeC(pArchive, fp, pHeader->mhArchiveCreateWhen, &crc); - Nu_WriteDateTimeC(pArchive, fp, pHeader->mhArchiveModWhen, &crc); - Nu_WriteTwoC(pArchive, fp, pHeader->mhMasterVersion, &crc); - Nu_WriteBytesC(pArchive, fp, pHeader->mhReserved1, - kNufileMasterReserved1Len, &crc); - Nu_WriteFourC(pArchive, fp, pHeader->mhMasterEOF, &crc); - Nu_WriteBytesC(pArchive, fp, pHeader->mhReserved2, - kNufileMasterReserved2Len, &crc); - - /* go back and write the CRC (sadly, the seek will flush the stdio buf) */ - pHeader->mhMasterCRC = crc; - err = Nu_FSeek(fp, crcOffset, SEEK_SET); - BailError(err); - Nu_WriteTwo(pArchive, fp, pHeader->mhMasterCRC); - - /* check for errors in any of the above writes */ - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed writing master header"); - goto bail; - } - - DBUG(("--- Master header written successfully at %ld (crc=0x%04x)\n", - crcOffset - kNufileIDLen, crc)); - -bail: - return err; -} - - -/* - * =========================================================================== - * Close an archive - * =========================================================================== - */ - -/* - * Close all open files, and free the memory associated with the structure. - * - * If it's a brand-new archive, and we didn't add anything to it, then we - * want to remove the stub archive file. - */ -static void Nu_CloseAndFree(NuArchive* pArchive) -{ - if (pArchive->archiveFp != NULL) { - DBUG(("--- Closing archive\n")); - fclose(pArchive->archiveFp); - pArchive->archiveFp = NULL; - } - - if (pArchive->tmpFp != NULL) { - DBUG(("--- Closing and removing temp file\n")); - fclose(pArchive->tmpFp); - pArchive->tmpFp = NULL; - Assert(pArchive->tmpPathnameUNI != NULL); - if (remove(pArchive->tmpPathnameUNI) != 0) { - Nu_ReportError(NU_BLOB, errno, "Unable to remove temp file '%s'", - pArchive->tmpPathnameUNI); - /* keep going */ - } - } - - if (pArchive->newlyCreated && Nu_RecordSet_IsEmpty(&pArchive->origRecordSet)) - { - DBUG(("--- Newly-created archive unmodified; removing it\n")); - if (remove(pArchive->archivePathnameUNI) != 0) { - Nu_ReportError(NU_BLOB, errno, "Unable to remove archive file '%s'", - pArchive->archivePathnameUNI); - } - } - - Nu_NuArchiveFree(pArchive); -} - -/* - * Flush pending changes to the archive, then close it. - */ -NuError Nu_Close(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - uint32_t flushStatus; - - Assert(pArchive != NULL); - - if (!Nu_IsReadOnly(pArchive)) - err = Nu_Flush(pArchive, &flushStatus); - if (err == kNuErrNone) - Nu_CloseAndFree(pArchive); - else { - DBUG(("--- Close NuFlush status was 0x%4lx\n", flushStatus)); - } - - if (err != kNuErrNone) { - DBUG(("--- Nu_Close returning error %d\n", err)); - } - return err; -} - - -/* - * =========================================================================== - * Delete and replace an archive - * =========================================================================== - */ - -/* - * Delete the archive file, which should already have been closed. - */ -NuError Nu_DeleteArchiveFile(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - Assert(pArchive->archiveFp == NULL); - Assert(pArchive->archivePathnameUNI != NULL); - - return Nu_DeleteFile(pArchive->archivePathnameUNI); -} - -/* - * Rename the temp file on top of the original archive. The temp file - * should be closed, and the archive file should be deleted. - */ -NuError Nu_RenameTempToArchive(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - Assert(pArchive->archiveFp == NULL); - Assert(pArchive->tmpFp == NULL); - Assert(pArchive->archivePathnameUNI != NULL); - Assert(pArchive->tmpPathnameUNI != NULL); - - return Nu_RenameFile(pArchive->tmpPathnameUNI, - pArchive->archivePathnameUNI); -} - diff --git a/ciderpress/nufxlib/ArchiveIO.c b/ciderpress/nufxlib/ArchiveIO.c deleted file mode 100644 index 893584b..0000000 --- a/ciderpress/nufxlib/ArchiveIO.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Functions for reading from and writing to the archive. These are - * specialized functions that deal with byte ordering and CRC computation. - * The functions associated with reading from an archive work equally well - * with streaming archives. - */ -#include "NufxLibPriv.h" - - -/* this makes valgrind and purify happy, at some tiny cost in speed */ -#define CLEAN_INIT =0 -/*#define CLEAN_INIT */ - - -/* - * =========================================================================== - * Read and write - * =========================================================================== - */ - -/* - * Read one byte, optionally computing a CRC. - */ -uint8_t Nu_ReadOneC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc) -{ - int ic; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - - return (uint8_t) ic; -} - -uint8_t Nu_ReadOne(NuArchive* pArchive, FILE* fp) -{ - uint16_t dummyCrc CLEAN_INIT; - return Nu_ReadOneC(pArchive, fp, &dummyCrc); -} - -/* - * Write one byte, optionally computing a CRC. - */ -void Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uint8_t val, uint16_t* pCrc) -{ - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - putc(val, fp); -} - -void Nu_WriteOne(NuArchive* pArchive, FILE* fp, uint8_t val) -{ - uint16_t dummyCrc CLEAN_INIT; - Nu_WriteOneC(pArchive, fp, val, &dummyCrc); -} - - -/* - * Read two little-endian bytes, optionally computing a CRC. - */ -uint16_t Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc) -{ - int ic1, ic2; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic1 = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic1, *pCrc); - ic2 = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic2, *pCrc); - - return ic1 | ic2 << 8; -} - -uint16_t Nu_ReadTwo(NuArchive* pArchive, FILE* fp) -{ - uint16_t dummyCrc CLEAN_INIT; - return Nu_ReadTwoC(pArchive, fp, &dummyCrc); -} - - -/* - * Write two little-endian bytes, optionally computing a CRC. - */ -void Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, uint16_t val, uint16_t* pCrc) -{ - int ic1, ic2; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic1 = val & 0xff; - *pCrc = Nu_UpdateCRC16((uint8_t)ic1, *pCrc); - ic2 = val >> 8; - *pCrc = Nu_UpdateCRC16((uint8_t)ic2, *pCrc); - - putc(ic1, fp); - putc(ic2, fp); -} - -void Nu_WriteTwo(NuArchive* pArchive, FILE* fp, uint16_t val) -{ - uint16_t dummyCrc CLEAN_INIT; - Nu_WriteTwoC(pArchive, fp, val, &dummyCrc); -} - - -/* - * Read four little-endian bytes, optionally computing a CRC. - */ -uint32_t Nu_ReadFourC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc) -{ - int ic1, ic2, ic3, ic4; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic1 = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic1, *pCrc); - ic2 = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic2, *pCrc); - ic3 = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic3, *pCrc); - ic4 = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic4, *pCrc); - - return ic1 | ic2 << 8 | (uint32_t)ic3 << 16 | (uint32_t)ic4 << 24; -} - -uint32_t Nu_ReadFour(NuArchive* pArchive, FILE* fp) -{ - uint16_t dummyCrc CLEAN_INIT; - return Nu_ReadFourC(pArchive, fp, &dummyCrc); -} - - -/* - * Write four little-endian bytes, optionally computing a CRC. - */ -void Nu_WriteFourC(NuArchive* pArchive, FILE* fp, uint32_t val, uint16_t* pCrc) -{ - int ic1, ic2, ic3, ic4; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic1 = val & 0xff; - *pCrc = Nu_UpdateCRC16((uint8_t)ic1, *pCrc); - ic2 = (val >> 8) & 0xff; - *pCrc = Nu_UpdateCRC16((uint8_t)ic2, *pCrc); - ic3 = (val >> 16) & 0xff; - *pCrc = Nu_UpdateCRC16((uint8_t)ic3, *pCrc); - ic4 = val >> 24; - *pCrc = Nu_UpdateCRC16((uint8_t)ic4, *pCrc); - - putc(ic1, fp); - putc(ic2, fp); - putc(ic3, fp); - putc(ic4, fp); -} - -void Nu_WriteFour(NuArchive* pArchive, FILE* fp, uint32_t val) -{ - uint16_t dummyCrc CLEAN_INIT; - Nu_WriteFourC(pArchive, fp, val, &dummyCrc); -} - - -/* - * Read an 8-byte NuFX Date/Time structure. - * - * I've chosen *not* to filter away the Y2K differences between P8 ShrinkIt - * and GS/ShrinkIt. It's easy enough to deal with, and I figure the less - * messing-with, the better. - */ -NuDateTime Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc) -{ - NuDateTime temp; - int ic; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.second = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.minute = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.hour = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.year = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.day = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.month = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.extra = ic; - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - temp.weekDay = ic; - - return temp; -} - -NuDateTime Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, uint16_t* pCrc) -{ - uint16_t dummyCrc CLEAN_INIT; - return Nu_ReadDateTimeC(pArchive, fp, &dummyCrc); -} - - -/* - * Write an 8-byte NuFX Date/Time structure. - */ -void Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime, - uint16_t* pCrc) -{ - int ic; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - ic = dateTime.second; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.minute; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.hour; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.year; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.day; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.month; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.extra; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - ic = dateTime.weekDay; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); -} - -void Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime) -{ - uint16_t dummyCrc CLEAN_INIT; - Nu_WriteDateTimeC(pArchive, fp, dateTime, &dummyCrc); -} - - -/* - * Read N bytes from the stream, optionally computing a CRC. - */ -void Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count, - uint16_t* pCrc) -{ - uint8_t* buffer = vbuffer; - int ic; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - Assert(buffer != NULL); - Assert(count > 0); - - while (count--) { - ic = getc(fp); - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - *buffer++ = ic; - } -} - -void Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count) -{ - uint16_t dummyCrc CLEAN_INIT; - Nu_ReadBytesC(pArchive, fp, vbuffer, count, &dummyCrc); -} - - -/* - * Write N bytes to the stream, optionally computing a CRC. - */ -void Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer, - long count, uint16_t* pCrc) -{ - const uint8_t* buffer = vbuffer; - int ic; - - Assert(pArchive != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - Assert(buffer != NULL); - Assert(count > 0); - - while (count--) { - ic = *buffer++; - *pCrc = Nu_UpdateCRC16((uint8_t)ic, *pCrc); - putc(ic, fp); - } -} - -void Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer, - long count) -{ - uint16_t dummyCrc CLEAN_INIT; - Nu_WriteBytesC(pArchive, fp, vbuffer, count, &dummyCrc); -} - - -/* - * =========================================================================== - * General - * =========================================================================== - */ - -/* - * Determine whether the stream completed the last set of operations - * successfully. - */ -NuError Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp) -{ - if (feof(fp) || ferror(fp)) - return kNuErrFile; - else - return kNuErrNone; -} - - -/* - * Seek around in an archive file. If this is a streaming-mode archive, - * we only allow forward relative seeks, which are emulated with read calls. - * - * The values for "ptrname" are the same as for fseek(). - */ -NuError Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset, int ptrname) -{ - if (Nu_IsStreaming(pArchive)) { - Assert(ptrname == SEEK_CUR); - Assert(offset >= 0); - - /* OPT: might be faster to fread a chunk at a time */ - while (offset--) - (void) getc(fp); - - if (ferror(fp) || feof(fp)) - return kNuErrFileSeek; - } else { - if (fseek(fp, offset, ptrname) < 0) - return kNuErrFileSeek; - } - - return kNuErrNone; -} - - -/* - * Rewind an archive to the start of NuFX record data. - * - * Note that rewind(3S) resets the error indication, but this doesn't. - */ -NuError Nu_RewindArchive(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - Assert(!Nu_IsStreaming(pArchive)); - - if (Nu_SeekArchive(pArchive, pArchive->archiveFp, - pArchive->headerOffset + kNuMasterHeaderSize, SEEK_SET) != 0) - return kNuErrFileSeek; - - pArchive->currentOffset = pArchive->headerOffset + kNuMasterHeaderSize; - - return kNuErrNone; -} - diff --git a/ciderpress/nufxlib/Bzip2.c b/ciderpress/nufxlib/Bzip2.c deleted file mode 100644 index 6515e21..0000000 --- a/ciderpress/nufxlib/Bzip2.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Support for the "bzip2" (BTW+Huffman) algorithm, via "libbz2". - * - * This compression format is totally unsupported on the Apple II. This - * is provided primarily for the benefit of Apple II emulators that want - * a better storage format for disk images than SHK+LZW or a ZIP file. - * - * This code was developed and tested with libz2 version 1.0.2. Visit - * http://sources.redhat.com/bzip2/ for more information. - */ -#include "NufxLibPriv.h" - -#ifdef ENABLE_BZIP2 -#include "bzlib.h" - -#define kBZBlockSize 8 /* use 800K blocks */ -#define kBZVerbosity 1 /* library verbosity level (0-4) */ - - -/* - * Alloc and free functions provided to libbz2. - */ -static void* Nu_bzalloc(void* opaque, int items, int size) -{ - return Nu_Malloc(opaque, items * size); -} -static void Nu_bzfree(void* opaque, void* address) -{ - Nu_Free(opaque, address); -} - - -/* - * =========================================================================== - * Compression - * =========================================================================== - */ - -/* - * Compress "srcLen" bytes from "pStraw" to "fp". - */ -NuError Nu_CompressBzip2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - bz_stream bzstream; - int bzerr; - uint8_t* outbuf = NULL; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - Assert(fp != NULL); - Assert(srcLen > 0); - Assert(pDstLen != NULL); - Assert(pCrc != NULL); - - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - /* allocate a similarly-sized buffer for the output */ - outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); - BailAlloc(outbuf); - - /* - * Initialize the bz2lib stream. - */ - bzstream.bzalloc = Nu_bzalloc; - bzstream.bzfree = Nu_bzfree; - bzstream.opaque = pArchive; - bzstream.next_in = NULL; - bzstream.avail_in = 0; - bzstream.next_out = outbuf; - bzstream.avail_out = kNuGenCompBufSize; - - /* fourth arg is "workFactor"; set to zero for default (30) */ - bzerr = BZ2_bzCompressInit(&bzstream, kBZBlockSize, kBZVerbosity, 0); - if (bzerr != BZ_OK) { - err = kNuErrInternal; - if (bzerr == BZ_CONFIG_ERROR) { - Nu_ReportError(NU_BLOB, err, "error configuring bz2lib"); - } else { - Nu_ReportError(NU_BLOB, err, - "call to BZ2_bzCompressInit failed (bzerr=%d)", bzerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - uint32_t getSize; - int action; - - /* should be able to read a full buffer every time */ - if (bzstream.avail_in == 0 && srcLen) { - getSize = (srcLen > kNuGenCompBufSize) ? kNuGenCompBufSize : srcLen; - DBUG(("+++ reading %ld bytes\n", getSize)); - - err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "bzip2 read failed"); - goto bz_bail; - } - - srcLen -= getSize; - - *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getSize); - - bzstream.next_in = pArchive->compBuf; - bzstream.avail_in = getSize; - } - - if (srcLen == 0) - action = BZ_FINISH; /* tell libbz2 that we're done */ - else - action = BZ_RUN; /* more to come! */ - - bzerr = BZ2_bzCompress(&bzstream, action); - if (bzerr != BZ_RUN_OK && bzerr != BZ_FINISH_OK && bzerr != BZ_STREAM_END) - { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, err, - "libbz2 compress call failed (bzerr=%d)", bzerr); - goto bz_bail; - } - - /* write when we're full or when we're done */ - if (bzstream.avail_out == 0 || - (bzerr == BZ_STREAM_END && bzstream.avail_out != kNuGenCompBufSize)) - { - DBUG(("+++ writing %d bytes\n", - (uint8_t*)bzstream.next_out - outbuf)); - err = Nu_FWrite(fp, outbuf, (uint8_t*)bzstream.next_out - outbuf); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "fwrite failed in bzip2"); - goto bz_bail; - } - - bzstream.next_out = outbuf; - bzstream.avail_out = kNuGenCompBufSize; - } - } while (bzerr != BZ_STREAM_END); - - *pDstLen = bzstream.total_out_lo32; - Assert(bzstream.total_out_hi32 == 0); /* no huge files for us */ - -bz_bail: - BZ2_bzCompressEnd(&bzstream); /* free up any allocated structures */ - -bail: - if (outbuf != NULL) - Nu_Free(NULL, outbuf); - return err; -} - - -/* - * =========================================================================== - * Expansion - * =========================================================================== - */ - -/* - * Expand from "infp" to "pFunnel". - */ -NuError Nu_ExpandBzip2(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - bz_stream bzstream; - int bzerr; - uint32_t compRemaining; - uint8_t* outbuf; - - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(infp != NULL); - Assert(pFunnel != NULL); - - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - /* allocate a similarly-sized buffer for the output */ - outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); - BailAlloc(outbuf); - - compRemaining = pThread->thCompThreadEOF; - - /* - * Initialize the libbz2 stream. - */ - bzstream.bzalloc = Nu_bzalloc; - bzstream.bzfree = Nu_bzfree; - bzstream.opaque = pArchive; - bzstream.next_in = NULL; - bzstream.avail_in = 0; - bzstream.next_out = outbuf; - bzstream.avail_out = kNuGenCompBufSize; - - /* third arg is "small" (set nonzero to reduce mem) */ - bzerr = BZ2_bzDecompressInit(&bzstream, kBZVerbosity, 0); - if (bzerr != BZ_OK) { - err = kNuErrInternal; - if (bzerr == BZ_CONFIG_ERROR) { - Nu_ReportError(NU_BLOB, err, "error configuring libbz2"); - } else { - Nu_ReportError(NU_BLOB, err, - "call to BZ2_bzDecompressInit failed (bzerr=%d)", bzerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - uint32_t getSize; - - /* read as much as we can */ - if (bzstream.avail_in == 0) { - getSize = (compRemaining > kNuGenCompBufSize) ? - kNuGenCompBufSize : compRemaining; - DBUG(("+++ reading %ld bytes (%ld left)\n", getSize, - compRemaining)); - - err = Nu_FRead(infp, pArchive->compBuf, getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "bzip2 read failed"); - goto bz_bail; - } - - compRemaining -= getSize; - - bzstream.next_in = pArchive->compBuf; - bzstream.avail_in = getSize; - } - - /* uncompress the data */ - bzerr = BZ2_bzDecompress(&bzstream); - if (bzerr != BZ_OK && bzerr != BZ_STREAM_END) { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, err, - "libbz2 decompress call failed (bzerr=%d)", bzerr); - goto bz_bail; - } - - /* write every time there's anything (buffer will usually be full) */ - if (bzstream.avail_out != kNuGenCompBufSize) { - DBUG(("+++ writing %d bytes\n", - (uint8_t*) bzstream.next_out - outbuf)); - err = Nu_FunnelWrite(pArchive, pFunnel, outbuf, - (uint8_t*)bzstream.next_out - outbuf); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "write failed in bzip2"); - goto bz_bail; - } - - if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, outbuf, - (uint8_t*) bzstream.next_out - outbuf); - - bzstream.next_out = outbuf; - bzstream.avail_out = kNuGenCompBufSize; - } - } while (bzerr == BZ_OK); - - Assert(bzerr == BZ_STREAM_END); /* other errors should've been caught */ - - Assert(bzstream.total_out_hi32 == 0); /* no huge files for us */ - - if (bzstream.total_out_lo32 != pThread->actualThreadEOF) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, - "size mismatch on expanded bzip2 file (%d vs %ld)", - bzstream.total_out_lo32, pThread->actualThreadEOF); - goto bz_bail; - } - -bz_bail: - BZ2_bzDecompressEnd(&bzstream); /* free up any allocated structures */ - -bail: - if (outbuf != NULL) - Nu_Free(NULL, outbuf); - return err; -} - -#endif /*ENABLE_BZIP2*/ diff --git a/ciderpress/nufxlib/CMakeLists.txt b/ciderpress/nufxlib/CMakeLists.txt deleted file mode 100644 index af17c50..0000000 --- a/ciderpress/nufxlib/CMakeLists.txt +++ /dev/null @@ -1,63 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -set(CMAKE_BUILD_TYPE DEBUG) - -set(BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) - -set(PROJECT_NAME nufx) -set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR}) -project(${PROJECT_NAME}) - - -set(ALL_DEFINES " " ) -set(DEBUG_OPT "-D_DEBUG -DDEBUG -O0 -g3 " ) -set(RELEASE_OPT "-O3 " ) - -set(CMAKE_C_FLAGS "-Wall ${ALL_DEFINES} ") -set(CMAKE_CXX_FLAGS "-Wall ${ALL_DEFINES} ") - -set(CMAKE_CXX_FLAGS_DEBUG "${DEBUG_OPT} ") -set(CMAKE_CXX_FLAGS_RELEASE "${RELEASE_OPT}") -set(CMAKE_C_FLAGS_DEBUG "${DEBUG_OPT} ") -set(CMAKE_C_FLAGS_RELEASE "${RELEASE_OPT}") - -set(FIND_LIBRARY_USE_LIB64_PATHS TRUE) - -set (SOURCE -Archive.c -ArchiveIO.c -Bzip2.c -Charset.c -Compress.c -Crc16.c -Debug.c -Deferred.c -Deflate.c -Entry.c -Expand.c -FileIO.c -Funnel.c -Lzc.c -Lzw.c -MiscStuff.c -MiscUtils.c -Record.c -SourceSink.c -Squeeze.c -Thread.c -Value.c -Version.c -) - -include_directories(BEFORE - ${PROJECT_ROOT} -) - -add_library( ${PROJECT_NAME} SHARED ${SOURCE}) -add_library( ${PROJECT_NAME}_static STATIC ${SOURCE}) - -target_link_libraries ( -${PROJECT_NAME} -) - - - diff --git a/ciderpress/nufxlib/COPYING-LIB b/ciderpress/nufxlib/COPYING-LIB deleted file mode 100644 index d27a925..0000000 --- a/ciderpress/nufxlib/COPYING-LIB +++ /dev/null @@ -1,29 +0,0 @@ -Copyright (C) 2007, Andy McFadden. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the copyright holder nor the names of project - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, -OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/ciderpress/nufxlib/ChangeLog.txt b/ciderpress/nufxlib/ChangeLog.txt deleted file mode 100644 index 628bb6f..0000000 --- a/ciderpress/nufxlib/ChangeLog.txt +++ /dev/null @@ -1,310 +0,0 @@ -2017/09/21 ***** v3.1.0 shipped ***** - -2016/01/11 fadden - - Fix handling of disk images (broken by previous change). - -2015/12/26 fadden - - Fix handling of entries with missing threads. - - Improve handling of Mac OS X file type attributes. - -2015/01/09 ***** v3.0.0 shipped ***** - -2015/01/03 fadden - - Mac OS X: replace Carbon FinderInfo calls with BSD xattr. - - Mac OS X: fix resource fork naming. - - Mac OS X: disable use of native resource forks. - -2015/01/02 fadden - - Distinguish Unicode and Mac OS Roman strings. - -2014/12/22 fadden - - Source code cleanup. - -2014/10/30 ***** v2.2.2 shipped ***** - -2014/10/28 fadden - - Switched from CVS on sourceforge to github. - - Updated configure scripts and makefiles. - -2007/02/19 ***** v2.2.0 shipped ***** - -2007/02/19 fadden - - Auto-detect and handle "bad Mac" archives. - - Switched from LGPL to BSD license. - -2006/12/02 fadden - - Check for overrun when unpacking RLE. - -2006/02/18 ***** v2.1.1 shipped ***** - -2006/02/18 fadden - - Correct a wayward assert. (Changing the filetype of a file from an - HFS disk, which has zero-length data fork, falsely triggered the - assert.) - -2005/09/17 ***** v2.1.0 shipped ***** - -2005/09/17 fadden - - Added "kNuValIgnoreLZW2Len" flag, which enables NuLib2 to handle - archives created by an unknown but badly broken program. - - Fixed build for gcc v4.0. - -2004/10/11 ***** v2.0.3 shipped ***** - -2004/09/25 fadden - - Fixed: attempting to add files after deleting *all* entries in an - archive would fail. - - Removed use of a "ushort" from NufxLib.h. - -2004/09/20 fadden - - Corrected behavior after flush when original archive can't be - deleted. - -2004/09/09 fadden - - Added header offset and junk offset to NuGetAttr. - -2004/08/22 fadden - - Fixed obscure bug when recompressing a GSHK-added zero-length file - when "fake threads" is enabled. - -2004/03/10 ***** v2.0.2 shipped ***** - -2004/03/09 fadden - - Set access permissions based on umask when extracting a "locked" - file. My thanks to Matthew Fischer for sending a patch. - - Reject archives with a MasterEOF == 48, not <= 48. There are - some otherwise valid archives created by an old version of ShrinkIt - that have MasterEOF==0. - -2003/10/16 ***** v2.0.1 shipped ***** - -2003/10/16 fadden - - Added workaround for bad HFS option lists created by GSHK. - - Added junk-skipping feature. Up to 1024 bytes of crud (e.g. - MacBinary headers or HTTP remnants) will be searched for evidence - of an archive. - -2003/06/19 sheppy - - Added support for resource forks and file and aux types when built - for Mac OS X. - -2003/03/18 ***** v2.0.0 shipped ***** - -2003/03/10 fadden - - Added support for automatic high-ASCII text stripping. - -2003/02/23 fadden - - Added test-twirl to samples. - -2003/02/22 fadden - - Turn off EOL conversion when extracting disk images. - - Added NuTestRecord(). - -2003/02/18 fadden - - Added "original pathname" fields to NuFileDetails and NuErrorStatus. - - Changed callback setters to return NuCallback instead of NuError. - - Switched to case-sensitive filename comparisons. - -2003/02/08 fadden - - Upped version to v2.0.0. - - Changed DataSource API. Removed "doClose" and added an optional - callback function that handles releasing of resources. Necessary - to make Win32 DLLs work right with unsuspecting apps. - - Changed DataSource "copy" function to use refcounting. Still - not quite right, but it'll do for now. Memory leaks in DataSource - handling appear to be fixed. (I love valgrind.) - -2003/01/10 fadden - - Added version numbers to header. - - Added kNuValueMaskThreadless to control handling of "threadless" - records. Now records without threads can be silently "fixed" so - the application does need to handle them specially. - -2002/12/06 fadden - - Made changes to allow NufxLib to be built as a Win32 DLL. - -2002/10/20 ***** v1.1.0 shipped ***** - -2002/10/10 fadden - - changed behavior so that deleting all records is allowed - -2002/10/09 fadden - - added support for "bzip2" compression via libbz2 - - added ability to selectively disable compression methods - - added "-m" flag to samples/launder so you can specify compression - -2002/09/30 fadden - - added support for "deflate" compression via zlib - -2002/09/27 fadden - - added support for 12-bit and 16-bit LZC (UNIX compress) - -2002/09/26 fadden - - added support for SQueezed files (both compress and expand) - -2002/09/23 fadden - - ran the code through valgrind; found and fixed some minor bugs - -2002/09/20 fadden - - pulled the sources out and started fiddling with them again - - changed hard tabs to spaces - -2000/05/22 ***** v1.0.1 shipped ***** - -2000/05/22 fadden - - added workaround for buggy 140K DOS3.3 GSHK images - -2000/05/18 ***** v1.0.0 shipped ***** - -2000/05/18 fadden - - updated version information to indicate final release - -2000/03/25 ***** v0.6.1 shipped ***** - -2000/03/25 fadden - - Sheppy says Mac OS X PPC v1.02 and v1.2 work with minor SysDefs tweak - -2000/03/05 ***** v0.6.0 (beta) shipped ***** - -2000/03/05 fadden - - modified NuOpenRW to call mktemp or mkstemp if tmpPath looks like - a template - - removed DEBUG_MSGS from default CFLAGS - - updated version information to indicate beta release - -2000/02/24 ***** v0.5.1 shipped ***** - -2000/02/20 changes from Scott Blackman - - portability fixes for DJGPP under Win95 - -2000/02/17 changes from Devin Reade - - portability fixes for BSD, AIX, and others - -2000/02/09 ***** v0.5.0 (alpha) shipped ***** - -2000/02/08 fadden - - tweaked the BeOS/PPC config around a little - - deleted some commas to make "gcc -pendantic" happy - -2000/02/06 fadden - - include @CFLAGS@ in case somebody wants to override them - -2000/02/06 ***** v0.4.0b shipped ***** - -2000/02/06 fadden - - added "install-shared" make target - - portability fixes for HP/UX - - configure.in test for presence of snprintf/vsnprintf declarations - -2000/02/06 ***** v0.4.0a shipped ***** - -2000/02/06 fadden - - massaged configure.in for BeOS, and added some type casts for mwerks - -2000/02/06 ***** v0.4.0 shipped ***** - -2000/02/06 fadden - - added value range checking to Nu_SetValue - -2000/02/05 fadden - - finished "test-basic" - - added an "install" target to copy libnufx and NufxLib.h - - added "mkinstalldirs" - - fixed a memory leak in NuTest - - made several implicit typecasts explicit for Visual C++'s benefit - - renamed MiscStuff's replacement function to "Nu_function" - - use "rb" or "wb" as fopen arg in sample code for Win32 - -2000/02/04 fadden - - wrote a fair piece of "test-basic" - - added "stickyErr" to "toBuffer" data sink so we can catch overruns - -2000/02/02 fadden - - minor changes to get it working under Win32 (Visual C++ 6.0) - - added --enable-dmalloc to configuration - - instead of constantly allocating 16K buffers, use pArchive->compBuf - - ignore DataSink convertEOL value when doExpand is false - -2000/02/01 fadden - - added system-specific PATH_SEP define for samples (imgconv, exerciser) - - set the pathname in ErrorStatus for CRC failures - -2000/01/31 fadden - - fixed a typo causing zero-byte GSHK-damaged files to report CRC errors - - added support for DOS-ordered 2MG images to "imgconv" - -2000/01/29 ***** v0.3.0 shipped ***** - -2000/01/29 fadden - - renamed "tests" to "samples" - - changed library version to x.y.z format (major, minor, bug-fix) - - added DEBUG_VERBOSE define, took some stuff out of DEBUG_MSGS - -2000/01/28 fadden - - make the Skip result work when an input file can't be opened - - don't allow leading fssep chars in AddRecord - - don't treat a multi-file BNY that happens to have a ShrinkIt archive - in the first slot as a BXY - - added "-t" flag (write to temp) to "launder" - - in OpenReadWrite, treat zero-length archive files as newly-created - - added workaround for GSHK's zero-byte data fork bug - -2000/01/26 fadden - - added status result flags to NuFlush - - dropped kNuAbortAll and added kNuIgnore - - implemented kNuValueIgnoreCRC - - update the storageType whenever we change the record - -2000/01/25 fadden - - don't remove the temp file if the rename fails - - Nu_ReportError now optionally uses a callback instead of stderr - - pass NuArchive* and all the trimmings into Nu_ReportError so we can - do the callback thing; required adding arguments to lots of places - - clearly labeled BailError output as debug-only, then replaced most - of the BailErrorQuiet calls with BailError - - added global error message for when pArchive doesn't exist (e.g. Open) - -2000/01/24 fadden - - added args to "launder", and made it work right with 0-length threads - - reject disk image threads that aren't a valid size - - in NuFlush, recognize when a "copy" set hasn't had any changes made - - AddThread no longer makes a copy of the DataSource - -2000/01/24 ***** v0.2 shipped ***** - -2000/01/23 fadden - - added "sec" (Set ErrorHandler Callback) to exerciser - - wrote "launder" test program - - made "doExpand" option on data sinks work - -2000/01/22 fadden - - added OnlyUpdateOlder attribute and implemented for add and extract - - made HandleExisting work for AddFile/AddRecord - - AddThread's validation now blocks data and control threads in same - record - - AddFile and AddRecord now use same validation function as AddThread - -2000/01/20 fadden - - added Eric Shepherd's BeOS shared lib stuff to configure.in - - restructed the progress updater, and made it work when adding files - -2000/01/19 fadden - - normalized SysDefs.h, changing UNIX to UNIX_LIKE and defining for BeOS - - added "shared" target to makefile - - added BeOS stuff to autoconf setup - -2000/01/17 fadden - - fixed Makefile issue preventing "tests" from working with old GNU make - - fixed Lzw.c problem fouling up SunOS gcc v2.5.8 - - discovered "<" vs "<=" flapping in GSHK, which I can't Mimic - - fixed option list dump in debug print - - properly return from all Malloc errors; abort is now debug-only again - - lots of BeOS/Metrowerks "it's not gcc" changes from Eric Shepherd - -2000/01/17 ***** v0.1 shipped ***** - -(much time passes) - -mid-1998 fadden - - work begins - diff --git a/ciderpress/nufxlib/Charset.c b/ciderpress/nufxlib/Charset.c deleted file mode 100644 index 9ab10c7..0000000 --- a/ciderpress/nufxlib/Charset.c +++ /dev/null @@ -1,553 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2014 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Miscellaneous NufxLib utility functions. - */ -#include "NufxLibPriv.h" - -/* - * Convert Mac OS Roman to Unicode. Mapping comes from: - * - * http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/ROMAN.TXT - * - * We use the "Control Pictures" block for the control characters - * (0x00-0x1f, 0x7f --> 0x2400-0x241f, 0x2421). This is a bit nicer - * than embedding control characters in filenames. - */ -static const uint16_t gMORToUnicode[256] = { - /*0x00*/ 0x2400, // [control] NULL - /*0x01*/ 0x2401, // [control] START OF HEADING - /*0x02*/ 0x2402, // [control] START OF TEXT - /*0x03*/ 0x2403, // [control] END OF TEXT - /*0x04*/ 0x2404, // [control] END OF TRANSMISSION - /*0x05*/ 0x2405, // [control] ENQUIRY - /*0x06*/ 0x2406, // [control] ACKNOWLEDGE - /*0x07*/ 0x2407, // [control] BELL - /*0x08*/ 0x2408, // [control] BACKSPACE - /*0x09*/ 0x2409, // [control] HORIZONTAL TABULATION - /*0x0a*/ 0x240a, // [control] LINE FEED - /*0x0b*/ 0x240b, // [control] VERTICAL TABULATION - /*0x0c*/ 0x240c, // [control] FORM FEED - /*0x0d*/ 0x240d, // [control] CARRIAGE RETURN - /*0x0e*/ 0x240e, // [control] SHIFT OUT - /*0x0f*/ 0x240f, // [control] SHIFT IN - /*0x10*/ 0x2410, // [control] DATA LINK ESCAPE - /*0x11*/ 0x2411, // [control] DEVICE CONTROL ONE - /*0x12*/ 0x2412, // [control] DEVICE CONTROL TWO - /*0x13*/ 0x2413, // [control] DEVICE CONTROL THREE - /*0x14*/ 0x2414, // [control] DEVICE CONTROL FOUR - /*0x15*/ 0x2415, // [control] NEGATIVE ACKNOWLEDGE - /*0x16*/ 0x2416, // [control] SYNCHRONOUS IDLE - /*0x17*/ 0x2417, // [control] END OF TRANSMISSION BLOCK - /*0x18*/ 0x2418, // [control] CANCEL - /*0x19*/ 0x2419, // [control] END OF MEDIUM - /*0x1a*/ 0x241a, // [control] SUBSTITUTE - /*0x1b*/ 0x241b, // [control] ESCAPE - /*0x1c*/ 0x241c, // [control] FILE SEPARATOR - /*0x1d*/ 0x241d, // [control] GROUP SEPARATOR - /*0x1e*/ 0x241e, // [control] RECORD SEPARATOR - /*0x1f*/ 0x241f, // [control] UNIT SEPARATOR - /*0x20*/ 0x0020, // SPACE - /*0x21*/ 0x0021, // EXCLAMATION MARK - /*0x22*/ 0x0022, // QUOTATION MARK - /*0x23*/ 0x0023, // NUMBER SIGN - /*0x24*/ 0x0024, // DOLLAR SIGN - /*0x25*/ 0x0025, // PERCENT SIGN - /*0x26*/ 0x0026, // AMPERSAND - /*0x27*/ 0x0027, // APOSTROPHE - /*0x28*/ 0x0028, // LEFT PARENTHESIS - /*0x29*/ 0x0029, // RIGHT PARENTHESIS - /*0x2A*/ 0x002A, // ASTERISK - /*0x2B*/ 0x002B, // PLUS SIGN - /*0x2C*/ 0x002C, // COMMA - /*0x2D*/ 0x002D, // HYPHEN-MINUS - /*0x2E*/ 0x002E, // FULL STOP - /*0x2F*/ 0x002F, // SOLIDUS - /*0x30*/ 0x0030, // DIGIT ZERO - /*0x31*/ 0x0031, // DIGIT ONE - /*0x32*/ 0x0032, // DIGIT TWO - /*0x33*/ 0x0033, // DIGIT THREE - /*0x34*/ 0x0034, // DIGIT FOUR - /*0x35*/ 0x0035, // DIGIT FIVE - /*0x36*/ 0x0036, // DIGIT SIX - /*0x37*/ 0x0037, // DIGIT SEVEN - /*0x38*/ 0x0038, // DIGIT EIGHT - /*0x39*/ 0x0039, // DIGIT NINE - /*0x3A*/ 0x003A, // COLON - /*0x3B*/ 0x003B, // SEMICOLON - /*0x3C*/ 0x003C, // LESS-THAN SIGN - /*0x3D*/ 0x003D, // EQUALS SIGN - /*0x3E*/ 0x003E, // GREATER-THAN SIGN - /*0x3F*/ 0x003F, // QUESTION MARK - /*0x40*/ 0x0040, // COMMERCIAL AT - /*0x41*/ 0x0041, // LATIN CAPITAL LETTER A - /*0x42*/ 0x0042, // LATIN CAPITAL LETTER B - /*0x43*/ 0x0043, // LATIN CAPITAL LETTER C - /*0x44*/ 0x0044, // LATIN CAPITAL LETTER D - /*0x45*/ 0x0045, // LATIN CAPITAL LETTER E - /*0x46*/ 0x0046, // LATIN CAPITAL LETTER F - /*0x47*/ 0x0047, // LATIN CAPITAL LETTER G - /*0x48*/ 0x0048, // LATIN CAPITAL LETTER H - /*0x49*/ 0x0049, // LATIN CAPITAL LETTER I - /*0x4A*/ 0x004A, // LATIN CAPITAL LETTER J - /*0x4B*/ 0x004B, // LATIN CAPITAL LETTER K - /*0x4C*/ 0x004C, // LATIN CAPITAL LETTER L - /*0x4D*/ 0x004D, // LATIN CAPITAL LETTER M - /*0x4E*/ 0x004E, // LATIN CAPITAL LETTER N - /*0x4F*/ 0x004F, // LATIN CAPITAL LETTER O - /*0x50*/ 0x0050, // LATIN CAPITAL LETTER P - /*0x51*/ 0x0051, // LATIN CAPITAL LETTER Q - /*0x52*/ 0x0052, // LATIN CAPITAL LETTER R - /*0x53*/ 0x0053, // LATIN CAPITAL LETTER S - /*0x54*/ 0x0054, // LATIN CAPITAL LETTER T - /*0x55*/ 0x0055, // LATIN CAPITAL LETTER U - /*0x56*/ 0x0056, // LATIN CAPITAL LETTER V - /*0x57*/ 0x0057, // LATIN CAPITAL LETTER W - /*0x58*/ 0x0058, // LATIN CAPITAL LETTER X - /*0x59*/ 0x0059, // LATIN CAPITAL LETTER Y - /*0x5A*/ 0x005A, // LATIN CAPITAL LETTER Z - /*0x5B*/ 0x005B, // LEFT SQUARE BRACKET - /*0x5C*/ 0x005C, // REVERSE SOLIDUS - /*0x5D*/ 0x005D, // RIGHT SQUARE BRACKET - /*0x5E*/ 0x005E, // CIRCUMFLEX ACCENT - /*0x5F*/ 0x005F, // LOW LINE - /*0x60*/ 0x0060, // GRAVE ACCENT - /*0x61*/ 0x0061, // LATIN SMALL LETTER A - /*0x62*/ 0x0062, // LATIN SMALL LETTER B - /*0x63*/ 0x0063, // LATIN SMALL LETTER C - /*0x64*/ 0x0064, // LATIN SMALL LETTER D - /*0x65*/ 0x0065, // LATIN SMALL LETTER E - /*0x66*/ 0x0066, // LATIN SMALL LETTER F - /*0x67*/ 0x0067, // LATIN SMALL LETTER G - /*0x68*/ 0x0068, // LATIN SMALL LETTER H - /*0x69*/ 0x0069, // LATIN SMALL LETTER I - /*0x6A*/ 0x006A, // LATIN SMALL LETTER J - /*0x6B*/ 0x006B, // LATIN SMALL LETTER K - /*0x6C*/ 0x006C, // LATIN SMALL LETTER L - /*0x6D*/ 0x006D, // LATIN SMALL LETTER M - /*0x6E*/ 0x006E, // LATIN SMALL LETTER N - /*0x6F*/ 0x006F, // LATIN SMALL LETTER O - /*0x70*/ 0x0070, // LATIN SMALL LETTER P - /*0x71*/ 0x0071, // LATIN SMALL LETTER Q - /*0x72*/ 0x0072, // LATIN SMALL LETTER R - /*0x73*/ 0x0073, // LATIN SMALL LETTER S - /*0x74*/ 0x0074, // LATIN SMALL LETTER T - /*0x75*/ 0x0075, // LATIN SMALL LETTER U - /*0x76*/ 0x0076, // LATIN SMALL LETTER V - /*0x77*/ 0x0077, // LATIN SMALL LETTER W - /*0x78*/ 0x0078, // LATIN SMALL LETTER X - /*0x79*/ 0x0079, // LATIN SMALL LETTER Y - /*0x7A*/ 0x007A, // LATIN SMALL LETTER Z - /*0x7B*/ 0x007B, // LEFT CURLY BRACKET - /*0x7C*/ 0x007C, // VERTICAL LINE - /*0x7D*/ 0x007D, // RIGHT CURLY BRACKET - /*0x7E*/ 0x007E, // TILDE - /*0x7f*/ 0x2421, // [control] DELETE - /*0x80*/ 0x00C4, // LATIN CAPITAL LETTER A WITH DIAERESIS - /*0x81*/ 0x00C5, // LATIN CAPITAL LETTER A WITH RING ABOVE - /*0x82*/ 0x00C7, // LATIN CAPITAL LETTER C WITH CEDILLA - /*0x83*/ 0x00C9, // LATIN CAPITAL LETTER E WITH ACUTE - /*0x84*/ 0x00D1, // LATIN CAPITAL LETTER N WITH TILDE - /*0x85*/ 0x00D6, // LATIN CAPITAL LETTER O WITH DIAERESIS - /*0x86*/ 0x00DC, // LATIN CAPITAL LETTER U WITH DIAERESIS - /*0x87*/ 0x00E1, // LATIN SMALL LETTER A WITH ACUTE - /*0x88*/ 0x00E0, // LATIN SMALL LETTER A WITH GRAVE - /*0x89*/ 0x00E2, // LATIN SMALL LETTER A WITH CIRCUMFLEX - /*0x8A*/ 0x00E4, // LATIN SMALL LETTER A WITH DIAERESIS - /*0x8B*/ 0x00E3, // LATIN SMALL LETTER A WITH TILDE - /*0x8C*/ 0x00E5, // LATIN SMALL LETTER A WITH RING ABOVE - /*0x8D*/ 0x00E7, // LATIN SMALL LETTER C WITH CEDILLA - /*0x8E*/ 0x00E9, // LATIN SMALL LETTER E WITH ACUTE - /*0x8F*/ 0x00E8, // LATIN SMALL LETTER E WITH GRAVE - /*0x90*/ 0x00EA, // LATIN SMALL LETTER E WITH CIRCUMFLEX - /*0x91*/ 0x00EB, // LATIN SMALL LETTER E WITH DIAERESIS - /*0x92*/ 0x00ED, // LATIN SMALL LETTER I WITH ACUTE - /*0x93*/ 0x00EC, // LATIN SMALL LETTER I WITH GRAVE - /*0x94*/ 0x00EE, // LATIN SMALL LETTER I WITH CIRCUMFLEX - /*0x95*/ 0x00EF, // LATIN SMALL LETTER I WITH DIAERESIS - /*0x96*/ 0x00F1, // LATIN SMALL LETTER N WITH TILDE - /*0x97*/ 0x00F3, // LATIN SMALL LETTER O WITH ACUTE - /*0x98*/ 0x00F2, // LATIN SMALL LETTER O WITH GRAVE - /*0x99*/ 0x00F4, // LATIN SMALL LETTER O WITH CIRCUMFLEX - /*0x9A*/ 0x00F6, // LATIN SMALL LETTER O WITH DIAERESIS - /*0x9B*/ 0x00F5, // LATIN SMALL LETTER O WITH TILDE - /*0x9C*/ 0x00FA, // LATIN SMALL LETTER U WITH ACUTE - /*0x9D*/ 0x00F9, // LATIN SMALL LETTER U WITH GRAVE - /*0x9E*/ 0x00FB, // LATIN SMALL LETTER U WITH CIRCUMFLEX - /*0x9F*/ 0x00FC, // LATIN SMALL LETTER U WITH DIAERESIS - /*0xA0*/ 0x2020, // DAGGER - /*0xA1*/ 0x00B0, // DEGREE SIGN - /*0xA2*/ 0x00A2, // CENT SIGN - /*0xA3*/ 0x00A3, // POUND SIGN - /*0xA4*/ 0x00A7, // SECTION SIGN - /*0xA5*/ 0x2022, // BULLET - /*0xA6*/ 0x00B6, // PILCROW SIGN - /*0xA7*/ 0x00DF, // LATIN SMALL LETTER SHARP S - /*0xA8*/ 0x00AE, // REGISTERED SIGN - /*0xA9*/ 0x00A9, // COPYRIGHT SIGN - /*0xAA*/ 0x2122, // TRADE MARK SIGN - /*0xAB*/ 0x00B4, // ACUTE ACCENT - /*0xAC*/ 0x00A8, // DIAERESIS - /*0xAD*/ 0x2260, // NOT EQUAL TO - /*0xAE*/ 0x00C6, // LATIN CAPITAL LETTER AE - /*0xAF*/ 0x00D8, // LATIN CAPITAL LETTER O WITH STROKE - /*0xB0*/ 0x221E, // INFINITY - /*0xB1*/ 0x00B1, // PLUS-MINUS SIGN - /*0xB2*/ 0x2264, // LESS-THAN OR EQUAL TO - /*0xB3*/ 0x2265, // GREATER-THAN OR EQUAL TO - /*0xB4*/ 0x00A5, // YEN SIGN - /*0xB5*/ 0x00B5, // MICRO SIGN - /*0xB6*/ 0x2202, // PARTIAL DIFFERENTIAL - /*0xB7*/ 0x2211, // N-ARY SUMMATION - /*0xB8*/ 0x220F, // N-ARY PRODUCT - /*0xB9*/ 0x03C0, // GREEK SMALL LETTER PI - /*0xBA*/ 0x222B, // INTEGRAL - /*0xBB*/ 0x00AA, // FEMININE ORDINAL INDICATOR - /*0xBC*/ 0x00BA, // MASCULINE ORDINAL INDICATOR - /*0xBD*/ 0x03A9, // GREEK CAPITAL LETTER OMEGA - /*0xBE*/ 0x00E6, // LATIN SMALL LETTER AE - /*0xBF*/ 0x00F8, // LATIN SMALL LETTER O WITH STROKE - /*0xC0*/ 0x00BF, // INVERTED QUESTION MARK - /*0xC1*/ 0x00A1, // INVERTED EXCLAMATION MARK - /*0xC2*/ 0x00AC, // NOT SIGN - /*0xC3*/ 0x221A, // SQUARE ROOT - /*0xC4*/ 0x0192, // LATIN SMALL LETTER F WITH HOOK - /*0xC5*/ 0x2248, // ALMOST EQUAL TO - /*0xC6*/ 0x2206, // INCREMENT - /*0xC7*/ 0x00AB, // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK - /*0xC8*/ 0x00BB, // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK - /*0xC9*/ 0x2026, // HORIZONTAL ELLIPSIS - /*0xCA*/ 0x00A0, // NO-BREAK SPACE - /*0xCB*/ 0x00C0, // LATIN CAPITAL LETTER A WITH GRAVE - /*0xCC*/ 0x00C3, // LATIN CAPITAL LETTER A WITH TILDE - /*0xCD*/ 0x00D5, // LATIN CAPITAL LETTER O WITH TILDE - /*0xCE*/ 0x0152, // LATIN CAPITAL LIGATURE OE - /*0xCF*/ 0x0153, // LATIN SMALL LIGATURE OE - /*0xD0*/ 0x2013, // EN DASH - /*0xD1*/ 0x2014, // EM DASH - /*0xD2*/ 0x201C, // LEFT DOUBLE QUOTATION MARK - /*0xD3*/ 0x201D, // RIGHT DOUBLE QUOTATION MARK - /*0xD4*/ 0x2018, // LEFT SINGLE QUOTATION MARK - /*0xD5*/ 0x2019, // RIGHT SINGLE QUOTATION MARK - /*0xD6*/ 0x00F7, // DIVISION SIGN - /*0xD7*/ 0x25CA, // LOZENGE - /*0xD8*/ 0x00FF, // LATIN SMALL LETTER Y WITH DIAERESIS - /*0xD9*/ 0x0178, // LATIN CAPITAL LETTER Y WITH DIAERESIS - /*0xDA*/ 0x2044, // FRACTION SLASH - /*0xDB*/ 0x00A4, // CURRENCY SIGN (was EURO SIGN) - /*0xDC*/ 0x2039, // SINGLE LEFT-POINTING ANGLE QUOTATION MARK - /*0xDD*/ 0x203A, // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK - /*0xDE*/ 0xFB01, // LATIN SMALL LIGATURE FI - /*0xDF*/ 0xFB02, // LATIN SMALL LIGATURE FL - /*0xE0*/ 0x2021, // DOUBLE DAGGER - /*0xE1*/ 0x00B7, // MIDDLE DOT - /*0xE2*/ 0x201A, // SINGLE LOW-9 QUOTATION MARK - /*0xE3*/ 0x201E, // DOUBLE LOW-9 QUOTATION MARK - /*0xE4*/ 0x2030, // PER MILLE SIGN - /*0xE5*/ 0x00C2, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX - /*0xE6*/ 0x00CA, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX - /*0xE7*/ 0x00C1, // LATIN CAPITAL LETTER A WITH ACUTE - /*0xE8*/ 0x00CB, // LATIN CAPITAL LETTER E WITH DIAERESIS - /*0xE9*/ 0x00C8, // LATIN CAPITAL LETTER E WITH GRAVE - /*0xEA*/ 0x00CD, // LATIN CAPITAL LETTER I WITH ACUTE - /*0xEB*/ 0x00CE, // LATIN CAPITAL LETTER I WITH CIRCUMFLEX - /*0xEC*/ 0x00CF, // LATIN CAPITAL LETTER I WITH DIAERESIS - /*0xED*/ 0x00CC, // LATIN CAPITAL LETTER I WITH GRAVE - /*0xEE*/ 0x00D3, // LATIN CAPITAL LETTER O WITH ACUTE - /*0xEF*/ 0x00D4, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX - /*0xF0*/ 0xF8FF, // Apple logo - /*0xF1*/ 0x00D2, // LATIN CAPITAL LETTER O WITH GRAVE - /*0xF2*/ 0x00DA, // LATIN CAPITAL LETTER U WITH ACUTE - /*0xF3*/ 0x00DB, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX - /*0xF4*/ 0x00D9, // LATIN CAPITAL LETTER U WITH GRAVE - /*0xF5*/ 0x0131, // LATIN SMALL LETTER DOTLESS I - /*0xF6*/ 0x02C6, // MODIFIER LETTER CIRCUMFLEX ACCENT - /*0xF7*/ 0x02DC, // SMALL TILDE - /*0xF8*/ 0x00AF, // MACRON - /*0xF9*/ 0x02D8, // BREVE - /*0xFA*/ 0x02D9, // DOT ABOVE - /*0xFB*/ 0x02DA, // RING ABOVE - /*0xFC*/ 0x00B8, // CEDILLA - /*0xFD*/ 0x02DD, // DOUBLE ACUTE ACCENT - /*0xFE*/ 0x02DB, // OGONEK - /*0xFF*/ 0x02C7 // CARON -}; - -/* - * Static table, populated on first use. Provides the inverse map. - * - * An entry with 0x00 indicates no conversion. That's incorrect for - * the entry for '\0', but since we're operating on null-terminated - * strings that's never valid anyway. (It's possible for a filename - * to contain 0x2400, but that would translate to 0x00, which we don't - * allow; so it makes more sense to treat it as illegal.) - */ -static uint8_t gUnicodeToMOR[65536] = { 0xff /*indicates not initialized*/ }; - -static void Nu_GenerateUnicodeToMOR(void) -{ - memset(gUnicodeToMOR, 0, sizeof(gUnicodeToMOR)); - - int i; - for (i = 0; i < 256; i++) { - int codePoint = gMORToUnicode[i]; - Assert(codePoint >= 0 && codePoint < 65536); - gUnicodeToMOR[codePoint] = i; - } -} - - -/* - * Converts stringMOR to Unicode, storing the output in bufUNI until it's - * full. Null termination is guaranteed. If the buffer size is zero or - * bufUNI is NULL, no string data is returned. - * - * Returns the number of bytes required to represent stringMOR in Unicode. - */ -size_t Nu_ConvertMORToUNI(const char* stringMOR, UNICHAR* bufUNI, - size_t bufSize) -{ - Assert(stringMOR != 0); - -#ifdef _WIN32 - /* place-holder if we're not using UTF-16 yet */ - Assert(sizeof(UNICHAR) == 1); - size_t morLen = strlen(stringMOR) + 1; - if (bufUNI != NULL && bufSize != 0) { - size_t copyLen = morLen < bufSize ? morLen : bufSize; - memcpy(bufUNI, stringMOR, copyLen); - bufUNI[bufSize-1] = '\0'; - } - return morLen; -#else - /* - * Convert Mac OS Roman to UTF-8. We only output full code points, - * so if only the first byte of a UTF-8 sequence will fit we just - * stop early. - */ - size_t uniLen = 0; - Boolean doOutput = (bufUNI != NULL); - - while (*stringMOR != '\0') { - // ASCII values just "convert" to themselves in this table - uint16_t us = gMORToUnicode[(uint8_t)*stringMOR]; - if (us < 0x80) { - // single byte, no conversion - if (uniLen+1 >= bufSize) { - doOutput = false; - } - if (doOutput) { - bufUNI[uniLen] = (char) us; - } - uniLen++; - } else if (us < 0x7ff) { - // two bytes - if (uniLen+2 >= bufSize) { - doOutput = false; - } - if (doOutput) { - bufUNI[uniLen] = (us >> 6) | 0xc0; - bufUNI[uniLen+1] = (us & 0x3f) | 0x80; - } - uniLen += 2; - } else { - // three bytes - if (uniLen+3 >= bufSize) { - doOutput = false; - } - if (doOutput) { - bufUNI[uniLen] = (us >> 12) | 0xe0; - bufUNI[uniLen+1] = ((us >> 6) & 0x3f) | 0x80; - bufUNI[uniLen+2] = (us & 0x3f) | 0x80; - } - uniLen += 3; - } - - stringMOR++; - } - - // null-terminate - if (doOutput && uniLen < bufSize) { - bufUNI[uniLen] = '\0'; - } - uniLen++; - - return uniLen; -#endif -} - -/* - * Decode a single Unicode code point from a UTF-8 string. This will - * consume 1 to 4 bytes. If an error is detected, only one byte is - * consumed, and the code point value will be 0xDCnn (invalid). - * - * cf. http://en.wikipedia.org/wiki/UTF-8#Sample_code - */ -static uint32_t Nu_DecodeUTF8(const char** pStr) -{ - const uint8_t* str = (const uint8_t*) *pStr; - uint32_t codePoint; - uint32_t uc1, uc2, uc3, uc4; - uc1 = *str++; - - if (uc1 < 0x80) { - // single byte - codePoint = uc1; - } else if (uc1 < 0xc2) { - // illegal: continuation or overlong 2-byte sequence - goto fail; - } else if (uc1 < 0xe0) { - // 2-byte sequence - uc2 = *str++; - if ((uc2 & 0xc0) != 0x80) { - goto fail; // not a continuation - } - codePoint = (uc1 << 6) + uc2 - 0x3080; - } else if (uc1 < 0xf0) { - // 3-byte sequence */ - uc2 = *str++; - if ((uc2 & 0xc0) != 0x80) { - goto fail; // not a continuation - } - if (uc1 == 0xe0 && uc2 < 0xa0) { - goto fail; // overlong - } - uc3 = *str++; - if ((uc3 & 0xc0) != 0x80) { - goto fail; // not a continuation - } - codePoint = (uc1 << 12) + (uc2 << 6) + uc3 - 0xE2080; - } else if (uc1 < 0xf5) { - uc2 = *str++; - if ((uc2 & 0xc0) != 0x80) { - goto fail; // not a continuation - } - if (uc1 == 0xf0 && uc2 < 0x90) { - goto fail; // overlong - } - if (uc1 == 0xf4 && uc2 >= 0x90) { - goto fail; // U+10FFFF - } - uc3 = *str++; - if ((uc3 & 0xc0) != 0x80) { - goto fail; // not a continuation - } - uc4 = *str++; - if ((uc4 & 0xc0) != 0x80) { - goto fail; // not a continuation - } - codePoint = (uc1 << 18) + (uc2 << 12) + (uc3 << 6) + uc4 - 0x3C82080; - } else { - // illegal: > U+10FFFF - goto fail; - } - - *pStr = (const UNICHAR*) str; - return codePoint; - -fail: - (*pStr)++; // advance one char only - return 0xdc00 | uc1; -} - -/* - * Converts stringUNI to Mac OS Roman, storing the output in bufMOR - * until it's full. Null termination is guaranteed. If the buffer - * size is zero or bufMOR is NULL, no string data is returned. - * - * Returns the number of bytes required to represent stringUNI in MOR. - */ -size_t Nu_ConvertUNIToMOR(const UNICHAR* stringUNI, char* bufMOR, - size_t bufSize) -{ - Assert(stringUNI != 0); - -#ifdef _WIN32 - /* - * Place-holder if we're not using UTF-16 yet. This doesn't pass - * tests that check for behavior with non-MOR Unicode values. - */ - Assert(sizeof(UNICHAR) == 1); - size_t uniLen = strlen(stringUNI) + 1; - if (bufMOR != NULL && bufSize != 0) { - size_t copyLen = uniLen < bufSize ? uniLen : bufSize; - memcpy(bufMOR, stringUNI, copyLen); - bufMOR[bufSize-1] = '\0'; - } - return uniLen; -#else - /* - * Convert UTF-8 to Mac OS Roman. If the code point doesn't have - * a valid conversion (either because it's not in the table, or the - * UTF-8 code is damaged) we just insert an ASCII '?'. - */ - if (gUnicodeToMOR[0] == 0xff) { - Nu_GenerateUnicodeToMOR(); - Assert(gUnicodeToMOR[0] != 0xff); - } - - uint32_t codePoint; - size_t morLen = 0; - Boolean doOutput = (bufMOR != NULL); - - while (*stringUNI != '\0') { - codePoint = Nu_DecodeUTF8(&stringUNI); - char mc; - - if (codePoint < 0x80) { - mc = (char) codePoint; - } else if (codePoint < 0xffff) { - // UTF-8 errors come back as 0xDCnn, which has no mapping in table - mc = gUnicodeToMOR[codePoint]; - if (mc == 0x00) { - mc = '?'; - } - } else { - // non-BMP code point - mc = '?'; - } - if (morLen+1 >= bufSize) { - doOutput = false; - } - if (doOutput) { - bufMOR[morLen] = mc; - } - morLen++; - } - - // null-terminate - if (doOutput && morLen < bufSize) { - bufMOR[morLen] = '\0'; - } - morLen++; - - return morLen; -#endif -} - -/* - * Utility function that wraps NuConvertMORToUTF8, allocating a new - * buffer to hold the converted string. The caller must free the result. - * - * Returns NULL if stringMOR is NULL or the conversion fails. - */ -UNICHAR* Nu_CopyMORToUNI(const char* stringMOR) -{ - size_t uniLen; - UNICHAR* uniBuf; - - if (stringMOR == NULL) { - return NULL; - } - - uniLen = Nu_ConvertMORToUNI(stringMOR, NULL, 0); - if (uniLen == (size_t) -1) { - return NULL; - } - uniBuf = (UNICHAR*) Nu_Malloc(NULL, uniLen); - Nu_ConvertMORToUNI(stringMOR, uniBuf, uniLen); - return uniBuf; -} diff --git a/ciderpress/nufxlib/Compress.c b/ciderpress/nufxlib/Compress.c deleted file mode 100644 index d6bfc6a..0000000 --- a/ciderpress/nufxlib/Compress.c +++ /dev/null @@ -1,401 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Compress data into an archive. - */ -#include "NufxLibPriv.h" - -/* for ShrinkIt-mimic mode, don't compress files under 512 bytes */ -#define kNuSHKLZWThreshold 512 - - -/* - * "Compress" an uncompressed thread. - */ -static NuError Nu_CompressUncompressed(NuArchive* pArchive, NuStraw* pStraw, - FILE* fp, uint32_t srcLen, uint32_t* pDstLen, uint16_t *pCrc) -{ - NuError err = kNuErrNone; - /*uint8_t* buffer = NULL;*/ - uint32_t count, getsize; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - Assert(fp != NULL); - Assert(srcLen > 0); - - *pDstLen = srcLen; /* get this over with */ - - err = Nu_AllocCompressionBufferIFN(pArchive); - BailError(err); - - if (pCrc != NULL) - *pCrc = kNuInitialThreadCRC; - count = srcLen; - - while (count) { - getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; - - err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getsize); - BailError(err); - if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getsize); - err = Nu_FWrite(fp, pArchive->compBuf, getsize); - BailError(err); - - count -= getsize; - } - -bail: - /*Nu_Free(pArchive, buffer);*/ - return err; -} - - -/* - * Compress from a data source to an archive. - * - * All archive-specified fields in "pThread" will be filled in, as will - * "actualThreadEOF". The "nuThreadIdx" and "fileOffset" fields will - * not be modified, and must be specified before calling here. - * - * If "sourceFormat" is uncompressed: - * "targetFormat" will be used to compress the data - * the data source length will be placed into pThread->thThreadEOF - * the compressed size will be placed into pThread->thCompThreadEOF - * the CRC is computed - * - * If "sourceFormat" is compressed: - * the data will be copied without compression (targetFormat is ignored) - * the data source "otherLen" value will be placed into pThread->thThreadEOF - * the data source length will be placed into pThread->thCompThreadEOF - * the CRC is retrieved from Nu_DataSourceGetRawCrc - * - * The actual format used will be placed in pThread->thThreadFormat, and - * the CRC of the uncompressed data will be placed in pThread->thThreadCRC. - * The remaining fields of "pThread", thThreadClass and thThreadKind, will - * be set based on the fields in "pDataSource". - * - * Data will be written to "dstFp", which must be positioned at the - * correct point in the output. The position is expected to match - * pThread->fileOffset. - * - * On exit, the output file will be positioned after the last byte of the - * output. (For a pre-sized buffer, this may not be the desired result.) - */ -NuError Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource, - NuThreadID threadID, NuThreadFormat sourceFormat, - NuThreadFormat targetFormat, NuProgressData* pProgressData, FILE* dstFp, - NuThread* pThread) -{ - NuError err; - long origOffset; - NuStraw* pStraw = NULL; - NuDataSink* pDataSink = NULL; - uint32_t srcLen = 0, dstLen = 0; - uint16_t threadCrc; - - Assert(pArchive != NULL); - Assert(pDataSource != NULL); - /* okay if pProgressData is NULL */ - Assert(dstFp != NULL); - Assert(pThread != NULL); - - /* remember file offset, so we can back up if compression fails */ - err = Nu_FTell(dstFp, &origOffset); - BailError(err); - Assert(origOffset == pThread->fileOffset); /* can get rid of ftell? */ - - /* fill in some thread fields */ - threadCrc = kNuInitialThreadCRC; - - pThread->thThreadClass = NuThreadIDGetClass(threadID); - pThread->thThreadKind = NuThreadIDGetKind(threadID); - pThread->actualThreadEOF = (uint32_t)-1; - /* nuThreadIdx and fileOffset should already be set */ - - /* - * Get the input length. For "buffer" and "fp" sources, this is just - * a value passed in. For "file" sources, this is the length of the - * file on disk. The file should already have been opened successfully - * by the caller. - * - * If the input file is zero bytes long, "store" it uncompressed and - * bail immediately. - * - * (Our desire to store uncompressible data without compression clashes - * with a passing interest in doing CRLF conversions on input data. We - * want to know the length ahead of time, which potentially makes the - * compression code simpler, but prevents us from doing the conversion - * unless we pre-flight the conversion with a separate pass through the - * input file. Of course, it's still possible for the application to - * convert the file into a temp file and add from there, so all is - * not lost.) - */ - srcLen = Nu_DataSourceGetDataLen(pDataSource); - /*DBUG(("+++ input file length is %lu\n", srcLen));*/ - - /* - * Create a "Straw" to slurp the input through and track progress. - */ - err = Nu_StrawNew(pArchive, pDataSource, pProgressData, &pStraw); - BailError(err); - - if (!srcLen) { - /* empty file! */ - if (sourceFormat != kNuThreadFormatUncompressed) { - DBUG(("ODD: empty source is compressed?\n")); - } - pThread->thThreadFormat = kNuThreadFormatUncompressed; - pThread->thThreadCRC = threadCrc; - pThread->thThreadEOF = 0; - pThread->thCompThreadEOF = 0; - pThread->actualThreadEOF = 0; - goto done; /* send final progress message */ - } - - if (sourceFormat == kNuThreadFormatUncompressed) { - /* - * Compress the input to the requested target format. - */ - - /* for some reason, GSHK doesn't compress anything under 512 bytes */ - if (pArchive->valMimicSHK && srcLen < kNuSHKLZWThreshold) - targetFormat = kNuThreadFormatUncompressed; - - if (pProgressData != NULL) { - if (targetFormat != kNuThreadFormatUncompressed) - Nu_StrawSetProgressState(pStraw, kNuProgressCompressing); - else - Nu_StrawSetProgressState(pStraw, kNuProgressStoring); - } - err = Nu_ProgressDataCompressPrep(pArchive, pStraw, targetFormat, - srcLen); - BailError(err); - - switch (targetFormat) { - case kNuThreadFormatUncompressed: - err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen, - &dstLen, &threadCrc); - break; - #ifdef ENABLE_SQ - case kNuThreadFormatHuffmanSQ: - err = Nu_CompressHuffmanSQ(pArchive, pStraw, dstFp, srcLen, - &dstLen, &threadCrc); - break; - #endif - #ifdef ENABLE_LZW - case kNuThreadFormatLZW1: - err = Nu_CompressLZW1(pArchive, pStraw, dstFp, srcLen, &dstLen, - &threadCrc); - break; - case kNuThreadFormatLZW2: - err = Nu_CompressLZW2(pArchive, pStraw, dstFp, srcLen, &dstLen, - &threadCrc); - break; - #endif - #ifdef ENABLE_LZC - case kNuThreadFormatLZC12: - err = Nu_CompressLZC12(pArchive, pStraw, dstFp, srcLen, &dstLen, - &threadCrc); - break; - case kNuThreadFormatLZC16: - err = Nu_CompressLZC16(pArchive, pStraw, dstFp, srcLen, &dstLen, - &threadCrc); - break; - #endif - #ifdef ENABLE_DEFLATE - case kNuThreadFormatDeflate: - err = Nu_CompressDeflate(pArchive, pStraw, dstFp, srcLen, &dstLen, - &threadCrc); - break; - #endif - #ifdef ENABLE_BZIP2 - case kNuThreadFormatBzip2: - err = Nu_CompressBzip2(pArchive, pStraw, dstFp, srcLen, &dstLen, - &threadCrc); - break; - #endif - default: - /* should've been blocked in Value.c */ - Assert(0); - err = kNuErrInternal; - goto bail; - } - - BailError(err); - - pThread->thThreadCRC = threadCrc; /* CRC of uncompressed data */ - - if (dstLen < srcLen || - (dstLen == srcLen && targetFormat == kNuThreadFormatUncompressed)) - { - /* got smaller, or we didn't try to compress it; keep it */ - pThread->thThreadEOF = srcLen; - pThread->thCompThreadEOF = dstLen; - pThread->thThreadFormat = targetFormat; - } else { - /* got bigger, store it uncompressed */ - err = Nu_FSeek(dstFp, origOffset, SEEK_SET); - BailError(err); - err = Nu_StrawRewind(pArchive, pStraw); - BailError(err); - if (pProgressData != NULL) - Nu_StrawSetProgressState(pStraw, kNuProgressStoring); - err = Nu_ProgressDataCompressPrep(pArchive, pStraw, - kNuThreadFormatUncompressed, srcLen); - BailError(err); - - DBUG(("--- compression (%d) failed (%ld vs %ld), storing\n", - targetFormat, dstLen, srcLen)); - err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen, - &dstLen, &threadCrc); - BailError(err); - - /* - * This holds so long as the previous attempt at compressing - * computed a CRC on the entire file (i.e. didn't stop early - * when it noticed the output was larger than the input). If - * this is always the case, then we can change "&threadCrc" - * a few lines back to "NULL" and avoid re-computing the CRC. - * If this is not always the case, remove this assert. - */ - Assert(threadCrc == pThread->thThreadCRC); - - pThread->thThreadEOF = srcLen; - pThread->thCompThreadEOF = dstLen; - pThread->thThreadFormat = kNuThreadFormatUncompressed; - } - - } else { - /* - * Copy the already-compressed input. - */ - if (pProgressData != NULL) - Nu_StrawSetProgressState(pStraw, kNuProgressCopying); - err = Nu_ProgressDataCompressPrep(pArchive, pStraw, - kNuThreadFormatUncompressed, srcLen); - BailError(err); - - err = Nu_CompressUncompressed(pArchive, pStraw, dstFp, srcLen, - &dstLen, NULL); - BailError(err); - - pThread->thThreadEOF = Nu_DataSourceGetOtherLen(pDataSource); - pThread->thCompThreadEOF = srcLen; - pThread->thThreadFormat = sourceFormat; - pThread->thThreadCRC = Nu_DataSourceGetRawCrc(pDataSource); - } - pThread->actualThreadEOF = pThread->thThreadEOF; - -done: - DBUG(("+++ srcLen=%ld, dstLen=%ld, actual=%ld\n", - srcLen, dstLen, pThread->actualThreadEOF)); - - /* make sure we send a final "success" progress message at 100% */ - if (pProgressData != NULL) { - (void) Nu_StrawSetProgressState(pStraw, kNuProgressDone); - err = Nu_StrawSendProgressUpdate(pArchive, pStraw); - BailError(err); - } - -bail: - (void) Nu_StrawFree(pArchive, pStraw); - (void) Nu_DataSinkFree(pDataSink); - return err; -} - - -/* - * Copy pre-sized data into the archive at the current offset. - * - * All archive-specified fields in "pThread" will be filled in, as will - * "actualThreadEOF". The "nuThreadIdx" and "fileOffset" fields will - * not be modified. - * - * Pre-sized data is always uncompressed, and doesn't have a CRC. This - * will copy the data, and then continue writing zeros to fill out the rest - * of the pre-sized buffer. - */ -NuError Nu_CopyPresizedToArchive(NuArchive* pArchive, NuDataSource* pDataSource, - NuThreadID threadID, FILE* dstFp, NuThread* pThread, char** ppSavedCopy) -{ - NuError err = kNuErrNone; - NuStraw* pStraw = NULL; - uint32_t srcLen, bufferLen; - uint32_t count, getsize; - - srcLen = Nu_DataSourceGetDataLen(pDataSource); - bufferLen = Nu_DataSourceGetOtherLen(pDataSource); - if (bufferLen < srcLen) { - /* hey, this won't fit! */ - DBUG(("--- can't fit %lu into buffer of %lu!\n", srcLen, bufferLen)); - err = kNuErrPreSizeOverflow; - goto bail; - } - DBUG(("+++ copying %lu into buffer of %lu\n", srcLen, bufferLen)); - - pThread->thThreadClass = NuThreadIDGetClass(threadID); - pThread->thThreadFormat = kNuThreadFormatUncompressed; - pThread->thThreadKind = NuThreadIDGetKind(threadID); - pThread->thThreadCRC = 0; /* no CRC on pre-sized stuff */ - pThread->thThreadEOF = srcLen; - pThread->thCompThreadEOF = bufferLen; - pThread->actualThreadEOF = srcLen; - /* nuThreadIdx and fileOffset should already be set */ - - /* - * Prepare to copy the data through a buffer. The "straw" thing - * is a convenient way to deal with the dataSource, even though we - * don't have a progress updater. - */ - err = Nu_StrawNew(pArchive, pDataSource, NULL, &pStraw); - BailError(err); - - count = srcLen; - err = Nu_AllocCompressionBufferIFN(pArchive); - BailError(err); - - while (count) { - getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; - - err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getsize); - BailError(err); - err = Nu_FWrite(dstFp, pArchive->compBuf, getsize); - BailError(err); - - if (ppSavedCopy != NULL && *ppSavedCopy == NULL) { - /* - * Grab a copy of the filename for our own use. This assumes - * that the filename fits in kNuGenCompBufSize, which is a - * pretty safe thing to assume. - */ - Assert(threadID == kNuThreadIDFilename); - Assert(count == getsize); - *ppSavedCopy = Nu_Malloc(pArchive, getsize+1); - BailAlloc(*ppSavedCopy); - memcpy(*ppSavedCopy, pArchive->compBuf, getsize); - (*ppSavedCopy)[getsize] = '\0'; /* make sure it's terminated */ - } - - count -= getsize; - } - - /* - * Pad out the rest of the buffer. Could probably do this more - * efficiently through the buffer we've allocated, but these regions - * tend to be either 32 or 200 bytes. - */ - count = bufferLen - srcLen; - while (count--) - Nu_WriteOne(pArchive, dstFp, 0); - -bail: - (void) Nu_StrawFree(pArchive, pStraw); - /*Nu_Free(pArchive, buffer);*/ - return err; -} - diff --git a/ciderpress/nufxlib/Crc16.c b/ciderpress/nufxlib/Crc16.c deleted file mode 100644 index 1f92397..0000000 --- a/ciderpress/nufxlib/Crc16.c +++ /dev/null @@ -1,107 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Compute 16-bit CRCs. Depending on the hardware, the table version - * might be slower than the loop computation. - */ -#include "NufxLibPriv.h" - -#define CRC_TAB -#ifdef CRC_TAB -/* - * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. - * NOTE: First srgument must be in range 0 to 255. - * Second argument is referenced twice. - * - * Programmers may incorporate any or all code into their programs, - * giving proper credit within the source. Publication of the - * source routines is permitted so long as proper credit is given - * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, - * Omen Technology. - */ - - -/*#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp)*/ -#define updcrc(cp, crc) ( (crctab[((crc >> 8) & 0xFF) ^ cp] ^ (crc << 8)) & 0xFFFF) - - -/* crctab calculated by Mark G. Mendel, Network Systems Corporation */ -const uint16_t gNuCrc16Table[256] = { - 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, - 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, - 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, - 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, - 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, - 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, - 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, - 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, - 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, - 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, - 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, - 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, - 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, - 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, - 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, - 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, - 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, - 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, - 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, - 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, - 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, - 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, - 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, - 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, - 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, - 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, - 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, - 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, - 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, - 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, - 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, - 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 -}; -#endif - - -/* - * Calculate CRC on a region - * - * A CRC is the result of a mathematical operation based on the - * coefficients of a polynomial when multiplied by X^16 then divided by - * the generator polynomial (X^16 + X^12 + X^5 + 1) using modulo two - * arithmetic. - * - * This routine is a slightly modified verison of one found in: - * _Advanced Programming Techniques for the Apple //gs Toolbox_ - * By Morgan Davis and Dan Gookin (Compute! Publications, Inc.) - * It can either calculate the CRC bit-by-bit or use a table. - * - * Depending on CPU architecture, one may be dramatically faster than - * the other. - */ -uint16_t Nu_CalcCRC16(uint16_t seed, const uint8_t* ptr, int count) -{ - uint16_t CRC = seed; -#ifndef CRC_TAB - int x; -#endif - - do { -#ifndef CRC_TAB - CRC ^= *ptr++ << 8; /* XOR hi-byte of CRC w/dat */ - for (x = 8; x; --x) /* Then, for 8 bit shifts... */ - if (CRC & 0x8000) /* Test hi order bit of CRC */ - CRC = CRC << 1 ^ 0x1021; /* if set, shift & XOR w/$1021 */ - else - CRC <<= 1; /* Else, just shift left once. */ -#else - CRC = Nu_UpdateCRC16(*ptr++, CRC); /* look up new value in table */ -#endif - } while (--count); - - return (CRC); -} - diff --git a/ciderpress/nufxlib/Debug.c b/ciderpress/nufxlib/Debug.c deleted file mode 100644 index 8a704f9..0000000 --- a/ciderpress/nufxlib/Debug.c +++ /dev/null @@ -1,390 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Debugging functions. These are omitted from the non-debug build. - */ -#include "NufxLibPriv.h" - -#if defined(DEBUG_MSGS) - - -/* pull a string out of one of the static arrays */ -#define GetStaticString(index, staticArray) ( \ - (index) >= NELEM(staticArray) ? "" : staticArray[index] \ - ) - -/* thread's thread_class */ -static const char* gThreadClassNames[] = { - "message_thread", - "control_thread", - "data_thread", - "filename_thread", -}; - -/* thread's thread_format */ -static const char* gThreadFormatNames[] = { - "uncompressed", - "Huffman Squeeze", - "dynamic LZW/1", - "dynamic LZW/2", - "12-bit LZC", - "16-bit LZC", - "deflate", - "bzip2" -}; - -/* days of the week */ -static const char* gDayNames[] = { - "[ null ]", - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" -}; -/* months of the year */ -static const char* gMonths[] = { - "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" -}; - -#define kNuDateOutputLen 64 - -/* file_sys_id values */ -static const char* gFileSysIDs[] = { - "Reserved/unknown ($00)", "ProDOS/SOS", "DOS 3.3", "DOS 3.2", - "Apple II Pascal", "Macintosh (HFS)", "Macintosh (MFS)", - "LISA file system", "Apple CP/M", "Reserved 0x09", "MS-DOS", - "High-Sierra", "ISO 9660", "AppleShare" -}; - - -/* - * Convert a DateTime structure into something printable. - * - * The buffer passed in must hold at least kNuDateOutputLen bytes. - * - * Returns "buffer" for the benefit of printf() calls. - */ -static char* Nu_DebugDumpDate(const NuDateTime* pDateTime, char* buffer) -{ - char* cp; - - /* is it valid? */ - if (pDateTime->day > 30 || pDateTime->month > 11 || pDateTime->hour > 24 || - pDateTime->minute > 59) - { - strcpy(buffer, " "); - goto bail; - } - - /* is it empty? */ - if ((pDateTime->second | pDateTime->minute | pDateTime->hour | - pDateTime->year | pDateTime->day | pDateTime->month | - pDateTime->extra | pDateTime->weekDay) == 0) - { - strcpy(buffer, " [No Date] "); - goto bail; - } - - cp = buffer; - - /* only print weekDay if one was stored */ - if (pDateTime->weekDay) { - if (pDateTime->weekDay < NELEM(gDayNames)) - sprintf(cp, "%s, ", gDayNames[pDateTime->weekDay]); - else - sprintf(cp, "??%d, ", pDateTime->weekDay); - cp += strlen(cp); - } - - sprintf(cp, "%02d-%s-%04d %02d:%02d:%02d", - pDateTime->day+1, gMonths[pDateTime->month], - pDateTime->year < 40 ? pDateTime->year + 2000 : pDateTime->year + 1900, - pDateTime->hour, pDateTime->minute, pDateTime->second); - -bail: - sprintf(buffer + strlen(buffer), " [s%d m%d h%d Y%d D%d M%d x%d w%d]", - pDateTime->second, pDateTime->minute, pDateTime->hour, - pDateTime->year, pDateTime->day, pDateTime->month, pDateTime->extra, - pDateTime->weekDay); - - return buffer; -} - - -/* - * Convert a buffer into a hexadecimal character string. - * - * The result will be 2x the size of the original, +1 for a null byte. - */ -static void ConvertToHexStr(const uint8_t* inBuf, int inLen, char* outBuf) -{ - while (inLen--) { - *outBuf++ = HexConv((*inBuf >> 4) & 0x0f); - *outBuf++ = HexConv(*inBuf & 0x0f); - inBuf++; - } - *outBuf = '\0'; -} - - -/* - * Dump everything we know about pThread. - */ -void Nu_DebugDumpThread(const NuThread* pThread) -{ - static const char* kInd = " "; - NuThreadID threadID; - const char* descr; - - Assert(pThread != NULL); - - printf("%sThreadClass: 0x%04x (%s)\n", kInd, - pThread->thThreadClass, - GetStaticString(pThread->thThreadClass, gThreadClassNames)); - printf("%sThreadFormat: 0x%04x (%s)\n", kInd, - pThread->thThreadFormat, - GetStaticString(pThread->thThreadFormat, gThreadFormatNames)); - - threadID = NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind); - switch (threadID) { - case kNuThreadIDOldComment: descr = "old comment"; break; - case kNuThreadIDComment: descr = "comment"; break; - case kNuThreadIDIcon: descr = "icon"; break; - case kNuThreadIDMkdir: descr = "mkdir"; break; - case kNuThreadIDDataFork: descr = "data fork"; break; - case kNuThreadIDDiskImage: descr = "disk image"; break; - case kNuThreadIDRsrcFork: descr = "rsrc fork"; break; - case kNuThreadIDFilename: descr = "filename"; break; - default: descr = ""; break; - } - printf("%sThreadKind: 0x%04x (%s)\n", kInd, - pThread->thThreadKind, descr); - - printf("%sThreadCRC: 0x%04x ThreadEOF: %u CompThreadEOF: %u\n", kInd, - pThread->thThreadCRC, pThread->thThreadEOF, pThread->thCompThreadEOF); - printf("%s*File data offset: %ld actualThreadEOF: %d\n", kInd, - pThread->fileOffset, pThread->actualThreadEOF); -} - -/* - * Dump everything we know about pRecord, including its threads and ThreadMods. - * - * Changes to existing records are made to the "copy" set, not the "orig" - * set. Pass in the "orig" copy in "pRecord", and optionally pass in the - * "copy" set in "pXrefRecord" to glean data from both. - */ -static void Nu_DebugDumpRecord(NuArchive* pArchive, const NuRecord* pRecord, - const NuRecord* pXrefRecord, Boolean isDeleted) -{ - NuError err; /* dummy */ - static const char* kInd = " "; - char dateBuf[kNuDateOutputLen]; - const NuThreadMod* pThreadMod; - const NuThread* pThread; - uint32_t idx; - - Assert(pRecord != NULL); - - /*printf("PTR: pRecord=0x%08lx pXrefRecord=0x%08lx\n", (long) pRecord, - (long) pXrefRecord);*/ - - UNICHAR* filenameUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); - printf("%s%s%sFilename: '%s' (idx=%u)\n", kInd, - isDeleted ? "[DEL] " : "", - pXrefRecord != NULL && pXrefRecord->pThreadMods != NULL ? "[MOD] " : "", - filenameUNI == NULL ? "" : filenameUNI, - pRecord->recordIdx); - free(filenameUNI); - printf("%sHeaderID: '%.4s' VersionNumber: 0x%04x HeaderCRC: 0x%04x\n", - kInd, - pRecord->recNufxID, pRecord->recVersionNumber, pRecord->recHeaderCRC); - printf("%sAttribCount: %u TotalThreads: %u\n", kInd, - pRecord->recAttribCount, pRecord->recTotalThreads); - printf("%sFileSysID: %u (%s) FileSysInfo: 0x%04x ('%c')\n", kInd, - pRecord->recFileSysID, - GetStaticString(pRecord->recFileSysID, gFileSysIDs), - pRecord->recFileSysInfo, - NuGetSepFromSysInfo(pRecord->recFileSysInfo)); - /* do something fancy for ProDOS? */ - printf("%sFileType: 0x%08x ExtraType: 0x%08x Access: 0x%08x\n", kInd, - pRecord->recFileType, pRecord->recExtraType, pRecord->recAccess); - printf("%sCreateWhen: %s\n", kInd, - Nu_DebugDumpDate(&pRecord->recCreateWhen, dateBuf)); - printf("%sModWhen: %s\n", kInd, - Nu_DebugDumpDate(&pRecord->recModWhen, dateBuf)); - printf("%sArchiveWhen: %s\n", kInd, - Nu_DebugDumpDate(&pRecord->recArchiveWhen, dateBuf)); - printf("%sStorageType: %u OptionSize: %u FilenameLength: %u\n", kInd, - pRecord->recStorageType, pRecord->recOptionSize, - pRecord->recFilenameLength); - if (pRecord->recOptionSize) { - char* outBuf = Nu_Malloc(pArchive, pRecord->recOptionSize * 2 +1); - BailAlloc(outBuf); - Assert(pRecord->recOptionList != NULL); - ConvertToHexStr(pRecord->recOptionList, pRecord->recOptionSize, outBuf); - printf("%sOptionList: [%s]\n", kInd, outBuf); - Nu_Free(pArchive, outBuf); - } - - printf("%s*ExtraCount: %d RecFileOffset: %ld RecHeaderLength: %d\n", - kInd, - pRecord->extraCount, pRecord->fileOffset, pRecord->recHeaderLength); - - for (idx = 0; idx < pRecord->recTotalThreads; idx++) { - Boolean isFake; - - isFake = (idx >= pRecord->recTotalThreads - pRecord->fakeThreads); - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - printf("%s--Thread #%u (idx=%u)%s\n", kInd, idx, pThread->threadIdx, - isFake ? " [FAKE]" : ""); - Nu_DebugDumpThread(pThread); - } - - if (pXrefRecord != NULL) - pThreadMod = pXrefRecord->pThreadMods; - else - pThreadMod = pRecord->pThreadMods; /* probably empty */ - - if (pThreadMod != NULL) - printf("%s*ThreadMods -----\n", kInd); - while (pThreadMod != NULL) { - switch (pThreadMod->entry.kind) { - case kNuThreadModAdd: - printf("%s *-ThreadMod ADD 0x%08x 0x%04x (sourceType=%d)\n", kInd, - pThreadMod->entry.add.threadID, - pThreadMod->entry.add.threadFormat, - Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource)); - break; - case kNuThreadModUpdate: - printf("%s *-ThreadMod UPDATE %6d\n", kInd, - pThreadMod->entry.update.threadIdx); - break; - case kNuThreadModDelete: - printf("%s *-ThreadMod DELETE %6d\n", kInd, - pThreadMod->entry.delete.threadIdx); - break; - case kNuThreadModUnknown: - default: - Assert(0); - printf("%s++ThreadMod UNKNOWN\n", kInd); - break; - } - - pThreadMod = pThreadMod->pNext; - } - - /*printf("%s*TotalLength: %ld TotalCompLength: %ld\n", - kInd, pRecord->totalLength, pRecord->totalCompLength);*/ - printf("%s*TotalCompLength: %u\n", kInd, pRecord->totalCompLength); - printf("\n"); - -bail: - return; -} - -/* - * Dump the records in a RecordSet. - */ -static void Nu_DebugDumpRecordSet(NuArchive* pArchive, - const NuRecordSet* pRecordSet, const NuRecordSet* pXrefSet) -{ - const NuRecord* pRecord; - const NuRecord* pXrefRecord; - Boolean doXref; - long count; - - doXref = false; - pXrefRecord = NULL; - if (pXrefSet != NULL && Nu_RecordSet_GetLoaded(pXrefSet)) { - pXrefRecord = Nu_RecordSet_GetListHead(pXrefSet); - doXref = true; - } - - /* dump every record, if we've loaded them */ - count = Nu_RecordSet_GetNumRecords(pRecordSet); - pRecord = Nu_RecordSet_GetListHead(pRecordSet); - if (pRecord != NULL) { - Assert(count != 0); - while (count--) { - Assert(pRecord != NULL); - - if (pXrefRecord != NULL && - pRecord->recordIdx == pXrefRecord->recordIdx) - { - Nu_DebugDumpRecord(pArchive, pRecord, pXrefRecord, false); - pXrefRecord = pXrefRecord->pNext; - } else { - Nu_DebugDumpRecord(pArchive, pRecord, NULL, doXref); - } - pRecord = pRecord->pNext; - } - } else { - Assert(count == 0); - } -} - -/* - * Dump the master header block. - */ -static void Nu_DebugDumpMH(const NuMasterHeader* pMasterHeader) -{ - static const char* kInd = " "; - char dateBuf1[kNuDateOutputLen]; - - Assert(pMasterHeader != NULL); - - printf("%sNufileID: '%.6s' MasterCRC: 0x%04x TotalRecords: %u\n", kInd, - pMasterHeader->mhNufileID, pMasterHeader->mhMasterCRC, - pMasterHeader->mhTotalRecords); - printf("%sArchiveCreateWhen: %s\n", kInd, - Nu_DebugDumpDate(&pMasterHeader->mhArchiveCreateWhen, dateBuf1)); - printf("%sArchiveModWhen: %s\n", kInd, - Nu_DebugDumpDate(&pMasterHeader->mhArchiveModWhen, dateBuf1)); - printf("%sMasterVersion: %u MasterEOF: %u\n", kInd, - pMasterHeader->mhMasterVersion, pMasterHeader->mhMasterEOF); -} - -/* - * Dump everything we know about pArchive. - * - * This will only print the records that we have seen so far. If the - * application hasn't caused us to scan through all of the records in - * the archive, then this won't be very interesting. This will never - * show any records for streaming-mode archives. - */ -void Nu_DebugDumpAll(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - - printf("*Archive pathname: '%s'\n", pArchive->archivePathnameUNI); - printf("*Archive type: %d\n", pArchive->archiveType); - printf("*Header offset: %ld (junk offset=%ld)\n", - pArchive->headerOffset, pArchive->junkOffset); - printf("*Num records: %u orig, %u copy, %u new\n", - Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet), - Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet), - Nu_RecordSet_GetNumRecords(&pArchive->newRecordSet)); - printf("*NuRecordIdx seed: %u NuRecordIdx next: %u\n", - pArchive->recordIdxSeed, pArchive->nextRecordIdx); - - /* master header */ - Nu_DebugDumpMH(&pArchive->masterHeader); - - printf(" *ORIG record set (x-ref with COPY):\n"); - Nu_DebugDumpRecordSet(pArchive, &pArchive->origRecordSet, - &pArchive->copyRecordSet); - printf(" *NEW record set:\n"); - Nu_DebugDumpRecordSet(pArchive, &pArchive->newRecordSet, NULL); - - if (!Nu_RecordSet_GetLoaded(&pArchive->origRecordSet) && - !Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) - { - printf("*** DEBUG: original records not loaded yet? ***\n"); - } - -} - -#endif /*DEBUG_MSGS*/ diff --git a/ciderpress/nufxlib/Deferred.c b/ciderpress/nufxlib/Deferred.c deleted file mode 100644 index c5f293d..0000000 --- a/ciderpress/nufxlib/Deferred.c +++ /dev/null @@ -1,2559 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Deferred write handling. - */ -#include "NufxLibPriv.h" - - -/* - * =========================================================================== - * NuThreadMod functions - * =========================================================================== - */ - -/* - * Alloc and initialize a new "add" ThreadMod. - * - * Caller is allowed to dispose of the data source, as this makes a copy. - * - * NOTE: threadFormat is how you want the data to be compressed. The - * threadFormat passed to DataSource describes the source data. - */ -NuError Nu_ThreadModAdd_New(NuArchive* pArchive, NuThreadID threadID, - NuThreadFormat threadFormat, NuDataSource* pDataSource, - NuThreadMod** ppThreadMod) -{ - Assert(ppThreadMod != NULL); - Assert(pDataSource != NULL); - - *ppThreadMod = Nu_Calloc(pArchive, sizeof(**ppThreadMod)); - if (*ppThreadMod == NULL) - return kNuErrMalloc; - - (*ppThreadMod)->entry.kind = kNuThreadModAdd; - (*ppThreadMod)->entry.add.used = false; - (*ppThreadMod)->entry.add.threadIdx = Nu_GetNextThreadIdx(pArchive); - (*ppThreadMod)->entry.add.threadID = threadID; - (*ppThreadMod)->entry.add.threadFormat = threadFormat; - (*ppThreadMod)->entry.add.pDataSource = Nu_DataSourceCopy(pDataSource); - - /* decide if this is a pre-sized thread [do we want to do this here??] */ - (*ppThreadMod)->entry.add.isPresized = Nu_IsPresizedThreadID(threadID); - - return kNuErrNone; -} - -/* - * Alloc and initialize a new "update" ThreadMod. - * - * Caller is allowed to dispose of the data source. - */ -NuError Nu_ThreadModUpdate_New(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSource* pDataSource, NuThreadMod** ppThreadMod) -{ - Assert(ppThreadMod != NULL); - Assert(pDataSource != NULL); - - *ppThreadMod = Nu_Calloc(pArchive, sizeof(**ppThreadMod)); - if (*ppThreadMod == NULL) - return kNuErrMalloc; - - (*ppThreadMod)->entry.kind = kNuThreadModUpdate; - (*ppThreadMod)->entry.update.used = false; - (*ppThreadMod)->entry.update.threadIdx = threadIdx; - (*ppThreadMod)->entry.update.pDataSource = Nu_DataSourceCopy(pDataSource); - - return kNuErrNone; -} - -/* - * Alloc and initialize a new "delete" ThreadMod. - * - * The "threadID" argument is really only needed for filename threads. We - * use it when trying to track how many filename threads we really have. - */ -NuError Nu_ThreadModDelete_New(NuArchive* pArchive, NuThreadIdx threadIdx, - NuThreadID threadID, NuThreadMod** ppThreadMod) -{ - Assert(ppThreadMod != NULL); - - *ppThreadMod = Nu_Calloc(pArchive, sizeof(**ppThreadMod)); - if (*ppThreadMod == NULL) - return kNuErrMalloc; - - (*ppThreadMod)->entry.kind = kNuThreadModDelete; - (*ppThreadMod)->entry.delete.used = false; - (*ppThreadMod)->entry.delete.threadIdx = threadIdx; - (*ppThreadMod)->entry.delete.threadID = threadID; - - return kNuErrNone; -} - -/* - * Free a single NuThreadMod. - */ -void Nu_ThreadModFree(NuArchive* pArchive, NuThreadMod* pThreadMod) -{ - if (pThreadMod == NULL) - return; - - switch (pThreadMod->entry.kind) { - case kNuThreadModAdd: - Nu_DataSourceFree(pThreadMod->entry.add.pDataSource); - break; - case kNuThreadModUpdate: - Nu_DataSourceFree(pThreadMod->entry.update.pDataSource); - break; - default: - break; - } - - Nu_Free(pArchive, pThreadMod); -} - - -/* - * Return a threadMod with a matching "threadIdx", if any. Because "add" - * threads can't have a threadIdx that matches an existing thread, this - * will only return updates and deletes. - * - * We don't allow more than one threadMod on the same thread, so we don't - * have to deal with having more than one match. (To be safe, we go - * ahead and do debug-only checks for multiple matches. There shouldn't - * be more than three or four threads per record, so the extra search - * isn't costly.) - * - * Returns "NULL" if nothing found. - */ -NuThreadMod* Nu_ThreadMod_FindByThreadIdx(const NuRecord* pRecord, - NuThreadIdx threadIdx) -{ - NuThreadMod* pThreadMod; - NuThreadMod* pMatch = NULL; - - pThreadMod = pRecord->pThreadMods; - while (pThreadMod) { - switch (pThreadMod->entry.kind) { - case kNuThreadModAdd: - /* can't happen */ - Assert(pThreadMod->entry.add.threadIdx != threadIdx); - break; - case kNuThreadModUpdate: - if (pThreadMod->entry.update.threadIdx == threadIdx) { - Assert(pMatch == NULL); - pMatch = pThreadMod; - } - break; - case kNuThreadModDelete: - if (pThreadMod->entry.delete.threadIdx == threadIdx) { - Assert(pMatch == NULL); - pMatch = pThreadMod; - } - break; - default: - Assert(0); - /* keep going, I guess */ - } - pThreadMod = pThreadMod->pNext; - } - - return pMatch; -} - - -/* - * =========================================================================== - * ThreadMod list operations - * =========================================================================== - */ - -/* - * Search for an "add" ThreadMod, by threadID. - */ -NuError Nu_ThreadModAdd_FindByThreadID(const NuRecord* pRecord, - NuThreadID threadID, NuThreadMod** ppThreadMod) -{ - NuThreadMod* pThreadMod; - - Assert(pRecord != NULL); - Assert(ppThreadMod != NULL); - - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - if (pThreadMod->entry.kind != kNuThreadModAdd) - continue; - - if (pThreadMod->entry.add.threadID == threadID) { - *ppThreadMod = pThreadMod; - return kNuErrNone; - } - - pThreadMod = pThreadMod->pNext; - } - - return kNuErrNotFound; -} - - -/* - * Free up the list of NuThreadMods in this record. - */ -void Nu_FreeThreadMods(NuArchive* pArchive, NuRecord* pRecord) -{ - NuThreadMod* pThreadMod; - NuThreadMod* pNext; - - Assert(pRecord != NULL); - pThreadMod = pRecord->pThreadMods; - - if (pThreadMod == NULL) - return; - - while (pThreadMod != NULL) { - pNext = pThreadMod->pNext; - - Nu_ThreadModFree(pArchive, pThreadMod); - pThreadMod = pNext; - } - - pRecord->pThreadMods = NULL; -} - - -/* - * =========================================================================== - * Temporary structure for holding updated thread info - * =========================================================================== - */ - -/* used when constructing a new set of threads */ -typedef struct { - int numThreads; /* max #of threads */ - int nextSlot; /* where the next one goes */ - NuThread* pThreads; /* static-sized array */ -} NuNewThreads; - -/* - * Allocate and initialize a NuNewThreads struct. - */ -static NuError Nu_NewThreads_New(NuArchive* pArchive, - NuNewThreads** ppNewThreads, long numThreads) -{ - NuError err = kNuErrNone; - - *ppNewThreads = Nu_Malloc(pArchive, sizeof(**ppNewThreads)); - BailAlloc(*ppNewThreads); - (*ppNewThreads)->numThreads = numThreads; - (*ppNewThreads)->nextSlot = 0; - (*ppNewThreads)->pThreads = - Nu_Malloc(pArchive, numThreads * sizeof(NuThread)); - BailAlloc((*ppNewThreads)->pThreads); - -bail: - return err; -} - -/* - * Free a NuNewThreads struct. - */ -static void Nu_NewThreads_Free(NuArchive* pArchive, NuNewThreads* pNewThreads) -{ - if (pNewThreads != NULL) { - Nu_Free(pArchive, pNewThreads->pThreads); - Nu_Free(pArchive, pNewThreads); - } -} - -/* - * Returns true if "pNewThreads" has room for another entry, false otherwise. - */ -static Boolean Nu_NewThreads_HasRoom(const NuNewThreads* pNewThreads) -{ - if (pNewThreads->nextSlot < pNewThreads->numThreads) - return true; - else - return false; -} - -/* - * Get the next available slot. The contents of the slot are first - * initialized. - * - * The "next slot" marker is automatically advanced. - */ -static NuThread* Nu_NewThreads_GetNext(NuNewThreads* pNewThreads, - NuArchive* pArchive) -{ - NuThread* pThread; - - pThread = &pNewThreads->pThreads[pNewThreads->nextSlot]; - memset(pThread, 0, sizeof(*pThread)); - - pThread->fileOffset = -1; /* mark as invalid */ - - /* advance slot */ - pNewThreads->nextSlot++; - Assert(pNewThreads->nextSlot <= pNewThreads->numThreads); - - return pThread; -} - -/* - * Return the #of threads we're meant to hold. - */ -static int Nu_NewThreads_GetNumThreads(const NuNewThreads* pNewThreads) -{ - Assert(pNewThreads != NULL); - - return pNewThreads->numThreads; -} - -/* - * Total up the compressed EOFs of all threads. - */ -static uint32_t Nu_NewThreads_TotalCompThreadEOF(NuNewThreads* pNewThreads) -{ - uint32_t compThreadEOF; - int i; - - /* we should be all full up at this point; if not, we have a bug */ - Assert(pNewThreads != NULL); - Assert(pNewThreads->numThreads == pNewThreads->nextSlot); - - compThreadEOF = 0; - for (i = 0; i < pNewThreads->numThreads; i++) - compThreadEOF += pNewThreads->pThreads[i].thCompThreadEOF; - - return compThreadEOF; -} - - -/* - * "Donate" the thread collection to the caller. This returns a pointer - * to the thread array, and then nukes our copy of the pointer. This - * allows us to transfer ownership of the storage to the caller. - */ -static NuThread* Nu_NewThreads_DonateThreads(NuNewThreads* pNewThreads) -{ - NuThread* pThreads = pNewThreads->pThreads; - - pNewThreads->pThreads = NULL; - return pThreads; -} - - -/* - * =========================================================================== - * Archive construction - Record-level functions - * =========================================================================== - */ - -/* - * Copy an entire record (threads and all) from the source archive to the - * current offset in the temp file. - * - * Pass in the record from the *copy* set, not the original. - */ -static NuError Nu_CopyArchiveRecord(NuArchive* pArchive, NuRecord* pRecord) -{ - NuError err = kNuErrNone; - long offsetAdjust; - long outputOffset; - int i; - - err = Nu_FTell(pArchive->tmpFp, &outputOffset); - BailError(err); - offsetAdjust = outputOffset - pRecord->fileOffset; - - DBUG(("--- Copying record '%s' (curOff=%ld adj=%ld)\n", pRecord->filename, - outputOffset, offsetAdjust)); - - /* seek to the start point in the source file, and copy the whole thing */ - err = Nu_FSeek(pArchive->archiveFp, pRecord->fileOffset, SEEK_SET); - BailError(err); - err = Nu_CopyFileSection(pArchive, pArchive->tmpFp, pArchive->archiveFp, - pRecord->recHeaderLength + pRecord->totalCompLength); - BailError(err); - - /* adjust the file offsets in the record header and in the threads */ - pRecord->fileOffset += offsetAdjust; - - for (i = 0; i < (int)pRecord->recTotalThreads; i++) { - NuThread* pThread = Nu_GetThread(pRecord, i); - - pThread->fileOffset += offsetAdjust; - } - - Assert(outputOffset + pRecord->recHeaderLength + pRecord->totalCompLength == - (uint32_t)ftell(pArchive->tmpFp)); - Assert(pRecord->fileOffset == outputOffset); - -bail: - return err; -} - - -/* - * Count the number of threads that will eventually inhabit this record. - * - * Returns -1 on error. - */ -static NuError Nu_CountEventualThreads(const NuRecord* pRecord, - long* pTotalThreads, long* pFilenameThreads) -{ - const NuThreadMod* pThreadMod; - const NuThread* pThread; - long idx, numThreads, numFilenameThreads; - - /* - * Number of threads is equal to: - * the number of existing threads - * MINUS the number of "delete" threadMods (you can't delete the same - * thread more than once) - * PLUS the number of "add" threadMods - */ - numThreads = pRecord->recTotalThreads; - numFilenameThreads = 0; - - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - switch (pThreadMod->entry.kind) { - case kNuThreadModAdd: - numThreads++; - if (pThreadMod->entry.add.threadID == kNuThreadIDFilename) - numFilenameThreads++; - break; - case kNuThreadModDelete: - numThreads--; - if (pThreadMod->entry.delete.threadID == kNuThreadIDFilename) - numFilenameThreads--; - break; - case kNuThreadModUpdate: - break; - default: - Assert(0); - break; - } - - pThreadMod = pThreadMod->pNext; - } - - /* - * If the record has more than one filename thread, we only keep - * the first one, so remove it from our accounting here. It should - * not have been possible to add a new filename thread when an - * existing one was present, so we don't check the threadMods. - */ - for (idx = 0; idx < (long)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - if (NuGetThreadID(pThread) == kNuThreadIDFilename) - numFilenameThreads++; - } - Assert(numFilenameThreads >= 0); - if (numFilenameThreads > 1) { - DBUG(("--- ODD: found multiple filename threads (%ld)\n", - numFilenameThreads)); - numThreads -= (numFilenameThreads -1); - } - - /* - * Records with no threads should've been screened out already. - */ - if (numThreads <= 0) - return kNuErrInternal; - - *pTotalThreads = numThreads; - *pFilenameThreads = numFilenameThreads; /* [should cap this at 1?] */ - return kNuErrNone; -} - - -/* - * Verify that all of the threads and threadMods in a record have - * been touched. This is done after the record has been written to - * the destination archive, in order to ensure that we don't leave - * anything behind. - * - * All items, including things like duplicate filename threads that - * we ignore, are marked "used" during processing, so we don't need - * to be terribly bright here. - */ -static Boolean Nu_VerifyAllTouched(NuArchive* pArchive, const NuRecord* pRecord) -{ - const NuThreadMod* pThreadMod; - const NuThread* pThread; - long idx; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - Assert(pThreadMod->entry.generic.used == false || - pThreadMod->entry.generic.used == true); - if (!pThreadMod->entry.generic.used) - return false; - pThreadMod = pThreadMod->pNext; - } - - for (idx = 0; idx < (long)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - Assert(pThread->used == false || pThread->used == true); - if (!pThread->used) - return false; - } - - return true; -} - - -/* - * Set the threadFilename field of a record to a new value. This does - * not affect the record header filename. - * - * This call should only be made after an "add" or "update" threadMod has - * successfully completed. - * - * "newName" must be allocated storage, Mac OS Roman charset. - */ -static void Nu_SetNewThreadFilename(NuArchive* pArchive, NuRecord* pRecord, - char* newNameMOR) -{ - Assert(pRecord != NULL); - Assert(newNameMOR != NULL); - - Nu_Free(pArchive, pRecord->threadFilenameMOR); - pRecord->threadFilenameMOR = newNameMOR; - pRecord->filenameMOR = pRecord->threadFilenameMOR; -} - -/* - * If this is a disk image, we require that the uncompressed length - * be equal to recExtraType * recStorageType (where recStorageType - * is the block size, usually 512). If they haven't set those to - * appropriate values, we'll set them on their behalf, so long as - * the uncompressed size is a multiple of 512. - */ -static NuError Nu_UpdateDiskImageFields(NuArchive* pArchive, NuRecord* pRecord, - long sourceLen) -{ - NuError err = kNuErrNone; - long actualLen; - - if (pRecord->recStorageType <= 13) - pRecord->recStorageType = 512; - actualLen = pRecord->recExtraType * pRecord->recStorageType; - - if (actualLen != sourceLen) { - /* didn't match, see if we can fix it */ - DBUG(("--- fixing up disk image size\n")); - if ((sourceLen & 0x1ff) == 0) { - pRecord->recStorageType = 512; - pRecord->recExtraType = sourceLen / 512; - } else { - /* oh dear */ - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, kNuErrNone,"disk image size of %ld invalid", - sourceLen); - /* fall through and out */ - } - } - - return err; -} - -/* - * As part of thread construction or in-place updating, handle a single - * "update" threadMod. We have an existing thread, and are replacing - * the contents of it with new data. - * - * "pThread" is a thread from the copy list or a "new" thread (a copy of - * the thread from the "copy" list), and "pThreadMod" is a threadMod that - * effects pThread. - * - * "fp" is a pointer into the archive at the offset where the data is - * to be written. On exit, "fp" will point past the end of the pre-sized - * buffer. - * - * Possible side-effects on "pRecord": threadFilename may be updated. - */ -static NuError Nu_ConstructArchiveUpdate(NuArchive* pArchive, FILE* fp, - NuRecord* pRecord, NuThread* pThread, const NuThreadMod* pThreadMod) -{ - NuError err; - NuDataSource* pDataSource = NULL; - uint32_t sourceLen; - uint32_t threadBufSize; - - /* - * We're going to copy the data out of the data source. Because - * "update" actions only operate on pre-sized chunks, and the data - * is never compressed, this should be straightforward. However, - * we do need to make sure that the data will fit. - * - * I expect these to be small, and it's just a raw data copy, so no - * progress updater is used. - */ - Assert(Nu_IsPresizedThreadID(NuGetThreadID(pThread))); - Assert(pThread->thCompThreadEOF >= pThread->thThreadEOF); - threadBufSize = pThread->thCompThreadEOF; - pDataSource = pThreadMod->entry.update.pDataSource; - Assert(pDataSource != NULL); - - err = Nu_DataSourcePrepareInput(pArchive, pDataSource); - if (err == kNuErrSkipped) { - /* something failed (during file open?), just skip this one */ - DBUG(("--- skipping pre-sized thread update to %ld\n", - pThread->threadIdx)); - err = kNuErrNone; - goto skip_update; - } else if (err != kNuErrNone) - goto bail; - - /* - * Check to see if the data will fit. In some cases we can verify - * the size during the UpdatePresizedThread call, but if it's being - * added from a file we can't tell until now. - * - * We could be nice and give the user a chance to do something about - * this, but frankly the application should have checked the file - * size before handing it to us. - */ - sourceLen = Nu_DataSourceGetDataLen(pDataSource); - if (sourceLen > pThread->thCompThreadEOF) { - err = kNuErrPreSizeOverflow; - Nu_ReportError(NU_BLOB, err, "can't fit %u bytes into %u-byte buffer", - sourceLen, pThread->thCompThreadEOF); - goto bail; - } - - /* - * During an update operation, the user's specification of "otherLen" - * doesn't really matter, because we're not going to change the size - * of the region in the archive. However, this size *is* used by - * the code to figure out how big the buffer should be, and will - * determine where the file pointer ends up when the call returns. - * So, we jam in the "real" value. - */ - Nu_DataSourceSetOtherLen(pDataSource, pThread->thCompThreadEOF); - - if (NuGetThreadID(pThread) == kNuThreadIDFilename) { - /* special handling for filename updates */ - char* savedCopyMOR = NULL; - err = Nu_CopyPresizedToArchive(pArchive, pDataSource, - NuGetThreadID(pThread), fp, pThread, &savedCopyMOR); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "thread update failed"); - goto bail; - } - Nu_SetNewThreadFilename(pArchive, pRecord, savedCopyMOR); - - } else { - err = Nu_CopyPresizedToArchive(pArchive, pDataSource, - NuGetThreadID(pThread), fp, pThread, NULL); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "thread update failed"); - goto bail; - } - } - Assert((uint32_t)ftell(fp) == pThread->fileOffset + threadBufSize); - -skip_update: - Nu_DataSourceUnPrepareInput(pArchive, pDataSource); - -bail: - return err; -} - - -/* - * Handle all "add" threadMods in the current record. This is invoked both - * when creating a new record from the "new" list or constructing a - * modified record from the "copy" list. - * - * Writes to either the archiveFp or tmpFp; pass in the correct one, - * properly positioned. - * - * If something goes wrong with one of the "adds", this will return - * immediately with kNuErrSkipped. The caller is expected to abort the - * entire record, so there's no point in continuing to process other - * threads. - * - * Possible side-effects on "pRecord": disk image fields may be revised - * (storage type, extra type), and threadFilename may be updated. - */ -static NuError Nu_HandleAddThreadMods(NuArchive* pArchive, NuRecord* pRecord, - NuThreadID threadID, Boolean doKeepFirstOnly, NuNewThreads* pNewThreads, - FILE* dstFp) -{ - NuError err = kNuErrNone; - - NuProgressData progressData; - NuProgressData* pProgressData; - NuThreadMod* pThreadMod; - NuThread* pNewThread; - UNICHAR* pathnameUNIStorage = NULL; - Boolean foundOne = false; - - /* - * Now find all "add" threadMods with matching threadIDs. Allow - * matching by wildcards, but don't re-use "used" entries. - */ - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - if (pThreadMod->entry.kind == kNuThreadModAdd && - !pThreadMod->entry.generic.used && - (pThreadMod->entry.add.threadID == threadID || - threadID == kNuThreadIDWildcard)) - { - DBUG(("+++ found ADD for 0x%08lx\n", pThreadMod->entry.add.threadID)); - pThreadMod->entry.generic.used = true; - - /* if we're adding filename threads, stop after first one */ - /* [shouldn't be able to happen... we only allow one filename!] */ - if (doKeepFirstOnly && foundOne) { - Assert(0); /* can this happen?? */ - continue; - } - foundOne = true; - - if (!Nu_NewThreads_HasRoom(pNewThreads)) { - Assert(0); - err = kNuErrInternal; - goto bail; - } - - /* if this is a data thread, prepare the progress message */ - pProgressData = NULL; - if (NuThreadIDGetClass(pThreadMod->entry.add.threadID) == - kNuThreadClassData) - { - /* - * We're going to show the name as it appears in the - * archive, rather than the name of the file we're - * reading the data out of. We could do this differently - * for a "file" data source, but we might as well be - * consistent. - * - * [Actually, the above remark is bogus. During a bulk add - * there's no other way to recover the original filename. - * Do something different here for data sinks with - * filenames attached. ++ATM 2003/02/17] - */ - pathnameUNIStorage = Nu_CopyMORToUNI(pRecord->filenameMOR); - if (Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource) - == kNuDataSourceFromFile) - { - /* use on-disk filename */ - err = Nu_ProgressDataInit_Compress(pArchive, &progressData, - pRecord, Nu_DataSourceFile_GetPathname( - pThreadMod->entry.add.pDataSource), - pathnameUNIStorage); - } else { - /* use archive filename for both */ - err = Nu_ProgressDataInit_Compress(pArchive, &progressData, - pRecord, pathnameUNIStorage, pathnameUNIStorage); - } - BailError(err); - - /* send initial progress so they see name if "open" fails */ - progressData.state = kNuProgressOpening; - err = Nu_SendInitialProgress(pArchive, &progressData); - BailError(err); - - pProgressData = &progressData; - } - - /* get new thread storage, and init the thread's data offset */ - /* (the threadIdx is set by GetNext) */ - pNewThread = Nu_NewThreads_GetNext(pNewThreads, pArchive); - pNewThread->threadIdx = pThreadMod->entry.add.threadIdx; - err = Nu_FTell(dstFp, &pNewThread->fileOffset); - BailError(err); - - /* this returns kNuErrSkipped if user elects to skip */ - err = Nu_DataSourcePrepareInput(pArchive, - pThreadMod->entry.add.pDataSource); - BailError(err); - - /* - * If they're adding a disk image thread, make sure the disk- - * related fields in the record header are correct. - */ - if (pThreadMod->entry.add.threadID == kNuThreadIDDiskImage) { - const NuDataSource* pDataSource = - pThreadMod->entry.add.pDataSource; - uint32_t uncompLen; - - if (Nu_DataSourceGetThreadFormat(pDataSource) == - kNuThreadFormatUncompressed) - { - uncompLen = Nu_DataSourceGetDataLen(pDataSource); - } else { - uncompLen = Nu_DataSourceGetOtherLen(pDataSource); - } - - err = Nu_UpdateDiskImageFields(pArchive, pRecord, uncompLen); - BailError(err); - } - - if (Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource) == - kNuDataSourceFromFile) - { - DBUG(("+++ ADDING from '%s' for '%s' (idx=%ld id=0x%08lx)\n", - Nu_DataSourceFile_GetPathname(pThreadMod->entry.add.pDataSource), - pRecord->filename, - pThreadMod->entry.add.threadIdx, - pThreadMod->entry.add.threadID)); - } else { - DBUG(("+++ ADDING from (type=%d) for '%s' (idx=%ld id=0x%08lx)\n", - Nu_DataSourceGetType(pThreadMod->entry.add.pDataSource), - pRecord->filename, - pThreadMod->entry.add.threadIdx, - pThreadMod->entry.add.threadID)); - } - - if (pThreadMod->entry.add.threadID == kNuThreadIDFilename) { - /* filenames are special */ - char* savedCopyMOR = NULL; - - Assert(pThreadMod->entry.add.threadFormat == - kNuThreadFormatUncompressed); - err = Nu_CopyPresizedToArchive(pArchive, - pThreadMod->entry.add.pDataSource, - pThreadMod->entry.add.threadID, - dstFp, pNewThread, &savedCopyMOR); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "fn thread add failed"); - goto bail; - } - /* NOTE: on failure, "dropRecFilename" is still set. This - doesn't matter though, since we'll either copy the original - record, or abort the entire thing. At any rate, we can't - just clear it, because we've already made space for the - record header, and didn't include the filename in it. */ - - Nu_SetNewThreadFilename(pArchive, pRecord, savedCopyMOR); - - } else if (pThreadMod->entry.add.isPresized) { - /* don't compress, just copy */ - Assert(pThreadMod->entry.add.threadFormat == - kNuThreadFormatUncompressed); - err = Nu_CopyPresizedToArchive(pArchive, - pThreadMod->entry.add.pDataSource, - pThreadMod->entry.add.threadID, - dstFp, pNewThread, NULL); - /* fall through with err */ - - } else { - /* compress (possibly by just copying) the source to dstFp */ - err = Nu_CompressToArchive(pArchive, - pThreadMod->entry.add.pDataSource, - pThreadMod->entry.add.threadID, - Nu_DataSourceGetThreadFormat( - pThreadMod->entry.add.pDataSource), - pThreadMod->entry.add.threadFormat, - pProgressData, dstFp, pNewThread); - /* fall through with err */ - } - - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "thread add failed"); - goto bail; - } - Nu_DataSourceUnPrepareInput(pArchive, - pThreadMod->entry.add.pDataSource); - } - - pThreadMod = pThreadMod->pNext; - } - -bail: - Nu_Free(pArchive, pathnameUNIStorage); - return err; -} - -/* - * Run through the list of threads and threadMods, looking for threads - * with an ID that matches "threadID". When one is found, we take all - * the appropriate steps to get the data into the archive. - * - * This takes into account the ThreadMods, including "delete" (ignore - * existing thread), "update" (use data from threadMod instead of - * existing thread), and "add" (use data from threadMod). - * - * Threads that are used or discarded will have a flag set so that - * future examinations, notably those where "threadID" is a wildcard, - * will ignore them. - * - * Always writes to the temp file. The temp file must be positioned in - * the proper location. - * - * "pRecord" must be from the "copy" data set. - */ -static NuError Nu_ConstructArchiveThreads(NuArchive* pArchive, - NuRecord* pRecord, NuThreadID threadID, Boolean doKeepFirstOnly, - NuNewThreads* pNewThreads) -{ - NuError err = kNuErrNone; - NuThread* pThread; - NuThreadMod* pThreadMod; - Boolean foundOne = false; - NuThread* pNewThread; - int idx; - - /* - * First, find any existing threads that match. If they have a - * "delete" threadMod, ignore them; if they have an "update" threadMod, - * use that instead. - */ - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - DBUG(("+++ THREAD #%d (used=%d)\n", idx, pThread->used)); - if (threadID == kNuThreadIDWildcard || - threadID == NuGetThreadID(pThread)) - { - /* match! */ - DBUG(("+++ MATCH THREAD #%d\n", idx)); - if (pThread->used) - continue; - pThread->used = true; /* no matter what, we're done with this */ - - pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, - pThread->threadIdx); - - if (pThreadMod != NULL) { - /* - * The thread has a related ThreadMod. Deal with it. - */ - - pThreadMod->entry.generic.used = true; /* for Assert, later */ - - if (pThreadMod->entry.kind == kNuThreadModDelete) { - /* this is a delete, ignore this thread */ - DBUG(("+++ deleted %ld!\n", pThread->threadIdx)); - continue; - } else if (pThreadMod->entry.kind == kNuThreadModUpdate) { - /* update pre-sized data in place */ - - DBUG(("+++ updating threadIdx=%ld\n", - pThread->threadIdx)); - - /* only one filename per customer */ - /* [does this make sense here??] */ - if (doKeepFirstOnly && foundOne) - continue; - foundOne = true; - - /* add an entry in the new list of threads */ - pNewThread = Nu_NewThreads_GetNext(pNewThreads, pArchive); - Nu_CopyThreadContents(pNewThread, pThread); - - /* set the thread's file offset */ - err = Nu_FTell(pArchive->tmpFp, &pNewThread->fileOffset); - BailError(err); - - err = Nu_ConstructArchiveUpdate(pArchive, pArchive->tmpFp, - pRecord, pNewThread, pThreadMod); - BailError(err); - } else { - /* unknown ThreadMod type - this shouldn't happen! */ - Assert(0); - err = kNuErrInternal; - goto bail; - } - } else { - /* - * Thread is unmodified. - */ - - /* only one filename per customer */ - if (doKeepFirstOnly && foundOne) - continue; - foundOne = true; - - /* - * Copy the original data to the new location. Right now, - * pThread->fileOffset has the correct offset for the - * original file, and tmpFp is positioned at the correct - * output offset. We want to seek the source file, replace - * pThread->fileOffset with the *new* offset, and then - * copy the data. - * - * This feels skankier than it really is because we're - * using the thread in the "copy" set for two purposes. - * It'd be cleaner to pass in the thread from the "orig" - * set, but there's really not much value in doing so. - * - * [should this have a progress meter associated?] - */ - DBUG(("+++ just copying threadIdx=%ld\n", - pThread->threadIdx)); - err = Nu_FSeek(pArchive->archiveFp, pThread->fileOffset, - SEEK_SET); - BailError(err); - err = Nu_FTell(pArchive->tmpFp, &pThread->fileOffset); - BailError(err); - err = Nu_CopyFileSection(pArchive, pArchive->tmpFp, - pArchive->archiveFp, pThread->thCompThreadEOF); - BailError(err); - - /* copy an entry over into the replacement thread list */ - pNewThread = Nu_NewThreads_GetNext(pNewThreads, pArchive); - Nu_CopyThreadContents(pNewThread, pThread); - } - } - } - - /* no need to check for "add" mods; there can't be one for us */ - if (doKeepFirstOnly && foundOne) - goto bail; - - /* - * Now handle any "add" threadMods. - */ - err = Nu_HandleAddThreadMods(pArchive, pRecord, threadID, doKeepFirstOnly, - pNewThreads, pArchive->tmpFp); - BailError(err); - -bail: - return err; -} - -/* - * Construct a record in the temp file, based on the contents of the - * original. Takes into account "dirty" headers and threadMod changes. - * - * Pass in the record from the *copy* set, not the original. The temp - * file should be positioned at the correct spot. - * - * If something goes wrong, and the user wants to abort the record but - * not the entire operation, we rewind the temp file to the initial - * position. It's not possible to abandon part of a record; either you - * get everything you asked for or nothing at all. We then return - * kNuErrSkipped, which should cause the caller to simply copy the - * previous record. - */ -static NuError Nu_ConstructArchiveRecord(NuArchive* pArchive, NuRecord* pRecord) -{ - NuError err; - NuNewThreads* pNewThreads = NULL; - long threadDisp; - long initialOffset, finalOffset; - long numThreads, numFilenameThreads; - int newHeaderSize; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - - DBUG(("--- Reconstructing '%s'\n", pRecord->filename)); - - err = Nu_FTell(pArchive->tmpFp, &initialOffset); - BailError(err); - Assert(initialOffset != 0); - - /* - * Figure out how large the record header is. This requires - * measuring the static elements, the not-so-static elements like - * the GS/OS option list and perhaps the filename, and getting an - * accurate count of the number of threads. - * - * Since we're going to keep any option lists and extra junk stored in - * the header originally, the size of the new base record header is - * equal to the original recAttribCount. The attribute count conveniently - * does *not* include the filename, so if we've moved it out of the - * record header and into a thread, it won't affect us here. - */ - err = Nu_CountEventualThreads(pRecord, &numThreads, &numFilenameThreads); - BailError(err); - Assert(numThreads > 0); /* threadless records should already be gone */ - if (numThreads <= 0) { - err = kNuErrInternal; - goto bail; - } - - /* - * Handle filename deletion. - */ - if (!numFilenameThreads && pRecord->threadFilenameMOR != NULL) { - /* looks like a previously existing filename thread got removed */ - DBUG(("--- Dropping thread filename '%s'\n", - pRecord->threadFilenameMOR)); - if (pRecord->filenameMOR == pRecord->threadFilenameMOR) - pRecord->filenameMOR = NULL; /* don't point at freed memory! */ - Nu_Free(pArchive, pRecord->threadFilenameMOR); - pRecord->threadFilenameMOR = NULL; - - /* I don't think this is possible, but check it anyway */ - if (pRecord->filenameMOR == NULL && pRecord->recFilenameMOR != NULL && - !pRecord->dropRecFilename) - { - DBUG(("--- HEY, how did this happen?\n")); - pRecord->filenameMOR = pRecord->recFilenameMOR; - } - } - if (pRecord->filenameMOR == NULL) - pRecord->filenameMOR = kNuDefaultRecordName; - - /* - * Make a hole, including the header filename if we're not dropping it. - * - * This ignores fake vs. non-fake threads, because once we're done - * writing they're all "real". - */ - newHeaderSize = pRecord->recAttribCount + numThreads * kNuThreadHeaderSize; - if (!pRecord->dropRecFilename) - newHeaderSize += pRecord->recFilenameLength; - - DBUG(("+++ new header size = %d\n", newHeaderSize)); - err = Nu_FSeek(pArchive->tmpFp, newHeaderSize, SEEK_CUR); - BailError(err); - - /* - * It is important to arrange the threads in a specific order. For - * example, we can have trouble doing a streaming archive read if the - * filename isn't the first thread the collection. It's prudent to - * mimic GSHK's behavior, so we act to ensure that things appear in - * the following order: - * - * (1) filename thread - * (2) comment thread(s) - * (3) data thread with data fork - * (4) data thread with disk image - * (5) data thread with rsrc fork - * (6) everything else - * - * If we ended up with two filename threads (perhaps some other aberrant - * application created the archive; we certainly wouldn't do that), we - * keep the first one. We're more lenient on propagating strange - * multiple comment and data thread situations, even though the - * thread updating mechanism in this library won't necessarily allow - * such situations. - */ - - err = Nu_NewThreads_New(pArchive, &pNewThreads, numThreads); - BailError(err); - - err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDFilename, - true, pNewThreads); - BailError(err); - err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDComment, - false, pNewThreads); - BailError(err); - err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDDataFork, - false, pNewThreads); - BailError(err); - err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDDiskImage, - false, pNewThreads); - BailError(err); - err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDRsrcFork, - false, pNewThreads); - BailError(err); - err = Nu_ConstructArchiveThreads(pArchive, pRecord, kNuThreadIDWildcard, - false, pNewThreads); - BailError(err); - - /* - * Perform some sanity checks. - */ - Assert(!Nu_NewThreads_HasRoom(pNewThreads)); - - /* verify that all threads and threadMods have been touched */ - if (!Nu_VerifyAllTouched(pArchive, pRecord)) { - err = kNuErrInternal; - Assert(0); - goto bail; - } - - /* verify that file displacement is where it should be */ - threadDisp = (long)Nu_NewThreads_TotalCompThreadEOF(pNewThreads); - err = Nu_FTell(pArchive->tmpFp, &finalOffset); - BailError(err); - Assert(finalOffset > initialOffset); - if (finalOffset - (initialOffset + newHeaderSize) != threadDisp) { - Nu_ReportError(NU_BLOB, kNuErrNone, - "ERROR: didn't end up where expected (%ld %ld %ld)", - initialOffset, finalOffset, threadDisp); - err = kNuErrInternal; - Assert(0); - goto bail; - } - - /* - * Free existing Threads and ThreadMods, and move the list from - * pNewThreads over. - */ - Nu_Free(pArchive, pRecord->pThreads); - Nu_FreeThreadMods(pArchive, pRecord); - pRecord->pThreads = Nu_NewThreads_DonateThreads(pNewThreads); - pRecord->recTotalThreads = Nu_NewThreads_GetNumThreads(pNewThreads); - - /* - * Now, seek back and write the record header. - */ - err = Nu_FSeek(pArchive->tmpFp, initialOffset, SEEK_SET); - BailError(err); - err = Nu_WriteRecordHeader(pArchive, pRecord, pArchive->tmpFp); - BailError(err); - - Assert(newHeaderSize == (int) pRecord->recHeaderLength); - - /* - * Seek forward once again, so we are positioned at the correct - * place to write the next record. - */ - err = Nu_FSeek(pArchive->tmpFp, finalOffset, SEEK_SET); - BailError(err); - - /* update the record's fileOffset to reflect its new position */ - DBUG(("+++ record shifted by %ld bytes\n", - initialOffset - pRecord->fileOffset)); - pRecord->fileOffset = initialOffset; - -bail: - if (err == kNuErrSkipped) { - /* - * Something went wrong and they want to skip this record but - * keep going otherwise. We need to back up in the file so the - * original copy of the record can go here. - */ - err = Nu_FSeek(pArchive->tmpFp, initialOffset, SEEK_SET); - if (err == kNuErrNone) - err = kNuErrSkipped; /* tell the caller we skipped it */ - } - - Nu_NewThreads_Free(pArchive, pNewThreads); - return err; -} - - -/* - * Construct a new record and add it to the original or temp file. The - * new record has no threads but some number of threadMods. (This - * function is a cousin to Nu_ConstructArchiveRecord.) "pRecord" must - * come from the "new" record set. - * - * The original/temp file should be positioned at the correct spot. - * - * If something goes wrong, and the user wants to abort the record but - * not the entire operation, we rewind the temp file to the initial - * position and return kNuErrSkipped. - */ -static NuError Nu_ConstructNewRecord(NuArchive* pArchive, NuRecord* pRecord, - FILE* fp) -{ - NuError err; - NuNewThreads* pNewThreads = NULL; - NuThreadMod* pThreadMod; - long threadDisp; - long initialOffset, finalOffset; - long numThreadMods, numFilenameThreads; - int newHeaderSize; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - - DBUG(("--- Constructing '%s'\n", pRecord->filename)); - - err = Nu_FTell(fp, &initialOffset); - BailError(err); - Assert(initialOffset != 0); - - /* - * Quick sanity check: verify that the record has no threads of its - * own, and all threadMods are "add" threadMods. While we're at it, - * make ourselves useful by counting up the number of eventual - * threads, and verify that there is exactly one filename thread. - */ - Assert(pRecord->pThreads == NULL); - - numThreadMods = 0; - numFilenameThreads = 0; - pThreadMod = pRecord->pThreadMods; - while (pThreadMod) { - if (pThreadMod->entry.kind != kNuThreadModAdd) { - Nu_ReportError(NU_BLOB, kNuErrNone, "unexpected non-add threadMod"); - err = kNuErrInternal; - Assert(0); - goto bail; - } - numThreadMods++; - if (pThreadMod->entry.add.threadID == kNuThreadIDFilename) - numFilenameThreads++; - - pThreadMod = pThreadMod->pNext; - } - Assert(numFilenameThreads <= 1); - - /* - * If there's no filename thread, make one. We do this for brand-new - * records when the application doesn't explicitly add a thread. - */ - if (!numFilenameThreads) { - NuDataSource* pTmpDataSource = NULL; - NuThreadMod* pNewThreadMod = NULL; - int len, maxLen; - - /* - * Generally speaking, the "add file" call should set the - * filename. If somehow it didn't, assign a default. - */ - if (pRecord->filenameMOR == NULL) { - pRecord->newFilenameMOR = strdup(kNuDefaultRecordName); - pRecord->filenameMOR = pRecord->newFilenameMOR; - } - - DBUG(("--- No filename thread found, adding one ('%s')\n", - pRecord->filenameMOR)); - - /* - * Create a trivial data source for the filename. The size of - * the filename buffer is the larger of the filename length and - * the default filename buffer size. This mimics GSHK's behavior. - * (If we're really serious about renaming it, maybe we should - * leave some extra space on the end...?) - */ - len = strlen(pRecord->filenameMOR); - maxLen = len > kNuDefaultFilenameThreadSize ? - len : kNuDefaultFilenameThreadSize; - err = Nu_DataSourceBuffer_New(kNuThreadFormatUncompressed, - maxLen, (const uint8_t*)pRecord->filenameMOR, 0, - strlen(pRecord->filenameMOR), NULL, &pTmpDataSource); - BailError(err); - - /* put in a new "add" threadMod (which copies the data source) */ - err = Nu_ThreadModAdd_New(pArchive, kNuThreadIDFilename, - kNuThreadFormatUncompressed, pTmpDataSource, &pNewThreadMod); - Nu_DataSourceFree(pTmpDataSource); - BailError(err); - - /* add it to the list */ - Nu_RecordAddThreadMod(pRecord, pNewThreadMod); - pNewThreadMod = NULL; - - numFilenameThreads++; - numThreadMods++; - } - - /* - * Figure out how large the record header is. We don't generate - * GS/OS option lists or "extra" data here, and we always put the - * filename in a thread, so the size is constant. (If somebody - * does a GS/OS or Mac port and wants to add option lists, it should - * not be hard to adjust the size accordingly.) - * - * This initializes the record's attribCount. We use the "base size" - * and add two for the (unused) filename length. - */ - pRecord->recAttribCount = kNuRecordHeaderBaseSize +2; - newHeaderSize = pRecord->recAttribCount + numThreadMods * kNuThreadHeaderSize; - - DBUG(("+++ new header size = %d\n", newHeaderSize)); - - /* leave a hole */ - err = Nu_FSeek(fp, newHeaderSize, SEEK_CUR); - BailError(err); - - /* - * It is important to arrange the threads in a specific order. See - * the comments in Nu_ConstructArchiveRecord for the rationale. - */ - err = Nu_NewThreads_New(pArchive, &pNewThreads, numThreadMods); - BailError(err); - - err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDFilename, - true, pNewThreads, fp); - BailError(err); - err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDComment, - false, pNewThreads, fp); - BailError(err); - err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDDataFork, - false, pNewThreads, fp); - BailError(err); - err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDDiskImage, - false, pNewThreads, fp); - BailError(err); - err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDRsrcFork, - false, pNewThreads, fp); - BailError(err); - err = Nu_HandleAddThreadMods(pArchive, pRecord, kNuThreadIDWildcard, - false, pNewThreads, fp); - BailError(err); - - /* - * Perform some sanity checks. - */ - Assert(!Nu_NewThreads_HasRoom(pNewThreads)); - - /* verify that all threads and threadMods have been touched */ - if (!Nu_VerifyAllTouched(pArchive, pRecord)) { - err = kNuErrInternal; - Assert(0); - goto bail; - } - - /* verify that file displacement is where it should be */ - threadDisp = Nu_NewThreads_TotalCompThreadEOF(pNewThreads); - err = Nu_FTell(fp, &finalOffset); - BailError(err); - Assert(finalOffset > initialOffset); - if (finalOffset - (initialOffset + newHeaderSize) != threadDisp) { - Nu_ReportError(NU_BLOB, kNuErrNone, - "ERROR: didn't end up where expected (%ld %ld %ld)", - initialOffset, finalOffset, threadDisp); - err = kNuErrInternal; - Assert(0); - goto bail; - } - - /* - * Install pNewThreads as the thread list. - */ - Assert(pRecord->pThreads == NULL && pRecord->recTotalThreads == 0); - pRecord->pThreads = Nu_NewThreads_DonateThreads(pNewThreads); - pRecord->recTotalThreads = Nu_NewThreads_GetNumThreads(pNewThreads); - - /* - * Fill in misc record header fields. - * - * We could set recArchiveWhen here, if we wanted to override what - * the application set, but I don't think there's any value in that. - */ - pRecord->fileOffset = initialOffset; - - /* - * Now, seek back and write the record header. - */ - err = Nu_FSeek(fp, initialOffset, SEEK_SET); - BailError(err); - err = Nu_WriteRecordHeader(pArchive, pRecord, fp); - BailError(err); - - /* - * Seek forward once again, so we are positioned at the correct - * place to write the next record. - */ - err = Nu_FSeek(fp, finalOffset, SEEK_SET); - BailError(err); - - /* - * Trash the threadMods. - */ - Nu_FreeThreadMods(pArchive, pRecord); - -bail: - if (err == kNuErrSkipped) { - /* - * Something went wrong and they want to skip this record but - * keep going otherwise. We need to back up in the file so the - * next record can go here. - */ - err = Nu_FSeek(fp, initialOffset, SEEK_SET); - if (err == kNuErrNone) - err = kNuErrSkipped; /* tell the caller we skipped it */ - } - - Nu_NewThreads_Free(pArchive, pNewThreads); - return err; -} - - -/* - * Update a given record in the original archive file. - * - * "pRecord" is the record from the "copy" set. It can have the - * "dirtyHeader" flag set, and may have "update" threadMods, but - * that's all. - * - * The position of pArchive->archiveFp on entry and on exit is not - * defined. - */ -static NuError Nu_UpdateRecordInOriginal(NuArchive* pArchive, NuRecord* pRecord) -{ - NuError err = kNuErrNone; - NuThread* pThread; - const NuThreadMod* pThreadMod; - - /* - * Loop through all threadMods. - */ - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - Assert(pThreadMod->entry.kind == kNuThreadModUpdate); - - /* find the thread associated with this threadMod */ - err = Nu_FindThreadByIdx(pRecord, pThreadMod->entry.update.threadIdx, - &pThread); - BailError(err); /* should never happen */ - - /* seek to the appropriate spot */ - err = Nu_FSeek(pArchive->archiveFp, pThread->fileOffset, SEEK_SET); - BailError(err); - - /* do the update; this updates "pThread" with the new info */ - err = Nu_ConstructArchiveUpdate(pArchive, pArchive->archiveFp, - pRecord, pThread, pThreadMod); - BailError(err); - - pThreadMod = pThreadMod->pNext; - } - - - /* - * We have to write a new record header without disturbing - * anything around it. Nothing we've done should've changed - * the size of the record header, so just go ahead and write it. - * - * We have to do this regardless of "dirtyHeader", because we just - * tweaked some of our threads around, and we need to rewrite the - * thread headers (which updates the record header CRC, and so on). - */ - err = Nu_FSeek(pArchive->archiveFp, pRecord->fileOffset, SEEK_SET); - BailError(err); - err = Nu_WriteRecordHeader(pArchive, pRecord, pArchive->archiveFp); - BailError(err); - - /* - * Let's be paranoid and verify that the write didn't overflow - * into the thread header. We compare our current offset against - * the offset of the first thread. (If we're in a weird record - * with no threads, we could compare against the offset of the - * next record, but I don't want to deal with a case that should - * never happen anyway.) - */ - DBUG(("--- record header wrote %ld bytes\n", - pArchive->currentOffset - pRecord->fileOffset)); - pThread = pRecord->pThreads; - if (pThread != NULL && pArchive->currentOffset != pThread->fileOffset) { - /* guess what, we just trashed the archive */ - err = kNuErrDamaged; - Nu_ReportError(NU_BLOB, err, - "Bad record header write (off by %ld), archive damaged", - pArchive->currentOffset - pThread->fileOffset); - goto bail; - } - DBUG(("--- record header written safely\n")); - - - /* - * It's customary to throw out the thread mods when you're done. (I'm - * not really sure why I'm doing this now, but here we are.) - */ - Nu_FreeThreadMods(pArchive, pRecord); - -bail: - return err; -} - - -/* - * =========================================================================== - * Archive construction - main functions - * =========================================================================== - */ - -/* - * Fill in the temp file with the contents of the original archive. The - * file offsets and any other generated data in the "copy" set will be - * updated as appropriate, so that the "copy" set can eventually replace - * the "orig" set. - * - * On exit, pArchive->tmpFp will point at the archive EOF. - */ -static NuError Nu_CreateTempFromOriginal(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - - Assert(pArchive->tmpFp != 0); - Assert(ftell(pArchive->tmpFp) == 0); /* should be empty as well */ - - /* - * Leave space for the master header and (if we're preserving it) any - * header gunk. - */ - Assert(!pArchive->valDiscardWrapper || pArchive->headerOffset == 0); - err = Nu_FSeek(pArchive->tmpFp, - pArchive->headerOffset + kNuMasterHeaderSize, SEEK_SET); - BailError(err); - - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - /* - * Run through the "copy" records. If the original record header is - * umodified, just copy it; otherwise write a new one with a new CRC. - */ - if (Nu_RecordSet_IsEmpty(&pArchive->copyRecordSet)) { - /* new archive or all records deleted */ - DBUG(("--- No records in 'copy' set\n")); - goto bail; - } - pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); - } else { - /* - * There's no "copy" set defined. If we have an "orig" set, we - * must be doing nothing but add files to an existing archive - * without the "modify orig" flag set. - */ - if (Nu_RecordSet_IsEmpty(&pArchive->origRecordSet)) { - DBUG(("--- No records in 'copy' or 'orig' set\n")); - goto bail; - } - pRecord = Nu_RecordSet_GetListHead(&pArchive->origRecordSet); - } - - /* - * Reconstruct or copy the records. It's probably not necessary - * to reconstruct the entire record if we're just updating the - * record header, but since all we do is copy the data anyway, - * it's not much slower. - */ - while (pRecord != NULL) { - if (!pRecord->dirtyHeader && pRecord->pThreadMods == NULL) { - err = Nu_CopyArchiveRecord(pArchive, pRecord); - BailError(err); - } else { - err = Nu_ConstructArchiveRecord(pArchive, pRecord); - if (err == kNuErrSkipped) { - /* - * We're going to retain the original. This requires us - * to copy the original record from the "orig" record set - * and replace what we had in the "copy" set, so that at - * the end of the day the "copy" set accurately reflects - * what's in the archive. - */ - DBUG(("--- Skipping, copying %ld instead\n", - pRecord->recordIdx)); - err = Nu_RecordSet_ReplaceRecord(pArchive, - &pArchive->copyRecordSet, pRecord, - &pArchive->origRecordSet, &pRecord); - BailError(err); - err = Nu_CopyArchiveRecord(pArchive, pRecord); - BailError(err); - } - BailError(err); - } - - pRecord = pRecord->pNext; - } - -bail: - return err; -} - - -/* - * Perform updates to certain items in the original archive. None of - * the operations changes the position of items within. - * - * On exit, pArchive->archiveFp will point at the archive EOF. - */ -static NuError Nu_UpdateInOriginal(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - - if (!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - /* - * There's nothing for us to do; we probably just have a - * bunch of new stuff being added. - */ - DBUG(("--- UpdateInOriginal: nothing to do\n")); - goto done; - } - - /* - * Run through and process all the updates. - */ - pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); - while (pRecord != NULL) { - if (pRecord->dirtyHeader || pRecord->pThreadMods != NULL) { - err = Nu_UpdateRecordInOriginal(pArchive, pRecord); - BailError(err); - } - - pRecord = pRecord->pNext; - } - -done: - /* seek to the end of the archive */ - err = Nu_FSeek(pArchive->archiveFp, - pArchive->headerOffset + pArchive->masterHeader.mhMasterEOF, - SEEK_SET); - BailError(err); - -bail: - return err; -} - - -/* - * Create new records for all items in the "new" list, writing them to - * "fp" at the current offset. - * - * On completion, "fp" will point at the end of the archive. - */ -static NuError Nu_CreateNewRecords(NuArchive* pArchive, FILE* fp) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - - pRecord = Nu_RecordSet_GetListHead(&pArchive->newRecordSet); - while (pRecord != NULL) { - err = Nu_ConstructNewRecord(pArchive, pRecord, fp); - if (err == kNuErrSkipped) { - /* - * We decided to skip this record, so delete it from "new". - * - * (I think this is the only time we delete something from the - * "new" set...) - */ - NuRecord* pNextRecord = pRecord->pNext; - - DBUG(("--- Skipping, deleting new %ld\n", pRecord->recordIdx)); - err = Nu_RecordSet_DeleteRecord(pArchive, &pArchive->newRecordSet, - pRecord); - Assert(err == kNuErrNone); - BailError(err); - pRecord = pNextRecord; - } else { - BailError(err); - pRecord = pRecord->pNext; - } - } - -bail: - return err; -} - - -/* - * =========================================================================== - * Archive update helpers - * =========================================================================== - */ - -/* - * Determine if any "heavy updates" have been made. A "heavy" update is - * one that requires us to create and rename a temp file. - * - * If the "copy" record set hasn't been loaded, we're done. If it has - * been loaded, we scan through the list for thread mods other than updates - * to pre-sized fields. We also have to check to see if any records were - * deleted. - * - * At present, a "dirtyHeader" flag is not of itself cause to rebuild - * the archive, so we don't test for it here. - */ -static Boolean Nu_NoHeavyUpdates(NuArchive* pArchive) -{ - const NuRecord* pRecord; - long count; - - /* if not loaded, then *no* changes were made to original records */ - if (!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) - return true; - - /* - * You can't add to "copy" set, so any deletions are visible by the - * reduced record count. The function that deletes records from - * which all threads have been removed should be called before we - * get here. - */ - if (Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet) != - Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet)) - { - return false; - } - - /* - * Run through the set of records, looking for a threadMod with a - * change type we can't handle in place. - */ - count = Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet); - pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); - while (count--) { - const NuThreadMod* pThreadMod; - - Assert(pRecord != NULL); - - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - /* the only acceptable kind is "update" */ - if (pThreadMod->entry.kind != kNuThreadModUpdate) - return false; - - pThreadMod = pThreadMod->pNext; - } - - pRecord = pRecord->pNext; - } - - return true; -} - - -/* - * Purge any records that don't have any threads. This has to take into - * account pending modifications, so that we dispose of any records that - * have had all of their threads deleted. - * - * Simplest approach is to count up the #of "delete" mods and subtract - * it from the number of threads, skipping on if the record has any - * "add" thread mods. - */ -static NuError Nu_PurgeEmptyRecords(NuArchive* pArchive, - NuRecordSet* pRecordSet) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - NuRecord** ppRecord; - - Assert(pArchive != NULL); - Assert(pRecordSet != NULL); - - if (Nu_RecordSet_IsEmpty(pRecordSet)) - return kNuErrNone; - - ppRecord = Nu_RecordSet_GetListHeadPtr(pRecordSet); - Assert(ppRecord != NULL); - Assert(*ppRecord != NULL); - - /* maintain a pointer to the pointer, so we can delete easily */ - while (*ppRecord != NULL) { - pRecord = *ppRecord; - - if (Nu_RecordIsEmpty(pArchive, pRecord)) { - DBUG(("--- Purging empty record %06ld '%s' (0x%08lx-->0x%08lx)\n", - pRecord->recordIdx, pRecord->filename, - (uint32_t)ppRecord, (uint32_t)pRecord)); - err = Nu_RecordSet_DeleteRecordPtr(pArchive, pRecordSet, ppRecord); - BailError(err); - /* pRecord is now invalid, and *ppRecord has been updated */ - } else { - ppRecord = &pRecord->pNext; - } - } - -bail: - return err; -} - - -/* - * Update the "new" master header block with the contents of the modified - * archive, and write it to the file. - * - * Pass in a correctly positioned "fp" and the total length of the archive - * file. - */ -static NuError Nu_UpdateMasterHeader(NuArchive* pArchive, FILE* fp, - long archiveEOF) -{ - NuError err; - long numRecords; - - Nu_MasterHeaderCopy(pArchive, &pArchive->newMasterHeader, - &pArchive->masterHeader); - - numRecords = 0; - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) - numRecords += Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet); - else - numRecords += Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet); - if (Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) - numRecords += Nu_RecordSet_GetNumRecords(&pArchive->newRecordSet); - #if 0 /* we allow delete-all now */ - if (numRecords == 0) { - /* don't allow empty archives */ - DBUG(("--- UpdateMasterHeader didn't find any records\n")); - err = kNuErrNoRecords; - goto bail; - } - #endif - - pArchive->newMasterHeader.mhTotalRecords = numRecords; - pArchive->newMasterHeader.mhMasterEOF = archiveEOF; - pArchive->newMasterHeader.mhMasterVersion = kNuOurMHVersion; - Nu_SetCurrentDateTime(&pArchive->newMasterHeader.mhArchiveModWhen); - - err = Nu_WriteMasterHeader(pArchive, fp, &pArchive->newMasterHeader); - BailError(err); - -bail: - return err; -} - - -/* - * Reset the temp file to a known (empty) state. - */ -static NuError Nu_ResetTempFile(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - - /* read-only archives don't have a temp file */ - if (Nu_IsReadOnly(pArchive)) - return kNuErrNone; /* or kNuErrArchiveRO? */ - - Assert(pArchive != NULL); - Assert(pArchive->tmpPathnameUNI != NULL); - -#if 0 /* keep the temp file around for examination */ -if (pArchive->tmpFp != NULL) { - DBUG(("--- NOT Resetting temp file\n")); - fflush(pArchive->tmpFp); - goto bail; -} -#endif - - DBUG(("--- Resetting temp file\n")); - - /* if we renamed the temp over the original, we need to open a new temp */ - if (pArchive->tmpFp == NULL) { - // as in Nu_OpenTempFile, skip the wchar conversion for the temp - // file name, which we lazily assume to be ASCII - pArchive->tmpFp = fopen(pArchive->tmpPathnameUNI, - kNuFileOpenReadWriteCreat); - if (pArchive->tmpFp == NULL) { - err = errno ? errno : kNuErrFileOpen; - Nu_ReportError(NU_BLOB, errno, "Unable to open temp file '%s'", - pArchive->tmpPathnameUNI); - goto bail; - } - } else { - /* - * Truncate the temp file. - */ - err = Nu_FSeek(pArchive->tmpFp, 0, SEEK_SET); - BailError(err); - err = Nu_TruncateOpenFile(pArchive->tmpFp, 0); - if (err == kNuErrInternal) { - /* do it the hard way if we don't have ftruncate or equivalent */ - err = kNuErrNone; - fclose(pArchive->tmpFp); - pArchive->tmpFp = fopen(pArchive->tmpPathnameUNI, - kNuFileOpenWriteTrunc); - if (pArchive->tmpFp == NULL) { - err = errno ? errno : kNuErrFileOpen; - Nu_ReportError(NU_BLOB, err, "failed truncating tmp file"); - goto bail; - } - fclose(pArchive->tmpFp); - pArchive->tmpFp = fopen(pArchive->tmpPathnameUNI, - kNuFileOpenReadWriteCreat); - if (pArchive->tmpFp == NULL) { - err = errno ? errno : kNuErrFileOpen; - Nu_ReportError(NU_BLOB, err, "Unable to open temp file '%s'", - pArchive->tmpPathnameUNI); - goto bail; - } - } - } - -bail: - return err; -} - -/* - * Ensure that all of the threads and threadMods in a record are in - * a pristine state, i.e. "threads" aren't marked used and "threadMods" - * don't even exist. This is done as we are cleaning up the record sets - * after a successful (or aborted) update. - */ -static NuError Nu_RecordResetUsedFlags(NuArchive* pArchive, NuRecord* pRecord) -{ - NuThread* pThread; - long idx; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - - /* these should already be clear */ - if (pRecord->pThreadMods) { - Assert(0); - return kNuErrInternal; - } - - /* these might still be set */ - for (idx = 0; idx < (long)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - pThread->used = false; - } - - /* and this */ - pRecord->dirtyHeader = false; - - return kNuErrNone; -} - -/* - * Invoke Nu_RecordResetUsedFlags on all records in a record set. - */ -static NuError Nu_ResetUsedFlags(NuArchive* pArchive, NuRecordSet* pRecordSet) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - - pRecord = Nu_RecordSet_GetListHead(pRecordSet); - while (pRecord != NULL) { - err = Nu_RecordResetUsedFlags(pArchive, pRecord); - if (err != kNuErrNone) { - Assert(0); - break; - } - - pRecord = pRecord->pNext; - } - - return err; -} - - -/* - * If nothing in the "copy" set has actually been disturbed, throw it out. - */ -static void Nu_ResetCopySetIfUntouched(NuArchive* pArchive) -{ - const NuRecord* pRecord; - - /* have any records been deleted? */ - if (Nu_RecordSet_GetNumRecords(&pArchive->copyRecordSet) != - pArchive->masterHeader.mhTotalRecords) - { - return; - } - - /* do we have any thread mods or dirty record headers? */ - pRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); - while (pRecord != NULL) { - if (pRecord->pThreadMods != NULL || pRecord->dirtyHeader) - return; - - pRecord = pRecord->pNext; - } - - /* looks like nothing has been touched */ - DBUG(("--- copy set untouched, trashing it\n")); - (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); -} - - -/* - * GSHK always adds a comment to the first new record added to an archive. - * Imitate this behavior. - */ -static NuError Nu_AddCommentToFirstNewRecord(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - NuThreadMod* pThreadMod = NULL; - NuThreadMod* pExistingThreadMod = NULL; - NuDataSource* pDataSource = NULL; - - /* if there aren't any records there, skip this */ - if (Nu_RecordSet_IsEmpty(&pArchive->newRecordSet)) - goto bail; - - pRecord = Nu_RecordSet_GetListHead(&pArchive->newRecordSet); - - /* - * See if this record already has a comment. If so, don't add - * another one. - */ - err = Nu_ThreadModAdd_FindByThreadID(pRecord, kNuThreadIDComment, - &pExistingThreadMod); - if (err == kNuErrNone) { - DBUG(("+++ record already has a comment, not adding another\n")); - goto bail; /* already exists */ - } - err = kNuErrNone; - - /* create a new data source with nothing in it */ - err = Nu_DataSourceBuffer_New(kNuThreadFormatUncompressed, - kNuDefaultCommentSize, NULL, 0, 0, NULL, &pDataSource); - BailError(err); - Assert(pDataSource != NULL); - - /* create a new ThreadMod */ - err = Nu_ThreadModAdd_New(pArchive, kNuThreadIDComment, - kNuThreadFormatUncompressed, pDataSource, &pThreadMod); - BailError(err); - Assert(pThreadMod != NULL); - /*pDataSource = NULL;*/ /* ThreadModAdd_New makes a copy */ - - /* add the thread mod to the record */ - Nu_RecordAddThreadMod(pRecord, pThreadMod); - pThreadMod = NULL; /* don't free on exit */ - -bail: - Nu_ThreadModFree(pArchive, pThreadMod); - Nu_DataSourceFree(pDataSource); - return err; -} - - -/* - * =========================================================================== - * Main entry points - * =========================================================================== - */ - -/* - * Force all deferred changes to occur. - * - * If the flush fails, the archive state may be aborted or even placed - * into read-only mode to prevent problems from compounding. - * - * If the things this function is doing aren't making any sense at all, - * read "NOTES.txt" for an introduction. - */ -NuError Nu_Flush(NuArchive* pArchive, uint32_t* pStatusFlags) -{ - NuError err = kNuErrNone; - Boolean canAbort = true; - Boolean writeToTemp = true; - Boolean deleteAll = false; - long initialEOF, finalOffset; - - DBUG(("--- FLUSH\n")); - - if (pStatusFlags == NULL) - return kNuErrInvalidArg; - /* these do get set on error, so clear them no matter what */ - *pStatusFlags = 0; - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - - err = Nu_GetFileLength(pArchive, pArchive->archiveFp, &initialEOF); - BailError(err); - - /* - * Step 1: figure out if we have anything to do. If the "copy" and "new" - * lists are empty, then there's nothing for us to do. - * - * As a special case, we test for an archive that had all of its - * records deleted. This looks a lot like an archive that has had - * nothing done, because we would have made a "copy" list and then - * deleted all the records, leaving us with an empty list. (The - * difference is that an untouched archive wouldn't have a "copy" - * list allocated.) - * - * In some cases, such as doing a bulk delete that doesn't end up - * matching anything or an attempted UpdatePresizedThread on a thread - * that isn't actually pre-sized, we create the "copy" list but don't - * actually change anything. We deal with that by frying the "copy" - * list if it doesn't have anything interesting in it (i.e. it's an - * exact match of the "orig" list). - */ - Nu_ResetCopySetIfUntouched(pArchive); - if (Nu_RecordSet_IsEmpty(&pArchive->copyRecordSet) && - Nu_RecordSet_IsEmpty(&pArchive->newRecordSet)) - { - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - DBUG(("--- All records deleted!\n")); - #if 0 - /* - * Options: - * (1) allow it, leaving an archive with nothing but a header - * that will probably be rejected by other NuFX applications - * (2) reject it, returning an error - * (3) allow it, and just delete the original archive - * - * I dislike #1, and #3 can be implemented by the application - * when it gets a #2. - */ - err = kNuErrAllDeleted; - goto bail; - #else - /* - * (4) go ahead and delete everything, then mark the archive - * as brand new, so that closing the archive with new - * records in it will trigger deletion of the archive file. - */ - deleteAll = true; - #endif - } else { - DBUG(("--- Nothing pending\n")); - goto flushed; - } - } - - /* if we have any changes, we certainly should have the TOC by now */ - Assert(pArchive->haveToc); - Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); - - /* - * Step 2: purge any records from the "copy" and "new" lists that don't - * have any threads. You can't delete threads from the "new" list, but - * it's possible somebody called NuAddRecord and never put anything in it. - */ - err = Nu_PurgeEmptyRecords(pArchive, &pArchive->copyRecordSet); - BailError(err); - err = Nu_PurgeEmptyRecords(pArchive, &pArchive->newRecordSet); - BailError(err); - - /* we checked delete-all actions above, so just check for empty */ - if (Nu_RecordSet_IsEmpty(&pArchive->copyRecordSet) && - Nu_RecordSet_IsEmpty(&pArchive->newRecordSet) && - !deleteAll) - { - DBUG(("--- Nothing pending after purge\n")); - goto flushed; - } - - /* - * Step 3: if we're in ShrinkIt-compatibility mode, add a comment - * thread to the first record in the new list. GSHK does this every - * time it adds files, regardless of the prior contents of the archive. - */ - if (pArchive->valMimicSHK) { - err = Nu_AddCommentToFirstNewRecord(pArchive); - BailError(err); - } - - /* - * Step 4: decide if we want to make changes in place, or write to - * a temp file. Any deletions or additions to existing records will - * require writing to a temp file. Additions of new records and - * updates to pre-sized threads can be done in place. - */ - writeToTemp = true; - if (pArchive->valModifyOrig && Nu_NoHeavyUpdates(pArchive)) - writeToTemp = false; - /* discard the wrapper, if desired */ - if (writeToTemp && pArchive->valDiscardWrapper) - pArchive->headerOffset = 0; - - /* - * Step 5: handle updates to existing records. - */ - if (!writeToTemp) { - /* - * Step 5a: modifying in place, process all UPDATE ThreadMods now. - */ - DBUG(("--- No heavy updates found, updating in place\n")); - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) - canAbort = false; /* modifying original, can't cleanly abort */ - - err = Nu_UpdateInOriginal(pArchive); - if (err == kNuErrDamaged) - *pStatusFlags |= kNuFlushCorrupted; - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "update to original failed"); - goto bail; - } - } else { - /* - * Step 5b: not modifying in place, reconstruct the appropriate - * parts of the original archive in the temp file, possibly copying - * the front bits over first. Updates and thread-adds will be - * done here. - */ - DBUG(("--- Updating to temp file (valModifyOrig=%ld)\n", - pArchive->valModifyOrig)); - err = Nu_CreateTempFromOriginal(pArchive); - if (err != kNuErrNone) { - DBUG(("--- Create temp from original failed\n")); - goto bail; - } - } - /* on completion, tmpFp (or archiveFp) points to current archive EOF */ - - /* - * Step 6: add the new records from the "new" list, if any. Add a - * filename thread to records where one wasn't provided. These records - * are either added to the original archive or the temp file as - * appropriate. - */ - if (writeToTemp) - err = Nu_CreateNewRecords(pArchive, pArchive->tmpFp); - else - err = Nu_CreateNewRecords(pArchive, pArchive->archiveFp); - BailError(err); - - /* on completion, tmpFp (or archiveFp) points to current archive EOF */ - - /* - * Step 7: truncate the archive. This isn't strictly necessary. It - * comes in handy if we were compressing the very last file and it - * actually expanded. We went back and wrote the uncompressed data, - * but there's a bunch of junk after it from the first try. - * - * On systems like Win32 that don't support ftruncate, this will fail, - * so we just ignore the result. - */ - if (writeToTemp) { - err = Nu_FTell(pArchive->tmpFp, &finalOffset); - BailError(err); - (void) Nu_TruncateOpenFile(pArchive->tmpFp, finalOffset); - } else { - err = Nu_FTell(pArchive->archiveFp, &finalOffset); - BailError(err); - (void) Nu_TruncateOpenFile(pArchive->archiveFp, finalOffset); - } - - /* - * Step 8: create an updated master header, and write it to the - * appropriate file. The "newMasterHeader" field in pArchive will - * hold the new header. - */ - Assert(!pArchive->newMasterHeader.isValid); - if (writeToTemp) { - err = Nu_FSeek(pArchive->tmpFp, pArchive->headerOffset, SEEK_SET); - BailError(err); - err = Nu_UpdateMasterHeader(pArchive, pArchive->tmpFp, - finalOffset - pArchive->headerOffset); - /* fall through with err */ - } else { - err = Nu_FSeek(pArchive->archiveFp, pArchive->headerOffset, SEEK_SET); - BailError(err); - err = Nu_UpdateMasterHeader(pArchive, pArchive->archiveFp, - finalOffset - pArchive->headerOffset); - /* fall through with err */ - } - if (err == kNuErrNoRecords && !deleteAll) { - /* - * Somehow we ended up without any records at all. If we managed - * to get this far, it could only be because the user told us to - * skip adding everything. - */ - Nu_ReportError(NU_BLOB, kNuErrNone, "no records in this archive"); - goto bail; - } else if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "failed writing master header"); - goto bail; - } - Assert(pArchive->newMasterHeader.isValid); - - - /* - * Step 9: carry forward the BXY, SEA, or BSE header, if necessary. This - * implicitly assumes that the header doesn't change size. If this - * assumption is invalid, we'd need to adjust "headerOffset" earlier, - * or do lots of data copying. Looks like Binary II and SEA headers - * are both fixed size, so we should be okay. - * - * We also carry forward any unrecognized junk. - */ - if (pArchive->headerOffset) { - if (writeToTemp) { - if (!pArchive->valDiscardWrapper) { - DBUG(("--- Preserving wrapper\n")); - /* copy header to temp */ - err = Nu_CopyWrapperToTemp(pArchive); - BailError(err); - /* update fields that require it */ - err = Nu_UpdateWrapper(pArchive, pArchive->tmpFp); - BailError(err); - /* check the padding */ - err = Nu_AdjustWrapperPadding(pArchive, pArchive->tmpFp); - BailError(err); - } - } else { - /* may need to tweak what's in place? */ - DBUG(("--- Updating wrapper\n")); - err = Nu_UpdateWrapper(pArchive, pArchive->archiveFp); - BailError(err); - /* should only be necessary if we've added new records */ - err = Nu_AdjustWrapperPadding(pArchive, pArchive->archiveFp); - BailError(err); - } - } - - /* - * Step 10: if necessary, remove the original file and rename the - * temp file over it. - * - * I'm not messing with access permissions on the archive file here, - * because if they opened it read-write then the archive itself - * must also be read-write (unless somebody snuck in and chmodded it - * while we were busy). The temp file is certainly writable, so we - * should be able to just leave it all alone. - * - * I'm closing both temp and archive before renaming, because on some - * operating systems you can't do certain things with open files. - */ - if (writeToTemp) { - canAbort = false; /* no going back */ - *pStatusFlags |= kNuFlushSucceeded; /* temp file is fully valid */ - - fclose(pArchive->archiveFp); - pArchive->archiveFp = NULL; - - err = Nu_DeleteArchiveFile(pArchive); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "unable to remove original archive"); - Nu_ReportError(NU_BLOB, kNuErrNone, "New data is in '%s'", - pArchive->tmpPathnameUNI); - *pStatusFlags |= kNuFlushInaccessible; - goto bail_reopen; /* must re-open archiveFp */ - } - - fclose(pArchive->tmpFp); - pArchive->tmpFp = NULL; - - err = Nu_RenameTempToArchive(pArchive); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "unable to rename temp file"); - Nu_ReportError(NU_BLOB, kNuErrNone, - "NOTE: only copy of archive is in '%s'", - pArchive->tmpPathnameUNI); - /* maintain Entry.c semantics (and keep them from removing temp) */ - Nu_Free(pArchive, pArchive->archivePathnameUNI); - pArchive->archivePathnameUNI = NULL; - Nu_Free(pArchive, pArchive->tmpPathnameUNI); - pArchive->tmpPathnameUNI = NULL; - /* bail will put us into read-only mode, which is what we want */ - goto bail; - } - -bail_reopen: - pArchive->archiveFp = fopen(pArchive->archivePathnameUNI, - kNuFileOpenReadWrite); - if (pArchive->archiveFp == NULL) { - err = errno ? errno : -1; - Nu_ReportError(NU_BLOB, err, - "unable to reopen archive file '%s' after rename", - pArchive->archivePathnameUNI); - *pStatusFlags |= kNuFlushInaccessible; - goto bail; /* the Entry.c funcs will obstruct further use */ - } - - if (err != kNuErrNone) // earlier failure? - goto bail; - } else { - fflush(pArchive->archiveFp); - if (ferror(pArchive->archiveFp)) { - err = kNuErrFileWrite; - Nu_ReportError(NU_BLOB, kNuErrNone, "final archive flush failed"); - *pStatusFlags |= kNuFlushCorrupted; - goto bail; - } - canAbort = false; - *pStatusFlags |= kNuFlushSucceeded; - } - - Assert(canAbort == false); - - /* - * Step 11: clean up data structures. If we have a "copy" list, then - * throw out the "orig" list and move the "copy" list over it. Append - * anything in the "new" list to it. Move the "new" master header - * over the original. - */ - Assert(pArchive->newMasterHeader.isValid); - Nu_MasterHeaderCopy(pArchive, &pArchive->masterHeader, - &pArchive->newMasterHeader); - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - err = Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->origRecordSet); - BailError(err); - err = Nu_RecordSet_MoveAllRecords(pArchive, &pArchive->origRecordSet, - &pArchive->copyRecordSet); - BailError(err); - } - err = Nu_RecordSet_MoveAllRecords(pArchive, &pArchive->origRecordSet, - &pArchive->newRecordSet); - BailError(err); - err = Nu_ResetUsedFlags(pArchive, &pArchive->origRecordSet); - BailError(err); - -flushed: - /* - * Step 12: reset the "copy" and "new" lists, and reset the temp file. - * Clear out the "new" master header copy. - */ - err = Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); - BailError(err); - err = Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->newRecordSet); - BailError(err); - pArchive->newMasterHeader.isValid = false; - - err = Nu_ResetTempFile(pArchive); - if (err != kNuErrNone) { - /* can't NuAbort() our way out of a bad temp file */ - canAbort = false; - goto bail; - } - - if (deleteAll) { - /* there's nothing in it, so treat it like a newly-created archive */ - /* (that way it gets deleted if the app closes without adding stuff) */ - DBUG(("--- marking archive as newly created\n")); - pArchive->newlyCreated = true; - /*pArchive->valModifyOrig = true;*/ - } - -bail: - if (err != kNuErrNone) { - if (canAbort) { - (void) Nu_Abort(pArchive); - Assert(!(*pStatusFlags & kNuFlushSucceeded)); - *pStatusFlags |= kNuFlushAborted; - - /* - * If we were adding to original archive, truncate it back if - * we are able to do so. This retains any BXY/BSE wrapper padding. - */ - if (!writeToTemp) { - NuError err2; - err2 = Nu_TruncateOpenFile(pArchive->archiveFp, initialEOF); - if (err2 == kNuErrNone) { - DBUG(("+++ truncated orig archive back to %ld\n", - initialEOF)); - } else { - DBUG(("+++ truncate orig failed (err=%d)\n", err2)); - } - } - } else { - Nu_ReportError(NU_BLOB, kNuErrNone, - "disabling write access after failed update"); - pArchive->openMode = kNuOpenRO; - *pStatusFlags |= kNuFlushReadOnly; - } - } - - /* last-minute sanity check */ - Assert(pArchive->origRecordSet.numRecords == 0 || - (pArchive->origRecordSet.nuRecordHead != NULL && - pArchive->origRecordSet.nuRecordTail != NULL)); - - return err; -} - - -/* - * Abort any pending changes. - */ -NuError Nu_Abort(NuArchive* pArchive) -{ - Assert(pArchive != NULL); - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - - DBUG(("--- Aborting changes\n")); - - /* - * Throw out the "copy" and "new" record sets, and reset the - * temp file. - */ - (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->copyRecordSet); - (void) Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->newRecordSet); - pArchive->newMasterHeader.isValid = false; - - return Nu_ResetTempFile(pArchive); -} - diff --git a/ciderpress/nufxlib/Deflate.c b/ciderpress/nufxlib/Deflate.c deleted file mode 100644 index 6a0e979..0000000 --- a/ciderpress/nufxlib/Deflate.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Support for the "deflate" algorithm, via the "zlib" library. - * - * This compression format is totally unsupported on the Apple II. This - * is provided primarily for the benefit of Apple II emulators that want - * a better storage format for disk images than SHK+LZW or a ZIP file. - * - * This code was developed and tested with ZLIB_VERSION "1.1.3". It is - * expected to work with any version >= 1.1.3 and < 2.x. Please visit - * http://www.zlib.org/ for more information. - */ -#include "NufxLibPriv.h" - -#ifdef ENABLE_DEFLATE -#include "zlib.h" - -#define kNuDeflateLevel 9 /* use maximum compression */ - - -/* - * Alloc and free functions provided to zlib. - */ -static voidpf Nu_zalloc(voidpf opaque, uInt items, uInt size) -{ - return Nu_Malloc(opaque, items * size); -} -static void Nu_zfree(voidpf opaque, voidpf address) -{ - Nu_Free(opaque, address); -} - - -/* - * =========================================================================== - * Compression - * =========================================================================== - */ - -/* - * Compress "srcLen" bytes from "pStraw" to "fp". - */ -NuError Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - z_stream zstream; - int zerr; - Bytef* outbuf = NULL; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - Assert(fp != NULL); - Assert(srcLen > 0); - Assert(pDstLen != NULL); - Assert(pCrc != NULL); - - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - /* allocate a similarly-sized buffer for the output */ - outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); - BailAlloc(outbuf); - - /* - * Initialize the zlib stream. - */ - zstream.zalloc = Nu_zalloc; - zstream.zfree = Nu_zfree; - zstream.opaque = pArchive; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outbuf; - zstream.avail_out = kNuGenCompBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = deflateInit(&zstream, kNuDeflateLevel); - if (zerr != Z_OK) { - err = kNuErrInternal; - if (zerr == Z_VERSION_ERROR) { - Nu_ReportError(NU_BLOB, err, - "installed zlib is not compatible with linked version (%s)", - ZLIB_VERSION); - } else { - Nu_ReportError(NU_BLOB, err, - "call to deflateInit failed (zerr=%d)", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - uint32_t getSize; - int flush; - - /* should be able to read a full buffer every time */ - if (zstream.avail_in == 0 && srcLen) { - getSize = (srcLen > kNuGenCompBufSize) ? kNuGenCompBufSize : srcLen; - DBUG(("+++ reading %ld bytes\n", getSize)); - - err = Nu_StrawRead(pArchive, pStraw, pArchive->compBuf, getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "deflate read failed"); - goto z_bail; - } - - srcLen -= getSize; - - *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getSize); - - zstream.next_in = pArchive->compBuf; - zstream.avail_in = getSize; - } - - if (srcLen == 0) - flush = Z_FINISH; /* tell zlib that we're done */ - else - flush = Z_NO_FLUSH; /* more to come! */ - - zerr = deflate(&zstream, flush); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, err, "zlib deflate call failed (zerr=%d)", - zerr); - goto z_bail; - } - - /* write when we're full or when we're done */ - if (zstream.avail_out == 0 || - (zerr == Z_STREAM_END && zstream.avail_out != kNuGenCompBufSize)) - { - DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf)); - err = Nu_FWrite(fp, outbuf, zstream.next_out - outbuf); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "fwrite failed in deflate"); - goto z_bail; - } - - zstream.next_out = outbuf; - zstream.avail_out = kNuGenCompBufSize; - } - } while (zerr == Z_OK); - - Assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - *pDstLen = zstream.total_out; - -z_bail: - deflateEnd(&zstream); /* free up any allocated structures */ - -bail: - if (outbuf != NULL) - free(outbuf); - return err; -} - - -/* - * =========================================================================== - * Expansion - * =========================================================================== - */ - -/* - * Expand from "infp" to "pFunnel". - */ -NuError Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - z_stream zstream; - int zerr; - uint32_t compRemaining; - Bytef* outbuf; - - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(infp != NULL); - Assert(pFunnel != NULL); - - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - /* allocate a similarly-sized buffer for the output */ - outbuf = Nu_Malloc(pArchive, kNuGenCompBufSize); - BailAlloc(outbuf); - - compRemaining = pThread->thCompThreadEOF; - - /* - * Initialize the zlib stream. - */ - zstream.zalloc = Nu_zalloc; - zstream.zfree = Nu_zfree; - zstream.opaque = pArchive; - zstream.next_in = NULL; - zstream.avail_in = 0; - zstream.next_out = outbuf; - zstream.avail_out = kNuGenCompBufSize; - zstream.data_type = Z_UNKNOWN; - - zerr = inflateInit(&zstream); - if (zerr != Z_OK) { - err = kNuErrInternal; - if (zerr == Z_VERSION_ERROR) { - Nu_ReportError(NU_BLOB, err, - "installed zlib is not compatible with linked version (%s)", - ZLIB_VERSION); - } else { - Nu_ReportError(NU_BLOB, err, - "call to inflateInit failed (zerr=%d)", zerr); - } - goto bail; - } - - /* - * Loop while we have data. - */ - do { - uint32_t getSize; - - /* read as much as we can */ - if (zstream.avail_in == 0) { - getSize = (compRemaining > kNuGenCompBufSize) ? - kNuGenCompBufSize : compRemaining; - DBUG(("+++ reading %ld bytes (%ld left)\n", getSize, - compRemaining)); - - err = Nu_FRead(infp, pArchive->compBuf, getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "inflate read failed"); - goto z_bail; - } - - compRemaining -= getSize; - - zstream.next_in = pArchive->compBuf; - zstream.avail_in = getSize; - } - - /* uncompress the data */ - zerr = inflate(&zstream, Z_NO_FLUSH); - if (zerr != Z_OK && zerr != Z_STREAM_END) { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, err, "zlib inflate call failed (zerr=%d)", - zerr); - goto z_bail; - } - - /* write every time there's anything (buffer will usually be full) */ - if (zstream.avail_out != kNuGenCompBufSize) { - DBUG(("+++ writing %d bytes\n", zstream.next_out - outbuf)); - err = Nu_FunnelWrite(pArchive, pFunnel, outbuf, - zstream.next_out - outbuf); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "write failed in inflate"); - goto z_bail; - } - - if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, outbuf, zstream.next_out - outbuf); - - zstream.next_out = outbuf; - zstream.avail_out = kNuGenCompBufSize; - } - } while (zerr == Z_OK); - - Assert(zerr == Z_STREAM_END); /* other errors should've been caught */ - - if (zstream.total_out != pThread->actualThreadEOF) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, - "size mismatch on inflated file (%ld vs %u)", - zstream.total_out, pThread->actualThreadEOF); - goto z_bail; - } - -z_bail: - inflateEnd(&zstream); /* free up any allocated structures */ - -bail: - if (outbuf != NULL) - free(outbuf); - return err; -} - -#endif /*ENABLE_DEFLATE*/ diff --git a/ciderpress/nufxlib/Entry.c b/ciderpress/nufxlib/Entry.c deleted file mode 100644 index e9f0fd3..0000000 --- a/ciderpress/nufxlib/Entry.c +++ /dev/null @@ -1,832 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * All external entry points. - */ -#include "NufxLibPriv.h" - - -/* - * =========================================================================== - * Misc utils - * =========================================================================== - */ - -/* - * Set the busy flag. - * - * The busy flag is intended to prevent the caller from executing illegal - * operations while inside a callback function. It is NOT intended to - * allow concurrent access to the same archive from multiple threads, so - * it does not follow all sorts of crazy semaphore semantics. If you - * have the need, go ahead and fix it. - */ -static inline void Nu_SetBusy(NuArchive* pArchive) -{ - pArchive->busy = true; -} - -/* - * Clear the busy flag. - */ -static inline void Nu_ClearBusy(NuArchive* pArchive) -{ - pArchive->busy = false; -} - - -/* - * Do a partial validation on NuArchive. Some calls, such as GetExtraData, - * can be made during callback functions when the archive isn't fully - * consistent. - */ -static NuError Nu_PartiallyValidateNuArchive(const NuArchive* pArchive) -{ - if (pArchive == NULL) - return kNuErrInvalidArg; - - if (pArchive->structMagic != kNuArchiveStructMagic) - return kNuErrBadStruct; - - return kNuErrNone; -} - -/* - * Validate the NuArchive* argument passed in to us. - */ -static NuError Nu_ValidateNuArchive(const NuArchive* pArchive) -{ - NuError err; - - err = Nu_PartiallyValidateNuArchive(pArchive); - if (err != kNuErrNone) - return err; - - /* explicitly block reentrant calls */ - if (pArchive->busy) - return kNuErrBusy; - - /* make sure the TOC state is consistent */ - if (pArchive->haveToc) { - if (pArchive->masterHeader.mhTotalRecords != 0) - Assert(Nu_RecordSet_GetListHead(&pArchive->origRecordSet) != NULL); - Assert(Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet) == - pArchive->masterHeader.mhTotalRecords); - } else { - Assert(Nu_RecordSet_GetListHead(&pArchive->origRecordSet) == NULL); - } - - /* make sure we have open files to work with */ - Assert(pArchive->archivePathnameUNI == NULL || pArchive->archiveFp != NULL); - if (pArchive->archivePathnameUNI != NULL && pArchive->archiveFp == NULL) - return kNuErrInternal; - Assert(pArchive->tmpPathnameUNI == NULL || pArchive->tmpFp != NULL); - if (pArchive->tmpPathnameUNI != NULL && pArchive->tmpFp == NULL) - return kNuErrInternal; - - /* further validations */ - - return kNuErrNone; -} - - -/* - * =========================================================================== - * Streaming and non-streaming read-only - * =========================================================================== - */ - -NUFXLIB_API NuError NuStreamOpenRO(FILE* infp, NuArchive** ppArchive) -{ - NuError err; - - if (infp == NULL || ppArchive == NULL) - return kNuErrInvalidArg; - - err = Nu_StreamOpenRO(infp, (NuArchive**) ppArchive); - - return err; -} - -NUFXLIB_API NuError NuContents(NuArchive* pArchive, NuCallback contentFunc) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - if (Nu_IsStreaming(pArchive)) - err = Nu_StreamContents(pArchive, contentFunc); - else - err = Nu_Contents(pArchive, contentFunc); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuExtract(NuArchive* pArchive) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - if (Nu_IsStreaming(pArchive)) - err = Nu_StreamExtract(pArchive); - else - err = Nu_Extract(pArchive); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuTest(NuArchive* pArchive) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - if (Nu_IsStreaming(pArchive)) - err = Nu_StreamTest(pArchive); - else - err = Nu_Test(pArchive); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuTestRecord(NuArchive* pArchive, NuRecordIdx recordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_TestRecord(pArchive, recordIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - - -/* - * =========================================================================== - * Strictly non-streaming read-only - * =========================================================================== - */ - -NUFXLIB_API NuError NuOpenRO(const UNICHAR* archivePathnameUNI, - NuArchive** ppArchive) -{ - NuError err; - - err = Nu_OpenRO(archivePathnameUNI, (NuArchive**) ppArchive); - - return err; -} - -NUFXLIB_API NuError NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_ExtractRecord(pArchive, recordIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSink* pDataSink) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_ExtractThread(pArchive, threadIdx, pDataSink); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecord** ppRecord) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_GetRecord(pArchive, recordIdx, ppRecord); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuGetRecordIdxByName(NuArchive* pArchive, - const char* nameMOR, NuRecordIdx* pRecordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_GetRecordIdxByName(pArchive, nameMOR, pRecordIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuGetRecordIdxByPosition(NuArchive* pArchive, uint32_t position, - NuRecordIdx* pRecordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_GetRecordIdxByPosition(pArchive, position, pRecordIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - - -/* - * =========================================================================== - * Read/Write - * =========================================================================== - */ - -NUFXLIB_API NuError NuOpenRW(const UNICHAR* archivePathnameUNI, - const UNICHAR* tmpPathnameUNI, uint32_t flags, NuArchive** ppArchive) -{ - NuError err; - - err = Nu_OpenRW(archivePathnameUNI, tmpPathnameUNI, flags, - (NuArchive**) ppArchive); - - return err; -} - -NUFXLIB_API NuError NuFlush(NuArchive* pArchive, uint32_t* pStatusFlags) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_Flush(pArchive, pStatusFlags); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuAbort(NuArchive* pArchive) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_Abort(pArchive); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuAddRecord(NuArchive* pArchive, - const NuFileDetails* pFileDetails, NuRecordIdx* pRecordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_AddRecord(pArchive, pFileDetails, pRecordIdx, NULL); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx, - NuThreadID threadID, NuDataSource* pDataSource, NuThreadIdx* pThreadIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_AddThread(pArchive, recordIdx, threadID, - pDataSource, pThreadIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuAddFile(NuArchive* pArchive, const UNICHAR* pathnameUNI, - const NuFileDetails* pFileDetails, short isFromRsrcFork, - NuRecordIdx* pRecordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_AddFile(pArchive, pathnameUNI, pFileDetails, - (Boolean)(isFromRsrcFork != 0), pRecordIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuRename(NuArchive* pArchive, NuRecordIdx recordIdx, - const char* pathnameMOR, char fssep) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_Rename(pArchive, recordIdx, pathnameMOR, fssep); - Nu_ClearBusy(pArchive); - } - - return err; -} - - -NUFXLIB_API NuError NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecordAttr* pRecordAttr) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_SetRecordAttr(pArchive, recordIdx, pRecordAttr); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuUpdatePresizedThread(NuArchive* pArchive, - NuThreadIdx threadIdx, NuDataSource* pDataSource, int32_t* pMaxLen) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_UpdatePresizedThread(pArchive, threadIdx, - pDataSource, pMaxLen); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuDelete(NuArchive* pArchive) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_Delete(pArchive); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_DeleteRecord(pArchive, recordIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_DeleteThread(pArchive, threadIdx); - Nu_ClearBusy(pArchive); - } - - return err; -} - - -/* - * =========================================================================== - * General interfaces - * =========================================================================== - */ - -NUFXLIB_API NuError NuClose(NuArchive* pArchive) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - Nu_SetBusy(pArchive); - err = Nu_Close(pArchive); - /* on success, pArchive has been freed */ - if (err != kNuErrNone) - Nu_ClearBusy(pArchive); - } - - return err; -} - -NUFXLIB_API NuError NuGetMasterHeader(NuArchive* pArchive, - const NuMasterHeader** ppMasterHeader) -{ - NuError err; - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) - err = Nu_GetMasterHeader(pArchive, ppMasterHeader); - - return err; -} - -NUFXLIB_API NuError NuGetExtraData(NuArchive* pArchive, void** ppData) -{ - NuError err; - - if (ppData == NULL) - return kNuErrInvalidArg; - if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) - *ppData = pArchive->extraData; - - return err; -} - -NUFXLIB_API NuError NuSetExtraData(NuArchive* pArchive, void* pData) -{ - NuError err; - - if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) - pArchive->extraData = pData; - - return err; -} - -NUFXLIB_API NuError NuGetValue(NuArchive* pArchive, NuValueID ident, - NuValue* pValue) -{ - NuError err; - - if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) - return Nu_GetValue(pArchive, ident, pValue); - - return err; -} - -NUFXLIB_API NuError NuSetValue(NuArchive* pArchive, NuValueID ident, - NuValue value) -{ - NuError err; - - if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) - return Nu_SetValue(pArchive, ident, value); - - return err; -} - -NUFXLIB_API NuError NuGetAttr(NuArchive* pArchive, NuAttrID ident, - NuAttr* pAttr) -{ - NuError err; - - if ((err = Nu_PartiallyValidateNuArchive(pArchive)) == kNuErrNone) - return Nu_GetAttr(pArchive, ident, pAttr); - - return err; -} - -NUFXLIB_API NuError NuDebugDumpArchive(NuArchive* pArchive) -{ -#if defined(DEBUG_MSGS) - /* skip validation checks for this one */ - Nu_DebugDumpAll(pArchive); - return kNuErrNone; -#else - /* function doesn't exist */ - return kNuErrGeneric; -#endif -} - - -/* - * =========================================================================== - * Sources and Sinks - * =========================================================================== - */ - -NUFXLIB_API NuError NuCreateDataSourceForFile(NuThreadFormat threadFormat, - uint32_t otherLen, const UNICHAR* pathnameUNI, short isFromRsrcFork, - NuDataSource** ppDataSource) -{ - return Nu_DataSourceFile_New(threadFormat, otherLen, - pathnameUNI, (Boolean)(isFromRsrcFork != 0), ppDataSource); -} - -NUFXLIB_API NuError NuCreateDataSourceForFP(NuThreadFormat threadFormat, - uint32_t otherLen, FILE* fp, long offset, long length, - NuCallback fcloseFunc, NuDataSource** ppDataSource) -{ - return Nu_DataSourceFP_New(threadFormat, otherLen, - fp, offset, length, fcloseFunc, ppDataSource); -} - -NUFXLIB_API NuError NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, - uint32_t otherLen, const uint8_t* buffer, long offset, - long length, NuCallback freeFunc, NuDataSource** ppDataSource) -{ - return Nu_DataSourceBuffer_New(threadFormat, otherLen, - buffer, offset, length, freeFunc, ppDataSource); -} - -NUFXLIB_API NuError NuFreeDataSource(NuDataSource* pDataSource) -{ - return Nu_DataSourceFree(pDataSource); -} - -NUFXLIB_API NuError NuDataSourceSetRawCrc(NuDataSource* pDataSource, - uint16_t crc) -{ - if (pDataSource == NULL) - return kNuErrInvalidArg; - Nu_DataSourceSetRawCrc(pDataSource, crc); - return kNuErrNone; -} - -NUFXLIB_API NuError NuCreateDataSinkForFile(short doExpand, NuValue convertEOL, - const UNICHAR* pathnameUNI, UNICHAR fssep, NuDataSink** ppDataSink) -{ - return Nu_DataSinkFile_New((Boolean)(doExpand != 0), convertEOL, - pathnameUNI, fssep, ppDataSink); -} - -NUFXLIB_API NuError NuCreateDataSinkForFP(short doExpand, NuValue convertEOL, - FILE* fp, NuDataSink** ppDataSink) -{ - return Nu_DataSinkFP_New((Boolean)(doExpand != 0), convertEOL, fp, - ppDataSink); -} - -NUFXLIB_API NuError NuCreateDataSinkForBuffer(short doExpand, - NuValue convertEOL, uint8_t* buffer, uint32_t bufLen, - NuDataSink** ppDataSink) -{ - return Nu_DataSinkBuffer_New((Boolean)(doExpand != 0), convertEOL, buffer, - bufLen, ppDataSink); -} - -NUFXLIB_API NuError NuFreeDataSink(NuDataSink* pDataSink) -{ - return Nu_DataSinkFree(pDataSink); -} - -NUFXLIB_API NuError NuDataSinkGetOutCount(NuDataSink* pDataSink, - uint32_t* pOutCount) -{ - if (pDataSink == NULL || pOutCount == NULL) - return kNuErrInvalidArg; - - *pOutCount = Nu_DataSinkGetOutCount(pDataSink); - return kNuErrNone; -} - - -/* - * =========================================================================== - * Non-archive operations - * =========================================================================== - */ - -NUFXLIB_API const char* NuStrError(NuError err) -{ - return Nu_StrError(err); -} - -NUFXLIB_API NuError NuGetVersion(int32_t* pMajorVersion, int32_t* pMinorVersion, - int32_t* pBugVersion, const char** ppBuildDate, const char** ppBuildFlags) -{ - return Nu_GetVersion(pMajorVersion, pMinorVersion, pBugVersion, - ppBuildDate, ppBuildFlags); -} - -NUFXLIB_API NuError NuTestFeature(NuFeature feature) -{ - NuError err = kNuErrUnsupFeature; - - switch (feature) { - case kNuFeatureCompressSQ: - #ifdef ENABLE_SQ - err = kNuErrNone; - #endif - break; - case kNuFeatureCompressLZW: - #ifdef ENABLE_LZW - err = kNuErrNone; - #endif - break; - case kNuFeatureCompressLZC: - #ifdef ENABLE_LZC - err = kNuErrNone; - #endif - break; - case kNuFeatureCompressDeflate: - #ifdef ENABLE_DEFLATE - err = kNuErrNone; - #endif - break; - case kNuFeatureCompressBzip2: - #ifdef ENABLE_BZIP2 - err = kNuErrNone; - #endif - break; - default: - err = kNuErrUnknownFeature; - break; - } - - return err; -} - -NUFXLIB_API void NuRecordCopyAttr(NuRecordAttr* pRecordAttr, - const NuRecord* pRecord) -{ - pRecordAttr->fileSysID = pRecord->recFileSysID; - /*pRecordAttr->fileSysInfo = pRecord->recFileSysInfo;*/ - pRecordAttr->access = pRecord->recAccess; - pRecordAttr->fileType = pRecord->recFileType; - pRecordAttr->extraType = pRecord->recExtraType; - pRecordAttr->createWhen = pRecord->recCreateWhen; - pRecordAttr->modWhen = pRecord->recModWhen; - pRecordAttr->archiveWhen = pRecord->recArchiveWhen; -} - -NUFXLIB_API NuError NuRecordCopyThreads(const NuRecord* pNuRecord, - NuThread** ppThreads) -{ - if (pNuRecord == NULL || ppThreads == NULL) - return kNuErrInvalidArg; - - Assert(pNuRecord->pThreads != NULL); - - *ppThreads = Nu_Malloc(NULL, pNuRecord->recTotalThreads * sizeof(NuThread)); - if (*ppThreads == NULL) - return kNuErrMalloc; - - memcpy(*ppThreads, pNuRecord->pThreads, - pNuRecord->recTotalThreads * sizeof(NuThread)); - - return kNuErrNone; -} - -NUFXLIB_API uint32_t NuRecordGetNumThreads(const NuRecord* pNuRecord) -{ - if (pNuRecord == NULL) - return -1; - - return pNuRecord->recTotalThreads; -} - -NUFXLIB_API const NuThread* NuThreadGetByIdx(const NuThread* pNuThread, - int32_t idx) -{ - if (pNuThread == NULL) - return NULL; - return &pNuThread[idx]; /* can't range-check here */ -} - -NUFXLIB_API short NuIsPresizedThreadID(NuThreadID threadID) -{ - return Nu_IsPresizedThreadID(threadID); -} - -NUFXLIB_API size_t NuConvertMORToUNI(const char* stringMOR, - UNICHAR* bufUNI, size_t bufSize) -{ - return Nu_ConvertMORToUNI(stringMOR, bufUNI, bufSize); -} - -NUFXLIB_API size_t NuConvertUNIToMOR(const UNICHAR* stringUNI, - char* bufMOR, size_t bufSize) -{ - return Nu_ConvertUNIToMOR(stringUNI, bufMOR, bufSize); -} - - -/* - * =========================================================================== - * Callback setters - * =========================================================================== - */ - -NUFXLIB_API NuCallback NuSetSelectionFilter(NuArchive* pArchive, - NuCallback filterFunc) -{ - NuError err; - NuCallback oldFunc = kNuInvalidCallback; - - /*Assert(!((uint32_t)filterFunc % 4));*/ - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - oldFunc = pArchive->selectionFilterFunc; - pArchive->selectionFilterFunc = filterFunc; - } - - return oldFunc; -} - -NUFXLIB_API NuCallback NuSetOutputPathnameFilter(NuArchive* pArchive, - NuCallback filterFunc) -{ - NuError err; - NuCallback oldFunc = kNuInvalidCallback; - - /*Assert(!((uint32_t)filterFunc % 4));*/ - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - oldFunc = pArchive->outputPathnameFunc; - pArchive->outputPathnameFunc = filterFunc; - } - - return oldFunc; -} - -NUFXLIB_API NuCallback NuSetProgressUpdater(NuArchive* pArchive, - NuCallback updateFunc) -{ - NuError err; - NuCallback oldFunc = kNuInvalidCallback; - - /*Assert(!((uint32_t)updateFunc % 4));*/ - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - oldFunc = pArchive->progressUpdaterFunc; - pArchive->progressUpdaterFunc = updateFunc; - } - - return oldFunc; -} - -NUFXLIB_API NuCallback NuSetErrorHandler(NuArchive* pArchive, - NuCallback errorFunc) -{ - NuError err; - NuCallback oldFunc = kNuInvalidCallback; - - /*Assert(!((uint32_t)errorFunc % 4));*/ - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - oldFunc = pArchive->errorHandlerFunc; - pArchive->errorHandlerFunc = errorFunc; - } - - return oldFunc; -} - -NUFXLIB_API NuCallback NuSetErrorMessageHandler(NuArchive* pArchive, - NuCallback messageHandlerFunc) -{ - NuError err; - NuCallback oldFunc = kNuInvalidCallback; - - /*Assert(!((uint32_t)messageHandlerFunc % 4));*/ - - if ((err = Nu_ValidateNuArchive(pArchive)) == kNuErrNone) { - oldFunc = pArchive->messageHandlerFunc; - pArchive->messageHandlerFunc = messageHandlerFunc; - } - - return oldFunc; -} - -NUFXLIB_API NuCallback NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc) -{ - NuCallback oldFunc = kNuInvalidCallback; - /*Assert(!((uint32_t)messageHandlerFunc % 4));*/ - - oldFunc = gNuGlobalErrorMessageHandler; - gNuGlobalErrorMessageHandler = messageHandlerFunc; - return oldFunc; -} - diff --git a/ciderpress/nufxlib/Expand.c b/ciderpress/nufxlib/Expand.c deleted file mode 100644 index 76b9d2b..0000000 --- a/ciderpress/nufxlib/Expand.c +++ /dev/null @@ -1,228 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Expand a thread from an archive. - */ -#include "NufxLibPriv.h" - - -/* - * "Expand" an uncompressed thread. - */ -static NuError Nu_ExpandUncompressed(NuArchive* pArchive, - const NuRecord* pRecord, const NuThread* pThread, FILE* infp, - NuFunnel* pFunnel, uint16_t* pCrc) -{ - NuError err; - /*uint8_t* buffer = NULL;*/ - uint32_t count, getsize; - - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(infp != NULL); - Assert(pFunnel != NULL); - - /* doesn't have to be same size as funnel, but it's not a bad idea */ - /*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/ - /*BailAlloc(buffer);*/ - err = Nu_AllocCompressionBufferIFN(pArchive); - BailError(err); - - /* quick assert for bad archive that should have been caught earlier */ - /* (filename threads are uncompressed, but compThreadEOF is buf len) */ - if (pThread->thThreadClass == kNuThreadClassData) - Assert(pThread->actualThreadEOF == pThread->thCompThreadEOF); - - count = pThread->actualThreadEOF; - - while (count) { - getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; - - err = Nu_FRead(infp, pArchive->compBuf, getsize); - BailError(err); - if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, pArchive->compBuf, getsize); - err = Nu_FunnelWrite(pArchive, pFunnel, pArchive->compBuf, getsize); - BailError(err); - - count -= getsize; - } - - err = Nu_FunnelFlush(pArchive, pFunnel); - BailError(err); - -bail: - /*Nu_Free(pArchive, buffer);*/ - return err; -} - -/* - * Copy the "raw" data out of the thread. Unlike the preceeding function, - * this reads up to "thCompThreadEOF", and doesn't even try to compute a CRC. - */ -static NuError Nu_ExpandRaw(NuArchive* pArchive, const NuThread* pThread, - FILE* infp, NuFunnel* pFunnel) -{ - NuError err; - /*uint8_t* buffer = NULL;*/ - uint32_t count, getsize; - - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(infp != NULL); - Assert(pFunnel != NULL); - - /* doesn't have to be same size as funnel, but it's not a bad idea */ - /*buffer = Nu_Malloc(pArchive, kNuFunnelBufSize);*/ - /*BailAlloc(buffer);*/ - err = Nu_AllocCompressionBufferIFN(pArchive); - BailError(err); - - count = pThread->thCompThreadEOF; - - while (count) { - getsize = (count > kNuGenCompBufSize) ? kNuGenCompBufSize : count; - - err = Nu_FRead(infp, pArchive->compBuf, getsize); - BailError(err); - err = Nu_FunnelWrite(pArchive, pFunnel, pArchive->compBuf, getsize); - BailError(err); - - count -= getsize; - } - - err = Nu_FunnelFlush(pArchive, pFunnel); - BailError(err); - -bail: - /*Nu_Free(pArchive, buffer);*/ - return err; -} - - -/* - * Expand a thread from "infp" to "pFunnel", using the compression - * and stream length specified by "pThread". - */ -NuError Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel) -{ - NuError err = kNuErrNone; - uint16_t calcCrc; - uint16_t* pCalcCrc; - - if (!pThread->thThreadEOF && !pThread->thCompThreadEOF) { - /* somebody stored an empty file! */ - goto done; - } - - /* - * A brief history of the "threadCRC" field in the thread header: - * record versions 0 and 1 didn't use the threadCRC field - * record version 2 put the CRC of the compressed data in threadCRC - * record version 3 put the CRC of the uncompressed data in threadCRC - * - * P8 ShrinkIt uses v1, GSHK uses v3. If something ever shipped with - * v2, it didn't last long enough to leave an impression, so I'm not - * going to support it. BTW, P8 ShrinkIt always uses LZW/1, which - * puts a CRC in the compressed stream. Your uncompressed data is, - * unfortunately, unprotected before v3. - */ - calcCrc = kNuInitialThreadCRC; - pCalcCrc = NULL; - if (Nu_ThreadHasCRC(pRecord->recVersionNumber, NuGetThreadID(pThread)) && - !pArchive->valIgnoreCRC) - { - pCalcCrc = &calcCrc; - } - - err = Nu_ProgressDataExpandPrep(pArchive, pFunnel, pThread); - BailError(err); - - /* - * If we're not expanding the data, use a simple copier. - */ - if (!Nu_FunnelGetDoExpand(pFunnel)) { - Nu_FunnelSetProgressState(pFunnel, kNuProgressCopying); - err = Nu_ExpandRaw(pArchive, pThread, infp, pFunnel); - BailError(err); - goto done; - } - - Nu_FunnelSetProgressState(pFunnel, kNuProgressExpanding); - switch (pThread->thThreadFormat) { - case kNuThreadFormatUncompressed: - Nu_FunnelSetProgressState(pFunnel, kNuProgressCopying); - err = Nu_ExpandUncompressed(pArchive, pRecord, pThread, infp, pFunnel, - pCalcCrc); - break; - #ifdef ENABLE_SQ - case kNuThreadFormatHuffmanSQ: - err = Nu_ExpandHuffmanSQ(pArchive, pRecord, pThread, infp, pFunnel, - pCalcCrc); - break; - #endif - #ifdef ENABLE_LZW - case kNuThreadFormatLZW1: - case kNuThreadFormatLZW2: - err = Nu_ExpandLZW(pArchive, pRecord, pThread, infp, pFunnel, pCalcCrc); - break; - #endif - #ifdef ENABLE_LZC - case kNuThreadFormatLZC12: - case kNuThreadFormatLZC16: - err = Nu_ExpandLZC(pArchive, pRecord, pThread, infp, pFunnel, pCalcCrc); - break; - #endif - #ifdef ENABLE_DEFLATE - case kNuThreadFormatDeflate: - err = Nu_ExpandDeflate(pArchive, pRecord, pThread, infp, pFunnel, - pCalcCrc); - break; - #endif - #ifdef ENABLE_BZIP2 - case kNuThreadFormatBzip2: - err = Nu_ExpandBzip2(pArchive, pRecord, pThread, infp, pFunnel, - pCalcCrc); - break; - #endif - default: - err = kNuErrBadFormat; - Nu_ReportError(NU_BLOB, err, - "compression format %u not supported", pThread->thThreadFormat); - break; - } - BailError(err); - - err = Nu_FunnelFlush(pArchive, pFunnel); - BailError(err); - - /* - * If we have a CRC to check, check it. - */ - if (pCalcCrc != NULL) { - if (calcCrc != pThread->thThreadCRC) { - if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadThreadCRC)) { - err = kNuErrBadDataCRC; - Nu_ReportError(NU_BLOB, err, "expected 0x%04x, got 0x%04x", - pThread->thThreadCRC, calcCrc); - goto bail; - } - } else { - DBUG(("--- thread CRCs match\n")); - } - } - -done: - /* make sure we send a final "success" progress message at 100% */ - (void) Nu_FunnelSetProgressState(pFunnel, kNuProgressDone); - err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel); - BailError(err); - -bail: - return err; -} - diff --git a/ciderpress/nufxlib/FileIO.c b/ciderpress/nufxlib/FileIO.c deleted file mode 100644 index 7e87685..0000000 --- a/ciderpress/nufxlib/FileIO.c +++ /dev/null @@ -1,1480 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Operations on output (i.e. non-archive) files, largely system-specific. - * Portions taken from NuLib, including some code that Devin Reade worked on. - * - * It could be argued that "create file" should be a callback function, - * since it is so heavily system-specific, and most of the other - * system dependencies are handled by the application rather than the - * NuFX library. It would also provide a cleaner solution for renaming - * extracted files. However, the goal of the library is to do the work - * for the application, not the other way around; and while it might be - * nice to offload all direct file handling on the application, it - * complicates rather than simplifies the interface. - */ -#include "NufxLibPriv.h" - -#ifdef MAC_LIKE -# include -#endif - -/* - * For systems (e.g. Visual C++ 6.0) that don't have these standard values. - */ -#ifndef S_IRUSR -# define S_IRUSR 0400 -# define S_IWUSR 0200 -# define S_IXUSR 0100 -# define S_IRWXU (S_IRUSR|S_IWUSR|S_IXUSR) -# define S_IRGRP (S_IRUSR >> 3) -# define S_IWGRP (S_IWUSR >> 3) -# define S_IXGRP (S_IXUSR >> 3) -# define S_IRWXG (S_IRWXU >> 3) -# define S_IROTH (S_IRGRP >> 3) -# define S_IWOTH (S_IWGRP >> 3) -# define S_IXOTH (S_IXGRP >> 3) -# define S_IRWXO (S_IRWXG >> 3) -#endif -#ifndef S_ISREG -# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) -# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) -#endif - -/* - * =========================================================================== - * DateTime conversions - * =========================================================================== - */ - -/* - * Dates and times in a NuFX archive are always considered to be in the - * local time zone. The use of GMT time stamps would have been more - * appropriate for an archive, but local time works well enough. - * - * Regarding Y2K on the Apple II: - * - * Dave says P8 drivers should return year values in the range 0..99, where - * 40..99 = 1940..1999, and 0..39 = 2000..2039. Year values 100..127 should - * never be used. For ProDOS 8, the year 2000 is "00". - * - * The IIgs ReadTimeHex call uses "year minus 1900". For GS/OS, the year - * 2000 is "100". - * - * The NuFX file type note says the archive format should work like - * The IIgs ReadTimeHex call, which uses "year minus 1900" as its - * format. GS/ShrinkIt v1.1 uses the IIgs date calls, and so stores the - * year 2000 as "100". P8 ShrinkIt v3.4 uses the P8 mapping, and stores - * it as "0". Neither really quite understands what the other is doing. - * - * For our purposes, we will follow the NuFX standard and emit "100" - * for the year 2000, but will accept and understand "0" as well. - */ - - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) -/* - * Convert from local time in a NuDateTime struct to GMT seconds since 1970. - * - * If the conversion is invalid, "*pWhen" is set to zero. - */ -static void Nu_DateTimeToGMTSeconds(const NuDateTime* pDateTime, time_t* pWhen) -{ - struct tm tmbuf; - time_t when; - - Assert(pDateTime != NULL); - Assert(pWhen != NULL); - - tmbuf.tm_sec = pDateTime->second; - tmbuf.tm_min = pDateTime->minute; - tmbuf.tm_hour = pDateTime->hour; - tmbuf.tm_mday = pDateTime->day +1; - tmbuf.tm_mon = pDateTime->month; - tmbuf.tm_year = pDateTime->year; - if (pDateTime->year < 40) - tmbuf.tm_year += 100; /* P8 uses 0-39 for 2000-2039 */ - tmbuf.tm_wday = 0; - tmbuf.tm_yday = 0; - tmbuf.tm_isdst = -1; /* let it figure DST and time zone */ - - #if defined(HAVE_MKTIME) - when = mktime(&tmbuf); - #elif defined(HAVE_TIMELOCAL) - when = timelocal(&tmbuf); - #else - # error "need time converter" - #endif - - if (when == (time_t) -1) - *pWhen = 0; - else - *pWhen = when; -} - -/* - * Convert from GMT seconds since 1970 to local time in a NuDateTime struct. - */ -static void Nu_GMTSecondsToDateTime(const time_t* pWhen, NuDateTime *pDateTime) -{ - struct tm* ptm; - - Assert(pWhen != NULL); - Assert(pDateTime != NULL); - - #if defined(HAVE_LOCALTIME_R) && defined(USE_REENTRANT_CALLS) - struct tm res; - ptm = localtime_r(pWhen, &res); - #else - /* NOTE: not thread-safe */ - ptm = localtime(pWhen); - #endif - pDateTime->second = ptm->tm_sec; - pDateTime->minute = ptm->tm_min; - pDateTime->hour = ptm->tm_hour; - pDateTime->day = ptm->tm_mday -1; - pDateTime->month = ptm->tm_mon; - pDateTime->year = ptm->tm_year; - pDateTime->extra = 0; - pDateTime->weekDay = ptm->tm_wday +1; -} -#endif - - -/* - * Fill in the current time. - */ -void Nu_SetCurrentDateTime(NuDateTime* pDateTime) -{ - Assert(pDateTime != NULL); - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - { - time_t now = time(NULL); - Nu_GMTSecondsToDateTime(&now, pDateTime); - } -#else - #error "Port this" -#endif -} - - -/* - * Returns "true" if "pWhen1" is older than "pWhen2". Returns false if - * "pWhen1" is the same age or newer than "pWhen2". - * - * On systems with mktime, it would be straightforward to convert the dates - * to time in seconds, and compare them that way. However, I don't want - * to rely on that function too heavily, so we just compare fields. - */ -Boolean Nu_IsOlder(const NuDateTime* pWhen1, const NuDateTime* pWhen2) -{ - long result, year1, year2; - - /* adjust for P8 ShrinkIt Y2K problem */ - year1 = pWhen1->year; - if (year1 < 40) - year1 += 100; - year2 = pWhen2->year; - if (year2 < 40) - year2 += 100; - - result = year1 - year2; - if (!result) - result = pWhen1->month - pWhen2->month; - if (!result) - result = pWhen1->day - pWhen2->day; - if (!result) - result = pWhen1->hour - pWhen2->hour; - if (!result) - result = pWhen1->minute - pWhen2->minute; - if (!result) - result = pWhen1->second - pWhen2->second; - - if (result < 0) - return true; - return false; -} - - -/* - * =========================================================================== - * Get/set file info - * =========================================================================== - */ - -/* - * System-independent (mostly) file info struct. - */ -typedef struct NuFileInfo { - Boolean isValid; /* init to "false", set "true" after we get data */ - - Boolean isRegularFile; /* is this a regular file? */ - Boolean isDirectory; /* is this a directory? */ - Boolean isForked; /* does file have a non-empty resource fork? */ - - uint32_t dataEof; - - NuDateTime modWhen; - mode_t unixMode; /* UNIX-style permissions */ -} NuFileInfo; - -#define kDefaultFileType 0 /* "NON" */ -#define kDefaultAuxType 0 /* $0000 */ - - -/* - * Determine whether the record has both data and resource forks. - * - * TODO: if we're not using "mask dataless", scanning threads may not - * get the right answer, because GSHK omits theads for zero-length forks. - * We could check pRecord->recStorageType, though we have to be careful - * because that's overloaded for disk images. In any event, the result - * from this method isn't relevant unless we're trying to use forked - * files on the native filesystem. - */ -static Boolean Nu_IsForkedFile(NuArchive* pArchive, const NuRecord* pRecord) -{ - const NuThread* pThread; - NuThreadID threadID; - Boolean gotData, gotRsrc; - int i; - - gotData = gotRsrc = false; - - for (i = 0; i < (int)pRecord->recTotalThreads; i++) { - pThread = Nu_GetThread(pRecord, i); - Assert(pThread != NULL); - - threadID = NuMakeThreadID(pThread->thThreadClass,pThread->thThreadKind); - if (threadID == kNuThreadIDDataFork) - gotData = true; - else if (threadID == kNuThreadIDRsrcFork) - gotRsrc = true; - } - - if (gotData && gotRsrc) - return true; - else - return false; -} - - -#if defined(MAC_LIKE) -# if defined(HAS_RESOURCE_FORKS) -/* - * String to append to the filename to access the resource fork. - * - * This appears to be the correct way to access the resource fork, since - * at least OS X 10.1. Up until 10.7 ("Lion", July 2011), you could also - * access the fork with "/rsrc". - */ -static const char kMacRsrcPath[] = "/..namedfork/rsrc"; - -/* - * Generates the resource fork pathname from the file path. - * - * The caller must free the string returned. - */ -static UNICHAR* GetResourcePath(const UNICHAR* pathnameUNI) -{ - Assert(pathnameUNI != NULL); - - // sizeof(kMacRsrcPath) includes the string and the terminating null byte - const size_t bufLen = - strlen(pathnameUNI) * sizeof(UNICHAR) + sizeof(kMacRsrcPath); - char* buf; - - buf = (char*) malloc(bufLen); - snprintf(buf, bufLen, "%s%s", pathnameUNI, kMacRsrcPath); - return buf; -} -# endif /*HAS_RESOURCE_FORKS*/ - -/* - * Due to historical reasons, the XATTR_FINDERINFO_NAME (defined to be - * ``com.apple.FinderInfo'') extended attribute must be 32 bytes; see the - * ATTR_CMN_FNDRINFO section in getattrlist(2). - * - * The FinderInfo block is the concatenation of a FileInfo structure - * and an ExtendedFileInfo (or ExtendedFolderInfo) structure -- see - * ATTR_CMN_FNDRINFO in getattrlist(2). - * - * All we're really interested in is the file type and creator code, - * which are stored big-endian in the first 8 bytes. - */ -static const int kFinderInfoSize = 32; - -/* - * Set the file type and creator type. - */ -static NuError Nu_SetFinderInfo(NuArchive* pArchive, const NuRecord* pRecord, - const UNICHAR* pathnameUNI) -{ - uint8_t fiBuf[kFinderInfoSize]; - - size_t actual = getxattr(pathnameUNI, XATTR_FINDERINFO_NAME, - fiBuf, sizeof(fiBuf), 0, 0); - if (actual == (size_t) -1 && errno == ENOATTR) { - // doesn't yet have Finder info - memset(fiBuf, 0, sizeof(fiBuf)); - } else if (actual != kFinderInfoSize) { - Nu_ReportError(NU_BLOB, errno, - "Finder info on '%s' returned %d", pathnameUNI, (int) actual); - return kNuErrFile; - } - - uint8_t proType = (uint8_t) pRecord->recFileType; - uint16_t proAux = (uint16_t) pRecord->recExtraType; - - /* - * Attempt to use one of the convenience types. If nothing matches, - * use the generic pdos/pXYZ approach. Note that PSYS/PS16 will - * lose the file's aux type. - * - * I'm told this is from page 336 of _Programmer's Reference for - * System 6.0_. - */ - uint8_t* fileTypeBuf = fiBuf; - uint8_t* creatorBuf = fiBuf + 4; - - memcpy(creatorBuf, "pdos", 4); - if (proType == 0x00 && proAux == 0x0000) { - memcpy(fileTypeBuf, "BINA", 4); - } else if (proType == 0x04 && proAux == 0x0000) { - memcpy(fileTypeBuf, "TEXT", 4); - } else if (proType == 0xff) { - memcpy(fileTypeBuf, "PSYS", 4); - } else if (proType == 0xb3 && (proAux & 0xff00) != 0xdb00) { - memcpy(fileTypeBuf, "PS16", 4); - } else if (proType == 0xd7 && proAux == 0x0000) { - memcpy(fileTypeBuf, "MIDI", 4); - } else if (proType == 0xd8 && proAux == 0x0000) { - memcpy(fileTypeBuf, "AIFF", 4); - } else if (proType == 0xd8 && proAux == 0x0001) { - memcpy(fileTypeBuf, "AIFC", 4); - } else if (proType == 0xe0 && proAux == 0x0005) { - memcpy(creatorBuf, "dCpy", 4); - memcpy(fileTypeBuf, "dImg", 4); - } else { - fileTypeBuf[0] = 'p'; - fileTypeBuf[1] = proType; - fileTypeBuf[2] = (uint8_t) (proAux >> 8); - fileTypeBuf[3] = (uint8_t) proAux; - } - - if (setxattr(pathnameUNI, XATTR_FINDERINFO_NAME, fiBuf, sizeof(fiBuf), - 0, 0) != 0) - { - Nu_ReportError(NU_BLOB, errno, - "Unable to set Finder info on '%s'", pathnameUNI); - return kNuErrFile; - } - - return kNuErrNone; -} -#endif /*MAC_LIKE*/ - - -/* - * Get the file info into a NuFileInfo struct. Fields which are - * inappropriate for the current system are set to default values. - */ -static NuError Nu_GetFileInfo(NuArchive* pArchive, const UNICHAR* pathnameUNI, - NuFileInfo* pFileInfo) -{ - NuError err = kNuErrNone; - Assert(pArchive != NULL); - Assert(pathnameUNI != NULL); - Assert(pFileInfo != NULL); - - pFileInfo->isValid = false; - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - { - struct stat sbuf; - int cc; - - cc = stat(pathnameUNI, &sbuf); - if (cc) { - if (errno == ENOENT) - err = kNuErrFileNotFound; - else - err = kNuErrFileStat; - goto bail; - } - - pFileInfo->isRegularFile = false; - if (S_ISREG(sbuf.st_mode)) - pFileInfo->isRegularFile = true; - pFileInfo->isDirectory = false; - if (S_ISDIR(sbuf.st_mode)) - pFileInfo->isDirectory = true; - - /* BUG: should check for 32-bit overflow from 64-bit off_t */ - pFileInfo->dataEof = sbuf.st_size; - pFileInfo->isForked = false; -# if defined(MAC_LIKE) && defined(HAS_RESOURCE_FORKS) - if (!pFileInfo->isDirectory) { - /* - * Check for the presence of a resource fork. You can check - * these from a terminal with "ls -l@" -- look for the - * "com.apple.ResourceFork" attribute. - * - * We can either use getxattr() and check for the presence of - * the attribute, or get the file length with stat(). I - * don't know if xattr has always worked with resource forks, - * so we'll stick with stat for now. - */ - UNICHAR* rsrcPath = GetResourcePath(pathnameUNI); - - struct stat res_sbuf; - - if (stat(rsrcPath, &res_sbuf) == 0) { - pFileInfo->isForked = (res_sbuf.st_size != 0); - } - - free(rsrcPath); - } -# endif - Nu_GMTSecondsToDateTime(&sbuf.st_mtime, &pFileInfo->modWhen); - pFileInfo->unixMode = sbuf.st_mode; - pFileInfo->isValid = true; - } -#else - #error "Port this" -#endif - -bail: - return err; -} - - -/* - * Determine whether a specific fork in the file exists. - * - * On systems that don't support forked files, the "checkRsrcFork" argument - * is ignored. If forked files are supported, and we are extracting a - * file with data and resource forks, we only claim it exists if it has - * nonzero length. - */ -static NuError Nu_FileForkExists(NuArchive* pArchive, - const UNICHAR* pathnameUNI, Boolean isForkedFile, Boolean checkRsrcFork, - Boolean* pExists, NuFileInfo* pFileInfo) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(pathnameUNI != NULL); - Assert(checkRsrcFork == true || checkRsrcFork == false); - Assert(pExists != NULL); - Assert(pFileInfo != NULL); - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) -# if !defined(MAC_LIKE) - /* - * On Unix and Windows we ignore "isForkedFile" and "checkRsrcFork". - * The file must not exist at all. - */ - Assert(pArchive->lastFileCreatedUNI == NULL); -# endif - - *pExists = true; - err = Nu_GetFileInfo(pArchive, pathnameUNI, pFileInfo); - if (err == kNuErrFileNotFound) { - err = kNuErrNone; - *pExists = false; - } - -# if defined(MAC_LIKE) - /* - * On Mac OS X, we'll use the resource fork, but we may not want to - * overwrite existing data. - */ - if (*pExists && checkRsrcFork) { - *pExists = pFileInfo->isForked; - } -# endif - -#elif defined(__ORCAC__) - /* - * If the file doesn't exist, great. If it does, and "lastFileCreated" - * matches up with this one, then we know that it exists because we - * created it. - * - * This is great unless the record has two data forks or something - * equally dopey, so we check to be sure that the fork we want to - * put the data into is currently empty. - * - * It is possible, though asinine, for a Mac OS or GS/OS extraction - * program to put the data and resource forks of a record into - * separate files, so we can't just assume that because we wrote - * the data fork to file A it is okay for file B to exist. That's - * why we compare the pathname instead of just remembering that - * we already created a file for this record. - */ - #error "Finish me" - -#else - #error "Port this" -#endif - - return err; -} - - -/* - * Set the dates on a file according to what's in the record. - */ -static NuError Nu_SetFileDates(NuArchive* pArchive, const NuRecord* pRecord, - const UNICHAR* pathnameUNI) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(pathnameUNI != NULL); - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - { - struct utimbuf utbuf; - - /* ignore create time, and set access time equal to mod time */ - Nu_DateTimeToGMTSeconds(&pRecord->recModWhen, &utbuf.modtime); - utbuf.actime = utbuf.modtime; - - /* only do it if the NuDateTime was valid */ - if (utbuf.modtime) { - if (utime(pathnameUNI, &utbuf) < 0) { - Nu_ReportError(NU_BLOB, errno, - "Unable to set time stamp on '%s'", pathnameUNI); - err = kNuErrFileSetDate; - goto bail; - } - } - } - -#else - #error "Port this" -#endif - -bail: - return err; -} - - -/* - * Returns "true" if the record is locked (in the ProDOS sense). - * - * Bits 31-8 reserved, must be zero - * Bit 7 (D) 1 = destroy enabled - * Bit 6 (R) 1 = rename enabled - * Bit 5 (B) 1 = file needs to be backed up - * Bits 4-3 reserved, must be zero - * Bit 2 (I) 1 = file is invisible - * Bit 1 (W) 1 = write enabled - * Bit 0 (R) 1 = read enabled - * - * A "locked" file would be 00?00001, "unlocked" 11?00011, with many - * possible variations. For our purposes, we treat all files as unlocked - * unless they match the classic "locked" bit pattern. - */ -static Boolean Nu_IsRecordLocked(const NuRecord* pRecord) -{ - if (pRecord->recAccess == 0x21L || pRecord->recAccess == 0x01L) - return true; - else - return false; -} - -/* - * Set the file access permissions based on what's in the record. - * - * This assumes that the file is currently writable, so we only need - * to do something if the original file was "locked". - */ -static NuError Nu_SetFileAccess(NuArchive* pArchive, const NuRecord* pRecord, - const UNICHAR* pathnameUNI) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(pathnameUNI != NULL); - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - /* only need to do something if the file was "locked" */ - if (Nu_IsRecordLocked(pRecord)) { - mode_t mask; - - /* set it to 444, modified by umask */ - mask = umask(0); - umask(mask); - //DBUG(("+++ chmod '%s' %03o (mask=%03o)\n", pathname, - // (S_IRUSR | S_IRGRP | S_IROTH) & ~mask, mask)); - if (chmod(pathnameUNI, (S_IRUSR | S_IRGRP | S_IROTH) & ~mask) < 0) { - Nu_ReportError(NU_BLOB, errno, - "unable to set access for '%s' to %03o", pathnameUNI, - (int) mask); - err = kNuErrFileSetAccess; - goto bail; - } - } - -#else - #error "Port this" -#endif - -bail: - return err; -} - - -/* - * =========================================================================== - * Create/open an output file - * =========================================================================== - */ - -/* - * Prepare an existing file for writing. - * - * Generally this just involves ensuring that the file is writable. If - * this is a convenient place to truncate it, we should do that too. - * - * 20150103: we don't seem to be doing the truncation here, so prepRsrc - * is unused. - */ -static NuError Nu_PrepareForWriting(NuArchive* pArchive, - const UNICHAR* pathnameUNI, Boolean prepRsrc, NuFileInfo* pFileInfo) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(pathnameUNI != NULL); - Assert(prepRsrc == true || prepRsrc == false); - Assert(pFileInfo != NULL); - - Assert(pFileInfo->isValid == true); - - /* don't go playing with directories, pipes, etc */ - if (pFileInfo->isRegularFile != true) - return kNuErrNotRegularFile; - -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - if (!(pFileInfo->unixMode & S_IWUSR)) { - /* make it writable by owner, plus whatever it was before */ - if (chmod(pathnameUNI, S_IWUSR | pFileInfo->unixMode) < 0) { - Nu_ReportError(NU_BLOB, errno, - "unable to set access for '%s'", pathnameUNI); - err = kNuErrFileSetAccess; - goto bail; - } - } - - return kNuErrNone; - -#else - #error "Port this" -#endif - -bail: - return err; -} - - -/* - * Invoke the system-dependent directory creation function. - */ -static NuError Nu_Mkdir(NuArchive* pArchive, const char* dir) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(dir != NULL); - -#if defined(UNIX_LIKE) - if (mkdir(dir, S_IRWXU | S_IRGRP|S_IXGRP | S_IROTH|S_IXOTH) < 0) { - err = errno ? errno : kNuErrDirCreate; - Nu_ReportError(NU_BLOB, err, "Unable to create dir '%s'", dir); - goto bail; - } - -#elif defined(WINDOWS_LIKE) - if (mkdir(dir) < 0) { - err = errno ? errno : kNuErrDirCreate; - Nu_ReportError(NU_BLOB, err, "Unable to create dir '%s'", dir); - goto bail; - } - -#else - #error "Port this" -#endif - -bail: - return err; -} - - -/* - * Create a single subdirectory if it doesn't exist. If the next-highest - * subdirectory level doesn't exist either, cut down the pathname and - * recurse. - */ -static NuError Nu_CreateSubdirIFN(NuArchive* pArchive, - const UNICHAR* pathStartUNI, const char* pathEnd, char fssep) -{ - NuError err = kNuErrNone; - NuFileInfo fileInfo; - char* tmpBuf = NULL; - - Assert(pArchive != NULL); - Assert(pathStartUNI != NULL); - Assert(pathEnd != NULL); - Assert(fssep != '\0'); - - /* pathStart might have whole path, but we only want up to "pathEnd" */ - tmpBuf = strdup(pathStartUNI); - tmpBuf[pathEnd - pathStartUNI +1] = '\0'; - - err = Nu_GetFileInfo(pArchive, tmpBuf, &fileInfo); - if (err == kNuErrFileNotFound) { - /* dir doesn't exist; move up a level and check parent */ - pathEnd = strrchr(tmpBuf, fssep); - if (pathEnd != NULL) { - pathEnd--; - Assert(pathEnd >= tmpBuf); - err = Nu_CreateSubdirIFN(pArchive, tmpBuf, pathEnd, fssep); - BailError(err); - } - - /* parent is taken care of; create this one */ - err = Nu_Mkdir(pArchive, tmpBuf); - BailError(err); - } else if (err != kNuErrNone) { - goto bail; - } else { - /* file does exist, make sure it's a directory */ - Assert(fileInfo.isValid == true); - if (!fileInfo.isDirectory) { - err = kNuErrNotDir; - Nu_ReportError(NU_BLOB, err, "Unable to create path '%s'", tmpBuf); - goto bail; - } - } - -bail: - Nu_Free(pArchive, tmpBuf); - return err; -} - -/* - * Create subdirectories, if needed. The paths leading up to the filename - * in "pathname" will be created. - * - * If "pathname" is just a filename, or the set of directories matches - * the last directory we created, we don't do anything. - */ -static NuError Nu_CreatePathIFN(NuArchive* pArchive, const UNICHAR* pathnameUNI, - UNICHAR fssep) -{ - NuError err = kNuErrNone; - const char* pathStart; - const char* pathEnd; - - Assert(pArchive != NULL); - Assert(pathnameUNI != NULL); - Assert(fssep != '\0'); - - pathStart = pathnameUNI; - -#if !defined(MAC_LIKE) /* On the Mac, if it's a full path, treat it like one */ - // 20150103: not sure what use case this is for - if (pathnameUNI[0] == fssep) - pathStart++; -#endif - - /* NOTE: not expecting names like "foo/bar/ack/", with terminating fssep */ - pathEnd = strrchr(pathStart, fssep); - if (pathEnd == NULL) { - /* no subdirectory components found */ - goto bail; - } - pathEnd--; - - Assert(pathEnd >= pathStart); - if (pathEnd - pathStart < 0) - goto bail; - - /* - * On some filesystems, strncasecmp would be appropriate here. However, - * this is meant solely as an optimization to avoid extra stat() calls, - * so we want to use the most restrictive case. - */ - if (pArchive->lastDirCreatedUNI && - strncmp(pathStart, pArchive->lastDirCreatedUNI, - pathEnd - pathStart +1) == 0) - { - /* we created this one recently, don't do it again */ - goto bail; - } - - /* - * Test to determine which directories exist. The most likely case - * is that some or all of the components have already been created, - * so we start with the last one and work backward. - */ - err = Nu_CreateSubdirIFN(pArchive, pathStart, pathEnd, fssep); - BailError(err); - -bail: - return err; -} - - -/* - * Open the file for writing, possibly truncating it. - */ -static NuError Nu_OpenFileForWrite(NuArchive* pArchive, - const UNICHAR* pathnameUNI, Boolean openRsrc, FILE** pFp) -{ -#if defined(MAC_LIKE) && defined(HAS_RESOURCE_FORKS) - if (openRsrc) { - UNICHAR* rsrcPath = GetResourcePath(pathnameUNI); - *pFp = fopen(rsrcPath, kNuFileOpenWriteTrunc); - free(rsrcPath); - } else { - *pFp = fopen(pathnameUNI, kNuFileOpenWriteTrunc); - } -#else - *pFp = fopen(pathnameUNI, kNuFileOpenWriteTrunc); -#endif - if (*pFp == NULL) - return errno ? errno : -1; - return kNuErrNone; -} - - -/* - * Open an output file and prepare it for writing. - * - * There are a number of things to take into consideration, including - * deal with "file exists" conditions, handling Mac/IIgs file types, - * coping with resource forks on extended files, and handling the - * "freshen" option that requires us to only update files that are - * older than what we have. - */ -NuError Nu_OpenOutputFile(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, const UNICHAR* newPathnameUNI, UNICHAR newFssep, - FILE** pFp) -{ - NuError err = kNuErrNone; - Boolean exists, isForkedFile, extractingRsrc = false; - NuFileInfo fileInfo; - NuErrorStatus errorStatus; - NuResult result; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(pThread != NULL); - Assert(newPathnameUNI != NULL); - Assert(pFp != NULL); - - /* set up some defaults, in case something goes wrong */ - errorStatus.operation = kNuOpExtract; - errorStatus.err = kNuErrInternal; - errorStatus.sysErr = 0; - errorStatus.message = NULL; - errorStatus.pRecord = pRecord; - errorStatus.pathnameUNI = newPathnameUNI; - errorStatus.origPathname = NULL; - errorStatus.filenameSeparator = newFssep; - /*errorStatus.origArchiveTouched = false;*/ - errorStatus.canAbort = true; - errorStatus.canRetry = true; - errorStatus.canIgnore = false; - errorStatus.canSkip = true; - errorStatus.canRename = true; - errorStatus.canOverwrite = true; - - /* decide if this is a forked file (i.e. has *both* forks) */ - isForkedFile = Nu_IsForkedFile(pArchive, pRecord); - - /* decide if we're extracting a resource fork */ - if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == - kNuThreadIDRsrcFork) - { - extractingRsrc = true; - } - - /* - * Determine whether the file and fork already exists. If the file - * is one we just created, and the fork we want to write to is - * empty, this will *not* set "exists". - */ - fileInfo.isValid = false; - err = Nu_FileForkExists(pArchive, newPathnameUNI, isForkedFile, - extractingRsrc, &exists, &fileInfo); - BailError(err); - - if (exists) { - Assert(fileInfo.isValid == true); - - /* - * The file exists when it shouldn't. Decide what to do, based - * on the options configured by the application. - */ - - /* - * Start by checking to see if we're willing to overwrite older files. - * If not, see if the application wants to rename the file, or force - * the overwrite. Most likely they'll just want to skip it. - */ - if ((pArchive->valOnlyUpdateOlder) && - !Nu_IsOlder(&fileInfo.modWhen, &pRecord->recModWhen)) - { - if (pArchive->errorHandlerFunc != NULL) { - errorStatus.err = kNuErrNotNewer; - result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); - - switch (result) { - case kNuAbort: - err = kNuErrAborted; - goto bail; - case kNuRetry: - case kNuRename: - err = kNuErrRename; - goto bail; - case kNuSkip: - err = kNuErrSkipped; - goto bail; - case kNuOverwrite: - break; /* fall back into main code */ - case kNuIgnore: - default: - err = kNuErrSyntax; - Nu_ReportError(NU_BLOB, err, - "Wasn't expecting result %d here", result); - goto bail; - } - } else { - err = kNuErrNotNewer; - goto bail; - } - } - - /* If they "might" allow overwrites, and they have an error-handling - * callback defined, call that to find out what they want to do - * here. Options include skipping the file, overwriting the file, - * and extracting to a different file. - */ - if (pArchive->valHandleExisting == kNuMaybeOverwrite) { - if (pArchive->errorHandlerFunc != NULL) { - errorStatus.err = kNuErrFileExists; - result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); - - switch (result) { - case kNuAbort: - err = kNuErrAborted; - goto bail; - case kNuRetry: - case kNuRename: - err = kNuErrRename; - goto bail; - case kNuSkip: - err = kNuErrSkipped; - goto bail; - case kNuOverwrite: - break; /* fall back into main code */ - case kNuIgnore: - default: - err = kNuErrSyntax; - Nu_ReportError(NU_BLOB, err, - "Wasn't expecting result %d here", result); - goto bail; - } - } else { - /* no error handler, return an error to the caller */ - err = kNuErrFileExists; - goto bail; - } - } else if (pArchive->valHandleExisting == kNuNeverOverwrite) { - err = kNuErrSkipped; - goto bail; - } - } else { - /* - * The file doesn't exist. If we're doing a "freshen" from the - * archive, we don't want to create a new file, so we return an - * error to the user instead. However, we give the application - * a chance to straighten things out. Most likely they'll just - * return kNuSkip. - */ - if (pArchive->valHandleExisting == kNuMustOverwrite) { - DBUG(("+++ can't freshen nonexistent file '%s'\n", newPathnameUNI)); - if (pArchive->errorHandlerFunc != NULL) { - errorStatus.err = kNuErrDuplicateNotFound; - - /* give them a chance to rename */ - result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); - switch (result) { - case kNuAbort: - err = kNuErrAborted; - goto bail; - case kNuRetry: - case kNuRename: - err = kNuErrRename; - goto bail; - case kNuSkip: - err = kNuErrSkipped; - goto bail; - case kNuOverwrite: - break; /* fall back into main code */ - case kNuIgnore: - default: - err = kNuErrSyntax; - Nu_ReportError(NU_BLOB, err, - "Wasn't expecting result %d here", result); - goto bail; - } - } else { - /* no error handler, return an error to the caller */ - err = kNuErrDuplicateNotFound; - goto bail; - } - } - } - - Assert(err == kNuErrNone); - - /* - * After the above, if the file exists then we need to prepare it for - * writing. On some systems -- notably those with forked files -- it - * may be easiest to delete the entire file and start over. On - * simpler systems, an (optional) chmod followed by an open that - * truncates the file should be sufficient. - * - * If the file didn't exist, we need to be sure that the path leading - * up to its eventual location exists. This might require creating - * several directories. We distinguish the case of "file isn't there" - * from "file is there but fork isn't" by seeing if we were able to - * get valid file info. - */ - if (exists) { - Assert(fileInfo.isValid == true); - err = Nu_PrepareForWriting(pArchive, newPathnameUNI, extractingRsrc, - &fileInfo); - BailError(err); - } else if (!fileInfo.isValid) { - err = Nu_CreatePathIFN(pArchive, newPathnameUNI, newFssep); - BailError(err); - } - - /* - * Open sesame. - */ - err = Nu_OpenFileForWrite(pArchive, newPathnameUNI, extractingRsrc, pFp); - BailError(err); - - -#if defined(HAS_RESOURCE_FORKS) - pArchive->lastFileCreatedUNI = newPathnameUNI; -#endif - -bail: - if (err != kNuErrNone) { - if (err != kNuErrSkipped && err != kNuErrRename && - err != kNuErrFileExists) - { - Nu_ReportError(NU_BLOB, err, "Unable to open '%s'%s", - newPathnameUNI, extractingRsrc ? " (rsrc fork)" : ""); - } - } - return err; -} - - -/* - * Close the output file, adjusting the modification date and access - * permissions as needed. - * - * On GS/OS and Mac OS, we may need to set the file type here, depending on - * how much we managed to do when the file was first created. IIRC, - * the GS/OS Open call should allow setting the file type. - * - * BUG: on GS/OS, if we set the file access after writing the data fork, - * we may not be able to open the same file for writing the rsrc fork. - * We can't suppress setting the access permissions, because we don't know - * if the application will want to write both forks to the same file, or - * for that matter will want to write the resource fork at all. Looks - * like we will have to be smart enough to reset the access permissions - * when writing a rsrc fork to a file with just a data fork. This isn't - * quite right, but it's close enough. - */ -NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord, - FILE* fp, const UNICHAR* pathnameUNI) -{ - NuError err; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(fp != NULL); - - fclose(fp); - - err = Nu_SetFileDates(pArchive, pRecord, pathnameUNI); - BailError(err); - -#if defined(MAC_LIKE) - /* could also do this earlier and pass the fd for fsetxattr */ - /* NOTE: must do this before Nu_SetFileAccess */ - err = Nu_SetFinderInfo(pArchive, pRecord, pathnameUNI); - BailError(err); -#endif - - err = Nu_SetFileAccess(pArchive, pRecord, pathnameUNI); - BailError(err); - -bail: - return kNuErrNone; -} - -/* - * =========================================================================== - * Open an input file - * =========================================================================== - */ - -/* - * Open the file for reading, in "binary" mode when necessary. - */ -static NuError Nu_OpenFileForRead(NuArchive* pArchive, - const UNICHAR* pathnameUNI, Boolean openRsrc, FILE** pFp) -{ - *pFp = fopen(pathnameUNI, kNuFileOpenReadOnly); - if (*pFp == NULL) - return errno ? errno : -1; - return kNuErrNone; -} - - -/* - * Open an input file and prepare it for reading. - * - * If the file can't be found, we give the application an opportunity to - * skip the absent file, retry, or abort the whole thing. - */ -NuError Nu_OpenInputFile(NuArchive* pArchive, const UNICHAR* pathnameUNI, - Boolean openRsrc, FILE** pFp) -{ - NuError err = kNuErrNone; - NuError openErr = kNuErrNone; - NuErrorStatus errorStatus; - NuResult result; - - Assert(pArchive != NULL); - Assert(pathnameUNI != NULL); - Assert(pFp != NULL); - -#if defined(MAC_LIKE) && defined(HAS_RESOURCE_FORKS) - UNICHAR* rsrcPath = NULL; - if (openRsrc) { - rsrcPath = GetResourcePath(pathnameUNI); - pathnameUNI = rsrcPath; - } -#endif - -retry: - /* - * Open sesame. - */ - err = Nu_OpenFileForRead(pArchive, pathnameUNI, openRsrc, pFp); - if (err == kNuErrNone) /* success! */ - goto bail; - - if (err == ENOENT) - openErr = kNuErrFileNotFound; - - if (pArchive->errorHandlerFunc != NULL) { - errorStatus.operation = kNuOpAdd; - errorStatus.err = openErr; - errorStatus.sysErr = 0; - errorStatus.message = NULL; - errorStatus.pRecord = NULL; - errorStatus.pathnameUNI = pathnameUNI; - errorStatus.origPathname = NULL; - errorStatus.filenameSeparator = '\0'; - /*errorStatus.origArchiveTouched = false;*/ - errorStatus.canAbort = true; - errorStatus.canRetry = true; - errorStatus.canIgnore = false; - errorStatus.canSkip = true; - errorStatus.canRename = false; - errorStatus.canOverwrite = false; - - DBUG(("--- invoking error handler function\n")); - result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); - - switch (result) { - case kNuAbort: - err = kNuErrAborted; - goto bail; - case kNuRetry: - goto retry; - case kNuSkip: - err = kNuErrSkipped; - goto bail; - case kNuRename: - case kNuOverwrite: - case kNuIgnore: - default: - err = kNuErrSyntax; - Nu_ReportError(NU_BLOB, err, - "Wasn't expecting result %d here", result); - goto bail; - } - } else { - DBUG(("+++ no error callback in OpenInputFile\n")); - } - -bail: - if (err == kNuErrNone) { - Assert(*pFp != NULL); - } else { - if (err != kNuErrSkipped && err != kNuErrRename && - err != kNuErrFileExists) - { - Nu_ReportError(NU_BLOB, err, "Unable to open '%s'%s", - pathnameUNI, openRsrc ? " (rsrc fork)" : ""); - } - } -#if defined(MAC_LIKE) && defined(HAS_RESOURCE_FORKS) - free(rsrcPath); -#endif - return err; -} - - -/* - * =========================================================================== - * Delete and rename files - * =========================================================================== - */ - -/* - * Delete a file. - */ -NuError Nu_DeleteFile(const UNICHAR* pathnameUNI) -{ -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - int cc; - - DBUG(("--- Deleting '%s'\n", pathnameUNI)); - - cc = unlink(pathnameUNI); - if (cc < 0) - return errno ? errno : -1; - else - return kNuErrNone; -#else - #error "Port this" -#endif -} - -/* - * Rename a file from "fromPath" to "toPath". - */ -NuError Nu_RenameFile(const UNICHAR* fromPathUNI, const UNICHAR* toPathUNI) -{ -#if defined(UNIX_LIKE) || defined(WINDOWS_LIKE) - int cc; - - DBUG(("--- Renaming '%s' to '%s'\n", fromPathUNI, toPathUNI)); - - cc = rename(fromPathUNI, toPathUNI); - if (cc < 0) - return errno ? errno : -1; - else - return kNuErrNone; -#else - #error "Port this" -#endif -} - - -/* - * =========================================================================== - * NuError wrappers for std functions - * =========================================================================== - */ - -/* - * Wrapper for ftell(). - */ -NuError Nu_FTell(FILE* fp, long* pOffset) -{ - Assert(fp != NULL); - Assert(pOffset != NULL); - - errno = 0; - *pOffset = ftell(fp); - if (*pOffset < 0) { - Nu_ReportError(NU_NILBLOB, errno, "file ftell failed"); - return errno ? errno : kNuErrFileSeek; - } - return kNuErrNone; -} - -/* - * Wrapper for fseek(). - */ -NuError Nu_FSeek(FILE* fp, long offset, int ptrname) -{ - Assert(fp != NULL); - Assert(ptrname == SEEK_SET || ptrname == SEEK_CUR || ptrname == SEEK_END); - - errno = 0; - if (fseek(fp, offset, ptrname) < 0) { - Nu_ReportError(NU_NILBLOB, errno, - "file fseek(%ld, %d) failed", offset, ptrname); - return errno ? errno : kNuErrFileSeek; - } - return kNuErrNone; -} - -/* - * Wrapper for fread(). Note the arguments resemble read(2) rather than the - * slightly silly ones used by fread(3S). - */ -NuError Nu_FRead(FILE* fp, void* buf, size_t nbyte) -{ - size_t result; - - errno = 0; - result = fread(buf, nbyte, 1, fp); - if (result != 1) - return errno ? errno : kNuErrFileRead; - return kNuErrNone; -} - -/* - * Wrapper for fwrite(). Note the arguments resemble write(2) rather than the - * slightly silly ones used by fwrite(3S). - */ -NuError Nu_FWrite(FILE* fp, const void* buf, size_t nbyte) -{ - size_t result; - - errno = 0; - result = fwrite(buf, nbyte, 1, fp); - if (result != 1) - return errno ? errno : kNuErrFileWrite; - return kNuErrNone; -} - -/* - * =========================================================================== - * Misc functions - * =========================================================================== - */ - -/* - * Copy a section from one file to another. - */ -NuError Nu_CopyFileSection(NuArchive* pArchive, FILE* dstFp, FILE* srcFp, - long length) -{ - NuError err; - long readLen; - - Assert(pArchive != NULL); - Assert(dstFp != NULL); - Assert(srcFp != NULL); - Assert(length >= 0); /* can be == 0, e.g. empty data fork from HFS */ - - /* nice big buffer, for speed... could use getc/putc for simplicity */ - err = Nu_AllocCompressionBufferIFN(pArchive); - BailError(err); - - DBUG(("+++ Copying %ld bytes\n", length)); - - while (length) { - readLen = length > kNuGenCompBufSize ? kNuGenCompBufSize : length; - - err = Nu_FRead(srcFp, pArchive->compBuf, readLen); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, - "Nu_FRead failed while copying file section " - "(fp=0x%08lx, readLen=%ld, length=%ld, err=%d)\n", - (long) srcFp, readLen, length, err); - goto bail; - } - err = Nu_FWrite(dstFp, pArchive->compBuf, readLen); - BailError(err); - - length -= readLen; - } - -bail: - return err; -} - - -/* - * Find the length of an open file. - * - * On UNIX it would be easier to just call fstat(), but fseek is portable. - * - * Only useful for files < 2GB in size. - * - * (pArchive is only used for BailError message reporting, so it's okay - * to call here with a NULL pointer if the archive isn't open yet.) - */ -NuError Nu_GetFileLength(NuArchive* pArchive, FILE* fp, long* pLength) -{ - NuError err; - long oldpos; - - Assert(fp != NULL); - Assert(pLength != NULL); - - err = Nu_FTell(fp, &oldpos); - BailError(err); - - err = Nu_FSeek(fp, 0, SEEK_END); - BailError(err); - - err = Nu_FTell(fp, pLength); - BailError(err); - - err = Nu_FSeek(fp, oldpos, SEEK_SET); - BailError(err); - -bail: - return err; -} - - -/* - * Truncate an open file. This differs from ftruncate() in that it takes - * a FILE* instead of an fd, and the length is a long instead of off_t. - */ -NuError Nu_TruncateOpenFile(FILE* fp, long length) -{ - #if defined(HAVE_FTRUNCATE) - if (ftruncate(fileno(fp), length) < 0) - return errno ? errno : -1; - return kNuErrNone; - #elif defined(HAVE_CHSIZE) - if (chsize(fileno(fp), length) < 0) - return errno ? errno : -1; - return kNuErrNone; - #else - /* not fatal; return this to indicate that it's an unsupported operation */ - return kNuErrInternal; - #endif -} - diff --git a/ciderpress/nufxlib/Funnel.c b/ciderpress/nufxlib/Funnel.c deleted file mode 100644 index 58a0797..0000000 --- a/ciderpress/nufxlib/Funnel.c +++ /dev/null @@ -1,904 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Implementation of NuFunnel, NuStraw and ProgressUpdater. - */ -#include "NufxLibPriv.h" - - -/* - * =========================================================================== - * Progress updater - * =========================================================================== - */ - -/* - * Initialize the fields in a ProgressData structure, prior to compressing - * data into a record. - * - * The same structure will be used when expanding all threads in a given - * record. - */ -NuError Nu_ProgressDataInit_Compress(NuArchive* pArchive, - NuProgressData* pProgressData, const NuRecord* pRecord, - const UNICHAR* origPathnameUNI, const UNICHAR* pathnameUNI) -{ - const char* cp; - - Assert(pProgressData != NULL); - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(origPathnameUNI != NULL); - Assert(pathnameUNI != NULL); - - pProgressData->pRecord = pRecord; - - pProgressData->origPathnameUNI = origPathnameUNI; - pProgressData->pathnameUNI = pathnameUNI; - cp = strrchr(pathnameUNI, NuGetSepFromSysInfo(pRecord->recFileSysInfo)); - if (cp == NULL || *(cp+1) == '\0') - pProgressData->filenameUNI = pProgressData->pathnameUNI; - else - pProgressData->filenameUNI = cp+1; - - pProgressData->operation = kNuOpAdd; - pProgressData->state = kNuProgressPreparing; - pProgressData->uncompressedLength = 0; - pProgressData->uncompressedProgress = 0; - - pProgressData->compress.threadFormat = (NuThreadFormat)-1; - - /* ya know... if this is NULL, none of the above matters much */ - pProgressData->progressFunc = pArchive->progressUpdaterFunc; - - return kNuErrNone; -} - - -/* - * Initialize the fields in a ProgressData structure, prior to expanding - * data from a record. - * - * The same structure will be used when expanding all threads in a given - * record. - */ -NuError Nu_ProgressDataInit_Expand(NuArchive* pArchive, - NuProgressData* pProgressData, const NuRecord* pRecord, - const UNICHAR* newPathnameUNI, UNICHAR newFssep, - const UNICHAR* origPathnameUNI, NuValue convertEOL) -{ - const NuThread* pThreadIter; - const char* cp; - int i; - - Assert(pProgressData != NULL); - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(newPathnameUNI != NULL); - Assert(origPathnameUNI != NULL); - Assert(newFssep != 0); - - pProgressData->pRecord = pRecord; - pProgressData->expand.pThread = NULL; - - pProgressData->origPathnameUNI = origPathnameUNI; - pProgressData->pathnameUNI = newPathnameUNI; - cp = strrchr(newPathnameUNI, newFssep); - if (cp == NULL || *(cp+1) == '\0') - pProgressData->filenameUNI = newPathnameUNI; - else - pProgressData->filenameUNI = cp+1; - - pProgressData->expand.convertEOL = convertEOL; - - /* total up the data threads */ - pProgressData->expand.totalCompressedLength = 0; - pProgressData->expand.totalUncompressedLength = 0; - - for (i = 0; i < (int)pRecord->recTotalThreads; i++) { - pThreadIter = Nu_GetThread(pRecord, i); - if (pThreadIter->thThreadClass != kNuThreadClassData) - continue; - pProgressData->expand.totalCompressedLength += pThreadIter->thCompThreadEOF; - pProgressData->expand.totalUncompressedLength += pThreadIter->actualThreadEOF; - } - - pProgressData->operation = kNuOpExtract; - if (pArchive->testMode) - pProgressData->operation = kNuOpTest; - pProgressData->state = kNuProgressPreparing; - pProgressData->uncompressedLength = 0; - pProgressData->uncompressedProgress = 0; - - /* ya know... if this is NULL, none of the above matters much */ - pProgressData->progressFunc = pArchive->progressUpdaterFunc; - - return kNuErrNone; -} - - -/* - * Do the setup on a ProgressData prior to compressing a thread. - */ -NuError Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw, - NuThreadFormat threadFormat, uint32_t sourceLen) -{ - NuProgressData* pProgressData; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - Assert(sourceLen < 32767*65536); - - pProgressData = pStraw->pProgress; - if (pProgressData == NULL) - return kNuErrNone; - - pProgressData->uncompressedLength = sourceLen; - pProgressData->compress.threadFormat = threadFormat; - - return kNuErrNone; -} - -/* - * Do the setup on a ProgressData prior to expanding a thread. - * - * "pThread" is the thread being expanded. - */ -NuError Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel, - const NuThread* pThread) -{ - NuProgressData* pProgressData; - - Assert(pArchive != NULL); - Assert(pFunnel != NULL); - Assert(pThread != NULL); - - pProgressData = pFunnel->pProgress; - if (pProgressData == NULL) - return kNuErrNone; - - /*pProgressData->compressedLength = pThread->thCompThreadEOF;*/ - pProgressData->uncompressedLength = pThread->actualThreadEOF; - pProgressData->expand.pThread = pThread; - - return kNuErrNone; -} - -/* - * Compute a completion percentage. - */ -static int Nu_ComputePercent(uint32_t total, uint32_t progress) -{ - uint32_t perc; - - if (!total) - return 0; - - if (total < 21474836) { - perc = (progress * 100 + 50) / total; - if (perc > 100) - perc = 100; - } else { - perc = progress / (total / 100); - if (perc > 100) - perc = 100; - } - - return (int) perc; -} - -/* - * Send the initial progress message, before the output file is opened - * (when extracting) or the input file is opened (when adding). - */ -NuError Nu_SendInitialProgress(NuArchive* pArchive, NuProgressData* pProgress) -{ - NuResult result; - - Assert(pArchive != NULL); - Assert(pProgress != NULL); - - if (pProgress->progressFunc == NULL) - return kNuErrNone; - - pProgress->percentComplete = Nu_ComputePercent( - pProgress->uncompressedLength, pProgress->uncompressedProgress); - - result = (*pProgress->progressFunc)(pArchive, (NuProgressData*) pProgress); - - if (result == kNuSkip) - return kNuErrSkipped; /* [dunno how well this works] */ - if (result == kNuAbort) - return kNuErrAborted; - - return kNuErrNone; -} - - -/* - * =========================================================================== - * NuFunnel object - * =========================================================================== - */ - -/* - * Allocate and initialize a Funnel. - */ -NuError Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink, - NuValue convertEOL, NuValue convertEOLTo, NuProgressData* pProgress, - NuFunnel** ppFunnel) -{ - NuError err = kNuErrNone; - NuFunnel* pFunnel = NULL; - - Assert(ppFunnel != NULL); - Assert(pDataSink != NULL); - Assert(convertEOL == kNuConvertOff || - convertEOL == kNuConvertOn || - convertEOL == kNuConvertAuto); - - pFunnel = Nu_Calloc(pArchive, sizeof(*pFunnel)); - BailAlloc(pFunnel); - pFunnel->buffer = Nu_Malloc(pArchive, kNuFunnelBufSize); - BailAlloc(pFunnel->buffer); - - pFunnel->pDataSink = pDataSink; - pFunnel->convertEOL = convertEOL; - pFunnel->convertEOLTo = convertEOLTo; - pFunnel->convertEOLFrom = kNuEOLUnknown; - pFunnel->pProgress = pProgress; - - pFunnel->checkStripHighASCII = (pArchive->valStripHighASCII != 0); - pFunnel->doStripHighASCII = false; /* determined on first write */ - - pFunnel->isFirstWrite = true; - -bail: - if (err != kNuErrNone) - Nu_FunnelFree(pArchive, pFunnel); - else - *ppFunnel = pFunnel; - return err; -} - - -/* - * Free a Funnel. - * - * The data should already have been written; it's not the duty of a - * "free" function to flush data out. - */ -NuError Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel) -{ - if (pFunnel == NULL) - return kNuErrNone; - -#ifdef DEBUG_MSGS - if (pFunnel->bufCount) - Nu_ReportError(NU_BLOB_DEBUG, kNuErrNone, - "freeing non-empty funnel"); -#endif - - Nu_Free(pArchive, pFunnel->buffer); - Nu_Free(pArchive, pFunnel); - - return kNuErrNone; -} - - -#if 0 -/* - * Set the maximum amount of output we're willing to push through the - * funnel. Attempts to write more than this many bytes will fail. This - * allows us to bail out as soon as it's apparent that compression is - * failing and is actually resulting in a larger file. - */ -void Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, uint32_t maxBytes) -{ - Assert(pFunnel != NULL); - Assert(maxBytes > 0); - - pFunnel->outMax = maxBytes; - if (pFunnel->outCount >= pFunnel->outMax) - pFunnel->outMaxExceeded = true; - else - pFunnel->outMaxExceeded = false; -} -#endif - - -/* - * Check to see if this is a high-ASCII file. To qualify, EVERY - * character must have its high bit set, except for spaces (0x20). - * (The exception is courtesy Glen Bredon's "Merlin".) - */ -static Boolean Nu_CheckHighASCII(const NuFunnel* pFunnel, const uint8_t* buffer, - uint32_t count) -{ - Boolean isHighASCII; - - Assert(buffer != NULL); - Assert(count != 0); - Assert(pFunnel->checkStripHighASCII); - - isHighASCII = true; - while (count--) { - if ((*buffer & 0x80) == 0 && *buffer != 0x20) { - isHighASCII = false; - break; - } - - buffer++; - } - - return isHighASCII; -} - -/* - * Table determining what's a binary character and what isn't. It would - * possibly be more compact to generate this from a simple description, - * but I'm hoping static/const data will end up in the code segment and - * save space on the heap. - * - * This corresponds to less-316's ISO-latin1 "8bcccbcc18b95.33b.". This - * may be too loose by itself; we may want to require that the lower-ASCII - * values appear in higher proportions than the upper-ASCII values. - * Otherwise we run the risk of converting a binary file with specific - * properties. (Note that "upper-ASCII" refers to umlauts and other - * accented characters, not DOS 3.3 "high ASCII".) - * - * The auto-detect mechanism will never be perfect though, so there's not - * much point in tweaking it to death. - */ -static const char gNuIsBinary[256] = { - 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, /* ^@-^O */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* ^P-^_ */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* - / */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 - ? */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ - O */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* P - _ */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* ` - o */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, /* p - DEL */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x80 */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 0x90 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xa0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xb0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xc0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xd0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xe0 */ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0xf0 */ -}; - -#define kNuMaxUpperASCII 1 /* max #of binary chars per 100 bytes */ -#define kNuMinConvThreshold 40 /* min of 40 chars for auto-detect */ -/* - * Decide, based on the contents of the buffer, whether we should do an - * EOL conversion on the data. - * - * We need to decide if we are looking at text data, and if so, what kind - * of line terminator is in use. - * - * If we don't have enough data to make a determination, don't mess with it. - * (Thought for the day: add a "bias" flag, based on the NuRecord fileType, - * that causes us to handle borderline or sub-min-threshold cases more - * reasonably. If it's of type TXT, it's probably text.) - * - * We try to figure out whether it's CR, LF, or CRLF, so that we can - * skip the CPU-intensive conversion process if it isn't necessary. - * - * We will also enable a "high-ASCII" stripper if requested. This is - * only enabled when EOL conversions are enabled. - * - * Returns kConvEOLOff or kConvEOLOn, and sets pFunnel->doStripHighASCII - * if pFunnel->CheckStripHighASCII is set. - */ -static NuValue Nu_DetermineConversion(NuFunnel* pFunnel, const uint8_t* buffer, - uint32_t count) -{ - uint32_t bufCount, numBinary, numLF, numCR; - Boolean isHighASCII; - uint8_t val; - - if (count < kNuMinConvThreshold) - return kNuConvertOff; - - /* - * Check to see if the buffer is all high-ASCII characters. If it is, - * we want to strip characters before we test them below. - */ - if (pFunnel->checkStripHighASCII) { - isHighASCII = Nu_CheckHighASCII(pFunnel, buffer, count); - DBUG(("+++ determined isHighASCII=%d\n", isHighASCII)); - } else { - isHighASCII = false; - DBUG(("+++ not even checking isHighASCII\n")); - } - - bufCount = count; - numBinary = numLF = numCR = 0; - while (bufCount--) { - val = *buffer++; - if (isHighASCII) - val &= 0x7f; - if (gNuIsBinary[val]) - numBinary++; - if (val == kNuCharLF) - numLF++; - if (val == kNuCharCR) - numCR++; - } - - /* if #found is > #allowed, it's a binary file */ - if (count < 100) { - /* use simplified check on files between kNuMinConvThreshold and 100 */ - if (numBinary > kNuMaxUpperASCII) - return kNuConvertOff; - } else if (numBinary > (count / 100) * kNuMaxUpperASCII) - return kNuConvertOff; - - /* - * If our "convert to" setting is the same as what we're converting - * from, we can turn off the converter and speed things up. - * - * These are simplistic, but this is intended as an optimization. We - * will blow it if the input has lots of CRs and LFs scattered about, - * and they just happen to be in equal amounts, but it's not clear - * to me that an automatic EOL conversion makes sense on that sort - * of file anyway. - * - * None of this applies if we also need to do a high-ASCII conversion. - */ - if (isHighASCII) { - pFunnel->doStripHighASCII = true; - } else { - if (numLF && !numCR) - pFunnel->convertEOLFrom = kNuEOLLF; - else if (!numLF && numCR) - pFunnel->convertEOLFrom = kNuEOLCR; - else if (numLF && numLF == numCR) - pFunnel->convertEOLFrom = kNuEOLCRLF; - else - pFunnel->convertEOLFrom = kNuEOLUnknown; - } - - return kNuConvertOn; -} - -/* - * Write a block of data to the appropriate output device. Test for - * excessive data, and raise "outMaxExceeded" if we overrun. - * - * This is either a Funnel function or a DataSink function, depending on - * your perspective. - */ -static inline void Nu_FunnelPutBlock(NuFunnel* pFunnel, const uint8_t* buf, - uint32_t len) -{ - Assert(pFunnel != NULL); - Assert(pFunnel->pDataSink != NULL); - Assert(buf != NULL); - Assert(len > 0); - -#if 0 - if (pFunnel->outMax) { - if (pFunnel->outMaxExceeded) - return; - if (pFunnel->outCount + len > pFunnel->outMax) { - pFunnel->outMaxExceeded = true; - return; - } - } - pFunnel->outCount += len; -#endif - - Nu_DataSinkPutBlock(pFunnel->pDataSink, buf, len); -} - - -/* - * Output the EOL marker requested for this system. - */ -static inline void Nu_PutEOL(NuFunnel* pFunnel) -{ - uint8_t ch; - - if (pFunnel->convertEOLTo == kNuEOLCR) { - ch = kNuCharCR; - Nu_FunnelPutBlock(pFunnel, &ch, 1); - } else if (pFunnel->convertEOLTo == kNuEOLLF) { - ch = kNuCharLF; - Nu_FunnelPutBlock(pFunnel, &ch, 1); - } else if (pFunnel->convertEOLTo == kNuEOLCRLF) { - ch = kNuCharCR; - Nu_FunnelPutBlock(pFunnel, &ch, 1); - ch = kNuCharLF; - Nu_FunnelPutBlock(pFunnel, &ch, 1); - } else { - Assert(0); - } -} - -/* - * Write a buffer of data, using the EOL conversion associated with the - * funnel (if any). - * - * When converting to the system's EOL convention, we take anything - * that looks like an EOL mark and convert it. Doesn't matter if it's - * CR, LF, or CRLF; all three get converted to whatever the system uses. - */ -static NuError Nu_FunnelWriteConvert(NuFunnel* pFunnel, const uint8_t* buffer, - uint32_t count) -{ - NuError err = kNuErrNone; - uint32_t progressCount = count; - - /*if (pFunnel->outMaxExceeded) - return kNuErrOutMax;*/ - - if (pFunnel->isFirstWrite) { - /* - * This is the first write/flush we've done on this Funnel. - * Check the data we have buffered to decide whether or not - * we want to do text conversions. - */ - if (pFunnel->convertEOL == kNuConvertAuto) { - pFunnel->convertEOL = Nu_DetermineConversion(pFunnel, buffer,count); - DBUG(("+++ DetermineConversion --> %ld / %ld (%d)\n", - pFunnel->convertEOL, pFunnel->convertEOLFrom, - pFunnel->doStripHighASCII)); - - if (pFunnel->convertEOLFrom == pFunnel->convertEOLTo) { - DBUG(("+++ Switching redundant converter off\n")); - pFunnel->convertEOL = kNuConvertOff; - } - /* put it where the progress meter can see it */ - if (pFunnel->pProgress != NULL) - pFunnel->pProgress->expand.convertEOL = pFunnel->convertEOL; - } else if (pFunnel->convertEOL == kNuConvertOn) { - if (pFunnel->checkStripHighASCII) { - /* assume this part of the buffer is representative */ - pFunnel->doStripHighASCII = Nu_CheckHighASCII(pFunnel, - buffer, count); - } else { - Assert(!pFunnel->doStripHighASCII); - } - DBUG(("+++ Converter is on, convHighASCII=%d\n", - pFunnel->doStripHighASCII)); - } - } - Assert(pFunnel->convertEOL != kNuConvertAuto); /* on or off now */ - pFunnel->isFirstWrite = false; - - if (pFunnel->convertEOL == kNuConvertOff) { - /* write it straight */ - Nu_FunnelPutBlock(pFunnel, buffer, count); - } else { - /* do the EOL conversion and optional high-bit stripping */ - Boolean lastCR = pFunnel->lastCR; /* make local copy */ - uint8_t uch; - int mask; - - if (pFunnel->doStripHighASCII) - mask = 0x7f; - else - mask = 0xff; - - /* - * We could get a significant speed improvement here by writing - * non-EOL chars as a larger block instead of single bytes. - */ - while (count--) { - uch = (*buffer) & mask; - - if (uch == kNuCharCR) { - Nu_PutEOL(pFunnel); - lastCR = true; - } else if (uch == kNuCharLF) { - if (!lastCR) - Nu_PutEOL(pFunnel); - lastCR = false; - } else { - Nu_FunnelPutBlock(pFunnel, &uch, 1); - lastCR = false; - } - buffer++; - } - pFunnel->lastCR = lastCR; /* save copy */ - - } - - /*if (pFunnel->outMaxExceeded) - err = kNuErrOutMax;*/ - - err = Nu_DataSinkGetError(pFunnel->pDataSink); - - /* update progress counter with pre-LFCR count */ - if (err == kNuErrNone && pFunnel->pProgress != NULL) - pFunnel->pProgress->uncompressedProgress += progressCount; - - return err; -} - - -/* - * Flush any data currently in the funnel. - */ -NuError Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel) -{ - NuError err = kNuErrNone; - - if (!pFunnel->bufCount) - goto bail; - - err = Nu_FunnelWriteConvert(pFunnel, pFunnel->buffer, pFunnel->bufCount); - BailError(err); - - pFunnel->bufCount = 0; - err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel); - /* fall through with error */ - -bail: - return err; -} - - -/* - * Write a bunch of bytes into a funnel. They will be held in the buffer - * if they fit, or flushed out the bottom if not. - */ -NuError Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel, - const uint8_t* buffer, uint32_t count) -{ - NuError err = kNuErrNone; - - /*pFunnel->inCount += count;*/ - - /* - * If it will fit into the buffer, just copy it in. - */ - if (pFunnel->bufCount + count < kNuFunnelBufSize) { - if (count == 1) /* minor optimization */ - *(pFunnel->buffer + pFunnel->bufCount) = *buffer; - else - memcpy(pFunnel->buffer + pFunnel->bufCount, buffer, count); - pFunnel->bufCount += count; - goto bail; - } else { - /* - * Won't fit. We have to flush what we have, and we can either - * blow out what we were just given or put it at the start of - * the buffer. - */ - if (pFunnel->bufCount) { - err = Nu_FunnelFlush(pArchive, pFunnel); - BailError(err); - } else { - err = Nu_FunnelSendProgressUpdate(pArchive, pFunnel); - BailError(err); - } - - Assert(pFunnel->bufCount == 0); - - if (count >= kNuFunnelBufSize / 4) { - /* it's more than 25% of the buffer, just write it now */ - err = Nu_FunnelWriteConvert(pFunnel, buffer, count); - BailError(err); - } else { - memcpy(pFunnel->buffer, buffer, count); - pFunnel->bufCount = count; - } - goto bail; - } - -bail: - return err; -} - - -/* - * Set the Funnel's progress state. - */ -NuError Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state) -{ - Assert(pFunnel != NULL); - - if (pFunnel->pProgress == NULL) - return kNuErrNone; - - pFunnel->pProgress->state = state; - - return kNuErrNone; -} - - -/* - * Send a progress update to the application, if they're interested. - */ -NuError Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel) -{ - NuProgressData* pProgress; - - Assert(pArchive != NULL); - Assert(pFunnel != NULL); - - pProgress = pFunnel->pProgress; - if (pProgress == NULL) - return kNuErrNone; /* no progress meter attached */ - - /* don't continue if they're not accepting progress messages */ - if (pProgress->progressFunc == NULL) - return kNuErrNone; - - /* other than the choice of arguments, it's pretty much the same story */ - return Nu_SendInitialProgress(pArchive, pProgress); -} - - -/* - * Pull the "doExpand" parameter out of the data source. - */ -Boolean Nu_FunnelGetDoExpand(NuFunnel* pFunnel) -{ - Assert(pFunnel != NULL); - Assert(pFunnel->pDataSink != NULL); - - return Nu_DataSinkGetDoExpand(pFunnel->pDataSink); -} - - -/* - * =========================================================================== - * NuStraw object - * =========================================================================== - */ - -/* - * Allocate and initialize a Straw. - */ -NuError Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource, - NuProgressData* pProgress, NuStraw** ppStraw) -{ - NuError err = kNuErrNone; - NuStraw* pStraw = NULL; - - Assert(ppStraw != NULL); - Assert(pDataSource != NULL); - - pStraw = Nu_Calloc(pArchive, sizeof(*pStraw)); - BailAlloc(pStraw); - pStraw->pDataSource = pDataSource; - pStraw->pProgress = pProgress; - pStraw->lastProgress = 0; - pStraw->lastDisplayed = 0; - -bail: - if (err != kNuErrNone) - Nu_StrawFree(pArchive, pStraw); - else - *ppStraw = pStraw; - return err; -} - -/* - * Free a Straw. - */ -NuError Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw) -{ - if (pStraw == NULL) - return kNuErrNone; - - /* we don't own the data source or progress meter */ - Nu_Free(pArchive, pStraw); - - return kNuErrNone; -} - - -/* - * Set the Straw's progress state. - */ -NuError Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state) -{ - Assert(pStraw != NULL); - Assert(pStraw->pProgress != NULL); - - pStraw->pProgress->state = state; - - return kNuErrNone; -} - -/* - * Send a progress update to the application, if they're interested. - */ -NuError Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw) -{ - NuProgressData* pProgress; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - - pProgress = pStraw->pProgress; - if (pProgress == NULL) - return kNuErrNone; /* no progress meter attached */ - - /* don't continue if they're not accepting progress messages */ - if (pProgress->progressFunc == NULL) - return kNuErrNone; - - /* other than the choice of arguments, it's pretty much the same story */ - return Nu_SendInitialProgress(pArchive, pProgress); -} - - -/* - * Read data from a straw. - */ -NuError Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uint8_t* buffer, - long len) -{ - NuError err; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - Assert(buffer != NULL); - Assert(len > 0); - - /* - * No buffering going on, so this is straightforward. - */ - - err = Nu_DataSourceGetBlock(pStraw->pDataSource, buffer, len); - BailError(err); - - /* - * Progress updating for adding is a little more complicated than - * for extracting. When extracting, the funnel controls the size - * of the output buffer, and only pushes an update when the output - * buffer fills. Here, we don't know how much will be asked for at - * a time, so we have to pace the updates or we risk flooding the - * application. - * - * We also have another problem: we want to indicate how much data - * has been processed, not how much data is *about* to be processed. - * So we have to set the percentage based on how much was requested - * on the previous call. (This assumes that whatever they asked for - * last time has already been fully processed.) - */ - if (pStraw->pProgress != NULL) { - pStraw->pProgress->uncompressedProgress = pStraw->lastProgress; - pStraw->lastProgress += len; - - if (!pStraw->pProgress->uncompressedProgress || - (pStraw->pProgress->uncompressedProgress - pStraw->lastDisplayed - > (kNuFunnelBufSize * 3 / 4))) - { - err = Nu_StrawSendProgressUpdate(pArchive, pStraw); - pStraw->lastDisplayed = pStraw->pProgress->uncompressedProgress; - BailError(err); - } - - } - -bail: - return err; -} - - -/* - * Rewind a straw. This rewinds the underlying data source, and resets - * some progress counters. - */ -NuError Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw) -{ - Assert(pStraw != NULL); - Assert(pStraw->pDataSource != NULL); - - pStraw->lastProgress = 0; - pStraw->lastDisplayed = 0; - - return Nu_DataSourceRewind(pStraw->pDataSource); -} - diff --git a/ciderpress/nufxlib/INSTALL b/ciderpress/nufxlib/INSTALL deleted file mode 100644 index 50dbe43..0000000 --- a/ciderpress/nufxlib/INSTALL +++ /dev/null @@ -1,183 +0,0 @@ -Basic Installation -================== - - These are generic installation instructions. - - The `configure' shell script attempts to guess correct values for -various system-dependent variables used during compilation. It uses -those values to create a `Makefile' in each directory of the package. -It may also create one or more `.h' files containing system-dependent -definitions. Finally, it creates a shell script `config.status' that -you can run in the future to recreate the current configuration, a file -`config.cache' that saves the results of its tests to speed up -reconfiguring, and a file `config.log' containing compiler output -(useful mainly for debugging `configure'). - - If you need to do unusual things to compile the package, please try -to figure out how `configure' could check whether to do them, and mail -diffs or instructions to the address given in the `README' so they can -be considered for the next release. If at some point `config.cache' -contains results you don't want to keep, you may remove or edit it. - - The file `configure.in' is used to create `configure' by a program -called `autoconf'. You only need `configure.in' if you want to change -it or regenerate `configure' using a newer version of `autoconf'. - -The simplest way to compile this package is: - - 1. `cd' to the directory containing the package's source code and type - `./configure' to configure the package for your system. If you're - using `csh' on an old version of System V, you might need to type - `sh ./configure' instead to prevent `csh' from trying to execute - `configure' itself. - - Running `configure' takes awhile. While running, it prints some - messages telling which features it is checking for. - - 2. Type `make' to compile the package. - - 3. Optionally, type `make check' to run any self-tests that come with - the package. - - 4. Type `make install' to install the programs and any data files and - documentation. - - 5. You can remove the program binaries and object files from the - source code directory by typing `make clean'. To also remove the - files that `configure' created (so you can compile the package for - a different kind of computer), type `make distclean'. There is - also a `make maintainer-clean' target, but that is intended mainly - for the package's developers. If you use it, you may have to get - all sorts of other programs in order to regenerate files that came - with the distribution. - -Compilers and Options -===================== - - Some systems require unusual options for compilation or linking that -the `configure' script does not know about. You can give `configure' -initial values for variables by setting them in the environment. Using -a Bourne-compatible shell, you can do that on the command line like -this: - CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure - -Or on systems that have the `env' program, you can do it like this: - env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure - -Compiling For Multiple Architectures -==================================== - - You can compile the package for more than one kind of computer at the -same time, by placing the object files for each architecture in their -own directory. To do this, you must use a version of `make' that -supports the `VPATH' variable, such as GNU `make'. `cd' to the -directory where you want the object files and executables to go and run -the `configure' script. `configure' automatically checks for the -source code in the directory that `configure' is in and in `..'. - - If you have to use a `make' that does not supports the `VPATH' -variable, you have to compile the package for one architecture at a time -in the source code directory. After you have installed the package for -one architecture, use `make distclean' before reconfiguring for another -architecture. - -Installation Names -================== - - By default, `make install' will install the package's files in -`/usr/local/bin', `/usr/local/man', etc. You can specify an -installation prefix other than `/usr/local' by giving `configure' the -option `--prefix=PATH'. - - You can specify separate installation prefixes for -architecture-specific files and architecture-independent files. If you -give `configure' the option `--exec-prefix=PATH', the package will use -PATH as the prefix for installing programs and libraries. -Documentation and other data files will still use the regular prefix. - - In addition, if you use an unusual directory layout you can give -options like `--bindir=PATH' to specify different values for particular -kinds of files. Run `configure --help' for a list of the directories -you can set and what kinds of files go in them. - - If the package supports it, you can cause programs to be installed -with an extra prefix or suffix on their names by giving `configure' the -option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. - -Optional Features -================= - - Some packages pay attention to `--enable-FEATURE' options to -`configure', where FEATURE indicates an optional part of the package. -They may also pay attention to `--with-PACKAGE' options, where PACKAGE -is something like `gnu-as' or `x' (for the X Window System). The -`README' should mention any `--enable-' and `--with-' options that the -package recognizes. - - For packages that use the X Window System, `configure' can usually -find the X include and library files automatically, but if it doesn't, -you can use the `configure' options `--x-includes=DIR' and -`--x-libraries=DIR' to specify their locations. - -Specifying the System Type -========================== - - There may be some features `configure' can not figure out -automatically, but needs to determine by the type of host the package -will run on. Usually `configure' can figure that out, but if it prints -a message saying it can not guess the host type, give it the -`--host=TYPE' option. TYPE can either be a short name for the system -type, such as `sun4', or a canonical name with three fields: - CPU-COMPANY-SYSTEM - -See the file `config.sub' for the possible values of each field. If -`config.sub' isn't included in this package, then this package doesn't -need to know the host type. - - If you are building compiler tools for cross-compiling, you can also -use the `--target=TYPE' option to select the type of system they will -produce code for and the `--build=TYPE' option to select the type of -system on which you are compiling the package. - -Sharing Defaults -================ - - If you want to set default values for `configure' scripts to share, -you can create a site shell script called `config.site' that gives -default values for variables like `CC', `cache_file', and `prefix'. -`configure' looks for `PREFIX/share/config.site' if it exists, then -`PREFIX/etc/config.site' if it exists. Or, you can set the -`CONFIG_SITE' environment variable to the location of the site script. -A warning: not all `configure' scripts look for a site script. - -Operation Controls -================== - - `configure' recognizes the following options to control how it -operates. - -`--cache-file=FILE' - Use and save the results of the tests in FILE instead of - `./config.cache'. Set FILE to `/dev/null' to disable caching, for - debugging `configure'. - -`--help' - Print a summary of the options to `configure', and exit. - -`--quiet' -`--silent' -`-q' - Do not print messages saying which checks are being made. To - suppress all normal output, redirect it to `/dev/null' (any error - messages will still be shown). - -`--srcdir=DIR' - Look for the package's source code in directory DIR. Usually - `configure' can determine that directory automatically. - -`--version' - Print the version of Autoconf used to generate the `configure' - script, and exit. - -`configure' also accepts some other, not widely useful, options. - diff --git a/ciderpress/nufxlib/Lzc.c b/ciderpress/nufxlib/Lzc.c deleted file mode 100644 index 5798ba3..0000000 --- a/ciderpress/nufxlib/Lzc.c +++ /dev/null @@ -1,1094 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * This is the LZW implementation found in the UNIX "compress" command, - * sometimes referred to as "LZC". GS/ShrinkIt v1.1 can unpack threads - * in LZC format, P8 ShrinkIt cannot. The only other application that - * is known to create LZC threads is the original NuLib. - * - * There's a lot of junk in here for the sake of smaller systems (e.g. MSDOS) - * and pre-ANSI compilers. For the most part it has been left unchanged. - * I have done some minor reformatting, and have undone the authors' - * penchant for assigning variables inside function call statements, but - * for the most part it is as it was. (A much cleaner implementation - * could probably be derived by adapting the NufxLib Lzw.c code...) - */ -#include "NufxLibPriv.h" - -#ifdef ENABLE_LZC - -/*#define DEBUG_LZC*/ - -/* - * Selected definitions from compress.h. - */ -typedef uint16_t CODE; -typedef uint8_t UCHAR; -typedef uint32_t INTCODE; -typedef uint32_t HASH; -typedef int FLAG; - -#ifndef FALSE /* let's get some sense to this */ -#define FALSE 0 -#define TRUE !FALSE -#endif - -#define CONST const -#ifndef FAR -# define FAR -#endif -#define NULLPTR(type) ((type FAR *) NULL) -#define ALLOCTYPE void - -#define INITBITS 9 -#define MINBITS 12 -#define MAXMAXBITS 16 -#define MAXBITS MAXMAXBITS -#define DFLTBITS MAXBITS - -#define UNUSED ((CODE)0) /* Indicates hash table value unused */ -#define CLEAR ((CODE)256) /* Code requesting table to be cleared */ -#define FIRSTFREE ((CODE)257) /* First free code for token encoding */ -#define MAXTOKLEN 512 /* Max chars in token; size of buffer */ -#define OK kNuErrNone /* Result codes from functions: */ - -#define BIT_MASK 0x1f -#define BLOCK_MASK 0x80 - -#define CHECK_GAP 10000L /* ratio check interval, for COMP40 */ - -static UCHAR gNu_magic_header[] = { 0x1F,0x9D }; - -/* don't need these */ -/*#define SPLIT_HT 1*/ -/*#define SPLIT_PFX 1*/ -/*#define COMP40 1*/ - -#define NOMEM kNuErrMalloc /* Ran out of memory */ -#define TOKTOOBIG kNuErrBadData /* Token longer than MAXTOKLEN chars */ -#define READERR kNuErrFileRead /* I/O error on input */ -#define WRITEERR kNuErrFileWrite /* I/O error on output */ -#define CODEBAD kNuErrBadData /* Infile contained a bad token code */ -#define TABLEBAD kNuErrInternal /* The tables got corrupted (!) */ -#define NOSAVING kNuErrNone /* no saving in file size */ - - -/* - * Normally in COMPUSI.UNI. - */ -static inline ALLOCTYPE FAR * -Nu_LZC_emalloc(NuArchive* pArchive, uint32_t x, int y) -{ - return Nu_Malloc(pArchive, x*y); -} -static inline void -Nu_LZC_efree(NuArchive* pArchive, ALLOCTYPE FAR * ptr) -{ - Nu_Free(pArchive, ptr); -} - -/*@H************************ < COMPRESS API > **************************** -* $@(#) compapi.c,v 4.3d 90/01/18 03:00:00 don Release ^ * -* * -* compress : compapi.c * -* * -* port by : Donald J. Gloistein * -* * -* Source, Documentation, Object Code: * -* released to Public Domain. This code is based on code as documented * -* below in release notes. * -* * -*--------------------------- Module Description --------------------------* -* Contains source code for modified Lempel-Ziv method (LZW) compression * -* and decompression. * -* * -* This code module can be maintained to keep current on releases on the * -* Unix system. The command shell and dos modules can remain the same. * -* * -*--------------------------- Implementation Notes --------------------------* -* * -* compiled with : compress.h compress.fns compress.c * -* linked with : compress.obj compusi.obj * -* * -* problems: * -* * -* * -* CAUTION: Uses a number of defines for access and speed. If you change * -* anything, make sure about side effects. * -* * -* Compression: * -* Algorithm: use open addressing double hashing (no chaining) on the * -* prefix code / next character combination. We do a variant of Knuth's * -* algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime * -* secondary probe. Here, the modular division first probe is gives way * -* to a faster exclusive-or manipulation. * -* Also block compression with an adaptive reset was used in original code, * -* whereby the code table is cleared when the compression ration decreases * -* but after the table fills. This was removed from this edition. The table * -* is re-sized at this point when it is filled , and a special CLEAR code is * -* generated for the decompressor. This results in some size difference from * -* straight version 4.0 joe Release. But it is fully compatible in both v4.0 * -* and v4.01 * -* * -* Decompression: * -* This routine adapts to the codes in the file building the "string" table * -* on-the-fly; requiring no table to be stored in the compressed file. The * -* tables used herein are shared with those of the compress() routine. * -* * -* Initials ---- Name --------------------------------- * -* DjG Donald J. Gloistein, current port to MsDos 16 bit * -* Plus many others, see rev.hst file for full list * -* LvR Lyle V. Rains, many thanks for improved implementation * -* of the compression and decompression routines. * -*************************************************************************@H*/ - -#include - -/* - * LZC state, largely variables with non-local scope. - */ -typedef struct LZCState { - NuArchive* pArchive; - int doCalcCRC; - uint16_t crc; - - /* compression */ - NuStraw* pStraw; - FILE* outfp; - long uncompRemaining; - - /* expansion */ - FILE* infp; - NuFunnel* pFunnel; - uint16_t* pCrc; - long compRemaining; - - - /* - * Globals from Compress sources. - */ - int offset; - long int in_count ; /* length of input */ - long int bytes_out; /* length of compressed output */ - - INTCODE prefxcode, nextfree; - INTCODE highcode; - INTCODE maxcode; - HASH hashsize; - int bits; - - char FAR *sfx; - - #if (SPLIT_PFX) - CODE FAR *pfx[2]; - #else - CODE FAR *pfx; - #endif - - #if (SPLIT_HT) - CODE FAR *ht[2]; - #else - CODE FAR *ht; - #endif - - #ifdef COMP40 - long int ratio; - long checkpoint; /* initialized to CHECK_GAP */ - #endif - - #ifdef DEBUG_LZC - int debug; /* initialized to FALSE */ - #endif - - NuError exit_stat; - - int maxbits; /* initialized to DFLTBITS */ - int block_compress; /* initialized to BLOCK_MASK */ - - /* - * Static local variables. Some of these were explicitly initialized - * to zero. - */ - INTCODE oldmaxcode; /* alloc_tables */ - HASH oldhashsize; /* alloc_tables */ - int oldbits; /* putcode */ - UCHAR outbuf[MAXBITS]; /* putcode */ - int prevbits; /* nextcode */ - int size; /* nextcode */ - UCHAR inbuf[MAXBITS]; /* nextcode */ -} LZCState; - - -/* - * The following two parameter tables are the hash table sizes and - * maximum code values for various code bit-lengths. The requirements - * are that Hashsize[n] must be a prime number and Maxcode[n] must be less - * than Maxhash[n]. Table occupancy factor is (Maxcode - 256)/Maxhash. - * Note: I am using a lower Maxcode for 16-bit codes in order to - * keep the hash table size less than 64k entries. - */ -static CONST HASH gNu_hs[] = { - 0x13FF, /* 12-bit codes, 75% occupancy */ - 0x26C3, /* 13-bit codes, 80% occupancy */ - 0x4A1D, /* 14-bit codes, 85% occupancy */ - 0x8D0D, /* 15-bit codes, 90% occupancy */ - 0xFFD9 /* 16-bit codes, 94% occupancy, 6% of code values unused */ -}; -#define Hashsize(maxb) (gNu_hs[(maxb) -MINBITS]) - -static CONST INTCODE gNu_mc[] = { - 0x0FFF, /* 12-bit codes */ - 0x1FFF, /* 13-bit codes */ - 0x3FFF, /* 14-bit codes */ - 0x7FFF, /* 15-bit codes */ - 0xEFFF /* 16-bit codes, 6% of code values unused */ -}; -#define Maxcode(maxb) (gNu_mc[(maxb) -MINBITS]) - -#ifdef __STDC__ -#ifdef DEBUG_LZC -#define allocx(type, ptr, size) \ - (((ptr) = (type FAR *) Nu_LZC_emalloc(pArchive, (uint32_t)(size),sizeof(type))) == NULLPTR(type) \ - ? (DBUG(("%s: "#ptr" -- ", "LZC")), NOMEM) : OK \ - ) -#else -#define allocx(type,ptr,size) \ - (((ptr) = (type FAR *) Nu_LZC_emalloc(pArchive, (uint32_t)(size),sizeof(type))) == NULLPTR(type) \ - ? NOMEM : OK \ - ) -#endif -#else -#define allocx(type,ptr,size) \ - (((ptr) = (type FAR *) Nu_LZC_emalloc(pArchive, (uint32_t)(size),sizeof(type))) == NULLPTR(type) \ - ? NOMEM : OK \ - ) -#endif - -#define free_array(type,ptr,offset) \ - if (ptr != NULLPTR(type)) { \ - Nu_LZC_efree(pArchive, (ALLOCTYPE FAR *)((ptr) + (offset))); \ - (ptr) = NULLPTR(type); \ - } - - /* - * Macro to allocate new memory to a pointer with an offset value. - */ -#define alloc_array(type, ptr, size, offset) \ - ( allocx(type, ptr, (size) - (offset)) != OK \ - ? NOMEM \ - : (((ptr) -= (offset)), OK) \ - ) - -/*static char FAR *sfx = NULLPTR(char) ;*/ -#define suffix(code) pLzcState->sfx[code] - - -#if (SPLIT_PFX) - /*static CODE FAR *pfx[2] = {NULLPTR(CODE), NULLPTR(CODE)};*/ -#else - /*static CODE FAR *pfx = NULLPTR(CODE);*/ -#endif - - -#if (SPLIT_HT) - /*static CODE FAR *ht[2] = {NULLPTR(CODE),NULLPTR(CODE)};*/ -#else - /*static CODE FAR *ht = NULLPTR(CODE);*/ -#endif - - -static int Nu_LZC_alloc_tables(LZCState* pLzcState, INTCODE newmaxcode, - HASH newhashsize) -{ - NuArchive* pArchive = pLzcState->pArchive; - /*static INTCODE oldmaxcode = 0;*/ - /*static HASH oldhashsize = 0;*/ - - if (newhashsize > pLzcState->oldhashsize) { -#if (SPLIT_HT) - free_array(CODE,pLzcState->ht[1], 0); - free_array(CODE,pLzcState->ht[0], 0); -#else - free_array(CODE,pLzcState->ht, 0); -#endif - pLzcState->oldhashsize = 0; - } - - if (newmaxcode > pLzcState->oldmaxcode) { -#if (SPLIT_PFX) - free_array(CODE,pLzcState->pfx[1], 128); - free_array(CODE,pLzcState->pfx[0], 128); -#else - free_array(CODE,pLzcState->pfx, 256); -#endif - free_array(char,pLzcState->sfx, 256); - - if ( alloc_array(char, pLzcState->sfx, newmaxcode + 1, 256) -#if (SPLIT_PFX) - || alloc_array(CODE, pLzcState->pfx[0], (newmaxcode + 1) / 2, 128) - || alloc_array(CODE, pLzcState->pfx[1], (newmaxcode + 1) / 2, 128) -#else - || alloc_array(CODE, pLzcState->pfx, (newmaxcode + 1), 256) -#endif - ) { - pLzcState->oldmaxcode = 0; - pLzcState->exit_stat = NOMEM; - return(NOMEM); - } - pLzcState->oldmaxcode = newmaxcode; - } - if (newhashsize > pLzcState->oldhashsize) { - if ( -#if (SPLIT_HT) - alloc_array(CODE, pLzcState->ht[0], (newhashsize / 2) + 1, 0) - || alloc_array(CODE, pLzcState->ht[1], newhashsize / 2, 0) -#else - alloc_array(CODE, pLzcState->ht, newhashsize, 0) -#endif - ) { - pLzcState->oldhashsize = 0; - pLzcState->exit_stat = NOMEM; - return(NOMEM); - } - pLzcState->oldhashsize = newhashsize; - } - return (OK); -} - -# if (SPLIT_PFX) - /* - * We have to split pfx[] table in half, - * because it's potentially larger than 64k bytes. - */ -# define prefix(code) (pLzcState->pfx[(code) & 1][(code) >> 1]) -# else - /* - * Then pfx[] can't be larger than 64k bytes, - * or we don't care if it is, so we don't split. - */ -# define prefix(code) (pLzcState->pfx[code]) -# endif - - -/* The initializing of the tables can be done quicker with memset() */ -/* but this way is portable through out the memory models. */ -/* If you use Microsoft halloc() to allocate the arrays, then */ -/* include the pragma #pragma function(memset) and make sure that */ -/* the length of the memory block is not greater than 64K. */ -/* This also means that you MUST compile in a model that makes the */ -/* default pointers to be far pointers (compact or large models). */ -/* See the file COMPUSI.DOS to modify function emalloc(). */ - -# if (SPLIT_HT) - /* - * We have to split ht[] hash table in half, - * because it's potentially larger than 64k bytes. - */ -# define probe(hash) (pLzcState->ht[(hash) & 1][(hash) >> 1]) -# define init_tables() \ - { \ - hash = pLzcState->hashsize >> 1; \ - pLzcState->ht[0][hash] = 0; \ - while (hash--) pLzcState->ht[0][hash] = pLzcState->ht[1][hash] = 0; \ - pLzcState->highcode = ~(~(INTCODE)0 << (pLzcState->bits = INITBITS)); \ - pLzcState->nextfree = (pLzcState->block_compress ? FIRSTFREE : 256); \ - } - -# else - - /* - * Then ht[] can't be larger than 64k bytes, - * or we don't care if it is, so we don't split. - */ -# define probe(hash) (pLzcState->ht[hash]) -# define init_tables() \ - { \ - hash = pLzcState->hashsize; \ - while (hash--) pLzcState->ht[hash] = 0; \ - pLzcState->highcode = ~(~(INTCODE)0 << (pLzcState->bits = INITBITS)); \ - pLzcState->nextfree = (pLzcState->block_compress ? FIRSTFREE : 256); \ - } - -# endif - - -/* - * =========================================================================== - * Compression - * =========================================================================== - */ - -static void Nu_prratio(long int num, long int den) -{ - register int q; /* Doesn't need to be long */ - - if(num > 214748L) { /* 2147483647/10000 */ - q = (int) (num / (den / 10000L)); - } - else { - q = (int) (10000L * num / den); /* Long calculations, though */ - } - if (q < 0) { - DBUG(("-")); - q = -q; - } - DBUG(("%d.%02d%%", q / 100, q % 100)); -} - -#ifdef COMP40 -/* table clear for block compress */ -/* this is for adaptive reset present in version 4.0 joe release */ -/* DjG, sets it up and returns TRUE to compress and FALSE to not compress */ -static int Nu_LZC_cl_block(LZCState* pLzcState) -{ - register long int rat; - - pLzcState->checkpoint = pLzcState->in_count + CHECK_GAP; -#ifdef DEBUG_LZC - if ( pLzcState->debug ) { - DBUG(( "count: %ld, ratio: ", pLzcState->in_count )); - Nu_prratio ( pLzcState->in_count, pLzcState->bytes_out ); - DBUG(( "\n")); - } -#endif - - if(pLzcState->in_count > 0x007fffff) { /* shift will overflow */ - rat = pLzcState->bytes_out >> 8; - if(rat == 0) /* Don't divide by zero */ - rat = 0x7fffffff; - else - rat = pLzcState->in_count / rat; - } - else - rat = (pLzcState->in_count << 8) / pLzcState->bytes_out; /* 8 fractional bits */ - - if ( rat > pLzcState->ratio ){ - pLzcState->ratio = rat; - return FALSE; - } - else { - pLzcState->ratio = 0; -#ifdef DEBUG_LZC - if(pLzcState->debug) { - DBUG(( "clear\n" )); - } -#endif - return TRUE; /* clear the table */ - } - return FALSE; /* don't clear the table */ -} -#endif - -static CONST UCHAR gNu_rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; - -static void Nu_LZC_putcode(LZCState* pLzcState, INTCODE code, register int bits) -{ - /*static int oldbits = 0;*/ - /*static UCHAR outbuf[MAXBITS];*/ - register UCHAR *buf; - register int shift; - - if (bits != pLzcState->oldbits) { - if (bits == 0) { - /* bits == 0 means EOF, write the rest of the buffer. */ - if (pLzcState->offset > 0) { - fwrite(pLzcState->outbuf,1,(pLzcState->offset +7) >> 3, pLzcState->outfp); - pLzcState->bytes_out += ((pLzcState->offset +7) >> 3); - } - pLzcState->offset = 0; - pLzcState->oldbits = 0; - fflush(pLzcState->outfp); - return; - } - else { - /* Change the code size. We must write the whole buffer, - * because the expand side won't discover the size change - * until after it has read a buffer full. - */ - if (pLzcState->offset > 0) { - fwrite(pLzcState->outbuf, 1, pLzcState->oldbits, pLzcState->outfp); - pLzcState->bytes_out += pLzcState->oldbits; - pLzcState->offset = 0; - } - pLzcState->oldbits = bits; - #ifdef DEBUG_LZC - if ( pLzcState->debug ) { - DBUG(( "\nChange to %d bits\n", bits )); - } - #endif /* DEBUG_LZC */ - } - } - /* Get to the first byte. */ - buf = pLzcState->outbuf + ((shift = pLzcState->offset) >> 3); - if ((shift &= 7) != 0) { - *(buf) |= (*buf & gNu_rmask[shift]) | (UCHAR)(code << shift); - *(++buf) = (UCHAR)(code >> (8 - shift)); - if (bits + shift > 16) - *(++buf) = (UCHAR)(code >> (16 - shift)); - } - else { - /* Special case for fast execution */ - *(buf) = (UCHAR)code; - *(++buf) = (UCHAR)(code >> 8); - } - if ((pLzcState->offset += bits) == (bits << 3)) { - pLzcState->bytes_out += bits; - fwrite(pLzcState->outbuf,1,bits,pLzcState->outfp); - pLzcState->offset = 0; - } - return; -} - - -#define kNuLZCEOF (-1) - -/* - * Get the next byte from the input straw. Also updates the CRC - * if "doCalcCRC" is set to true. - * - * Returns kNuLZCEOF as the value when we're out of data. - */ -static NuError Nu_LZCGetcCRC(LZCState* pLzcState, int* pSym) -{ - NuError err; - uint8_t c; - - if (!pLzcState->uncompRemaining) { - *pSym = kNuLZCEOF; - return kNuErrNone; - } - - err = Nu_StrawRead(pLzcState->pArchive, pLzcState->pStraw, &c, 1); - if (err == kNuErrNone) { - if (pLzcState->doCalcCRC) - pLzcState->crc = Nu_CalcCRC16(pLzcState->crc, &c, 1); - *pSym = c; - pLzcState->uncompRemaining--; - } - - return err; -} - -/* - * compress stdin to stdout - */ -static void Nu_LZC_compress(LZCState* pLzcState, uint32_t* pDstLen) -{ - int c,adjbits; - register HASH hash; - register INTCODE code; - HASH hashf[256]; - - Assert(pLzcState->outfp != NULL); - - pLzcState->maxcode = Maxcode(pLzcState->maxbits); - pLzcState->hashsize = Hashsize(pLzcState->maxbits); - -#ifdef COMP40 -/* Only needed for adaptive reset */ - pLzcState->checkpoint = CHECK_GAP; - pLzcState->ratio = 0; -#endif - - adjbits = pLzcState->maxbits -10; - for (c = 256; --c >= 0; ){ - hashf[c] = ((( c &0x7) << 7) ^ c) << adjbits; - } - pLzcState->exit_stat = OK; - if (Nu_LZC_alloc_tables(pLzcState, pLzcState->maxcode, pLzcState->hashsize)) /* exit_stat already set */ - return; - init_tables(); - - #if 0 - /* if not zcat or filter */ - if(is_list && !zcat_flg) { /* Open output file */ - if (freopen(ofname, WRITE_FILE_TYPE, pLzcState->outfp) == NULL) { - pLzcState->exit_stat = NOTOPENED; - return; - } - if (!quiet) - fprintf(stderr, "%s: ",ifname); /*#if 0*/ - setvbuf(Xstdout,zbuf,_IOFBF,ZBUFSIZE); - } - #endif - - /* - * Check the input stream for previously seen strings. We keep - * adding characters to the previously seen prefix string until we - * get a character which forms a new (unseen) string. We then send - * the code for the previously seen prefix string, and add the new - * string to our tables. The check for previous strings is done by - * hashing. If the code for the hash value is unused, then we have - * a new string. If the code is used, we check to see if the prefix - * and suffix values match the current input; if so, we have found - * a previously seen string. Otherwise, we have a hash collision, - * and we try secondary hash probes until we either find the current - * string, or we find an unused entry (which indicates a new string). - */ - if (1 /*!nomagic*/) { - putc(gNu_magic_header[0], pLzcState->outfp); - putc(gNu_magic_header[1], pLzcState->outfp); - putc((char)(pLzcState->maxbits | pLzcState->block_compress), pLzcState->outfp); - if(ferror(pLzcState->outfp)){ /* check it on entry */ - pLzcState->exit_stat = WRITEERR; - return; - } - pLzcState->bytes_out = 3L; /* includes 3-byte header mojo */ - } - else - pLzcState->bytes_out = 0L; /* no 3-byte header mojo */ - pLzcState->in_count = 1L; - pLzcState->offset = 0; - - pLzcState->exit_stat = Nu_LZCGetcCRC(pLzcState, &c); - if (pLzcState->exit_stat != kNuErrNone) - return; - pLzcState->prefxcode = (INTCODE)c; - - while (1) { - pLzcState->exit_stat = Nu_LZCGetcCRC(pLzcState, &c); - if (pLzcState->exit_stat != kNuErrNone) - return; - if (c == kNuLZCEOF) - break; - - pLzcState->in_count++; - hash = pLzcState->prefxcode ^ hashf[c]; - /* I need to check that my hash value is within range - * because my 16-bit hash table is smaller than 64k. - */ - if (hash >= pLzcState->hashsize) - hash -= pLzcState->hashsize; - if ((code = (INTCODE)probe(hash)) != UNUSED) { - if (suffix(code) != (char)c || (INTCODE)prefix(code) != pLzcState->prefxcode) { - /* hashdelta is subtracted from hash on each iteration of - * the following hash table search loop. I compute it once - * here to remove it from the loop. - */ - HASH hashdelta = (0x120 - c) << (adjbits); - do { - /* rehash and keep looking */ - Assert(code >= FIRSTFREE && code <= pLzcState->maxcode); - if (hash >= hashdelta) hash -= hashdelta; - else hash += (pLzcState->hashsize - hashdelta); - Assert(hash < pLzcState->hashsize); - if ((code = (INTCODE)probe(hash)) == UNUSED) - goto newcode; - } while (suffix(code) != (char)c || (INTCODE)prefix(code) != pLzcState->prefxcode); - } - pLzcState->prefxcode = code; - } - else { - newcode: { - Nu_LZC_putcode(pLzcState, pLzcState->prefxcode, pLzcState->bits); - code = pLzcState->nextfree; - Assert(hash < pLzcState->hashsize); - Assert(code >= FIRSTFREE); - Assert(code <= pLzcState->maxcode + 1); - if (code <= pLzcState->maxcode) { - probe(hash) = (CODE)code; - prefix(code) = (CODE)pLzcState->prefxcode; - suffix(code) = (char)c; - if (code > pLzcState->highcode) { - pLzcState->highcode += code; - ++pLzcState->bits; - } - pLzcState->nextfree = code + 1; - } -#ifdef COMP40 - else if (pLzcState->in_count >= pLzcState->checkpoint && pLzcState->block_compress ) { - if (Nu_LZC_cl_block(pLzcState)){ -#else - else if (pLzcState->block_compress){ -#endif - Nu_LZC_putcode(pLzcState, (INTCODE)c, pLzcState->bits); - Nu_LZC_putcode(pLzcState, CLEAR, pLzcState->bits); - init_tables(); - pLzcState->exit_stat = Nu_LZCGetcCRC(pLzcState, &c); - if (pLzcState->exit_stat != kNuErrNone) - return; - if (c == kNuLZCEOF) - break; - pLzcState->in_count++; -#ifdef COMP40 - } -#endif - } - pLzcState->prefxcode = (INTCODE)c; - } - } - } - Nu_LZC_putcode(pLzcState, pLzcState->prefxcode, pLzcState->bits); - Nu_LZC_putcode(pLzcState, CLEAR, 0); - /* - * Print out stats on stderr - */ - if(1 /*zcat_flg == 0 && !quiet*/) { -#ifdef DEBUG_LZC - DBUG(( - "%ld chars in, (%ld bytes) out, compression factor: ", - pLzcState->in_count, pLzcState->bytes_out )); - Nu_prratio( pLzcState->in_count, pLzcState->bytes_out ); - DBUG(( "\n")); - DBUG(( "\tCompression as in compact: " )); - Nu_prratio( pLzcState->in_count-pLzcState->bytes_out, pLzcState->in_count ); - DBUG(( "\n")); - DBUG(( "\tLargest code (of last block) was %d (%d bits)\n", - pLzcState->prefxcode - 1, pLzcState->bits )); -#else - DBUG(( "Compression: " )); - Nu_prratio( pLzcState->in_count-pLzcState->bytes_out, pLzcState->in_count ); -#endif /* DEBUG_LZC */ - } - if(pLzcState->bytes_out > pLzcState->in_count) /* if no savings */ - pLzcState->exit_stat = NOSAVING; - *pDstLen = pLzcState->bytes_out; - return ; -} - - -/* - * NufxLib interface to LZC compression. - */ -static NuError Nu_CompressLZC(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc, int maxbits) -{ - NuError err = kNuErrNone; - LZCState lzcState; - - memset(&lzcState, 0, sizeof(lzcState)); - lzcState.pArchive = pArchive; - lzcState.pStraw = pStraw; - lzcState.outfp = fp; - lzcState.uncompRemaining = srcLen; - - if (pCrc == NULL) { - lzcState.doCalcCRC = false; - } else { - lzcState.doCalcCRC = true; - lzcState.crc = *pCrc; - } - - lzcState.maxbits = maxbits; - lzcState.block_compress = BLOCK_MASK; /* enabled */ - - Nu_LZC_compress(&lzcState, pDstLen); - err = lzcState.exit_stat; - DBUG(("+++ LZC_compress returned with %d\n", err)); - -#if (SPLIT_HT) - free_array(CODE,lzcState.ht[1], 0); - free_array(CODE,lzcState.ht[0], 0); -#else - free_array(CODE,lzcState.ht, 0); -#endif - -#if (SPLIT_PFX) - free_array(CODE,lzcState.pfx[1], 128); - free_array(CODE,lzcState.pfx[0], 128); -#else - free_array(CODE,lzcState.pfx, 256); -#endif - free_array(char,lzcState.sfx, 256); - - if (pCrc != NULL) - *pCrc = lzcState.crc; - - return err; -} - -NuError Nu_CompressLZC12(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - return Nu_CompressLZC(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, 12); -} - -NuError Nu_CompressLZC16(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - return Nu_CompressLZC(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, 16); -} - - -/* - * =========================================================================== - * Expansion - * =========================================================================== - */ - -/* - * Write the next byte to the output funnel. Also updates the CRC - * if "doCalcCRC" is set to true. - * - * Returns kNuLZCEOF as the value when we're out of data. - */ -static NuError Nu_LZCPutcCRC(LZCState* pLzcState, char c) -{ - NuError err; - - err = Nu_FunnelWrite(pLzcState->pArchive, pLzcState->pFunnel, - (uint8_t*) &c, 1); - if (pLzcState->doCalcCRC) - pLzcState->crc = Nu_CalcCRC16(pLzcState->crc, (uint8_t*) &c, 1); - - return err; -} - - -static int Nu_LZC_nextcode(LZCState* pLzcState, INTCODE* codeptr) -/* Get the next code from input and put it in *codeptr. - * Return (TRUE) on success, or return (FALSE) on end-of-file. - * Adapted from COMPRESS V4.0. - */ -{ - /*static int prevbits = 0;*/ - register INTCODE code; - /*static int size;*/ - /*static UCHAR inbuf[MAXBITS];*/ - register int shift; - UCHAR *bp; - - /* If the next entry is a different bit-size than the preceeding one - * then we must adjust the size and scrap the old buffer. - */ - if (pLzcState->prevbits != pLzcState->bits) { - pLzcState->prevbits = pLzcState->bits; - pLzcState->size = 0; - } - /* If we can't read another code from the buffer, then refill it. - */ - shift = pLzcState->offset; - if (pLzcState->size - shift < pLzcState->bits) { - /* Read more input and convert size from # of bytes to # of bits */ - long getSize; - - getSize = pLzcState->bits; - if (getSize > pLzcState->compRemaining) - getSize = pLzcState->compRemaining; - if (!getSize) /* act like EOF */ - return FALSE; - pLzcState->size = fread(pLzcState->inbuf, 1, getSize, pLzcState->infp) << 3; - if (pLzcState->size <= 0 || ferror(pLzcState->infp)) - return(FALSE); - pLzcState->compRemaining -= getSize; - pLzcState->offset = shift = 0; - } - /* Get to the first byte. */ - bp = pLzcState->inbuf + (shift >> 3); - /* Get first part (low order bits) */ - code = (*bp++ >> (shift &= 7)); - /* high order bits. */ - code |= *bp++ << (shift = 8 - shift); - if ((shift += 8) < pLzcState->bits) code |= *bp << shift; - *codeptr = code & pLzcState->highcode; - pLzcState->offset += pLzcState->bits; - return (TRUE); -} - -static void Nu_LZC_decompress(LZCState* pLzcState, uint32_t compressedLen) -{ - NuArchive* pArchive = pLzcState->pArchive; - register int i; - register INTCODE code; - char sufxchar = 0; - INTCODE savecode; - FLAG fulltable = FALSE, cleartable; - /*static*/ char *token= NULL; /* String buffer to build token */ - /*static*/ int maxtoklen = MAXTOKLEN; - int flags; - - Assert(pLzcState->infp != NULL); - - pLzcState->exit_stat = OK; - - if (compressedLen < 3) { - /* not long enough to be valid! */ - pLzcState->exit_stat = kNuErrBadData; - Nu_ReportError(NU_BLOB, pLzcState->exit_stat, "thread too short to be valid LZC"); - return; - } - pLzcState->compRemaining = compressedLen; - - /* - * This comes out of "compress.c" rather than "compapi.c". - */ - if ((getc(pLzcState->infp)!=(gNu_magic_header[0] & 0xFF)) - || (getc(pLzcState->infp)!=(gNu_magic_header[1] & 0xFF))) - { - DBUG(("not in compressed format\n")); - pLzcState->exit_stat = kNuErrBadData; - return; - } - flags = getc(pLzcState->infp); /* set -b from file */ - pLzcState->block_compress = flags & BLOCK_MASK; - pLzcState->maxbits = flags & BIT_MASK; - if(pLzcState->maxbits > MAXBITS) { - DBUG(("compressed with %d bits, can only handle %d bits\n", - pLzcState->maxbits, MAXBITS)); - pLzcState->exit_stat = kNuErrBadData; - return; - } - - pLzcState->compRemaining -= 3; - - /* Initialze the token buffer. */ - token = (char*)Nu_Malloc(pArchive, maxtoklen); - if (token == NULL) { - pLzcState->exit_stat = NOMEM; - return; - } - - if (Nu_LZC_alloc_tables(pLzcState, pLzcState->maxcode = ~(~(INTCODE)0 << pLzcState->maxbits),0)) /* exit_stat already set */ - return; - - #if 0 - /* if not zcat or filter */ - if(is_list && !zcat_flg) { /* Open output file */ - if (freopen(ofname, WRITE_FILE_TYPE, stdout) == NULL) { - pLzcState->exit_stat = NOTOPENED; - return; - } - if (!quiet) - fprintf(stderr, "%s: ",ifname); /*#if 0*/ - setvbuf(stdout,xbuf,_IOFBF,XBUFSIZE); - } - #endif - - cleartable = TRUE; - savecode = CLEAR; - pLzcState->offset = 0; - do { - if ((code = savecode) == CLEAR && cleartable) { - pLzcState->highcode = ~(~(INTCODE)0 << (pLzcState->bits = INITBITS)); - fulltable = FALSE; - pLzcState->nextfree = (cleartable = pLzcState->block_compress) == FALSE ? 256 : FIRSTFREE; - if (!Nu_LZC_nextcode(pLzcState, &pLzcState->prefxcode)) - break; - /*putc((*/sufxchar = (char)pLzcState->prefxcode/*), stdout)*/; - pLzcState->exit_stat = Nu_LZCPutcCRC(pLzcState, sufxchar); - if (pLzcState->exit_stat != kNuErrNone) - return; - continue; - } - i = 0; - if (code >= pLzcState->nextfree && !fulltable) { - if (code != pLzcState->nextfree){ - DBUG(("ERROR: code (0x%x) != nextfree (0x%x)\n", - code, pLzcState->nextfree)); - pLzcState->exit_stat = CODEBAD; - return ; /* Non-existant code */ - } - /* Special case for sequence KwKwK (see text of article) */ - code = pLzcState->prefxcode; - token[i++] = sufxchar; - } - /* Build the token string in reverse order by chasing down through - * successive prefix tokens of the current token. Then output it. - */ - while (code >= 256) { - #ifdef DEBUG_LZC - /* These are checks to ease paranoia. Prefix codes must decrease - * monotonically, otherwise we must have corrupt tables. We can - * also check that we haven't overrun the token buffer. - */ - if (code <= (INTCODE)prefix(code)){ - pLzcState->exit_stat= TABLEBAD; - return; - } - #endif - if (i >= maxtoklen) { - maxtoklen *= 2; /* double the size of the token buffer */ - if ((token = Nu_Realloc(pArchive, token, maxtoklen)) == NULL) { - pLzcState->exit_stat = TOKTOOBIG; - return; - } - } - token[i++] = suffix(code); - code = (INTCODE)prefix(code); - } - /*putc(*/sufxchar = (char)code/*, stdout)*/; - pLzcState->exit_stat = Nu_LZCPutcCRC(pLzcState, sufxchar); - while (--i >= 0) { - /*putc(token[i], stdout);*/ - pLzcState->exit_stat = Nu_LZCPutcCRC(pLzcState, token[i]); - } - if (pLzcState->exit_stat != kNuErrNone) - return; - /* If table isn't full, add new token code to the table with - * codeprefix and codesuffix, and remember current code. - */ - if (!fulltable) { - code = pLzcState->nextfree; - Assert(256 <= code && code <= pLzcState->maxcode); - prefix(code) = (CODE)pLzcState->prefxcode; - suffix(code) = sufxchar; - pLzcState->prefxcode = savecode; - if (code++ == pLzcState->highcode) { - if (pLzcState->highcode >= pLzcState->maxcode) { - fulltable = TRUE; - --code; - } - else { - ++pLzcState->bits; - pLzcState->highcode += code; /* nextfree == highcode + 1 */ - } - } - pLzcState->nextfree = code; - } - } while (Nu_LZC_nextcode(pLzcState, &savecode)); - pLzcState->exit_stat = (ferror(pLzcState->infp))? READERR : OK; - - Nu_Free(pArchive, token); - return ; -} - - -/* - * NufxLib interface to LZC expansion. - */ -NuError Nu_ExpandLZC(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - LZCState lzcState; - - memset(&lzcState, 0, sizeof(lzcState)); - lzcState.pArchive = pArchive; - lzcState.infp = infp; - lzcState.pFunnel = pFunnel; - - if (pCrc == NULL) { - lzcState.doCalcCRC = false; - } else { - lzcState.doCalcCRC = true; - lzcState.crc = *pCrc; - } - - Nu_LZC_decompress(&lzcState, pThread->thCompThreadEOF); - err = lzcState.exit_stat; - DBUG(("+++ LZC_decompress returned with %d\n", err)); - -#if (SPLIT_HT) - free_array(CODE,lzcState.ht[1], 0); - free_array(CODE,lzcState.ht[0], 0); -#else - free_array(CODE,lzcState.ht, 0); -#endif - -#if (SPLIT_PFX) - free_array(CODE,lzcState.pfx[1], 128); - free_array(CODE,lzcState.pfx[0], 128); -#else - free_array(CODE,lzcState.pfx, 256); -#endif - free_array(char,lzcState.sfx, 256); - - if (pCrc != NULL) - *pCrc = lzcState.crc; - return err; -} - -#endif /*ENABLE_LZC*/ diff --git a/ciderpress/nufxlib/Lzw.c b/ciderpress/nufxlib/Lzw.c deleted file mode 100644 index 69ff120..0000000 --- a/ciderpress/nufxlib/Lzw.c +++ /dev/null @@ -1,1622 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * ShrinkIt LZW functions. The original code was developed by Kent Dickey - * and Andy Nicholas. - * - * Unisys holds US patent #4,558,302 (filed June 20, 1983 and issued December - * 10, 1985). A policy set in 1995 specifies the lifetime of a patent as - * the longer of 20 years from the date of application or 17 years from the - * date of grant, so the Unisys LZW patent expired on June 20, 2003 in the - * USA. Patents in some other countries expire after July 7, 2004. - * - * An older note: - * - * The Unisys patent is one of many that covers LZW compression, but Unisys - * is the only company actively attacking anyone who uses it. The statement - * Unisys made regarding LZW (and, specifically, GIF and TIFF-LZW) says: - * - * Q: I use LZW in my programs, but not for GIF or TIFF graphics. What should - * I do? - * A: If you are not a business, and the programs are for your own personal - * non-commercial or not-for-profit use, Unisys does not require you to - * obtain a license. If they are used as part of a business and/or you sell - * the programs for commercial or for-profit purposes, then you must contact - * the Welch Patent Licensing Department at Unisys and explain your - * circumstances. They will have a license agreement for your application of - * their LZW algorithm. - * - * According to this, the use of LZW in NufxLib has never required a license. - */ -#include "NufxLibPriv.h" - -#ifdef ENABLE_LZW - -/* the LZW algorithms operate on 4K chunks */ -#define kNuLZWBlockSize 4096 - -/* a little padding to avoid mysterious crashes on bad data */ -#define kNuSafetyPadding 64 - -#define kNuLZWClearCode 0x0100 -#define kNuLZWFirstCode 0x0101 - - -/* sometimes we want to get *really* verbose rather late in a large archive */ -#ifdef DEBUG_LZW - static Boolean gNuDebugVerbose = true; - #define DBUG_LZW(x) { if (gNuDebugVerbose) { DBUG(x); } } -#else - #define DBUG_LZW ((void)0) -#endif - - -/* - * =========================================================================== - * Compression - * =========================================================================== - */ - -/* - * We use a hash function borrowed from UNIX compress, which is described - * in the v4.3 sources as: - * - * Algorithm: use open addressing double hashing (no chaining) on the - * prefix code / next character combination. We do a variant of Knuth's - * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime - * secondary probe. Here, the modular division first probe is gives way - * to a faster exclusive-or manipulation. - * - * The function used to generate it is: - * - * int c, hashf[256]; - * for (c = 256; --c >= 0; ) { - * hashf[c] = (((c & 0x7) << 7) ^ c) << (maxbits-10); - * } - * - * It is used with: - * - * hash = prefixcode ^ hashf[c]; \* c is char from getchar() *\ - * - * The value for kNuLZWHashSize determines the size of the hash table and - * the % occupancy. We want a fair number of vacancies because we probe - * when we collide. Using 5119 (0x13ff) with 12-bit codes yields 75% - * occupancy. - */ - -#define kNuLZWHashSize 5119 /* must be prime */ -#define kNuLZWEntryUnused 0 /* indicates an unused hash entry */ -#define kNuLZWHashFuncTblSize 256 /* one entry per char value */ -#define kNuLZWDefaultVol 0xfe /* use this as volume number */ -#define kNuLZWHashDelta 0x120 /* used in secondary hashing */ -#define kNuLZWMinCode kNuLZWClearCode /* smallest 12-bit LZW code */ -#define kNuLZWMaxCode 0x0fff /* largest 12-bit LZW code */ -#define kNuLZW2StopCode 0x0ffd /* LZW/2 stops here */ - -/* - * Mask of bits, from 0 to 8. - */ -static const int gNuBitMask[] = { - 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff -}; - -#define kNuRLEDefaultEscape 0xdb /* ShrinkIt standard */ - -/* - * This holds all of the "big" dynamic state, plus a few things that I - * don't want to pass around. It's allocated once for each instance of - * an open archive, and re-used. - * - * The hash table consists of three parts. We have a choice for some of - * them, "ushort" or "uint". With "ushort" it uses less memory and is - * more likely to fit in a CPU cache, but on some processors you have to - * add instructions to manipulate 16-bit values in a 32-bit word. I'm - * guessing "ushort" is better overall. - */ -typedef struct LZWCompressState { - NuArchive* pArchive; - - uint16_t entry[kNuLZWHashSize]; /* uint or ushort */ - uint16_t prefix[kNuLZWMaxCode+1]; /* uint or ushort */ - uint8_t suffix[kNuLZWMaxCode+1]; - - uint16_t hashFunc[kNuLZWHashFuncTblSize]; /* uint or ushort */ - - uint8_t inputBuf[kNuLZWBlockSize]; /* 4K of raw input */ - uint8_t rleBuf[kNuLZWBlockSize*2 + kNuSafetyPadding]; - uint8_t lzwBuf[(kNuLZWBlockSize * 3) / 2 + kNuSafetyPadding]; - - uint16_t chunkCrc; /* CRC for LZW/1 */ - - /* LZW/2 state variables */ - int nextFree; - int codeBits; - int highCode; - Boolean initialClear; -} LZWCompressState; - - -/* - * Allocate some "reusable" state for LZW compression. - * - * The only thing that really needs to be retained across calls is - * the hash function. This way we don't have to re-create it for - * every file, or store it statically in the binary. - */ -static NuError Nu_AllocLZWCompressState(NuArchive* pArchive) -{ - NuError err; - LZWCompressState* lzwState; - int ic; - - Assert(pArchive != NULL); - Assert(pArchive->lzwCompressState == NULL); - - /* allocate the general-purpose compression buffer, if needed */ - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - pArchive->lzwCompressState = Nu_Malloc(pArchive, sizeof(LZWCompressState)); - if (pArchive->lzwCompressState == NULL) - return kNuErrMalloc; - - /* - * The "hashFunc" table only needs to be set up once. - */ - lzwState = pArchive->lzwCompressState; - for (ic = 256; --ic >= 0; ) - lzwState->hashFunc[ic] = (((ic & 0x7) << 7) ^ ic) << 2; - - return kNuErrNone; -} - - -/* - * Compress a block of input from lzwState->inputBuf to lzwState->rleBuf. - * The size of the output is returned in "*pRLESize" (will be zero if the - * block expanded instead of compressing). - * - * The maximum possible size of the output is 2x the original, which can - * only occur if the input is an alternating sequence of RLE delimiters - * and non-delimiters. It requires 3 bytes to encode a solitary 0xdb, - * so you get (4096 / 2) non-delimiters plus (4096 / 2) * 3 RLE-encoded - * delimiters. We deal with this by using an 8K output buffer, so we - * don't have to watch for overflow in the inner loop. - * - * The RLE format is " ", where count is zero-based - * (i.e. for three bytes we encode "2", allowing us to express 1-256). - */ -static NuError Nu_CompressBlockRLE(LZWCompressState* lzwState, int* pRLESize) -{ - const uint8_t* inPtr = lzwState->inputBuf; - const uint8_t* endPtr = inPtr + kNuLZWBlockSize; - uint8_t* outPtr = lzwState->rleBuf; - uint8_t matchChar; - int matchCount; - - while (inPtr < endPtr) { - matchChar = *inPtr; - matchCount = 1; - - /* count up the matching chars */ - while (*++inPtr == matchChar && inPtr < endPtr) - matchCount++; - - if (matchCount > 3) { - if (matchCount > 256) { - /* rare case - really long match */ - while (matchCount > 256) { - *outPtr++ = kNuRLEDefaultEscape; - *outPtr++ = matchChar; - *outPtr++ = 255; - matchCount -= 256; - } - - /* take care of the odd bits -- which might not form a run! */ - if (matchCount > 3) { - *outPtr++ = kNuRLEDefaultEscape; - *outPtr++ = matchChar; - *outPtr++ = matchCount -1; - } else { - while (matchCount--) - *outPtr++ = matchChar; - } - - } else { - /* common case */ - *outPtr++ = kNuRLEDefaultEscape; - *outPtr++ = matchChar; - *outPtr++ = matchCount -1; - } - - } else { - if (matchChar == kNuRLEDefaultEscape) { - /* encode 1-3 0xDBs */ - *outPtr++ = kNuRLEDefaultEscape; - *outPtr++ = kNuRLEDefaultEscape; - *outPtr++ = matchCount -1; - } else { - while (matchCount--) - *outPtr++ = matchChar; - } - } - } - - *pRLESize = outPtr - lzwState->rleBuf; - Assert(*pRLESize > 0 && *pRLESize < sizeof(lzwState->rleBuf)); - - return kNuErrNone; -} - - -/* - * Clear the LZW table. Also resets the LZW/2 state. - */ -static void Nu_ClearLZWTable(LZWCompressState* lzwState) -{ - Assert(lzwState != NULL); - - /*DBUG_LZW(("### clear table\n"));*/ - - /* reset table entries */ - Assert(kNuLZWEntryUnused == 0); /* make sure this is okay */ - memset(lzwState->entry, 0, sizeof(lzwState->entry)); - - /* reset state variables */ - lzwState->nextFree = kNuLZWFirstCode; - lzwState->codeBits = 9; - lzwState->highCode = ~(~0 << lzwState->codeBits); /* a/k/a 0x01ff */ - lzwState->initialClear = false; -} - - -/* - * Write a variable-width LZW code to the output. "prefixCode" has the - * value to write, and "codeBits" is the width. - * - * Data is written in little-endian order (lowest byte first). The - * putcode function in LZC is probably faster, but the format isn't - * compatible with SHK. - * - * The worst conceivable expansion for LZW is 12 bits of output for every - * byte of input. Because we're using variable-width codes and LZW is - * reasonably effective at finding matches, the actual expansion will - * certainly be less. Throwing the extra 2K onto the end of the buffer - * saves us from having to check for a buffer overflow here. - * - * On exit, "*pOutBuf" will point PAST the last byte we wrote (even if - * it's a partial byte), and "*pAtBit" will contain the bit offset. - * - * (Turning this into a macro might speed things up.) - */ -static inline void Nu_LZWPutCode(uint8_t** pOutBuf, uint32_t prefixCode, - int codeBits, int* pAtBit) -{ - int atBit = *pAtBit; - uint8_t* outBuf = *pOutBuf; - - /*DBUG_LZW(("### PUT: prefixCode=0x%04lx, codeBits=%d, atBit=%d\n", - prefixCode, codeBits, atBit));*/ - - Assert(atBit >= 0 && atBit < sizeof(gNuBitMask)); - - if (atBit) { - /* align the prefix code with the existing byte */ - prefixCode <<= atBit; - - /* merge it with the buffer contents (if necessary) and write lo bits */ - outBuf--; - *outBuf = (uint8_t)((*outBuf & gNuBitMask[atBit]) | prefixCode); - outBuf++; - } else { - /* nothing to merge with; write lo byte at next posn and advance */ - *outBuf++ = (uint8_t)prefixCode; - } - - /* codes are at least 9 bits, so we know we have to write one more */ - *outBuf++ = (uint8_t)(prefixCode >> 8); - - /* in some cases, we may have to write yet another */ - atBit += codeBits; - if (atBit > 16) - *outBuf++ = (uint8_t)(prefixCode >> 16); - - *pAtBit = atBit & 0x07; - *pOutBuf = outBuf; -} - - -/* - * Compress a block of data with LZW, from "inputBuf" to lzwState->lzwBuf. - * - * LZW/1 is just like LZW/2, except that for the former the table is - * always cleared before this function is called. Because of this, the - * table never fills completely, so none of the table-overflow code - * ever happens. - * - * This function is patterned after the LZC compress function, rather - * than the NuLib LZW code, because the NuLib code was abysmal (a rather - * straight translation from 6502 assembly). This function differs from LZC - * in a few areas in order to make the output match GS/ShrinkIt. - * - * There is a (deliberate) minor bug here: if a table clear is emitted - * when there is only one character left in the input, nothing will be - * added to the hash table (as there is nothing to add) but "nextFree" - * will be advanced. This mimics GSHK's behavior, and accounts for the - * "resetFix" logic in the expansion functions. Code 0x0101 is essentially - * lost in this situation. - */ -static NuError Nu_CompressLZWBlock(LZWCompressState* lzwState, - const uint8_t* inputBuf, int inputCount, int* pOutputCount) -{ - int nextFree, ic, atBit, codeBits; - int hash, hashDelta; - int prefixCode, code, highCode; - const uint8_t* inputEnd = inputBuf + inputCount; - /* local copies of lzwState members, for speed */ - const uint16_t* pHashFunc = lzwState->hashFunc; - uint16_t* pEntry = lzwState->entry; - uint16_t* pPrefix = lzwState->prefix; - uint8_t* pSuffix = lzwState->suffix; - uint8_t* outBuf = lzwState->lzwBuf; - - Assert(lzwState != NULL); - Assert(inputBuf != NULL); - Assert(inputCount > 0 && inputCount <= kNuLZWBlockSize); - /* make sure nobody has been messing with the types */ - Assert(sizeof(pHashFunc[0]) == sizeof(lzwState->hashFunc[0])); - Assert(sizeof(pEntry[0]) == sizeof(lzwState->entry[0])); - Assert(sizeof(pPrefix[0]) == sizeof(lzwState->prefix[0])); - Assert(sizeof(pSuffix[0]) == sizeof(lzwState->suffix[0])); - - /*DBUG_LZW(("### START LZW (nextFree=0x%04x)\n", lzwState->nextFree));*/ - - atBit = 0; - - if (lzwState->initialClear) { - /*DBUG_LZW(("### initialClear set\n"));*/ - codeBits = lzwState->codeBits; - Nu_LZWPutCode(&outBuf, kNuLZWClearCode, codeBits, &atBit); - Nu_ClearLZWTable(lzwState); - } - - table_cleared: - /* recover our state (or get newly-cleared state) */ - nextFree = lzwState->nextFree; - codeBits = lzwState->codeBits; - highCode = lzwState->highCode; - - prefixCode = *inputBuf++; - - /*DBUG_LZW(("### fchar=0x%02x\n", prefixCode));*/ - - while (inputBuf < inputEnd) { - ic = *inputBuf++; - /*DBUG_LZW(("### char=0x%02x\n", ic));*/ - - hash = prefixCode ^ pHashFunc[ic]; - code = pEntry[hash]; - - if (code != kNuLZWEntryUnused) { - /* something is here, either our prefix or a hash collision */ - if (pSuffix[code] != ic || pPrefix[code] != prefixCode) { - /* we've collided; do the secondary probe */ - hashDelta = (kNuLZWHashDelta - ic) << 2; - do { - /* rehash and keep looking */ - Assert(code >= kNuLZWMinCode && code <= kNuLZWMaxCode); - if (hash >= hashDelta) - hash -= hashDelta; - else - hash += kNuLZWHashSize - hashDelta; - Assert(hash >= 0 && hash < kNuLZWHashSize); - - if ((code = pEntry[hash]) == kNuLZWEntryUnused) - goto new_code; - } while (pSuffix[code] != ic || pPrefix[code] != prefixCode); - } - - /* else we found a matching string, and can keep searching */ - prefixCode = code; - - } else { - /* found an empty entry, add the prefix+suffix to the table */ - new_code: - Nu_LZWPutCode(&outBuf, prefixCode, codeBits, &atBit); - Assert(outBuf < lzwState->lzwBuf + sizeof(lzwState->lzwBuf)); - /*DBUG_LZW(("### outBuf now at +%d\n",outBuf - lzwState->lzwBuf));*/ - - code = nextFree; - Assert(hash < kNuLZWHashSize); - Assert(code >= kNuLZWMinCode); - Assert(code <= kNuLZWMaxCode); - - /* - * GSHK accepts 0x0ffd, and then sends the table clear - * immediately. We could improve on GSHK's compression slightly - * by using the entire table, but I want to generate the exact - * same output as GSHK. (The decoder believes the table clear - * is entry 0xffe, so we've got one more coming, and possibly - * two if we tweak getcode slightly.) - * - * Experiments show that switching to 0xffe increases the size - * of files that don't compress well, and decreases the size - * of files that do. In both cases, the difference in size - * is very small. - */ - Assert(code <= kNuLZW2StopCode); - /*if (code <= kNuLZW2StopCode) {*/ - /*DBUG_LZW(("### added new code 0x%04x prefix=0x%04x ch=0x%02x\n", - code, prefixCode, ic));*/ - - pEntry[hash] = code; - pPrefix[code] = prefixCode; - pSuffix[code] = ic; - - /* - * Check and see if it's time to increase the code size (note - * we flip earlier than LZC by one here). - */ - if (code >= highCode) { - highCode += code +1; - codeBits++; - } - - nextFree++; - - /*}*/ - - prefixCode = ic; - - /* if the table is full, clear it (only for LZW/2) */ - if (code == kNuLZW2StopCode) { - /* output last code */ - Nu_LZWPutCode(&outBuf, prefixCode, codeBits, &atBit); - - if (inputBuf < inputEnd) { - /* still have data, keep going */ - Nu_LZWPutCode(&outBuf, kNuLZWClearCode, codeBits, &atBit); - Nu_ClearLZWTable(lzwState); - goto table_cleared; - } else { - /* no more input, hold table clear for next block */ - DBUG(("--- RARE: block-end clear\n")); - lzwState->initialClear = true; - goto table_clear_finish; - } - } - - Assert(nextFree <= kNuLZW2StopCode); - } - } - - /* - * Output the last code. Since there's no following character, we don't - * need to add an entry to the table... whatever we've found is already - * in there. - */ - Nu_LZWPutCode(&outBuf, prefixCode, codeBits, &atBit); - - /* - * Update the counters so LZW/2 has continuity. - */ - Assert(nextFree <= kNuLZW2StopCode); - if (nextFree >= highCode) { - highCode += nextFree +1; - codeBits++; - } - nextFree++; /* make room for the code we just wrote */ - - if (nextFree > kNuLZW2StopCode) { - /* - * The code we just wrote, which was part of a longer string already - * in the tree, took the last entry in the table. We need to clear - * the table, but we can't do it in this block. We will have to - * emit a table clear as the very first thing in the next block. - */ - DBUG(("--- RARE: block-end inter clear\n")); - lzwState->initialClear = true; - } - table_clear_finish: - - /* save state for next pass through */ - lzwState->nextFree = nextFree; - lzwState->codeBits = codeBits; - lzwState->highCode = highCode; - - Assert(inputBuf == inputEnd); - - *pOutputCount = outBuf - lzwState->lzwBuf; - - /* - if (*pOutputCount < inputCount) { - DBUG_LZW(("### compressed from %d to %d\n", inputCount, *pOutputCount)); - } else { - DBUG_LZW(("### NO compression (%d to %d)\n", inputCount,*pOutputCount)); - } - */ - - return kNuErrNone; -} - -/* - * Compress ShrinkIt-style "LZW/1" and "LZW/2". - * - * "*pThreadCrc" should already be set to its initial value. On exit it - * will contain the CRC of the uncompressed data. - * - * On exit, the output file will be positioned past the last byte written. - */ -static NuError Nu_CompressLZW(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pThreadCrc, Boolean isType2) -{ - NuError err = kNuErrNone; - LZWCompressState* lzwState; - long initialOffset; - const uint8_t* lzwInputBuf; - uint32_t blockSize, rleSize, lzwSize; - long compressedLen; - Boolean keepLzw; - - Assert(pArchive != NULL); - Assert(pStraw != NULL); - Assert(fp != NULL); - Assert(srcLen > 0); - Assert(pDstLen != NULL); - Assert(pThreadCrc != NULL); - Assert(isType2 == true || isType2 == false); - - /* - * Do some initialization and set-up. - */ - if (pArchive->lzwCompressState == NULL) { - err = Nu_AllocLZWCompressState(pArchive); - BailError(err); - } - Assert(pArchive->lzwCompressState != NULL); - Assert(pArchive->compBuf != NULL); - - lzwState = pArchive->lzwCompressState; - lzwState->pArchive = pArchive; - compressedLen = 0; - - /* - * And now for something ugly: for LZW/1 we have to compute the CRC - * twice. Old versions of ShrinkIt used LZW/1 and put the CRC in - * the compressed block while newer versions used LZW/2 and put the - * CRC in the thread header. We're using LZW/1 with the newer record - * format, so we need two CRCs. For some odd reason Andy N. decided - * to use 0xffff as the initial value for the thread one, so we can't - * just store the same thing in two places. - * - * Of course, this also means that an LZW/2 chunk stored in an old - * pre-v3 record wouldn't have a CRC at all... - * - * LZW/1 is included here for completeness. I can't think of a reason - * why you'd want to use it, really. - */ - lzwState->chunkCrc = kNuInitialChunkCRC; /* 0x0000 */ - - /* - * An LZW/1 file starts off with a CRC of the data, which means we - * have to compress the whole thing, then seek back afterward and - * write the value. This annoyance went away in LZW/2. - */ - err = Nu_FTell(fp, &initialOffset); - BailError(err); - - if (!isType2) { - putc(0, fp); /* leave space for CRC */ - putc(0, fp); - compressedLen += 2; - } - putc(kNuLZWDefaultVol, fp); - putc(kNuRLEDefaultEscape, fp); - compressedLen += 2; - - if (isType2) - Nu_ClearLZWTable(lzwState); - - while (srcLen) { - /* - * Fill up the input buffer. - */ - blockSize = (srcLen > kNuLZWBlockSize) ? kNuLZWBlockSize : srcLen; - - err = Nu_StrawRead(pArchive, pStraw, lzwState->inputBuf, blockSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "compression read failed"); - goto bail; - } - - /* - * ShrinkIt was originally just going to be a 5.25" disk compressor, - * so the compression functions were organized around 4K blocks (the - * size of one track on a 5.25" disk). The block passed into the - * RLE function is always 4K, so we zero out any extra space. - */ - if (blockSize < kNuLZWBlockSize) { - memset(lzwState->inputBuf + blockSize, 0, - kNuLZWBlockSize - blockSize); - } - - /* - * Compute the CRC. For LZW/1 this is on the entire 4K block, for - * the "version 3" thread header CRC this is on just the "real" data. - */ - *pThreadCrc = Nu_CalcCRC16(*pThreadCrc, lzwState->inputBuf, blockSize); - if (!isType2) { - lzwState->chunkCrc = Nu_CalcCRC16(lzwState->chunkCrc, - lzwState->inputBuf, kNuLZWBlockSize); - } - - /* - * Try to compress with RLE, from inputBuf to rleBuf. - */ - err = Nu_CompressBlockRLE(lzwState, (int*) &rleSize); - BailError(err); - - if (rleSize < kNuLZWBlockSize) { - lzwInputBuf = lzwState->rleBuf; - } else { - lzwInputBuf = lzwState->inputBuf; - rleSize = kNuLZWBlockSize; - } - - /* - * Compress with LZW, into lzwBuf. - */ - if (!isType2) - Nu_ClearLZWTable(lzwState); - err = Nu_CompressLZWBlock(lzwState, lzwInputBuf, rleSize, - (int*) &lzwSize); - BailError(err); - - /* decide if we want to keep it, bearing in mind the LZW/2 header */ - if (pArchive->valMimicSHK) { - /* GSHK doesn't factor in header -- and *sometimes* uses "<=" !! */ - keepLzw = (lzwSize < rleSize); - } else { - if (isType2) - keepLzw = (lzwSize +2 < rleSize); - else - keepLzw = (lzwSize < rleSize); - } - - /* - * Write the compressed (or not) chunk. - */ - if (keepLzw) { - /* - * LZW succeeded. - */ - if (isType2) - rleSize |= 0x8000; /* for LZW/2, set "LZW used" flag */ - - putc(rleSize & 0xff, fp); /* size after RLE */ - putc(rleSize >> 8, fp); - compressedLen += 2; - - if (isType2) { - /* write compressed LZW len (+4 for header bytes) */ - putc((lzwSize+4) & 0xff, fp); - putc((lzwSize+4) >> 8, fp); - compressedLen += 2; - } else { - /* set LZW/1 "LZW used" flag */ - putc(1, fp); - compressedLen++; - } - - /* write data from LZW buffer */ - err = Nu_FWrite(fp, lzwState->lzwBuf, lzwSize); - BailError(err); - compressedLen += lzwSize; - } else { - /* - * LZW failed. - */ - putc(rleSize & 0xff, fp); /* size after RLE */ - putc(rleSize >> 8, fp); - compressedLen += 2; - - if (isType2) { - /* clear LZW/2 table; we can't use it next time */ - Nu_ClearLZWTable(lzwState); - } else { - /* set LZW/1 "LZW not used" flag */ - putc(0, fp); - compressedLen++; - } - - /* write data from RLE or plain-input buffer */ - err = Nu_FWrite(fp, lzwInputBuf, rleSize); - BailError(err); - compressedLen += rleSize; - } - - - /* - * Update the counter and continue. - */ - srcLen -= blockSize; - } - - /* - * For LZW/1, go back and write the CRC. - */ - if (!isType2) { - long curOffset; - - err = Nu_FTell(fp, &curOffset); - BailError(err); - err = Nu_FSeek(fp, initialOffset, SEEK_SET); - BailError(err); - putc(lzwState->chunkCrc & 0xff, fp); - putc(lzwState->chunkCrc >> 8, fp); - err = Nu_FSeek(fp, curOffset, SEEK_SET); - BailError(err); - } - - /* P8SHK and GSHK add an extra byte to LZW-compressed threads */ - if (pArchive->valMimicSHK) { - putc(0, fp); - compressedLen++; - } - - *pDstLen = compressedLen; - -bail: - return err; -} - -/* - * Compress ShrinkIt-style "LZW/1". - */ -NuError Nu_CompressLZW1(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - return Nu_CompressLZW(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, false); -} - -/* - * Compress ShrinkIt-style "LZW/2". - */ -NuError Nu_CompressLZW2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - return Nu_CompressLZW(pArchive, pStraw, fp, srcLen, pDstLen, pCrc, true); -} - - -/* - * =========================================================================== - * Expansion - * =========================================================================== - */ - -/* if we don't have at least this much data, we try to read more */ -/* (the "+3" is for the chunk header bytes) */ -#define kNuLZWDesiredChunk (kNuLZWBlockSize + 3) - -/* - * Static tables useful for bit manipulation. - */ -static const uint32_t gNuMaskTable[17] = { - 0x0000, 0x01ff, 0x03ff, 0x03ff, 0x07ff, 0x07ff, 0x07ff, 0x07ff, - 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, 0x0fff, - 0x0fff -}; -/* convert high byte of "entry" into a bit width */ -static const uint32_t gNuBitWidth[17] = { - 8,9,10,10,11,11,11,11,12,12,12,12,12,12,12,12,12 -}; - - -/* entry in the trie */ -typedef struct TableEntry { - uint8_t ch; - uint32_t prefix; -} TableEntry; - -/* - * This holds all of the "big" dynamic state, plus a few things that I - * don't want to pass around. It's allocated once for each instance of - * an open archive, and re-used. - */ -typedef struct LZWExpandState { - NuArchive* pArchive; - - TableEntry trie[4096-256]; /* holds from 9 bits to 12 bits */ - uint8_t stack[kNuLZWBlockSize]; - - // some of these don't need to be 32 bits; they were "uint" before - uint32_t entry; /* 16-bit index into table */ - uint32_t oldcode; /* carryover state for LZW/2 */ - uint32_t incode; /* carryover state for LZW/2 */ - uint32_t finalc; /* carryover state for LZW/2 */ - Boolean resetFix; /* work around an LZW/2 bug */ - - uint16_t chunkCrc; /* CRC we calculate for LZW/1 */ - uint16_t fileCrc; /* CRC stored with file */ - - uint8_t diskVol; /* disk volume # */ - uint8_t rleEscape; /* RLE escape char, usually 0xdb */ - - uint32_t dataInBuffer; /* #of bytes in compBuf */ - uint8_t* dataPtr; /* current data offset */ - - uint8_t lzwOutBuf[kNuLZWBlockSize + kNuSafetyPadding]; - uint8_t rleOutBuf[kNuLZWBlockSize + kNuSafetyPadding]; -} LZWExpandState; - - -/* - * Allocate some "reusable" state for LZW expansion. - */ -static NuError Nu_AllocLZWExpandState(NuArchive* pArchive) -{ - NuError err; - - Assert(pArchive != NULL); - Assert(pArchive->lzwExpandState == NULL); - - /* allocate the general-purpose compression buffer, if needed */ - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - pArchive->lzwExpandState = Nu_Malloc(pArchive, sizeof(LZWExpandState)); - if (pArchive->lzwExpandState == NULL) - return kNuErrMalloc; - return kNuErrNone; -} - - -#ifdef NDEBUG -# define Nu_LZWPush(uch) ( *stackPtr++ = (uch) ) -# define Nu_LZWPop() ( *(--stackPtr) ) -# define Nu_LZWStackEmpty() ( stackPtr == lzwState->stack ) - -#else -# define Nu_LZWPush(uch) \ - ( Nu_LZWPushCheck(uch, lzwState, stackPtr), *stackPtr++ = (uch) ) -# define Nu_LZWPop() \ - ( Nu_LZWPopCheck(lzwState, stackPtr), *(--stackPtr) ) -# define Nu_LZWStackEmpty() ( stackPtr == lzwState->stack ) - -static inline void Nu_LZWPushCheck(uint8_t uch, const LZWExpandState* lzwState, - const uint8_t* stackPtr) -{ - if (stackPtr >= lzwState->stack + sizeof(lzwState->stack)) { - Nu_ReportError(lzwState->NU_BLOB, kNuErrBadData, "stack overflow"); - abort(); - } -} - -static inline void Nu_LZWPopCheck(const LZWExpandState* lzwState, - const uint8_t* stackPtr) -{ - if (stackPtr == lzwState->stack) { - Nu_ReportError(lzwState->NU_BLOB, kNuErrBadData, "stack underflow"); - abort(); - } -} - -#endif - -/* - * Get the next LZW code from the input, advancing pointers as needed. - * - * This would be faster as a macro and less ugly with pass-by-reference. - * Resorting to globals is unacceptable. Might be less ugly if we clumped - * some stuff into a struct. Should be good enough as-is. - * - * Returns an integer up to 12 bits long. - * - * (Turning this into a macro might speed things up.) - */ -static inline uint32_t Nu_LZWGetCode(const uint8_t** pInBuf, uint32_t entry, - int* pAtBit, uint32_t* pLastByte) -{ - uint32_t numBits, startBit, lastBit; - uint32_t value; - - numBits = (entry +1) >> 8; /* bit-width of next code */ - startBit = *pAtBit; - lastBit = startBit + gNuBitWidth[numBits]; - - /* - * We need one or two bytes from the input. These have to be shifted - * around and merged with the bits we already have (if any). - */ - if (!startBit) - value = *(*pInBuf)++; - else - value = *pLastByte; - - if (lastBit > 16) { - /* need two more bytes */ - value |= *(*pInBuf)++ << 8; - *pLastByte = *(*pInBuf)++; - value |= (uint32_t) *pLastByte << 16; - } else { - /* only need one more byte */ - *pLastByte = *(*pInBuf)++; - value |= *pLastByte << 8; - } - - *pAtBit = lastBit & 0x07; - - /*printf("| EX: value=$%06lx mask=$%04x return=$%03lx\n", - value,gNuMaskTable[numBits], (value >> startBit) & gNuMaskTable[numBits]);*/ - - /*DBUG_LZW(("### getcode 0x%04lx\n", - (value >> startBit) & gNuMaskTable[numBits]));*/ - - /* I believe ANSI allows shifting by zero bits, so don't test "!startBit" */ - return (value >> startBit) & gNuMaskTable[numBits]; -} - - -/* - * Expand an LZW/1 chunk. - * - * Reads from lzwState->dataPtr, writes to lzwState->lzwOutBuf. - */ -static NuError Nu_ExpandLZW1(LZWExpandState* lzwState, uint32_t expectedLen) -{ - NuError err = kNuErrNone; - TableEntry* tablePtr; - int atBit; - uint32_t entry, oldcode, incode, ptr; - uint32_t lastByte, finalc; - const uint8_t* inbuf; - uint8_t* outbuf; - uint8_t* outbufend; - uint8_t* stackPtr; - - Assert(lzwState != NULL); - Assert(expectedLen > 0 && expectedLen <= kNuLZWBlockSize); - - inbuf = lzwState->dataPtr; - outbuf = lzwState->lzwOutBuf; - outbufend = outbuf + expectedLen; - tablePtr = lzwState->trie - 256; /* don't store 256 empties */ - stackPtr = lzwState->stack; - - atBit = 0; - lastByte = 0; - - entry = kNuLZWFirstCode; /* 0x101 */ - finalc = oldcode = incode = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); - *outbuf++ = incode; - Assert(incode <= 0xff); - if (incode > 0xff) { - err = kNuErrBadData; - Nu_ReportError(lzwState->NU_BLOB, err, "invalid initial LZW symbol"); - goto bail; - } - - while (outbuf < outbufend) { - incode = ptr = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); - - /* handle KwKwK case */ - if (ptr >= entry) { - //DBUG_LZW(("### KwKwK (ptr=%d entry=%d)\n", ptr, entry)); - if (ptr != entry) { - /* bad code -- this would make us read uninitialized data */ - DBUG(("--- bad code (ptr=%d entry=%d)\n", ptr, entry)); - err = kNuErrBadData; - return err; - } - Nu_LZWPush((uint8_t)finalc); - ptr = oldcode; - } - - /* fill the stack by chasing up the trie */ - while (ptr > 0xff) { - Nu_LZWPush(tablePtr[ptr].ch); - ptr = tablePtr[ptr].prefix; - Assert(ptr < 4096); - } - - /* done chasing up, now dump the stack, starting with ptr */ - finalc = ptr; - *outbuf++ = ptr; - /*printf("PUT 0x%02x\n", *(outbuf-1));*/ - while (!Nu_LZWStackEmpty()) { - *outbuf++ = Nu_LZWPop(); - /*printf("POP/PUT 0x%02x\n", *(outbuf-1));*/ - } - - /* add the new prefix to the trie -- last string plus new char */ - Assert(finalc <= 0xff); - tablePtr[entry].ch = finalc; - tablePtr[entry].prefix = oldcode; - entry++; - oldcode = incode; - } - -bail: - if (outbuf != outbufend) { - err = kNuErrBadData; - Nu_ReportError(lzwState->NU_BLOB, err, "LZW expansion failed"); - return err; - } - - /* adjust input buffer */ - lzwState->dataInBuffer -= (inbuf - lzwState->dataPtr); - Assert(lzwState->dataInBuffer < 32767*65536); - lzwState->dataPtr = (uint8_t*)inbuf; - - return err; -} - -/* - * Expand an LZW/2 chunk. Main difference from LZW/1 is that the state - * is carried over from the previous block in most cases, and the table - * is cleared explicitly. - * - * Reads from lzwState->dataPtr, writes to lzwState->lzwOutBuf. - * - * In some cases, "expectedInputUsed" will be -1 to indicate that the - * value is not known. - */ -static NuError Nu_ExpandLZW2(LZWExpandState* lzwState, uint32_t expectedLen, - uint32_t expectedInputUsed) -{ - NuError err = kNuErrNone; - TableEntry* tablePtr; - int atBit; - uint32_t entry, oldcode, incode, ptr; - uint32_t lastByte, finalc; - const uint8_t* inbuf; - const uint8_t* inbufend; - uint8_t* outbuf; - uint8_t* outbufend; - uint8_t* stackPtr; - - /*DBUG_LZW(("### LZW/2 block start (compIn=%d, rleOut=%d, entry=0x%04x)\n", - expectedInputUsed, expectedLen, lzwState->entry));*/ - Assert(lzwState != NULL); - Assert(expectedLen > 0 && expectedLen <= kNuLZWBlockSize); - - inbuf = lzwState->dataPtr; - inbufend = lzwState->dataPtr + expectedInputUsed; - outbuf = lzwState->lzwOutBuf; - outbufend = outbuf + expectedLen; - entry = lzwState->entry; - tablePtr = lzwState->trie - 256; /* don't store 256 empties */ - stackPtr = lzwState->stack; - - atBit = 0; - lastByte = 0; - - /* - * If the table isn't empty, initialize from the saved state and - * jump straight into the main loop. - * - * There's a funny situation that arises when a table clear is the - * second-to-last code in the previous chunk. After we see the - * table clear, we get the next code and use it to initialize "oldcode" - * and "incode" -- but we don't advance "entry" yet. The way that - * ShrinkIt originally worked, the next time we came through we'd - * see what we thought was an empty table and we'd reinitialize. So - * we use "resetFix" to keep track of this situation. - */ - if (entry != kNuLZWFirstCode || lzwState->resetFix) { - /* table not empty */ - oldcode = lzwState->oldcode; - incode = lzwState->incode; - finalc = lzwState->finalc; - lzwState->resetFix = false; - goto main_loop; - } - -clear_table: - /* table is either empty or was just explicitly cleared; reset */ - entry = kNuLZWFirstCode; /* 0x0101 */ - if (outbuf == outbufend) { - /* block must've ended on a table clear */ - DBUG(("--- RARE: ending clear\n")); - /* reset values, mostly to quiet gcc's "used before init" warnings */ - oldcode = incode = finalc = 0; - goto main_loop; /* the while condition will fall through */ - } - finalc = oldcode = incode = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); - *outbuf++ = incode; - /*printf("PUT 0x%02x\n", *(outbuf-1));*/ - if (incode > 0xff) { - err = kNuErrBadData; - Nu_ReportError(lzwState->NU_BLOB, err, "invalid initial LZW symbol"); - goto bail; - } - - if (outbuf == outbufend) { - /* if we're out of data, raise the "reset fix" flag */ - DBUG(("--- RARE: resetFix!\n")); - lzwState->resetFix = true; - /* fall through; the while condition will let us slip past */ - } - -main_loop: - while (outbuf < outbufend) { - incode = ptr = Nu_LZWGetCode(&inbuf, entry, &atBit, &lastByte); - //DBUG_LZW(("### read incode=0x%04x\n", incode)); - if (incode == kNuLZWClearCode) /* table clear - 0x0100 */ - goto clear_table; - - /* handle KwKwK case */ - if (ptr >= entry) { - //DBUG_LZW(("### KwKwK (ptr=%d entry=%d)\n", ptr, entry)); - if (ptr != entry) { - /* bad code -- this would make us read uninitialized data */ - DBUG(("--- bad code (ptr=%d entry=%d)\n", ptr, entry)); - err = kNuErrBadData; - return err; - } - Nu_LZWPush((uint8_t)finalc); - ptr = oldcode; - } - - /* fill the stack by chasing up the trie */ - while (ptr > 0xff) { - Nu_LZWPush(tablePtr[ptr].ch); - ptr = tablePtr[ptr].prefix; - Assert(ptr < 4096); - } - - /* done chasing up, now dump the stack, starting with ptr */ - finalc = ptr; - *outbuf++ = ptr; - /*printf("PUT 0x%02x\n", *(outbuf-1));*/ - while (!Nu_LZWStackEmpty()) { - *outbuf++ = Nu_LZWPop(); - /*printf("POP/PUT 0x%02x\n", *(outbuf-1));*/ - } - - /* add the new prefix to the trie -- last string plus new char */ - /*DBUG_LZW(("### entry 0x%04x gets prefix=0x%04x and ch=0x%02x\n", - entry, oldcode, finalc));*/ - Assert(finalc <= 0xff); - tablePtr[entry].ch = finalc; - tablePtr[entry].prefix = oldcode; - entry++; - oldcode = incode; - } - -bail: - /*DBUG_LZW(("### end of block\n"));*/ - if (expectedInputUsed != (uint32_t) -1 && inbuf != inbufend) { - /* data was corrupted; if we keep going this will get worse */ - DBUG(("--- inbuf != inbufend in ExpandLZW2 (diff=%d)\n", - inbufend - inbuf)); - err = kNuErrBadData; - return err; - } - Assert(outbuf == outbufend); - - /* adjust input buffer */ - lzwState->dataInBuffer -= (inbuf - lzwState->dataPtr); - Assert(lzwState->dataInBuffer < 32767*65536); - lzwState->dataPtr = (uint8_t*)inbuf; - - /* save off local copies of stuff */ - lzwState->entry = entry; - lzwState->oldcode = oldcode; - lzwState->incode = incode; - lzwState->finalc = finalc; - - return err; -} - - -/* - * Expands a chunk of RLEd data into 4K of output. - */ -static NuError Nu_ExpandRLE(LZWExpandState* lzwState, const uint8_t* inbuf, - uint32_t expectedInputUsed) -{ - NuError err = kNuErrNone; - uint8_t *outbuf; - uint8_t *outbufend; - const uint8_t *inbufend; - uint8_t uch, rleEscape; - int count; - - outbuf = lzwState->rleOutBuf; - outbufend = outbuf + kNuLZWBlockSize; - inbufend = inbuf + expectedInputUsed; - rleEscape = lzwState->rleEscape; - - while (outbuf < outbufend) { - uch = *inbuf++; - if (uch == rleEscape) { - uch = *inbuf++; - count = *inbuf++; - if (outbuf + count >= outbufend) { - /* don't overrun buffer */ - Assert(outbuf != outbufend); - break; - } - while (count-- >= 0) - *outbuf++ = uch; - } else { - *outbuf++ = uch; - } - } - - if (outbuf != outbufend) { - err = kNuErrBadData; - Nu_ReportError(lzwState->NU_BLOB, err, - "RLE output glitch (off by %d)", (int)(outbufend-outbuf)); - goto bail; - } - if (inbuf != inbufend) { - err = kNuErrBadData; - Nu_ReportError(lzwState->NU_BLOB, err, - "RLE input glitch (off by %d)", (int)(inbufend-inbuf)); - goto bail; - } - -bail: - return err; -} - - -/* - * Utility function to get a byte from the input buffer. - */ -static inline uint8_t Nu_GetHeaderByte(LZWExpandState* lzwState) -{ - lzwState->dataInBuffer--; - Assert(lzwState->dataInBuffer > 0); - return *lzwState->dataPtr++; -} - -/* - * Expand ShrinkIt-style "LZW/1" and "LZW/2". - * - * This manages the input data buffer, passing chunks of compressed data - * into the appropriate expansion function. - * - * Pass in NULL for "pThreadCrc" if no thread CRC is desired. Otherwise, - * "*pThreadCrc" should already be set to its initial value. On exit it - * will contain the CRC of the uncompressed data. - */ -NuError Nu_ExpandLZW(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, - uint16_t* pThreadCrc) -{ - NuError err = kNuErrNone; - Boolean isType2; - LZWExpandState* lzwState; - uint32_t compRemaining, uncompRemaining, minSize; - - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(infp != NULL); - Assert(pFunnel != NULL); - - /* - * Do some initialization and set-up. - */ - if (pArchive->lzwExpandState == NULL) { - err = Nu_AllocLZWExpandState(pArchive); - BailError(err); - } - Assert(pArchive->lzwExpandState != NULL); - Assert(pArchive->compBuf != NULL); - - lzwState = pArchive->lzwExpandState; - lzwState->pArchive = pArchive; - - if (pThread->thThreadFormat == kNuThreadFormatLZW1) { - isType2 = false; - minSize = 7; /* crc-lo,crc-hi,vol,rle-delim,len-lo,len-hi,lzw-used */ - lzwState->chunkCrc = kNuInitialChunkCRC; /* 0x0000 */ - } else if (pThread->thThreadFormat == kNuThreadFormatLZW2) { - isType2 = true; - minSize = 4; /* vol,rle-delim,len-lo,len-hi */ - } else { - err = kNuErrBadFormat; - goto bail; - } - - uncompRemaining = pThread->actualThreadEOF; - compRemaining = pThread->thCompThreadEOF; - if (compRemaining < minSize) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, "thread too short to be valid LZW"); - goto bail; - } - if (compRemaining && !uncompRemaining) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, - "compressed data but no uncompressed data??"); - goto bail; - } - - /* - * Read the LZW header out of the data stream. - */ - if (!isType2) { - lzwState->fileCrc = getc(infp); - lzwState->fileCrc |= getc(infp) << 8; - compRemaining -= 2; - } - lzwState->diskVol = getc(infp); /* disk volume #; not really used */ - lzwState->rleEscape = getc(infp); /* RLE escape char for this thread */ - compRemaining -= 2; - - lzwState->dataInBuffer = 0; - lzwState->dataPtr = NULL; - - /* reset pointers */ - lzwState->entry = kNuLZWFirstCode; /* 0x0101 */ - lzwState->resetFix = false; - - /*DBUG_LZW(("### LZW%d block, vol=0x%02x, rleEsc=0x%02x\n", - isType2 +1, lzwState->diskVol, lzwState->rleEscape));*/ - - /* - * Read large blocks of the source file into compBuf, taking care not - * to read past the end of the thread data. - * - * The motivation for doing it this way rather than just reading the - * next compressed chunk are (1) compBuf is considerably larger than - * stdio BUFSIZ on most systems, and (2) for LZW/1 we don't know the - * size of the compressed data anyway. - * - * We need to ensure that we have at least one full compressed chunk - * in the buffer. Since the compressor will refuse to store the - * compressed data if it grows, we know that we need 4K plus the - * chunk header. - * - * Once we have what looks like a full chunk, invoke the LZW decoder. - */ - while (uncompRemaining) { - Boolean rleUsed; - Boolean lzwUsed; - uint32_t getSize; - uint32_t rleLen; /* length after RLE; 4096 if no RLE */ - uint32_t lzwLen = 0; /* type 2 only */ - uint32_t writeLen, inCount; - const uint8_t* writeBuf; - - /* if we're low, and there's more data available, read more */ - if (lzwState->dataInBuffer < kNuLZWDesiredChunk && compRemaining) { - /* - * First thing we do is slide the old data to the start of - * the buffer. - */ - if (lzwState->dataInBuffer) { - Assert(lzwState->dataPtr != NULL); - Assert(pArchive->compBuf != lzwState->dataPtr); - memmove(pArchive->compBuf, lzwState->dataPtr, - lzwState->dataInBuffer); - } - lzwState->dataPtr = pArchive->compBuf; - - /* - * Next we read as much as we can. - */ - if (kNuGenCompBufSize - lzwState->dataInBuffer < compRemaining) - getSize = kNuGenCompBufSize - lzwState->dataInBuffer; - else - getSize = compRemaining; - - /*printf("+++ READING %ld\n", getSize);*/ - err = Nu_FRead(infp, lzwState->dataPtr + lzwState->dataInBuffer, - getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, - "failed reading compressed data (%u bytes)", getSize); - goto bail; - } - lzwState->dataInBuffer += getSize; - compRemaining -= getSize; - - Assert(compRemaining < 32767*65536); - Assert(lzwState->dataInBuffer <= kNuGenCompBufSize); - } - Assert(lzwState->dataInBuffer); - - /* - * Read the LZW block header. - */ - if (isType2) { - rleLen = Nu_GetHeaderByte(lzwState); - rleLen |= Nu_GetHeaderByte(lzwState) << 8; - lzwUsed = rleLen & 0x8000 ? true : false; - rleLen &= 0x1fff; - rleUsed = (rleLen != kNuLZWBlockSize); - - if (lzwUsed) { - lzwLen = Nu_GetHeaderByte(lzwState); - lzwLen |= Nu_GetHeaderByte(lzwState) << 8; - lzwLen -= 4; /* don't include header bytes */ - } - } else { - rleLen = Nu_GetHeaderByte(lzwState); - rleLen |= Nu_GetHeaderByte(lzwState) << 8; - lzwUsed = Nu_GetHeaderByte(lzwState); - if (lzwUsed != 0 && lzwUsed != 1) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, "garbled LZW header"); - goto bail; - } - rleUsed = (rleLen != kNuLZWBlockSize); - } - - /*DBUG_LZW(("### CHUNK rleLen=%d(%d) lzwLen=%d(%d) uncompRem=%ld\n", - rleLen, rleUsed, lzwLen, lzwUsed, uncompRemaining));*/ - - if (uncompRemaining <= kNuLZWBlockSize) - writeLen = uncompRemaining; /* last block */ - else - writeLen = kNuLZWBlockSize; - - #ifndef NDEBUG - writeBuf = NULL; - #endif - - /* - * Decode the chunk, and point "writeBuf" at the uncompressed data. - * - * LZW always expands from the read buffer into lzwState->lzwOutBuf. - * RLE expands from a specific buffer to lzwState->rleOutBuf. - */ - if (lzwUsed) { - if (!isType2) { - err = Nu_ExpandLZW1(lzwState, rleLen); - } else { - if (pRecord->isBadMac || pArchive->valIgnoreLZW2Len) { - /* might be big-endian, might be okay; just ignore it */ - lzwLen = (uint32_t) -1; - } else if (lzwState->dataInBuffer < lzwLen) { - /* rare -- GSHK will do this if you don't let it finish */ - err = kNuErrBufferUnderrun; - Nu_ReportError(NU_BLOB, err, "not enough compressed data " - "-- archive truncated during creation?"); - goto bail; - } - err = Nu_ExpandLZW2(lzwState, rleLen, lzwLen); - } - - BailError(err); - - if (rleUsed) { - err = Nu_ExpandRLE(lzwState, lzwState->lzwOutBuf, rleLen); - BailError(err); - writeBuf = lzwState->rleOutBuf; - } else { - writeBuf = lzwState->lzwOutBuf; - } - - } else { - if (rleUsed) { - err = Nu_ExpandRLE(lzwState, lzwState->dataPtr, rleLen); - BailError(err); - writeBuf = lzwState->rleOutBuf; - inCount = rleLen; - } else { - writeBuf = lzwState->dataPtr; - inCount = writeLen; - } - - /* - * Advance the input buffer data pointers to consume the input. - * The LZW expansion functions do this for us, but we're not - * using LZW. - */ - lzwState->dataPtr += inCount; - lzwState->dataInBuffer -= inCount; - Assert(lzwState->dataInBuffer < 32767*65536); - - /* no LZW used, reset pointers */ - lzwState->entry = kNuLZWFirstCode; /* 0x0101 */ - lzwState->resetFix = false; - } - - Assert(writeBuf != NULL); - - /* - * Compute the CRC of the uncompressed data, and write it. For - * LZW/1, the CRC of the last block includes the zeros that pad - * it out to 4096 bytes. - * - * See commentary in the compression code for why we have to - * compute two CRCs for LZW/1. - */ - if (pThreadCrc != NULL) { - *pThreadCrc = Nu_CalcCRC16(*pThreadCrc, writeBuf, writeLen); - } - if (!isType2) { - lzwState->chunkCrc = Nu_CalcCRC16(lzwState->chunkCrc, - writeBuf, kNuLZWBlockSize); - } - - /* write the data, possibly doing an EOL conversion */ - err = Nu_FunnelWrite(pArchive, pFunnel, writeBuf, writeLen); - if (err != kNuErrNone) { - if (err != kNuErrAborted) - Nu_ReportError(NU_BLOB, err, "unable to write output"); - goto bail; - } - - uncompRemaining -= writeLen; - Assert(uncompRemaining < 32767*65536); - } - - /* - * It appears that ShrinkIt appends an extra byte after the last - * LZW block. The byte is included in the compThreadEOF, but isn't - * consumed by the LZW expansion routine, so it's usually harmless. - * - * It is *possible* for extra bytes to be here legitimately, but very - * unlikely. The very last block is always padded out to 4K with - * zeros. If you found a situation where that last block failed - * to compress with RLE and LZW (perhaps the last block filled up - * all but the last 2 or 3 bytes with uncompressible data), but - * earlier data made the overall file compressible, you would have - * a few stray bytes in the archive. - * - * This is a little easier to do if the last block has lots of single - * 0xdb characters in it, since that requires RLE to escape them. - * - * Whatever the case, issue a warning if it looks like there's too - * many of them. - */ - if (lzwState->dataInBuffer > 1) { - DBUG(("--- Found %ld bytes following compressed data (compRem=%ld)\n", - lzwState->dataInBuffer, compRemaining)); - if (lzwState->dataInBuffer > 32) { - Nu_ReportError(NU_BLOB, kNuErrNone, "(Warning) lots of fluff (%u)", - lzwState->dataInBuffer); - } - } - - /* - * We might be okay with stray bytes in the thread, but we're definitely - * not okay with anything identified as compressed data being unused. - */ - if (compRemaining) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, - "not all compressed data was used (%u/%u)", - compRemaining, lzwState->dataInBuffer); - goto bail; - } - - /* - * ShrinkIt used to put the CRC in the stream and not in the thread - * header. For LZW/1, we check the CRC here; for LZW/2, we hope it's - * in the thread header. (As noted in the compression code, it's - * possible to end up with two CRCs or no CRCs.) - */ - if (!isType2 && !pArchive->valIgnoreCRC) { - if (lzwState->chunkCrc != lzwState->fileCrc) { - if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadDataCRC)) { - err = kNuErrBadDataCRC; - Nu_ReportError(NU_BLOB, err, - "expected 0x%04x, got 0x%04x (LZW/1)", - lzwState->fileCrc, lzwState->chunkCrc); - (void) Nu_FunnelFlush(pArchive, pFunnel); - goto bail; - } - } else { - DBUG(("--- LZW/1 CRCs match (0x%04x)\n", lzwState->chunkCrc)); - } - } - -bail: - return err; -} - -#endif /*ENABLE_LZW*/ diff --git a/ciderpress/nufxlib/Makefile b/ciderpress/nufxlib/Makefile deleted file mode 100644 index b51181e..0000000 --- a/ciderpress/nufxlib/Makefile +++ /dev/null @@ -1,141 +0,0 @@ -# -# NuFX archive manipulation library -# Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. -# This is free software; you can redistribute it and/or modify it under the -# terms of the BSD License, see the file COPYING-LIB. -# -# Makefile for nufxlib (should work with non-GNU "make"). -# -# You can use: -# make (builds library and sample applications) -# make shared (builds shared library if you're using GNU ld or similar) -# -# The shared library support currently leaves much to be desired. -# -# If you build with -DDEBUG_MSGS, nulib2 will be able to use the hidden -# 'g' command, which generates a verbose archive dump for debugging. -# - -# NufxLib install location. -prefix = /usr/local -exec_prefix = ${prefix} -includedir = ${prefix}/include -libdir = ${exec_prefix}/lib -srcdir = . - -SHELL = /bin/sh -INSTALL = /usr/bin/install -c -INSTALL_PROGRAM = ${INSTALL} -INSTALL_DATA = ${INSTALL} -m 644 -CC = gcc -AR = ar rcv -#OPT = -g -O2 -DNDEBUG -OPT = -g -O2 -#OPT = -g -O2 -DDEBUG_MSGS -#OPT = -g -O2 -DDEBUG_VERBOSE -GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow -CFLAGS = $(OPT) $(GCC_FLAGS) -I. -DHAVE_CONFIG_H -DOPTFLAGSTR="\"$(OPT)\"" - -SRCS = Archive.c ArchiveIO.c Bzip2.c Charset.c Compress.c Crc16.c \ - Debug.c Deferred.c Deflate.c Entry.c Expand.c FileIO.c Funnel.c \ - Lzc.c Lzw.c MiscStuff.c MiscUtils.c Record.c SourceSink.c \ - Squeeze.c Thread.c Value.c Version.c -OBJS = Archive.o ArchiveIO.o Bzip2.o Charset.o Compress.o Crc16.o \ - Debug.o Deferred.o Deflate.o Entry.o Expand.o FileIO.o Funnel.o \ - Lzc.o Lzw.o MiscStuff.o MiscUtils.o Record.o SourceSink.o \ - Squeeze.o Thread.o Value.o Version.o - -STATIC_PRODUCT = libnufx.a -SHARED_PRODUCT = libnufx.so -PRODUCT = $(STATIC_PRODUCT) - - -# -# Build stuff -# - -all: $(PRODUCT) samples - @true - -install: $(STATIC_PRODUCT) - $(srcdir)/mkinstalldirs $(libdir) - $(INSTALL_DATA) $(STATIC_PRODUCT) $(libdir) - $(srcdir)/mkinstalldirs $(includedir) - $(INSTALL_DATA) NufxLib.h $(includedir) - -install-shared: $(SHARED_PRODUCT) - $(srcdir)/mkinstalldirs $(libdir) - $(INSTALL_DATA) $(SHARED_PRODUCT) $(libdir) - $(srcdir)/mkinstalldirs $(includedir) - $(INSTALL_DATA) NufxLib.h $(includedir) - -samples:: - @echo "Building samples..." - @(cd samples; set +e; unset CFLAGS OBJS; set -e; \ - LIB_PRODUCT="../$(PRODUCT)" $(MAKE)) - -shared:: - PRODUCT="$(SHARED_PRODUCT)" $(MAKE) -e - -$(STATIC_PRODUCT): $(OBJS) - -rm -f $(STATIC_PRODUCT) $(SHARED_PRODUCT) - $(AR) $@ $(OBJS) - ranlib $@ - -# BUG: we need -fPIC, maybe -D_REENTRANT when compiling for this. -# BUG: for Linux we may want -Wl,-soname,libnufx.so.1 on the link line. -$(SHARED_PRODUCT): $(OBJS) - -rm -f $(STATIC_PRODUCT) $(SHARED_PRODUCT) - $(CC) -shared -o $@ $(OBJS) -lz - -clean: - (cd samples; make clean) - -rm -f *.o core - -rm -f $(SHARED_PRODUCT) $(STATIC_PRODUCT) - -# build tags; assumes fancy GNU tag generation -tags:: - @ctags -R --totals * - @#ctags *.[ch] - -distclean: clean - (cd samples; make distclean) - -rm -f Makefile Makefile.bak - -rm -f config.log config.cache config.status config.h - -rm -f tags - -# Make a tarfile with a backup of the essential files. We include "Makefile" -# so that we can do a "make distclean" during packaging. -baktar: - @tar cvf nufxlib.tar *.txt COPYING-LIB INSTALL configure *.in Makefile \ - Makefile.msc Makefile.dll install-sh config.guess config.sub \ - mkinstalldirs *.[ch] samples/*.txt samples/Makefile* samples/*.[ch] - @gzip -9 nufxlib.tar - @mv -i nufxlib.tar.gz /home/fadden/BAK/ - -# dependency info -COMMON_HDRS = NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h -Archive.o: Archive.c $(COMMON_HDRS) -ArchiveIO.o: ArchiveIO.c $(COMMON_HDRS) -Bzip2.o: Bzip2.c $(COMMON_HDRS) -Charset.o: Charset.c $(COMMON_HDRS) -Compress.o: Compress.c $(COMMON_HDRS) -Crc16.o: Crc16.c $(COMMON_HDRS) -Debug.o: Debug.c $(COMMON_HDRS) -Deferred.o: Deferred.c $(COMMON_HDRS) -Deflate.o: Deflate.c $(COMMON_HDRS) -Entry.o: Entry.c $(COMMON_HDRS) -Expand.o: Expand.c $(COMMON_HDRS) -FileIO.o: FileIO.c $(COMMON_HDRS) -Funnel.o: Funnel.c $(COMMON_HDRS) -Lzc.o: Lzc.c $(COMMON_HDRS) -Lzw.o: Lzw.c $(COMMON_HDRS) -MiscStuff.o: MiscStuff.c $(COMMON_HDRS) -MiscUtils.o: MiscUtils.c $(COMMON_HDRS) -Record.o: Record.c $(COMMON_HDRS) -SourceSink.o: SourceSink.c $(COMMON_HDRS) -Squeeze.o: Squeeze.c $(COMMON_HDRS) -Thread.o: Thread.c $(COMMON_HDRS) -Value.o: Value.c $(COMMON_HDRS) -Version.o: Version.c $(COMMON_HDRS) Makefile - diff --git a/ciderpress/nufxlib/Makefile.in b/ciderpress/nufxlib/Makefile.in deleted file mode 100644 index 66c2ae4..0000000 --- a/ciderpress/nufxlib/Makefile.in +++ /dev/null @@ -1,141 +0,0 @@ -# -# NuFX archive manipulation library -# Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. -# This is free software; you can redistribute it and/or modify it under the -# terms of the BSD License, see the file COPYING-LIB. -# -# Makefile for nufxlib (should work with non-GNU "make"). -# -# You can use: -# make (builds library and sample applications) -# make shared (builds shared library if you're using GNU ld or similar) -# -# The shared library support currently leaves much to be desired. -# -# If you build with -DDEBUG_MSGS, nulib2 will be able to use the hidden -# 'g' command, which generates a verbose archive dump for debugging. -# - -# NufxLib install location. -prefix = @prefix@ -exec_prefix = @exec_prefix@ -includedir = @includedir@ -libdir = @libdir@ -srcdir = @srcdir@ - -SHELL = @SHELL@ -INSTALL = @INSTALL@ -INSTALL_PROGRAM = @INSTALL_PROGRAM@ -INSTALL_DATA = @INSTALL_DATA@ -CC = @CC@ -AR = ar rcv -#OPT = @CFLAGS@ -DNDEBUG -OPT = @CFLAGS@ -#OPT = @CFLAGS@ -DDEBUG_MSGS -#OPT = @CFLAGS@ -DDEBUG_VERBOSE -GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow -CFLAGS = @BUILD_FLAGS@ -I. @DEFS@ -DOPTFLAGSTR="\"$(OPT)\"" - -SRCS = Archive.c ArchiveIO.c Bzip2.c Charset.c Compress.c Crc16.c \ - Debug.c Deferred.c Deflate.c Entry.c Expand.c FileIO.c Funnel.c \ - Lzc.c Lzw.c MiscStuff.c MiscUtils.c Record.c SourceSink.c \ - Squeeze.c Thread.c Value.c Version.c -OBJS = Archive.o ArchiveIO.o Bzip2.o Charset.o Compress.o Crc16.o \ - Debug.o Deferred.o Deflate.o Entry.o Expand.o FileIO.o Funnel.o \ - Lzc.o Lzw.o MiscStuff.o MiscUtils.o Record.o SourceSink.o \ - Squeeze.o Thread.o Value.o Version.o - -STATIC_PRODUCT = libnufx.a -SHARED_PRODUCT = libnufx.so -PRODUCT = $(STATIC_PRODUCT) - - -# -# Build stuff -# - -all: $(PRODUCT) samples - @true - -install: $(STATIC_PRODUCT) - $(srcdir)/mkinstalldirs $(libdir) - $(INSTALL_DATA) $(STATIC_PRODUCT) $(libdir) - $(srcdir)/mkinstalldirs $(includedir) - $(INSTALL_DATA) NufxLib.h $(includedir) - -install-shared: $(SHARED_PRODUCT) - $(srcdir)/mkinstalldirs $(libdir) - $(INSTALL_DATA) $(SHARED_PRODUCT) $(libdir) - $(srcdir)/mkinstalldirs $(includedir) - $(INSTALL_DATA) NufxLib.h $(includedir) - -samples:: - @echo "Building samples..." - @(cd samples; set +e; unset CFLAGS OBJS; set -e; \ - @SET_MAKE@ LIB_PRODUCT="../$(PRODUCT)" $(MAKE)) - -shared:: - PRODUCT="$(SHARED_PRODUCT)" $(MAKE) -e - -$(STATIC_PRODUCT): $(OBJS) - -rm -f $(STATIC_PRODUCT) $(SHARED_PRODUCT) - $(AR) $@ $(OBJS) - @RANLIB@ $@ - -# BUG: we need -fPIC, maybe -D_REENTRANT when compiling for this. -# BUG: for Linux we may want -Wl,-soname,libnufx.so.1 on the link line. -$(SHARED_PRODUCT): $(OBJS) - -rm -f $(STATIC_PRODUCT) $(SHARED_PRODUCT) - $(CC) @SHARE_FLAGS@ -o $@ $(OBJS) @LIBS@ - -clean: - (cd samples; make clean) - -rm -f *.o core - -rm -f $(SHARED_PRODUCT) $(STATIC_PRODUCT) - -# build tags; assumes fancy GNU tag generation -tags:: - @ctags -R --totals * - @#ctags *.[ch] - -distclean: clean - (cd samples; make distclean) - -rm -f Makefile Makefile.bak - -rm -f config.log config.cache config.status config.h - -rm -f tags - -# Make a tarfile with a backup of the essential files. We include "Makefile" -# so that we can do a "make distclean" during packaging. -baktar: - @tar cvf nufxlib.tar *.txt COPYING-LIB INSTALL configure *.in Makefile \ - Makefile.msc Makefile.dll install-sh config.guess config.sub \ - mkinstalldirs *.[ch] samples/*.txt samples/Makefile* samples/*.[ch] - @gzip -9 nufxlib.tar - @mv -i nufxlib.tar.gz /home/fadden/BAK/ - -# dependency info -COMMON_HDRS = NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h -Archive.o: Archive.c $(COMMON_HDRS) -ArchiveIO.o: ArchiveIO.c $(COMMON_HDRS) -Bzip2.o: Bzip2.c $(COMMON_HDRS) -Charset.o: Charset.c $(COMMON_HDRS) -Compress.o: Compress.c $(COMMON_HDRS) -Crc16.o: Crc16.c $(COMMON_HDRS) -Debug.o: Debug.c $(COMMON_HDRS) -Deferred.o: Deferred.c $(COMMON_HDRS) -Deflate.o: Deflate.c $(COMMON_HDRS) -Entry.o: Entry.c $(COMMON_HDRS) -Expand.o: Expand.c $(COMMON_HDRS) -FileIO.o: FileIO.c $(COMMON_HDRS) -Funnel.o: Funnel.c $(COMMON_HDRS) -Lzc.o: Lzc.c $(COMMON_HDRS) -Lzw.o: Lzw.c $(COMMON_HDRS) -MiscStuff.o: MiscStuff.c $(COMMON_HDRS) -MiscUtils.o: MiscUtils.c $(COMMON_HDRS) -Record.o: Record.c $(COMMON_HDRS) -SourceSink.o: SourceSink.c $(COMMON_HDRS) -Squeeze.o: Squeeze.c $(COMMON_HDRS) -Thread.o: Thread.c $(COMMON_HDRS) -Value.o: Value.c $(COMMON_HDRS) -Version.o: Version.c $(COMMON_HDRS) Makefile - diff --git a/ciderpress/nufxlib/Makefile.msc b/ciderpress/nufxlib/Makefile.msc deleted file mode 100644 index 8c2bb90..0000000 --- a/ciderpress/nufxlib/Makefile.msc +++ /dev/null @@ -1,160 +0,0 @@ -# Makefile for NufxLib using Microsoft Visual C++. This builds the library -# as a static lib and as a DLL, and builds all samples. The test-basic -# sample is built twice, once with the static lib, and once with the DLL. -# -# Tested with VS 2013 Pro. From the "VS2013 x86 Native Tools Command -# Prompt", run "nmake -f makefile.msc". -# -# If you're including zlib support, place copies of zlib.h, zconf.h, -# and the zlib library in this directory. -# -# Adapted from zlib's Makefile.msc. -# - -TOP = . - -STATICLIB = nufxlib2.lib -SHAREDLIB = nufxlib2.dll -IMPLIB = nufxdll.lib - -CC = cl -LD = link -AR = lib - -# C compiler flags -# -Fd: rename PDB file from "VCx0.pdb" (where 'x' is the version number); -# allows DLL debug info to be separate from app debug info -# -Ox: full optimization -# -Oy-: disable frame pointer omission (for easier debugging) -# -MD: create a multithreaded DLL using MSVCRT.lib; alternatively, -# use -MDd to create a debug executable with MSVCRTD.lib -# -nologo: suppress display of copyright banner -# -W3: set warning level to 3 (all production-level warnings) -# -Zi: generate a PDB file with full debugging info -# -# The OPTFLAGSTR define is used by Version.c to show how the library was -# built. Defining NUFXLIB_EXPORTS enables the __declspec(dllexport) -# macros that are required for creating the DLL. -OPTFLAGS = -Ox -Oy- -CFLAGS = -nologo -MD -W3 $(OPTFLAGS) -Zi -Fd"nufxlib" - -LIB_CFLAGS = -DOPTFLAGSTR="\"$(OPTFLAGS)\"" #-DNUFXLIB_EXPORTS - -# Warning suppression flags -WFLAGS = -D_CRT_SECURE_NO_DEPRECATE -D_CRT_NONSTDC_NO_DEPRECATE - -# Linker flags -# -debug: creates debugging info for EXE or DLL in PDB file -# -incremental:no: disable incremental linking, making the resulting library -# a tad smaller -# -nologo: suppress display of copyright banner -# -opt:ref: eliminates unreferenced functions and data (default for non-debug -# builds, but we've enabled debug info) -LDFLAGS = -nologo -debug -incremental:no -opt:ref - -# Library creator flags -ARFLAGS = -nologo - - -ZLIB=1 -!ifdef ZLIB -# enable deflate support; requires zlib -CFLAGS = $(CFLAGS) -DENABLE_DEFLATE -LDFLAGS = $(LDFLAGS) zlib.lib -!endif - - -# object files -OBJS = Archive.obj ArchiveIO.obj Bzip2.obj Charset.obj Compress.obj \ - Crc16.obj Debug.obj Deferred.obj Deflate.obj Entry.obj Expand.obj \ - FileIO.obj Funnel.obj Lzc.obj Lzw.obj MiscStuff.obj MiscUtils.obj \ - Record.obj SourceSink.obj Squeeze.obj Thread.obj Value.obj Version.obj - - -# build targets -- static library, dynamic library, and test programs -all: $(STATICLIB) $(SHAREDLIB) $(IMPLIB) \ - exerciser.exe imgconv.exe launder.exe test-basic.exe test-basic-d.exe \ - test-extract.exe test-names.exe test-simple.exe test-twirl.exe - -clean: - -del *.obj *.pdb *.exp - -del $(STATICLIB) $(SHAREDLIB) $(IMPLIB) - -$(STATICLIB): $(OBJS) - $(AR) $(ARFLAGS) -out:$@ $(OBJS) - -$(IMPLIB): $(SHAREDLIB) - -$(SHAREDLIB): $(OBJS) - $(LD) $(LDFLAGS) -dll -def:nufxlib.def -implib:$(IMPLIB) -out:$@ \ - $(OBJS) - -exerciser.exe: Exerciser.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ Exerciser.obj $(STATICLIB) - -imgconv.exe: ImgConv.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ ImgConv.obj $(STATICLIB) - -launder.exe: Launder.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ Launder.obj $(STATICLIB) - -test-basic.exe: TestBasic.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ TestBasic.obj $(STATICLIB) - -test-basic-d.exe: TestBasic.obj $(IMPLIB) - $(LD) $(LDFLAGS) -out:$@ TestBasic.obj $(IMPLIB) - -test-extract.exe: TestExtract.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ TestExtract.obj $(STATICLIB) - -test-names.exe: TestNames.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ TestNames.obj $(STATICLIB) - -test-simple.exe: TestSimple.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ TestSimple.obj $(STATICLIB) - -test-twirl.exe: TestTwirl.obj $(STATICLIB) - $(LD) $(LDFLAGS) -out:$@ TestTwirl.obj $(STATICLIB) - -# generic rules -{$(TOP)}.c.obj: - $(CC) -c $(WFLAGS) $(CFLAGS) $(LIB_CFLAGS) $< - -{$(TOP)/samples}.c.obj: - $(CC) -c -I$(TOP) $(WFLAGS) $(CFLAGS) $< - -# dependency info -COMMON_HDRS = NufxLibPriv.h NufxLib.h MiscStuff.h SysDefs.h -Archive.obj: Archive.c $(COMMON_HDRS) -ArchiveIO.obj: ArchiveIO.c $(COMMON_HDRS) -Bzip2.obj: Bzip2.c $(COMMON_HDRS) -Charset.obj: Charset.c $(COMMON_HDRS) -Compress.obj: Compress.c $(COMMON_HDRS) -Crc16.obj: Crc16.c $(COMMON_HDRS) -Debug.obj: Debug.c $(COMMON_HDRS) -Deferred.obj: Deferred.c $(COMMON_HDRS) -Deflate.obj: Deflate.c $(COMMON_HDRS) -Entry.obj: Entry.c $(COMMON_HDRS) -Expand.obj: Expand.c $(COMMON_HDRS) -FileIO.obj: FileIO.c $(COMMON_HDRS) -Funnel.obj: Funnel.c $(COMMON_HDRS) -Lzc.obj: Lzc.c $(COMMON_HDRS) -Lzw.obj: Lzw.c $(COMMON_HDRS) -MiscStuff.obj: MiscStuff.c $(COMMON_HDRS) -MiscUtils.obj: MiscUtils.c $(COMMON_HDRS) -Record.obj: Record.c $(COMMON_HDRS) -SourceSink.obj: SourceSink.c $(COMMON_HDRS) -Squeeze.obj: Squeeze.c $(COMMON_HDRS) -Thread.obj: Thread.c $(COMMON_HDRS) -Value.obj: Value.c $(COMMON_HDRS) -Version.obj: Version.c $(COMMON_HDRS) - -Exerciser.obj: samples/Exerciser.c $(COMMON_HDRS) -ImgConv.obj: samples/ImgConv.c $(COMMON_HDRS) -Launder.obj: samples/Launder.c $(COMMON_HDRS) -TestBasic.obj: samples/TestBasic.c $(COMMON_HDRS) -TestExtract.obj: samples/TestExtract.c $(COMMON_HDRS) -TestNames.obj: samples/TestNames.c $(COMMON_HDRS) -TestSimple.obj: samples/TestSimple.c $(COMMON_HDRS) -TestTwirl.obj: samples/TestTwirl.c $(COMMON_HDRS) - diff --git a/ciderpress/nufxlib/MiscStuff.c b/ciderpress/nufxlib/MiscStuff.c deleted file mode 100644 index ff92c2a..0000000 --- a/ciderpress/nufxlib/MiscStuff.c +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Misc stuff (shared between nufxlib and nulib2). This is a collection - * of standard functions that aren't available in libc on this system. - */ -#include "SysDefs.h" -#include "MiscStuff.h" -#include - - -#ifndef HAVE_STRERROR -/* - * Return a pointer to the appropriate string in the system table, or NULL - * if the value is out of bounds. - */ -const char* Nu_strerror(int errnum) -{ - extern int sys_nerr; - extern char *sys_errlist[]; - - if (errnum < 0 || errnum > sys_nerr) - return NULL; - - return sys_errlist[errnum]; -} -#endif - -#ifndef HAVE_MEMMOVE -/* - * Move a block of memory. Unlike memcpy, this is expected to work - * correctly with overlapping blocks. - * - * This is a straightforward implementation. A much faster implementation, - * from BSD, is available in the PGP 2.6.2 distribution, but this should - * suffice for those few systems that don't have memmove. - */ -void* Nu_memmove(void* dst, const void* src, size_t n) -{ - void* retval = dst; - char* srcp = (char*)src; - char* dstp = (char*)dst; - - /* you can normally get away with this if n==0 */ - Assert(dst != NULL); - Assert(src != NULL); - - if (dstp == srcp || !n) { - /* nothing to do */ - } else if (dstp > srcp) { - /* start from the end */ - (char*)dstp += n-1; - (char*)srcp += n-1; - while (n--) - *dstp-- = *srcp--; - } else { - /* start from the front */ - while (n--) - *dstp++ = *srcp++; - } - - return retval; -} -#endif - -#ifndef HAVE_STRTOUL -/* - * Perform strtol, but on an unsigned long. - * - * On systems that have strtol but don't have strtoul, the strtol - * function doesn't clamp the return value, making it similar in - * function to strtoul. The comparison is not exact, however, - * because strtoul is expected to lots of fancy things (like set - * errno to ERANGE). - * - * For our purposes here, strtol does all we need it to. Someday - * we should replace this with a "real" version. - */ -unsigned long Nu_strtoul(const char *nptr, char **endptr, int base) -{ - return strtol(nptr, endptr, base); -} -#endif - -#ifndef HAVE_STRCASECMP -/* - * Compare two strings, case-insensitive. - */ -int Nu_strcasecmp(const char *str1, const char *str2) -{ - while (*str1 && *str2 && toupper(*str1) == toupper(*str2)) - str1++, str2++; - return (toupper(*str1) - toupper(*str2)); -} - -#endif - -#ifndef HAVE_STRNCASECMP -/* - * Compare two strings, case-insensitive, stopping after "n" chars. - */ -int Nu_strncasecmp(const char *str1, const char *str2, size_t n) -{ - while (n && *str1 && *str2 && toupper(*str1) == toupper(*str2)) - str1++, str2++, n--; - - if (n) - return (toupper(*str1) - toupper(*str2)); - else - return 0; /* no mismatch in first n chars */ -} -#endif - diff --git a/ciderpress/nufxlib/MiscStuff.h b/ciderpress/nufxlib/MiscStuff.h deleted file mode 100644 index 493985c..0000000 --- a/ciderpress/nufxlib/MiscStuff.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Misc stuff (shared between nufxlib and nulib2). This is a collection - * of miscellaneous types and macros that I find generally useful. - */ -#ifndef NUFXLIB_MISCSTUFF_H -#define NUFXLIB_MISCSTUFF_H - -#define VALGRIND /* assume we're using it */ - -#include "SysDefs.h" - -/* - * Use our versions of functions if they don't exist locally. - */ -#ifndef HAVE_STRERROR -#define strerror Nu_strerror -const char* Nu_strerror(int errnum); -#endif -#ifndef HAVE_MEMMOVE -#define memmove Nu_memmove -void* Nu_memmove(void *dest, const void *src, size_t n); -#endif -#ifndef HAVE_STRTOUL -#define strtoul Nu_strtoul -unsigned long Nu_strtoul(const char *nptr, char **endptr, int base); -#endif -#ifndef HAVE_STRCASECMP -#define strcasecmp Nu_strcasecmp -int Nu_strcasecmp(const char *s1, const char *s2); -#endif -#ifndef HAVE_STRNCASECMP -#define strncasecmp Nu_strncasecmp -int Nu_strncasecmp(const char *s1, const char *s2, size_t n); -#endif - - -/* - * Misc types. - */ - -typedef unsigned char Boolean; -#define false (0) -#define true (!false) - - -/* - * Handy macros. - */ - -/* compute #of elements in a static array */ -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) - -/* convert single hex digit char to number */ -#define HexDigit(x) ( !isxdigit((int)(x)) ? -1 : \ - (x) <= '9' ? (x) - '0' : toupper(x) +10 - 'A' ) - -/* convert number from 0-15 to hex digit */ -#define HexConv(x) ( ((unsigned int)(x)) <= 15 ? \ - ( (x) <= 9 ? (x) + '0' : (x) -10 + 'A') : -1 ) - - -/* - * Debug stuff. - */ - -/* - * Redefine this if you want assertions to do something other than default. - * Changing the definition of assert is tough, because assert.h redefines - * it every time it's included. On a Solaris 2.7 system I was using, gcc - * pulled assert.h in with some of the system headers, and their definition - * resulted in corrupted core dumps. - */ -#define Assert assert - -#if defined(DEBUG_VERBOSE) - /* quick debug printf macro */ - #define DBUG(args) printf args -#else - #define DBUG(args) ((void)0) -#endif - - -#if defined(NDEBUG) - #define DebugFill(addr, len) ((void)0) - - #define DebugAbort() ((void)0) - -#else - /* when debugging, fill Malloc blocks with junk, unless we're using Purify */ - #if !defined(PURIFY) && !defined(VALGRIND) - #define DebugFill(addr, len) memset(addr, 0xa3, len) - #else - #define DebugFill(addr, len) ((void)0) - #endif - - #define DebugAbort() abort() -#endif - -#define kInvalidPtr ((void*)0xa3a3a3a3) - -#endif /*NUFXLIB_MISCSTUFF_H*/ diff --git a/ciderpress/nufxlib/MiscUtils.c b/ciderpress/nufxlib/MiscUtils.c deleted file mode 100644 index a3efd53..0000000 --- a/ciderpress/nufxlib/MiscUtils.c +++ /dev/null @@ -1,377 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Miscellaneous NufxLib utility functions. - */ -#include "NufxLibPriv.h" - -/* - * Big fat hairy global. Unfortunately this is unavoidable. - */ -NuCallback gNuGlobalErrorMessageHandler = NULL; - - -static const char* kNufxLibName = "nufxlib"; - - -/* - * strerror() equivalent for NufxLib errors. - */ -const char* Nu_StrError(NuError err) -{ - /* - * BUG: this should be set up as per-thread storage in an MT environment. - * I would be more inclined to worry about this if I was expecting - * it to be used. So long as valid values are passed in, and the - * switch statement is kept up to date, we should never have cause - * to return this. - * - * An easier solution, should this present a problem for someone, would - * be to have the function return NULL or "unknown error" when the - * error value isn't recognized. I'd recommend leaving it as-is for - * debug builds, though, as it's helpful to know *which* error is not - * recognized. - */ - static char defaultMsg[32]; - - switch (err) { - case kNuErrNone: - return "(no error)"; - - case kNuErrGeneric: - return "NufxLib generic error"; - case kNuErrInternal: - return "NufxLib internal error"; - case kNuErrUsage: - return "NufxLib usage error"; - case kNuErrSyntax: - return "NufxLib syntax error"; - case kNuErrMalloc: - return "NufxLib malloc error"; - case kNuErrInvalidArg: - return "Invalid arguments to NufxLib"; - case kNuErrBadStruct: - return "Bad NuArchive structure passed to NufxLib"; - case kNuErrBusy: - return "Attempted invalid reentrant call"; - - case kNuErrSkipped: - return "Skipped by user"; - case kNuErrAborted: - return "Processing aborted"; - case kNuErrRename: - return "User wants to rename file"; - - case kNuErrFile: - return "NufxLib trouble with a file"; - case kNuErrFileOpen: - return "NufxLib unable to open file"; - case kNuErrFileClose: - return "NufxLib unable to close file"; - case kNuErrFileRead: - return "NufxLib unable to read file"; - case kNuErrFileWrite: - return "NufxLib unable to write file"; - case kNuErrFileSeek: - return "NufxLib unable to seek file"; - case kNuErrFileExists: - return "File already exists"; - case kNuErrFileNotFound: - return "No such file or directory"; - case kNuErrFileStat: - return "Couldn't get file info"; - case kNuErrFileNotReadable: - return "Read access denied"; - - case kNuErrDirExists: - return "Directory already exists"; - case kNuErrNotDir: - return "Not a directory"; - case kNuErrNotRegularFile: - return "Not a regular file"; - case kNuErrDirCreate: - return "Unable to create directory"; - case kNuErrOpenDir: - return "Unable to open directory"; - case kNuErrReadDir: - return "Unable to read directory"; - case kNuErrFileSetDate: - return "Unable to set file date"; - case kNuErrFileSetAccess: - return "Unable to set file access"; - case kNuErrFileAccessDenied: - return "Access denied"; - - case kNuErrNotNuFX: - return "Input is not a NuFX archive"; - case kNuErrBadMHVersion: - return "Unrecognized Master Header version"; - case kNuErrRecHdrNotFound: - return "Next record not found"; - case kNuErrNoRecords: - return "No records in archive"; - case kNuErrBadRecord: - return "Bad data in record"; - case kNuErrBadMHCRC: - return "Bad Master Header CRC"; - case kNuErrBadRHCRC: - return "Bad Record header CRC"; - case kNuErrBadThreadCRC: - return "Bad Thread header CRC"; - case kNuErrBadDataCRC: - return "Data CRC mismatch"; - - case kNuErrBadFormat: - return "Thread compression format unsupported"; - case kNuErrBadData: - return "Bad data found"; - case kNuErrBufferOverrun: - return "Buffer overrun"; - case kNuErrBufferUnderrun: - return "Buffer underrun"; - case kNuErrOutMax: - return "Output limit exceeded"; - - case kNuErrNotFound: - return "Not found"; - case kNuErrRecordNotFound: - return "Record not found"; - case kNuErrRecIdxNotFound: - return "RecordIdx not found"; - case kNuErrThreadIdxNotFound: - return "ThreadIdx not found"; - case kNuErrThreadIDNotFound: - return "ThreadID not found"; - case kNuErrRecNameNotFound: - return "Record name not found"; - case kNuErrRecordExists: - return "Record already exists"; - - case kNuErrAllDeleted: - return "Tried to delete all files"; - case kNuErrArchiveRO: - return "Archive is in read-only mode"; - case kNuErrModRecChange: - return "Attempt to alter a modified record"; - case kNuErrModThreadChange: - return "Attempt to alter a modified thread"; - case kNuErrThreadAdd: - return "Can't add conflicting threadID"; - case kNuErrNotPreSized: - return "Operation only permitted on pre-sized threads"; - case kNuErrPreSizeOverflow: - return "Data exceeds pre-sized thread size"; - case kNuErrInvalidFilename: - return "Invalid filename"; - - case kNuErrLeadingFssep: - return "Storage name started with fssep char"; - case kNuErrNotNewer: - return "New item wasn't newer than existing"; - case kNuErrDuplicateNotFound: - return "Can only update an existing item"; - case kNuErrDamaged: - return "Original archive may have been damaged"; - - case kNuErrIsBinary2: - return "This is a Binary II archive"; - - case kNuErrUnknownFeature: - return "Unknown feature"; - case kNuErrUnsupFeature: - return "Feature not supported"; - - default: - sprintf(defaultMsg, "(error=%d)", err); - return defaultMsg; - } -} - - -#define kNuHeftyBufSize 256 /* all error messages should fit in this */ -#define kNuExtraGoodies 8 /* leave room for "\0" and other trivial chars*/ - -/* - * Similar to perror(), but takes the error as an argument, and knows - * about NufxLib errors as well as system errors. - * - * Depending on the compiler, "file", "line", and "function" may be NULL/zero. - * - * Calling here with "pArchive"==NULL is allowed, but should only be done - * if the archive is inaccessible (perhaps because it failed to open). We - * can't invoke the error message callback if the pointer is NULL. - */ -void Nu_ReportError(NuArchive* pArchive, const char* file, int line, - const char* function, Boolean isDebug, NuError err, - const UNICHAR* format, ...) -{ - NuErrorMessage errorMessage; - const char* msg; - va_list args; - char buf[kNuHeftyBufSize]; - int count; - #if !defined(HAVE_SNPRINTF) && defined(SPRINTF_RETURNS_INT) - int cc; - #endif - - Assert(format != NULL); - - - va_start(args, format); - - #if defined(HAVE_VSNPRINTF) && defined(VSNPRINTF_DECLARED) - count = vsnprintf(buf, sizeof(buf)-kNuExtraGoodies, format, args); - #else - #ifdef SPRINTF_RETURNS_INT - count = vsprintf(buf, format, args); - #else - vsprintf(buf, format, args); - count = strlen(buf); - #endif - #endif - - va_end(args); - - Assert(count > 0); - if (count < 0) - goto bail; - - /* print the error code data, if any */ - if (err != kNuErrNone) { - /* we know we have room for ": ", because of kNuExtraGoodies */ - strcpy(buf+count, ": "); - count += 2; - - msg = NULL; - if (err >= 0) - msg = strerror(err); - if (msg == NULL) - msg = Nu_StrError(err); - - #if defined(HAVE_SNPRINTF) && defined(SNPRINTF_DECLARED) - if (msg == NULL) - snprintf(buf+count, sizeof(buf) - count, - "(unknown err=%d)", err); - else - snprintf(buf+count, sizeof(buf) - count, "%s", msg); - #else - #ifdef SPRINTF_RETURNS_INT - if (msg == NULL) - cc = sprintf(buf+count, "(unknown err=%d)", err); - else - cc = sprintf(buf+count, "%s", msg); - Assert(cc > 0); - count += cc; - #else - if (msg == NULL) - sprintf(buf+count, "(unknown err=%d)", err); - else - sprintf(buf+count, "%s", msg); - count += strlen(buf + count); - #endif - #endif - - } - - #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) || \ - !defined(SNPRINTF_DELCARED) || !defined(VSNPRINTF_DECLARED) - /* couldn't do it right, so check for overflow */ - Assert(count <= kNuHeftyBufSize); - #endif - - if ((pArchive != NULL && pArchive->messageHandlerFunc == NULL) || - (pArchive == NULL && gNuGlobalErrorMessageHandler == NULL)) - { - if (isDebug) { - fprintf(stderr, "%s: [%s:%d %s] %s\n", kNufxLibName, - file, line, function, buf); - } else { - fprintf(stderr, "%s: ERROR: %s\n", kNufxLibName, buf); - } - } else { - errorMessage.message = buf; - errorMessage.err = err; - errorMessage.isDebug = isDebug; - errorMessage.file = file; - errorMessage.line = line; - errorMessage.function = function; - - if (pArchive == NULL) - (void) (*gNuGlobalErrorMessageHandler)(pArchive, &errorMessage); - else - (void) (*pArchive->messageHandlerFunc)(pArchive, &errorMessage); - } - -bail: - return; -} - - -/* - * Memory allocation wrappers. - * - * Under gcc these would be macros, but not all compilers can handle that. - * - * [ It should be possible to use mmalloc instead of malloc. Just tuck the - * mmalloc descriptor into the NuArchive struct. ] - */ - -#ifndef USE_DMALLOC -void* Nu_Malloc(NuArchive* pArchive, size_t size) -{ - void* _result; - - Assert(size > 0); - _result = malloc(size); - if (_result == NULL) { - Nu_ReportError(NU_BLOB, kNuErrMalloc, - "malloc(%u) failed", (unsigned int) size); - DebugAbort(); /* leave a core dump if we're built for it */ - } - DebugFill(_result, size); - return _result; -} - -void* Nu_Calloc(NuArchive* pArchive, size_t size) -{ - void* _cresult = Nu_Malloc(pArchive, size); - memset(_cresult, 0, size); - return _cresult; -} - -void* Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size) -{ - void* _result; - - Assert(ptr != NULL); /* disallow this usage */ - Assert(size > 0); /* disallow this usage */ - _result = realloc(ptr, size); - if (_result == NULL) { - Nu_ReportError(NU_BLOB, kNuErrMalloc, - "realloc(%u) failed", (unsigned int) size); - DebugAbort(); /* leave a core dump if we're built for it */ - } - return _result; -} - -void Nu_Free(NuArchive* pArchive, void* ptr) -{ - if (ptr != NULL) - free(ptr); -} -#endif - -/* - * If somebody internal wants to set doClose on a buffer DataSource - * (looks like "Rename" does), we need to supply a "free" callback. - */ -NuResult Nu_InternalFreeCallback(NuArchive* pArchive, void* args) -{ - DBUG(("+++ internal free callback 0x%08lx\n", (long) args)); - Nu_Free(NULL, args); - return kNuOK; -} - diff --git a/ciderpress/nufxlib/NOTES.md b/ciderpress/nufxlib/NOTES.md deleted file mode 100644 index caccd89..0000000 --- a/ciderpress/nufxlib/NOTES.md +++ /dev/null @@ -1,339 +0,0 @@ -NufxLib NOTES -============= -Last revised: 2015/01/04 - - -The interface is documented in "nufxlibapi.html", available from the -http://www.nulib.com/ web site. This discusses some of the internal -design that may be of interest. - -Some familiarity with the NuFX file format is assumed. - -- - - - -### Read-Write Data Structures ### - -For both read-only and read-write files (but not streaming read-only files), -the archive is represented internally as a linked list of Records, each -of which has an array of Threads attached. No attempt is made to -optimize searches by filename, so use of the "replace existing entry when -filenames match" option should be restricted to situations where it is -necessary. Otherwise, O(N^2) behavior can result. - -Modifications, such as deletions, changes to filename threads, and -additions of new records, are queued up in a separate list until a NuFlush -call is issued. The list works much the same way as the temporary file: -when the operation completes, the "new" list becomes the "original" list. -If the operation is aborted, the "new" list is scrubbed, and the "original" -list remains unmodified. - -Just as it is inefficient to write data to the temp file when it's not -necessary to do so, it is inefficient to allocate a complete copy of the -records from the original list if none are changed. As a result, there are -actually two "new" lists, one with a copy of the original record list, and -one with new additions. The "copy" list starts out uninitialized, and -remains that way until one of the entries from the original list is -modified. When that happens, the entire original list is duplicated, and -the changes are made directly to members of the "copy" list. (This is -important for really large archives, like a by-file archive with the -entire contents of a hard drive, where the record index could be several -megabytes in size.) - -It would be more *memory* efficient to simply maintain a list of what -has changed. However, we can't disturb the "original" list in any way or -we lose the ability to roll back quickly if the operation is aborted. -Consequently, we need to create a new list of records that reflects -the state of the new archive, so that when we rename the temp file over -the original, we can simply "rename" the new record list over the original. -Since we're going to need the new list eventually, we might as well create -it as soon as it is needed, and deal with memory allocation failures up -front rather than during the update process. (Some items, such as the -record's file offset in the archive, have to be updated even for records -that aren't themselves changing... which means we potentially need to -modify all existing record structures, so we need a complete copy of the -record list regardless of how little or how much has changed.) - -This also ties into the "modify original archive file directly if possible" -option, which avoids the need for creating and renaming a temp file. If -the only changes are updates to pre-sized records (e.g. renaming a file -inside the archive, or updating a comment), or adding new records onto the -end, there is little risk and possibly a huge efficiency gain in just -modifying the archive in place. If none of the operations caused the -"copy" list to be initialized, then clearly there's no need to write to a -temp file. (It's not actually this simple, because updates to pre-sized -threads are annotated in the "copy" list.) - -One of the goals was to be able to execute a sequence of operations like: - - open original archive - read original archive - modify archive - flush (success) - modify archive - flush (failure, rollback) - modify archive - flush (success) - close archive - -The archive is opened at the start and held open across many operations. -There is never a need to re-read the entire archive. We could avoid the -need to allocate two complete Record lists by requiring that the archive be -re-scanned after changes are aborted; if we did that, we could just modify -the original record list in place, and let the changes become "permanent" -after a successful write. In many ways, though, its cleaner to have two -lists. - -Archives with several thousand entries should be sufficiently rare, and -virtual memory should be sufficiently plentiful, that this won't be a -problem for anyone. Scanning repeatedly through a 15MB archive stored on a -CD-ROM is likely to be very annoying though, so the design makes every -attempt to avoid repeated scans of the archive. And in any event, this -only applies to archive updates. The memory requirements for simple file -extraction are minimal. - -In summary: - - - "orig" list has original set of records, and is not disturbed until - the changes are committed. - - "copy" list is created on first add/update/delete operation, and - initially contains a complete copy of "orig". - - "new" list contains all new additions to the archive, including - new additions that replace existing entries (the existing entry - is deleted from "copy" and then added to "new"). - - -Each Record in the list has a "thread modification" list attached to it. -Any changes to the record header or additions to the thread mod list are -made in the "copy" set; the "original" set remains untouched. The thread -mod list can have the following items in it: - - - delete thread (NuThreadIdx) - - add thread (type, otherSize, format, +contents) - - update pre-sized thread (NuThreadIdx, +contents) - -Contents are specified with a NuDataSource, which allows the application -to indicate that the data is already compressed. This is useful for -copying parts of records between archives without having to expand and -recompress the data. - -Some interactions and concepts that are important to understand: - - When a file is added, the file type information will be placed in the - "new" Record immediately (subject to some restrictions: adding a data - fork always causes the type info to be updated, adding a rsrc fork only - updates the type info if a data fork is not already present). - - Deleting a record results in the Record being removed from the "copy" - list immediately. Future modify operations on that NuRecordIdx will - fail. Future read operations will work just fine until the next - NuFlush is issued, because read operations use the "original" list. - - Deleting all threads from a record results in the record being - deleted, but not until the NuFlush call is issued. It is possible to - delete all the existing threads and then add new ones. - - It is *not* allowed to delete a modified thread, modify a deleted thread, - or delete a record that has been modified. This limitation was added to - keep the system simple. Note this does not mean you can't delete a data - fork and add a data fork; doing so results in operations on two threads - with different NuThreadIdx values. What you can't do is update the - filename thread and then delete it, or vice-versa. (If anyone can think - of a reason why you'd want to rename a file and then delete it with the - same NuFlush call, I'll figure out a way to support it.) - - Updating a filename thread is intercepted, and causes the Record's - filename cache to be updated as well. Adding a filename thread for - records where the filename is stored in the record itself cause the - "in-record" filename to be zeroed. Adding a filename thread to a - record that already has one isn't allowed; nufxlib restricts you to - a single filename thread per record. - - Some actions on an archive are allowed but strongly discouraged. For - example, deleting a filename thread but leaving the data threads behind - is a valid thing to do, but leaves most archivers in a state of mild - confusion. Deleting the data threads but leaving the filename thread is - similarly perplexing. - - You can't call "update thread" on a thread that doesn't yet exist, - even if an "add thread" call has been made. You can, however, call - "add thread" on a newly created Record. - -When a new record is created because of a "create record" call, a filename -thread is created automatically. It is not necessary to explicitly add the -filename. - -Failures encountered while committing changes to a record cause all -operations on that record to be rolled back. If, during a NuFlush, a -file add fails, the user is given the option of aborting the entire -operation or skipping the file in question (and perhaps retrying or other -options as well). Aborting the flush causes a complete rollback. If only -the thread mod operation is canceled, then all thread mods for that record -are ignored. The temp file (or archive file) will have its file pointer -reset to the original start of the record, and if the record already -existed in the original archive, the full original record will be copied -over. This may seem drastic, but it helps ensure that you don't end up -with a record in a partially created state. - -If a failure occurs during an "update in place", it isn't possible to -roll back all changes. If the failure was due to a bug in NufxLib, it -is possible that the archive could be unrecoverably damaged. NufxLib -tries to identify such situations, and will leave the archive open in -read-only mode after rolling back any new file additions. - -- - - - -### Updating Filenames ### - -Updating filenames is a small nightmare, because the filename can be -either in the record header or in a filename thread. It's possible, -but illogical, to have a single record with a filename in the record -header and two or more filenames in threads. - -NufxLib will not automatically "fix" broken records, but it will prevent -applications from creating situations that should not exist. - - - When reading an archive, NufxLib will use the filename from the - first filename thread found. If no filename threads are found, the - filename from the record header will be used. - - - If you add a filename thread to a record that has a filename in the - record header, the header name will be removed. - - - If you update a filename thread in a record that has a filename in - the record header, the header name will be left untouched. - - - Adding a filename thread is only allowed if no filename thread exists, - or all existing filename threads have been deleted. - - -- - - - -### Unicode Filenames ### - -Modern operating systems support filenames with a broader range of -characters than the Apple II did. This presents problems and opportunities. - -#### Background #### - -The Apple IIgs and old Macintoshes use the Mac OS Roman ("MOR") character -set. This defines a set of characters outside the ASCII range, i.e. -byte values with the high bit set. In addition to the usual collection -of vowels with accents and umlauts, MOR has some less-common characters, -including the Apple logo. - -On Windows, the high-ASCII values are generally interpreted according -to Windows Code Page 1252 ("CP-1252"), which defines a similar set -of vowels with accents and miscellaneous symbols. MOR and CP-1252 -have some overlap, but you can't really translate one into the other. -The standards-approved equivalent of CP-1252 is ISO-8859-1, though -according to [wikipedia](http://en.wikipedia.org/wiki/Windows-1252) -there was some confusion between the two. - -Modern operating systems support the Unicode Universal Character Set. -This system allows for a very large number of characters (over a million), -and includes definitions for all of the symbols in MOR and CP-1252. -Each character is assigned a "code point", which is a numeric value between -zero and 0x10FFFF. Most of the characters used in modern languages can -be found in the Basic Multilingual Plane (BMP), which uses code points -between zero and 0xFFFF (requiring only 16 bits). - -There are different ways of encoding code points. Consider, for example, -Unicode LATIN SMALL LETTER A WITH ACUTE: - - MOR: 0x87 - CP-1252: 0xE1 - Unicode: U+00E1 - UTF-16: 0x00E1 - UTF-8: 0xC3 0xA1 - -Or the humble TRADE MARK SIGN: - - MOR: 0xAA - CP-1252: 0x99 - Unicode: U+2122 - UTF-16: 0x2122 - UTF-8: 0xE2 0x84 0xA2 - -Modern Linux and Mac OS X use UTF-8 encoding in filenames. Because it's a -byte-oriented encoding, and 7-bit ASCII values are trivially represented -as 7-bit ASCII values, all of the existing system and library calls work -as they did before (i.e. if they took a `char*`, they still do). - -Windows uses UTF-16, which requires at least 16 bits per code point. -Filenames are now "wide" strings, based on `wchar_t*`. Windows includes -an elaborate system of defines based around the `TCHAR` type, which can -be either `char` or `wchar_t` depending on whether a program is compiled -with `_MBCS` (Multi-Byte Character System) or `_UNICODE`. A set of -preprocessor definitions is provided that will map I/O function names, -so you can call `_tfopen(TCHAR* ...)`, and the compiler will turn it into -either `fopen(char* ...)` or `_wfopen(wchar_t* ...)`. MBCS is deprecated -in favor of Unicode, so any new code should be strictly UTF-16 based. - -This means that, for code to work on both Linux and Windows, it has to -work with incompatible filename string types and different I/O functions. - -#### Opening Archive Files #### - -On Linux and Mac OS X, NuLib2 can open any file named on the command line. -On Windows, it's a bit trickier. - -The problem is that NuLib2 provides a `main()` function that is passed a -vector of "narrow" strings. The filenames provided on the command line -will be converted from wide to narrow, so unless the filename is entirely -composed of ASCII or CP-1252 characters, some information will be lost -and it will be impossible to open the file. - -NuLib2 must instead provide a `wmain()` function that takes wide strings. -The strings must be stored and passed around as wide throughout the -program, and passed into NufxLib this way (because NufxLib issues the -actual _wopen call). This means that NufxLib API must take narrow strings -when built for Linux, and wide strings when built for Windows. - -#### Adding/Extracting Mac OS Roman Files #### - -GS/ShrinkIt was designed to handle GS/OS files from HFS volumes, so NuFX -archive filenames use the MOR character set. To preserve the encoding -we could simply extract the values as-is and let them appear as whatever -values happen to line up in CP-1252, which is what pre-3.0 NuLib2 did. -It's much nicer to translate from MOR to Unicode when extracting, and -convert back from Unicode to MOR when adding files to an archive. - -The key consideration is that the character set associated with a -filename must be tracked. The code can't simply extract a filename from -the archive and pass it to a 'creat()` call. Character set conversions -must take place at appropriate times. - -With Windows it's a bit harder to confuse MOR and Unicode names, because -one uses 8-bit characters and the other uses UTF-16, but the compiler -doesn't catch everything. - -#### Current State #### - -NufxLib defines the UNICHAR type, which has a role very like TCHAR: -it can be `char*` or `wchar_t*`, and can be accompanied by a set of -preprocessor mappings that switch between I/O functions. The UNICHAR -type will be determined based on a define provided from the compiler -command line (perhaps `-DUSE_UTF16_FILENAMES`). - -The current version of NufxLib (v3.0.0) takes the first step, defining -all filename strings as either UNICHAR or MOR, and converting between them -as necessary. This, plus a few minor tweaks to NuLib2, was enough to -get Unicode filename support working on Linux and Mac OS X. - -None of the work needed to make Windows work properly has been done. -The string conversion functions are no-ops for Win32. As a result, -NuLib2 for Windows treats filenames the same way in 3.x as it did in 2.x. - -There are some situations where things can go awry even with UNICHAR, -most notably printf-style arguments. These are checked by gcc, but -not by Visual Studio unless you run the static analyzer. A simple -`printf("filename=%s\n", filename)` would be correct for narrow strings -but wrong for wide strings. It will likely be necessary to define a -filename format string (similar to `PRI64d` for 64-bit values) and switch -between "%s" and "%ls". - -This is a fair bit of work and requires some amount of uglification to -NuLib2 and NufxLib. Since Windows users can use CiderPress, and the -vast majority of NuFX archives use ASCII-only ProDOS file names, it's -not clear that the effort would be worthwhile. - diff --git a/ciderpress/nufxlib/NufxLib.h b/ciderpress/nufxlib/NufxLib.h deleted file mode 100644 index 349917c..0000000 --- a/ciderpress/nufxlib/NufxLib.h +++ /dev/null @@ -1,890 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * External interface (types, defines, and function prototypes). - */ -#ifndef NUFXLIB_NUFXLIB_H -#define NUFXLIB_NUFXLIB_H - -#include -#include - - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * NufxLib version number. Compare these values (which represent the - * version against which your application was compiled) to the values - * returned by NuGetVersion (representing the version against which - * your application is statically or dynamically linked). If the major - * number doesn't match exactly, an existing interface has changed and you - * should halt immediately. If the minor number from NuGetVersion is - * less, there may be new interfaces, new features, or bug fixes missing - * upon which your application depends, so you should halt immediately. - * (If the minor number is greater, there are new features, but your - * application will not be affected by them.) - * - * The "bug" version can usually be ignored, since it represents minor - * fixes. Unless, of course, your code depends upon that fix. - */ -#define kNuVersionMajor 3 -#define kNuVersionMinor 1 -#define kNuVersionBug 0 - - -/* - * =========================================================================== - * Types - * =========================================================================== - */ - -/* - * Unicode character type. For Linux and Mac OS X, filenames use "narrow" - * characters and UTF-8 encoding, which allows them to use standard file I/O - * functions like fopen(). Windows uses UTF-16, which requires a different - * character type and an alternative set of I/O functions like _wfopen(). - * - * The idea is that NufxLib API functions will operate on filenames with - * the OS dominant method, so on Windows the API accepts UTF-16. This - * definition is a bit like Windows TCHAR, but it's dependent on the OS, not - * on whether _MBCS or _UNICODE is defined. - * - * The app can include "Unichar.h" to get definitions for functions that - * switch between narrow and wide functions (e.g. "unistrlen()" becomes - * strlen() or wcslen() as appropriate). - * - * We switch based on _WIN32, because we're not really switching on - * filename-character size; the key issue is all the pesky wide I/O calls. - */ -#if defined(_WIN32) -// TODO: complete this -//# include -//# define UNICHAR wchar_t -# define UNICHAR char -#else -# define UNICHAR char -#endif - -/* - * Error values returned from functions. - * - * These are negative so that they don't conflict with system-defined - * errors (like ENOENT). A NuError can hold either. - */ -typedef enum NuError { - kNuErrNone = 0, - - kNuErrGeneric = -1, - kNuErrInternal = -2, - kNuErrUsage = -3, - kNuErrSyntax = -4, - kNuErrMalloc = -5, - kNuErrInvalidArg = -6, - kNuErrBadStruct = -7, - kNuErrUnexpectedNil = -8, - kNuErrBusy = -9, - - kNuErrSkipped = -10, /* processing skipped by request */ - kNuErrAborted = -11, /* processing aborted by request */ - kNuErrRename = -12, /* user wants to rename before extracting */ - - kNuErrFile = -20, - kNuErrFileOpen = -21, - kNuErrFileClose = -22, - kNuErrFileRead = -23, - kNuErrFileWrite = -24, - kNuErrFileSeek = -25, - kNuErrFileExists = -26, /* existed when it shouldn't */ - kNuErrFileNotFound = -27, /* didn't exist when it should have */ - kNuErrFileStat = -28, /* some sort of GetFileInfo failure */ - kNuErrFileNotReadable = -29, /* bad access permissions */ - - kNuErrDirExists = -30, /* dir exists, don't need to create it */ - kNuErrNotDir = -31, /* expected a dir, got a regular file */ - kNuErrNotRegularFile = -32, /* expected regular file, got weirdness */ - kNuErrDirCreate = -33, /* unable to create a directory */ - kNuErrOpenDir = -34, /* error opening directory */ - kNuErrReadDir = -35, /* error reading directory */ - kNuErrFileSetDate = -36, /* unable to set file date */ - kNuErrFileSetAccess = -37, /* unable to set file access permissions */ - kNuErrFileAccessDenied = -38, /* equivalent to EACCES */ - - kNuErrNotNuFX = -40, /* 'NuFile' missing; not a NuFX archive? */ - kNuErrBadMHVersion = -41, /* bad master header version */ - kNuErrRecHdrNotFound = -42, /* 'NuFX' missing; corrupted archive? */ - kNuErrNoRecords = -43, /* archive doesn't have any records */ - kNuErrBadRecord = -44, /* something about the record looked bad */ - kNuErrBadMHCRC = -45, /* bad master header CRC */ - kNuErrBadRHCRC = -46, /* bad record header CRC */ - kNuErrBadThreadCRC = -47, /* bad thread header CRC */ - kNuErrBadDataCRC = -48, /* bad CRC detected in the data */ - - kNuErrBadFormat = -50, /* compression type not supported */ - kNuErrBadData = -51, /* expansion func didn't like input */ - kNuErrBufferOverrun = -52, /* overflowed a user buffer */ - kNuErrBufferUnderrun = -53, /* underflowed a user buffer */ - kNuErrOutMax = -54, /* output limit exceeded */ - - kNuErrNotFound = -60, /* (generic) search unsuccessful */ - kNuErrRecordNotFound = -61, /* search for specific record failed */ - kNuErrRecIdxNotFound = -62, /* search by NuRecordIdx failed */ - kNuErrThreadIdxNotFound = -63, /* search by NuThreadIdx failed */ - kNuErrThreadIDNotFound = -64, /* search by NuThreadID failed */ - kNuErrRecNameNotFound = -65, /* search by storageName failed */ - kNuErrRecordExists = -66, /* found existing record with same name */ - - kNuErrAllDeleted = -70, /* attempt to delete everything */ - kNuErrArchiveRO = -71, /* archive is open in read-only mode */ - kNuErrModRecChange = -72, /* tried to change modified record */ - kNuErrModThreadChange = -73, /* tried to change modified thread */ - kNuErrThreadAdd = -74, /* adding that thread creates a conflict */ - kNuErrNotPreSized = -75, /* tried to update a non-pre-sized thread */ - kNuErrPreSizeOverflow = -76, /* too much data */ - kNuErrInvalidFilename = -77, /* invalid filename */ - - kNuErrLeadingFssep = -80, /* names in archives must not start w/sep */ - kNuErrNotNewer = -81, /* item same age or older than existing */ - kNuErrDuplicateNotFound = -82, /* "must overwrite" was set, but item DNE */ - kNuErrDamaged = -83, /* original archive may have been damaged */ - - kNuErrIsBinary2 = -90, /* this looks like a Binary II archive */ - - kNuErrUnknownFeature =-100, /* attempt to test unknown feature */ - kNuErrUnsupFeature = -101, /* feature not supported */ -} NuError; - -/* - * Return values from callback functions. - */ -typedef enum NuResult { - kNuOK = 0, - kNuSkip = 1, - kNuAbort = 2, - /*kNuAbortAll = 3,*/ - kNuRetry = 4, - kNuIgnore = 5, - kNuRename = 6, - kNuOverwrite = 7 -} NuResult; - -/* - * NuRecordIdxs are assigned to records in an archive. You may assume that - * the values are unique, but that is all. - */ -typedef uint32_t NuRecordIdx; - -/* - * NuThreadIdxs are assigned to threads within a record. Again, you may - * assume that the values are unique within a record, but that is all. - */ -typedef uint32_t NuThreadIdx; - -/* - * Thread ID, a combination of thread_class and thread_kind. Standard - * values have explicit identifiers. - */ -typedef uint32_t NuThreadID; -#define NuMakeThreadID(class, kind) /* construct a NuThreadID */ \ - ((uint32_t)(class) << 16 | (uint32_t)(kind)) -#define NuGetThreadID(pThread) /* pull NuThreadID out of NuThread */ \ - (NuMakeThreadID((pThread)->thThreadClass, (pThread)->thThreadKind)) -#define NuThreadIDGetClass(threadID) /* get threadClass from NuThreadID */ \ - ((uint16_t) ((uint32_t)(threadID) >> 16)) -#define NuThreadIDGetKind(threadID) /* get threadKind from NuThreadID */ \ - ((uint16_t) ((threadID) & 0xffff)) -#define kNuThreadClassMessage 0x0000 -#define kNuThreadClassControl 0x0001 -#define kNuThreadClassData 0x0002 -#define kNuThreadClassFilename 0x0003 -#define kNuThreadKindDataFork 0x0000 /* when class=data */ -#define kNuThreadKindDiskImage 0x0001 /* when class=data */ -#define kNuThreadKindRsrcFork 0x0002 /* when class=data */ -#define kNuThreadIDOldComment NuMakeThreadID(kNuThreadClassMessage, 0x0000) -#define kNuThreadIDComment NuMakeThreadID(kNuThreadClassMessage, 0x0001) -#define kNuThreadIDIcon NuMakeThreadID(kNuThreadClassMessage, 0x0002) -#define kNuThreadIDMkdir NuMakeThreadID(kNuThreadClassControl, 0x0000) -#define kNuThreadIDDataFork NuMakeThreadID(kNuThreadClassData, kNuThreadKindDataFork) -#define kNuThreadIDDiskImage NuMakeThreadID(kNuThreadClassData, kNuThreadKindDiskImage) -#define kNuThreadIDRsrcFork NuMakeThreadID(kNuThreadClassData, kNuThreadKindRsrcFork) -#define kNuThreadIDFilename NuMakeThreadID(kNuThreadClassFilename, 0x0000) -#define kNuThreadIDWildcard NuMakeThreadID(0xffff, 0xffff) - -/* enumerate the possible values for thThreadFormat */ -typedef enum NuThreadFormat { - kNuThreadFormatUncompressed = 0x0000, - kNuThreadFormatHuffmanSQ = 0x0001, - kNuThreadFormatLZW1 = 0x0002, - kNuThreadFormatLZW2 = 0x0003, - kNuThreadFormatLZC12 = 0x0004, - kNuThreadFormatLZC16 = 0x0005, - kNuThreadFormatDeflate = 0x0006, /* NOTE: not in NuFX standard */ - kNuThreadFormatBzip2 = 0x0007, /* NOTE: not in NuFX standard */ -} NuThreadFormat; - - -/* extract the filesystem separator char from the "file_sys_info" field */ -#define NuGetSepFromSysInfo(sysInfo) \ - ((UNICHAR) ((sysInfo) & 0xff)) -/* return a file_sys_info with a replaced filesystem separator */ -#define NuSetSepInSysInfo(sysInfo, newSep) \ - ((uint16_t) (((sysInfo) & 0xff00) | ((newSep) & 0xff)) ) - -/* GS/OS-defined file system identifiers; sadly, UNIX is not among them */ -typedef enum NuFileSysID { - kNuFileSysUnknown = 0, /* NuFX spec says use this */ - kNuFileSysProDOS = 1, - kNuFileSysDOS33 = 2, - kNuFileSysDOS32 = 3, - kNuFileSysPascal = 4, - kNuFileSysMacHFS = 5, - kNuFileSysMacMFS = 6, - kNuFileSysLisa = 7, - kNuFileSysCPM = 8, - kNuFileSysCharFST = 9, - kNuFileSysMSDOS = 10, - kNuFileSysHighSierra = 11, - kNuFileSysISO9660 = 12, - kNuFileSysAppleShare = 13 -} NuFileSysID; - -/* simplified definition of storage types */ -typedef enum NuStorageType { - kNuStorageUnknown = 0, /* (used by ProDOS for deleted files) */ - kNuStorageSeedling = 1, /* <= 512 bytes */ - kNuStorageSapling = 2, /* < 128KB */ - kNuStorageTree = 3, /* < 16MB */ - kNuStoragePascalVol = 4, /* (embedded pascal volume; rare) */ - kNuStorageExtended = 5, /* forked (any size) */ - kNuStorageDirectory = 13, /* directory */ - kNuStorageSubdirHeader = 14, /* (only used in subdir headers) */ - kNuStorageVolumeHeader = 15, /* (only used in volume dir header) */ -} NuStorageType; - -/* bit flags for NuOpenRW */ -enum { - kNuOpenCreat = 0x0001, - kNuOpenExcl = 0x0002 -}; - - -/* - * The actual NuArchive structure is opaque, and should only be visible - * to the library. We define it here as an ambiguous struct. - */ -typedef struct NuArchive NuArchive; - -/* - * Generic callback prototype. - */ -typedef NuResult (*NuCallback)(NuArchive* pArchive, void* args); - -/* - * Parameters that affect archive operations. - */ -typedef enum NuValueID { - kNuValueInvalid = 0, - kNuValueIgnoreCRC = 1, - kNuValueDataCompression = 2, - kNuValueDiscardWrapper = 3, - kNuValueEOL = 4, - kNuValueConvertExtractedEOL = 5, - kNuValueOnlyUpdateOlder = 6, - kNuValueAllowDuplicates = 7, - kNuValueHandleExisting = 8, - kNuValueModifyOrig = 9, - kNuValueMimicSHK = 10, - kNuValueMaskDataless = 11, - kNuValueStripHighASCII = 12, - kNuValueJunkSkipMax = 13, - kNuValueIgnoreLZW2Len = 14, - kNuValueHandleBadMac = 15 -} NuValueID; -typedef uint32_t NuValue; - -/* - * Enumerated values for things you pass in a NuValue. - */ -enum NuValueValue { - /* for the truly retentive */ - kNuValueFalse = 0, - kNuValueTrue = 1, - - /* for kNuValueDataCompression */ - kNuCompressNone = 10, - kNuCompressSQ = 11, - kNuCompressLZW1 = 12, - kNuCompressLZW2 = 13, - kNuCompressLZC12 = 14, - kNuCompressLZC16 = 15, - kNuCompressDeflate = 16, - kNuCompressBzip2 = 17, - - /* for kNuValueEOL */ - kNuEOLUnknown = 50, - kNuEOLCR = 51, - kNuEOLLF = 52, - kNuEOLCRLF = 53, - - /* for kNuValueConvertExtractedEOL */ - kNuConvertOff = 60, - kNuConvertOn = 61, - kNuConvertAuto = 62, - - /* for kNuValueHandleExisting */ - kNuMaybeOverwrite = 90, - kNuNeverOverwrite = 91, - kNuAlwaysOverwrite = 93, - kNuMustOverwrite = 94 -}; - - -/* - * Pull out archive attributes. - */ -typedef enum NuAttrID { - kNuAttrInvalid = 0, - kNuAttrArchiveType = 1, - kNuAttrNumRecords = 2, - kNuAttrHeaderOffset = 3, - kNuAttrJunkOffset = 4, -} NuAttrID; -typedef uint32_t NuAttr; - -/* - * Archive types. - */ -typedef enum NuArchiveType { - kNuArchiveUnknown, /* .??? */ - kNuArchiveNuFX, /* .SHK (sometimes .SDK) */ - kNuArchiveNuFXInBNY, /* .BXY */ - kNuArchiveNuFXSelfEx, /* .SEA */ - kNuArchiveNuFXSelfExInBNY, /* .BSE */ - - kNuArchiveBNY /* .BNY, .BQY - not supported */ -} NuArchiveType; - - -/* - * Some common values for "locked" and "unlocked". Under ProDOS each bit - * can be set independently, so don't use these defines to *interpret* - * what you see. They're reasonable things to *set* the access field to. - * - * The defined bits are: - * 0x80 'D' destroy enabled - * 0x40 'N' rename enabled - * 0x20 'B' file needs to be backed up - * 0x10 (reserved, must be zero) - * 0x08 (reserved, must be zero) - * 0x04 'I' file is invisible - * 0x02 'W' write enabled - * 0x01 'R' read enabled - */ -#define kNuAccessLocked 0x21 -#define kNuAccessUnlocked 0xe3 - - -/* - * NuFlush result flags. - */ -#define kNuFlushSucceeded (1L) -#define kNuFlushAborted (1L << 1) -#define kNuFlushCorrupted (1L << 2) -#define kNuFlushReadOnly (1L << 3) -#define kNuFlushInaccessible (1L << 4) - - -/* - * =========================================================================== - * NuFX archive defintions - * =========================================================================== - */ - -typedef struct NuThreadMod NuThreadMod; /* dummy def for internal struct */ -typedef union NuDataSource NuDataSource; /* dummy def for internal struct */ -typedef union NuDataSink NuDataSink; /* dummy def for internal struct */ - -/* - * NuFX Date/Time structure; same as TimeRec from IIgs "misctool.h". - */ -typedef struct NuDateTime { - uint8_t second; /* 0-59 */ - uint8_t minute; /* 0-59 */ - uint8_t hour; /* 0-23 */ - uint8_t year; /* year - 1900 */ - uint8_t day; /* 0-30 */ - uint8_t month; /* 0-11 */ - uint8_t extra; /* (must be zero) */ - uint8_t weekDay; /* 1-7 (1=sunday) */ -} NuDateTime; - -/* - * NuFX "thread" definition. - * - * Guaranteed not to have pointers in it. Can be copied with memcpy or - * assignment. - */ -typedef struct NuThread { - /* from the archive */ - uint16_t thThreadClass; - NuThreadFormat thThreadFormat; - uint16_t thThreadKind; - uint16_t thThreadCRC; /* comp or uncomp data; see rec vers */ - uint32_t thThreadEOF; - uint32_t thCompThreadEOF; - - /* extra goodies */ - NuThreadIdx threadIdx; - uint32_t actualThreadEOF; /* disk images might be off */ - long fileOffset; /* fseek offset to data in shk */ - - /* internal use only */ - uint16_t used; /* mark as uninteresting */ -} NuThread; - -/* - * NuFX "record" definition. - * - * (Note to developers: update Nu_AddRecord if this changes.) - * - * The filenames are in Mac OS Roman format. It's arguable whether MOR - * strings should be part of the interface at all. However, the API - * pre-dates the inclusion of Unicode support, and I'm leaving it alone. - */ -#define kNufxIDLen 4 /* len of 'NuFX' with funky MSBs */ -#define kNuReasonableAttribCount 256 -#define kNuReasonableFilenameLen 1024 -#define kNuReasonableTotalThreads 16 -#define kNuMaxRecordVersion 3 /* max we can handle */ -#define kNuOurRecordVersion 3 /* what we write */ -typedef struct NuRecord { - /* version 0+ */ - uint8_t recNufxID[kNufxIDLen]; - uint16_t recHeaderCRC; - uint16_t recAttribCount; - uint16_t recVersionNumber; - uint32_t recTotalThreads; - NuFileSysID recFileSysID; - uint16_t recFileSysInfo; - uint32_t recAccess; - uint32_t recFileType; - uint32_t recExtraType; - uint16_t recStorageType; /* NuStorage*,file_sys_block_size */ - NuDateTime recCreateWhen; - NuDateTime recModWhen; - NuDateTime recArchiveWhen; - - /* option lists only in version 1+ */ - uint16_t recOptionSize; - uint8_t* recOptionList; /* NULL if v0 or recOptionSize==0 */ - - /* data specified by recAttribCount, not accounted for by option list */ - int32_t extraCount; - uint8_t* extraBytes; - - uint16_t recFilenameLength; /* usually zero */ - char* recFilenameMOR; /* doubles as disk volume_name */ - - /* extra goodies; "dirtyHeader" does not apply to anything below */ - NuRecordIdx recordIdx; /* session-unique record index */ - char* threadFilenameMOR; /* extracted from filename thread */ - char* newFilenameMOR; /* memorized during "add file" call */ - const char* filenameMOR; /* points at recFilen or threadFilen */ - uint32_t recHeaderLength; /* size of rec hdr, incl thread hdrs */ - uint32_t totalCompLength; /* total len of data in archive file */ - uint32_t fakeThreads; /* used by "MaskDataless" */ - int isBadMac; /* malformed "bad mac" header */ - - long fileOffset; /* file offset of record header */ - - /* use provided interface to access this */ - struct NuThread* pThreads; /* ptr to thread array */ - - /* private -- things the application shouldn't look at */ - struct NuRecord* pNext; /* used internally */ - NuThreadMod* pThreadMods; /* used internally */ - short dirtyHeader; /* set in "copy" when hdr fields uptd */ - short dropRecFilename; /* if set, we're dropping this name */ -} NuRecord; - -/* - * NuFX "master header" definition. - * - * The "mhReserved2" entry doesn't appear in my copy of the $e0/8002 File - * Type Note, but as best as I can recall the MH block must be 48 bytes. - */ -#define kNufileIDLen 6 /* length of 'NuFile' with funky MSBs */ -#define kNufileMasterReserved1Len 8 -#define kNufileMasterReserved2Len 6 -#define kNuMaxMHVersion 2 /* max we can handle */ -#define kNuOurMHVersion 2 /* what we write */ -typedef struct NuMasterHeader { - uint8_t mhNufileID[kNufileIDLen]; - uint16_t mhMasterCRC; - uint32_t mhTotalRecords; - NuDateTime mhArchiveCreateWhen; - NuDateTime mhArchiveModWhen; - uint16_t mhMasterVersion; - uint8_t mhReserved1[kNufileMasterReserved1Len]; - uint32_t mhMasterEOF; - uint8_t mhReserved2[kNufileMasterReserved2Len]; - - /* private -- internal use only */ - short isValid; -} NuMasterHeader; - - -/* - * =========================================================================== - * Misc declarations - * =========================================================================== - */ - -/* - * Record attributes that can be changed with NuSetRecordAttr. This is - * a small subset of the full record. - */ -typedef struct NuRecordAttr { - NuFileSysID fileSysID; - /*uint16_t fileSysInfo;*/ - uint32_t access; - uint32_t fileType; - uint32_t extraType; - NuDateTime createWhen; - NuDateTime modWhen; - NuDateTime archiveWhen; -} NuRecordAttr; - -/* - * Some additional details about a file. - * - * Ideally (from an API cleanliness perspective) the storage name would - * be passed around as UTF-8 and converted internally. Passing it as - * MOR required fewer changes to the library, and allows us to avoid - * having to deal with illegal characters. - */ -typedef struct NuFileDetails { - /* used during AddFile call */ - NuThreadID threadID; /* data, rsrc, disk img? */ - const void* origName; /* arbitrary pointer, usually a string */ - - /* these go straight into the NuRecord */ - const char* storageNameMOR; - NuFileSysID fileSysID; - uint16_t fileSysInfo; - uint32_t access; - uint32_t fileType; - uint32_t extraType; - uint16_t storageType; /* use Unknown, or disk block size */ - NuDateTime createWhen; - NuDateTime modWhen; - NuDateTime archiveWhen; -} NuFileDetails; - - -/* - * Passed into the SelectionFilter callback. - */ -typedef struct NuSelectionProposal { - const NuRecord* pRecord; - const NuThread* pThread; -} NuSelectionProposal; - -/* - * Passed into the OutputPathnameFilter callback. - */ -typedef struct NuPathnameProposal { - const UNICHAR* pathnameUNI; - UNICHAR filenameSeparator; - const NuRecord* pRecord; - const NuThread* pThread; - - const UNICHAR* newPathnameUNI; - UNICHAR newFilenameSeparator; - /*NuThreadID newStorage;*/ - NuDataSink* newDataSink; -} NuPathnameProposal; - - -/* used by error handler and progress updater to indicate what we're doing */ -typedef enum NuOperation { - kNuOpUnknown = 0, - kNuOpAdd, - kNuOpExtract, - kNuOpTest, - kNuOpDelete, /* not used for progress updates */ - kNuOpContents /* not used for progress updates */ -} NuOperation; - -/* state of progress when adding or extracting */ -typedef enum NuProgressState { - kNuProgressPreparing, /* not started yet */ - kNuProgressOpening, /* opening files */ - - kNuProgressAnalyzing, /* analyzing data */ - kNuProgressCompressing, /* compressing data */ - kNuProgressStoring, /* storing (no compression) data */ - kNuProgressExpanding, /* expanding data */ - kNuProgressCopying, /* copying data (in or out) */ - - kNuProgressDone, /* all done, success */ - kNuProgressSkipped, /* all done, we skipped this one */ - kNuProgressAborted, /* all done, user cancelled the operation */ - kNuProgressFailed /* all done, failure */ -} NuProgressState; - -/* - * Passed into the ProgressUpdater callback. All pointers become - * invalid when the callback returns. - * - * [ Thought for the day: add an optional flag that causes us to only - * call the progressFunc when the "percentComplete" changes by more - * than a specified amount. ] - */ -typedef struct NuProgressData { - /* what are we doing */ - NuOperation operation; - /* what specifically are we doing */ - NuProgressState state; - /* how far along are we */ - short percentComplete; /* 0-100 */ - - /* original pathname (in archive for expand, on disk for compress) */ - const UNICHAR* origPathnameUNI; - /* processed pathname (PathnameFilter for expand, in-record for compress) */ - const UNICHAR* pathnameUNI; - /* basename of "pathname" (for convenience) */ - const UNICHAR* filenameUNI; - /* pointer to the record we're expanding from */ - const NuRecord* pRecord; - - uint32_t uncompressedLength; /* size of uncompressed data */ - uint32_t uncompressedProgress; /* #of bytes in/out */ - - struct { - NuThreadFormat threadFormat; /* compression being applied */ - } compress; - - struct { - uint32_t totalCompressedLength; /* all "data" threads */ - uint32_t totalUncompressedLength; - - /*uint32_t compressedLength; * size of compressed data */ - /*uint32_t compressedProgress; * #of compressed bytes in/out*/ - const NuThread* pThread; /* thread we're working on */ - NuValue convertEOL; /* set if LF/CR conv is on */ - } expand; - - /* pay no attention */ - NuCallback progressFunc; -} NuProgressData; - -/* - * Passed into the ErrorHandler callback. - */ -typedef struct NuErrorStatus { - NuOperation operation; /* were we adding, extracting, ?? */ - NuError err; /* library error code */ - int sysErr; /* system error code, if applicable */ - const UNICHAR* message; /* (optional) message to user */ - const NuRecord* pRecord; /* relevant record, if any */ - const UNICHAR* pathnameUNI; /* problematic pathname, if any */ - const void* origPathname; /* original pathname ref, if any */ - UNICHAR filenameSeparator; /* fssep for pathname, if any */ - /*char origArchiveTouched;*/ - - char canAbort; /* give option to abort */ - /*char canAbortAll;*/ /* abort + discard all recent changes */ - char canRetry; /* give option to retry same op */ - char canIgnore; /* give option to ignore error */ - char canSkip; /* give option to skip this file/rec */ - char canRename; /* give option to rename file */ - char canOverwrite; /* give option to overwrite file */ -} NuErrorStatus; - -/* - * Error message callback gets one of these. - */ -typedef struct NuErrorMessage { - const char* message; /* the message itself (UTF-8) */ - NuError err; /* relevant error code (may be none) */ - short isDebug; /* set for debug-only messages */ - - /* these identify where the message originated if lib built w/debug set */ - const char* file; /* source file (UTF-8) */ - int line; /* line number */ - const char* function; /* function name (might be NULL) */ -} NuErrorMessage; - - -/* - * Options for the NuTestFeature function. - */ -typedef enum NuFeature { - kNuFeatureUnknown = 0, - - kNuFeatureCompressSQ = 1, /* kNuThreadFormatHuffmanSQ */ - kNuFeatureCompressLZW = 2, /* kNuThreadFormatLZW1 and LZW2 */ - kNuFeatureCompressLZC = 3, /* kNuThreadFormatLZC12 and LZC16 */ - kNuFeatureCompressDeflate = 4, /* kNuThreadFormatDeflate */ - kNuFeatureCompressBzip2 = 5, /* kNuThreadFormatBzip2 */ -} NuFeature; - - -/* - * =========================================================================== - * Function prototypes - * =========================================================================== - */ - -/* - * Win32 dll magic. - */ -#if defined(_WIN32) -# include -# if defined(NUFXLIB_EXPORTS) - /* building the NufxLib DLL */ -# define NUFXLIB_API __declspec(dllexport) -# elif defined (NUFXLIB_DLL) - /* building to link against the NufxLib DLL */ -# define NUFXLIB_API __declspec(dllimport) -# else - /* using static libs */ -# define NUFXLIB_API -# endif -#else - /* not using Win32... hooray! */ -# define NUFXLIB_API -#endif - -/* streaming and non-streaming read-only interfaces */ -NUFXLIB_API NuError NuStreamOpenRO(FILE* infp, NuArchive** ppArchive); -NUFXLIB_API NuError NuContents(NuArchive* pArchive, NuCallback contentFunc); -NUFXLIB_API NuError NuExtract(NuArchive* pArchive); -NUFXLIB_API NuError NuTest(NuArchive* pArchive); - -/* strictly non-streaming read-only interfaces */ -NUFXLIB_API NuError NuOpenRO(const UNICHAR* archivePathnameUNI, - NuArchive** ppArchive); -NUFXLIB_API NuError NuExtractRecord(NuArchive* pArchive, NuRecordIdx recordIdx); -NUFXLIB_API NuError NuExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSink* pDataSink); -NUFXLIB_API NuError NuTestRecord(NuArchive* pArchive, NuRecordIdx recordIdx); -NUFXLIB_API NuError NuGetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecord** ppRecord); -NUFXLIB_API NuError NuGetRecordIdxByName(NuArchive* pArchive, - const char* nameMOR, NuRecordIdx* pRecordIdx); -NUFXLIB_API NuError NuGetRecordIdxByPosition(NuArchive* pArchive, - uint32_t position, NuRecordIdx* pRecordIdx); - -/* read/write interfaces */ -NUFXLIB_API NuError NuOpenRW(const UNICHAR* archivePathnameUNI, - const UNICHAR* tempPathnameUNI, uint32_t flags, - NuArchive** ppArchive); -NUFXLIB_API NuError NuFlush(NuArchive* pArchive, uint32_t* pStatusFlags); -NUFXLIB_API NuError NuAddRecord(NuArchive* pArchive, - const NuFileDetails* pFileDetails, NuRecordIdx* pRecordIdx); -NUFXLIB_API NuError NuAddThread(NuArchive* pArchive, NuRecordIdx recordIdx, - NuThreadID threadID, NuDataSource* pDataSource, - NuThreadIdx* pThreadIdx); -NUFXLIB_API NuError NuAddFile(NuArchive* pArchive, const UNICHAR* pathnameUNI, - const NuFileDetails* pFileDetails, short fromRsrcFork, - NuRecordIdx* pRecordIdx); -NUFXLIB_API NuError NuRename(NuArchive* pArchive, NuRecordIdx recordIdx, - const char* pathnameMOR, char fssep); -NUFXLIB_API NuError NuSetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecordAttr* pRecordAttr); -NUFXLIB_API NuError NuUpdatePresizedThread(NuArchive* pArchive, - NuThreadIdx threadIdx, NuDataSource* pDataSource, int32_t* pMaxLen); -NUFXLIB_API NuError NuDelete(NuArchive* pArchive); -NUFXLIB_API NuError NuDeleteRecord(NuArchive* pArchive, NuRecordIdx recordIdx); -NUFXLIB_API NuError NuDeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx); - -/* general interfaces */ -NUFXLIB_API NuError NuClose(NuArchive* pArchive); -NUFXLIB_API NuError NuAbort(NuArchive* pArchive); -NUFXLIB_API NuError NuGetMasterHeader(NuArchive* pArchive, - const NuMasterHeader** ppMasterHeader); -NUFXLIB_API NuError NuGetExtraData(NuArchive* pArchive, void** ppData); -NUFXLIB_API NuError NuSetExtraData(NuArchive* pArchive, void* pData); -NUFXLIB_API NuError NuGetValue(NuArchive* pArchive, NuValueID ident, - NuValue* pValue); -NUFXLIB_API NuError NuSetValue(NuArchive* pArchive, NuValueID ident, - NuValue value); -NUFXLIB_API NuError NuGetAttr(NuArchive* pArchive, NuAttrID ident, - NuAttr* pAttr); -NUFXLIB_API NuError NuDebugDumpArchive(NuArchive* pArchive); - -/* sources and sinks */ -NUFXLIB_API NuError NuCreateDataSourceForFile(NuThreadFormat threadFormat, - uint32_t otherLen, const UNICHAR* pathnameUNI, - short isFromRsrcFork, NuDataSource** ppDataSource); -NUFXLIB_API NuError NuCreateDataSourceForFP(NuThreadFormat threadFormat, - uint32_t otherLen, FILE* fp, long offset, long length, - NuCallback closeFunc, NuDataSource** ppDataSource); -NUFXLIB_API NuError NuCreateDataSourceForBuffer(NuThreadFormat threadFormat, - uint32_t otherLen, const uint8_t* buffer, long offset, - long length, NuCallback freeFunc, NuDataSource** ppDataSource); -NUFXLIB_API NuError NuFreeDataSource(NuDataSource* pDataSource); -NUFXLIB_API NuError NuDataSourceSetRawCrc(NuDataSource* pDataSource, - uint16_t crc); -NUFXLIB_API NuError NuCreateDataSinkForFile(short doExpand, NuValue convertEOL, - const UNICHAR* pathnameUNI, UNICHAR fssep, NuDataSink** ppDataSink); -NUFXLIB_API NuError NuCreateDataSinkForFP(short doExpand, NuValue convertEOL, - FILE* fp, NuDataSink** ppDataSink); -NUFXLIB_API NuError NuCreateDataSinkForBuffer(short doExpand, - NuValue convertEOL, uint8_t* buffer, uint32_t bufLen, - NuDataSink** ppDataSink); -NUFXLIB_API NuError NuFreeDataSink(NuDataSink* pDataSink); -NUFXLIB_API NuError NuDataSinkGetOutCount(NuDataSink* pDataSink, - uint32_t* pOutCount); - -/* miscellaneous non-archive operations */ -NUFXLIB_API NuError NuGetVersion(int32_t* pMajorVersion, int32_t* pMinorVersion, - int32_t* pBugVersion, const char** ppBuildDate, - const char** ppBuildFlags); -NUFXLIB_API const char* NuStrError(NuError err); -NUFXLIB_API NuError NuTestFeature(NuFeature feature); -NUFXLIB_API void NuRecordCopyAttr(NuRecordAttr* pRecordAttr, - const NuRecord* pRecord); -NUFXLIB_API NuError NuRecordCopyThreads(const NuRecord* pRecord, - NuThread** ppThreads); -NUFXLIB_API uint32_t NuRecordGetNumThreads(const NuRecord* pRecord); -NUFXLIB_API const NuThread* NuThreadGetByIdx(const NuThread* pThread, - int32_t idx); -NUFXLIB_API short NuIsPresizedThreadID(NuThreadID threadID); -NUFXLIB_API size_t NuConvertMORToUNI(const char* stringMOR, - UNICHAR* bufUNI, size_t bufSize); -NUFXLIB_API size_t NuConvertUNIToMOR(const UNICHAR* stringUNI, - char* bufMOR, size_t bufSize); - -#define NuGetThread(pRecord, idx) ( (const NuThread*) \ - ((uint32_t) (idx) < (pRecord)->recTotalThreads ? \ - &(pRecord)->pThreads[(idx)] : NULL) \ - ) - - -/* callback setters */ -#define kNuInvalidCallback ((NuCallback) 1) -NUFXLIB_API NuCallback NuSetSelectionFilter(NuArchive* pArchive, - NuCallback filterFunc); -NUFXLIB_API NuCallback NuSetOutputPathnameFilter(NuArchive* pArchive, - NuCallback filterFunc); -NUFXLIB_API NuCallback NuSetProgressUpdater(NuArchive* pArchive, - NuCallback updateFunc); -NUFXLIB_API NuCallback NuSetErrorHandler(NuArchive* pArchive, - NuCallback errorFunc); -NUFXLIB_API NuCallback NuSetErrorMessageHandler(NuArchive* pArchive, - NuCallback messageHandlerFunc); -NUFXLIB_API NuCallback NuSetGlobalErrorMessageHandler(NuCallback messageHandlerFunc); - - -#ifdef __cplusplus -} -#endif - -#endif /*NUFXLIB_NUFXLIB_H*/ diff --git a/ciderpress/nufxlib/NufxLibPriv.h b/ciderpress/nufxlib/NufxLibPriv.h deleted file mode 100644 index c18a4b7..0000000 --- a/ciderpress/nufxlib/NufxLibPriv.h +++ /dev/null @@ -1,861 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Global internal declarations and definitions. - */ -#ifndef NUFXLIB_NUFXLIBPRIV_H -#define NUFXLIB_NUFXLIBPRIV_H - -/* include files that everybody needs */ -#include "SysDefs.h" -#include "NufxLib.h" -#include "MiscStuff.h" - -#ifdef USE_DMALLOC -/* enable with something like "dmalloc -l logfile -i 100 medium" */ -# include "dmalloc.h" -#endif - - -/* - * =========================================================================== - * NuArchive definition - * =========================================================================== - */ - -/* - * Archives can be opened in streaming read-only, non-streaming read-only, - * and non-streaming read-write mode. - */ -typedef enum NuOpenMode { - kNuOpenUnknown, - kNuOpenStreamingRO, - kNuOpenRO, - kNuOpenRW -} NuOpenMode; -#define Nu_IsStreaming(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO) -#define Nu_IsReadOnly(pArchive) ((pArchive)->openMode == kNuOpenStreamingRO ||\ - (pArchive)->openMode == kNuOpenRO) - -#ifdef FOPEN_WANTS_B -# define kNuFileOpenReadOnly "rb" -# define kNuFileOpenReadWrite "r+b" -# define kNuFileOpenWriteTrunc "wb" -# define kNuFileOpenReadWriteCreat "w+b" -#else -# define kNuFileOpenReadOnly "r" -# define kNuFileOpenReadWrite "r+" -# define kNuFileOpenWriteTrunc "w" -# define kNuFileOpenReadWriteCreat "w+" -#endif - - -/* - * Some NuFX and Binary II definitions. - */ -#define kNuMasterHeaderSize 48 /* size of fixed-length master header */ -#define kNuRecordHeaderBaseSize 58 /* size of rec hdr up to variable stuff */ -#define kNuThreadHeaderSize 16 /* size of fixed-length thread header */ -#define kNuDefaultFilenameThreadSize 32 /* default size of filename thred */ -#define kNuDefaultCommentSize 200 /* size of GSHK-mimic comments */ -#define kNuBinary2BlockSize 128 /* size of bxy header and padding */ -#define kNuSEAOffset 0x2ee5 /* fixed(??) offset to data in SEA */ - -#define kNuInitialChunkCRC 0x0000 /* start for CRC in LZW/1 chunk */ -#define kNuInitialThreadCRC 0xffff /* start for CRC in v3 thread header */ - -/* size of general-purpose compression buffer */ -#define kNuGenCompBufSize 32768 - -#define kNuCharLF 0x0a -#define kNuCharCR 0x0d - - -/* - * A list of records. Generally we use one of these for read-only - * archives, and two for read-write. - * - * The "loaded" flag is set when we've made some use of the record set. - * Relying on "numRecords" won't always work; for example, if the "copy" - * record set was initialized from "orig", and then had all of its records - * deleted, you couldn't look at "numRecords" and decide whether it was - * appropriate to use "orig" or not. - */ -typedef struct NuRecordSet { - Boolean loaded; - uint32_t numRecords; - NuRecord* nuRecordHead; - NuRecord* nuRecordTail; -} NuRecordSet; - -/* - * Archive state. - */ -struct NuArchive { - uint32_t structMagic; - Boolean busy; - - NuOpenMode openMode; - Boolean newlyCreated; - UNICHAR* archivePathnameUNI; /* pathname or "(stream)" */ - FILE* archiveFp; - NuArchiveType archiveType; - - /* stuff before NuFX; both offsets are from 0, i.e. hdrOff includes junk */ - long junkOffset; /* skip past leading junk */ - long headerOffset; /* adjustment for BXY/SEA/BSE */ - - UNICHAR* tmpPathnameUNI; /* temp file, for writes */ - FILE* tmpFp; - - /* used during initial processing; helps avoid ftell() calls */ - long currentOffset; - - /* setting this changes Extract into Test */ - Boolean testMode; - - /* clumsy way of remembering name used for other fork in forked file */ - const UNICHAR* lastFileCreatedUNI; - /* clumsy way to avoid trying to create the same subdir several times */ - const UNICHAR* lastDirCreatedUNI; - - /* master header from the archive */ - NuMasterHeader masterHeader; /* original */ - NuMasterHeader newMasterHeader; /* working copy during update */ - - /* list of records from archive, plus some extra state */ - NuRecordIdx recordIdxSeed; /* where the NuRecordIdxs start */ - NuRecordIdx nextRecordIdx; /* next record gets this value */ - Boolean haveToc; /* set if we have full TOC */ - NuRecordSet origRecordSet; /* records from archive */ - NuRecordSet copyRecordSet; /* copy of orig, for write ops */ - NuRecordSet newRecordSet; /* newly-added records */ - - /* state for compression functions */ - uint8_t* compBuf; /* large general-purpose buffer */ - void* lzwCompressState; /* state for LZW/1 and LZW/2 */ - void* lzwExpandState; /* state for LZW/1 and LZW/2 */ - - /* options and attributes that the user can set */ - /* (these can be changed by a callback, so don't cache them internally) */ - void* extraData; /* application-defined pointer */ - - NuValue valAllowDuplicates; /* allow dups when adding? */ - NuValue valConvertExtractedEOL; /* convert EOL during extract? */ - NuValue valDataCompression; /* how to compress when adding? */ - NuValue valDiscardWrapper; /* remove BNY or SEA header? */ - NuValue valEOL; /* EOL value to convert to */ - NuValue valHandleExisting; /* how to deal with existing files*/ - NuValue valIgnoreCRC; /* don't compute or test CRCs */ - NuValue valMaskDataless; /* alter Records w/o data threads */ - NuValue valMimicSHK; /* mimic some ShrinkIt quirks */ - NuValue valModifyOrig; /* modify original arc in place? */ - NuValue valOnlyUpdateOlder; /* modify original arc in place? */ - NuValue valStripHighASCII; /* during EOL conv, strip hi bit? */ - NuValue valJunkSkipMax; /* scan this far for header */ - NuValue valIgnoreLZW2Len; /* don't verify LZW/II len field */ - NuValue valHandleBadMac; /* handle "bad Mac" archives */ - - /* callback functions */ - NuCallback selectionFilterFunc; - NuCallback outputPathnameFunc; - NuCallback progressUpdaterFunc; - NuCallback errorHandlerFunc; - NuCallback messageHandlerFunc; -}; - -#define kNuArchiveStructMagic 0xc0edbabe - -#define kNuDefaultRecordName "UNKNOWN" /* use ASCII charset */ - - -/* - * =========================================================================== - * ThreadMod definition - * =========================================================================== - */ - -/* operations we can perform on threads in a record */ -typedef enum ThreadModKind { - kNuThreadModUnknown = 0, - - kNuThreadModAdd, - kNuThreadModUpdate, - kNuThreadModDelete -} ThreadModKind; - -/* - * We attach a list of these to records we plan to modify. Care is taken - * to ensure that they don't conflict, e.g. you can't update a thread - * right after you delete it, nor delete one you have modified. - */ -struct NuThreadMod { - union { - ThreadModKind kind; - - struct { - ThreadModKind kind; - Boolean used; - } generic; - - struct { - ThreadModKind kind; - Boolean used; - Boolean isPresized; - NuThreadIdx threadIdx; - NuThreadID threadID; - NuThreadFormat threadFormat; - NuDataSource* pDataSource; - } add; - - struct { - ThreadModKind kind; - Boolean used; - NuThreadIdx threadIdx; - NuDataSource* pDataSource; - } update; - - struct { - ThreadModKind kind; - Boolean used; - NuThreadIdx threadIdx; - NuThreadID threadID; /* used for watching filename threads */ - } delete; - } entry; - - struct NuThreadMod* pNext; -}; - - -/* - * =========================================================================== - * NuFunnel/NuStraw definition - * =========================================================================== - */ - -#define kNuFunnelBufSize 16384 - -/* - * File funnel definition. This is used for writing output to files - * (so we can do things like pipe compressed output through an LF->CR - * converter) and archive files (so we can halt compression when the - * output size exceeds the uncompressed original). [ for various reasons, - * I'm not using this on the archive anymore. ] - * - * Funnels are unidirectional. You write data into them with a - * function call; the top-level action (which is usually compressing or - * expanding data) reads from the input and crams things into the pipe. - * We could fully abstract the concept, and write the compression - * functions so that they operate as a Funnel filter, but it's much - * easier to write block-oriented compression than stream-oriented (and - * more to the point, the ShrinkIt LZW functions are very much - * block-oriented). - */ -typedef struct NuFunnel { - /* data storage */ - uint8_t* buffer; /* kNuFunnelBufSize worth of storage */ - long bufCount; /* #of bytes in buffer */ - - /* text conversion; if "auto", on first flush we convert to "on" or "off" */ - NuValue convertEOL; /* on/off/auto */ - NuValue convertEOLTo; /* EOL to switch to */ - NuValue convertEOLFrom; /* EOL terminator we think we found */ - Boolean checkStripHighASCII; /* do we want to check for it? */ - Boolean doStripHighASCII; /* strip high ASCII during EOL conv */ - Boolean lastCR; /* was last char a CR? */ - - Boolean isFirstWrite; /* cleared on first write */ - -#if 0 - uint32_t inCount; /* total #of bytes in the top */ - uint32_t outCount; /* total #of bytes out the bottom */ - - uint32_t outMax; /* flag an err when outCount exceeds this */ - Boolean outMaxExceeded; /* in fact, it's this flag */ -#endif - - /* update this when stuff happens */ - NuProgressData* pProgress; - - /* data goeth out here */ - NuDataSink* pDataSink; -} NuFunnel; - - -/* - * File straw definition. This is used for slurping up input data. - * - * Mostly this is an encapsulation of an input source and a progress - * updater, useful for reading uncompressed data and feeding it to a - * compressor. It doesn't make sense to show a thermometer based on - * compressed output, since we don't know how big the eventual result - * will be, so we want to do it for the input. - */ -typedef struct NuStraw { - /* update this when stuff happens */ - NuProgressData* pProgress; - - /* data cometh in here */ - NuDataSource* pDataSource; - - /* progress update fields */ - uint32_t lastProgress; - uint32_t lastDisplayed; -} NuStraw; - -/*NuError Nu_CopyStreamToStream(FILE* outfp, FILE* infp, uint32_t count);*/ - - -/* - * =========================================================================== - * Data source and sink abstractions - * =========================================================================== - */ - -/* - * DataSource is used when adding data to an archive. - */ - -typedef enum NuDataSourceType { - kNuDataSourceUnknown = 0, - kNuDataSourceFromFile, - kNuDataSourceFromFP, - kNuDataSourceFromBuffer -} NuDataSourceType; - -typedef struct NuDataSourceCommon { - NuDataSourceType sourceType; - NuThreadFormat threadFormat; /* is it already compressed? */ - uint16_t rawCrc; /* crc for already-compressed data*/ - /*Boolean doClose; \* close on completion? */ - uint32_t dataLen; /* length of data (var for buf) */ - uint32_t otherLen; /* uncomp len or preset buf size */ - int refCount; /* so we can copy structs */ -} NuDataSourceCommon; - -union NuDataSource { - NuDataSourceType sourceType; - - NuDataSourceCommon common; - - struct { - NuDataSourceCommon common; - UNICHAR* pathnameUNI; - Boolean fromRsrcFork; - - /* temp storage; only valid when processing in library */ - FILE* fp; - } fromFile; - - struct { - NuDataSourceCommon common; - FILE* fp; - long offset; /* starting offset */ - - NuCallback fcloseFunc; /* how to fclose the file */ - } fromFP; - - struct { - NuDataSourceCommon common; - const uint8_t* buffer; /* non-const if doClose=true */ - long offset; /* starting offset */ - - long curOffset; /* current offset */ - long curDataLen; /* remaining data */ - - NuCallback freeFunc; /* how to free data */ - } fromBuffer; -}; - - -/* - * DataSink is used when extracting data from an archive. - */ - -typedef enum NuDataSinkType { - kNuDataSinkUnknown = 0, - kNuDataSinkToFile, - kNuDataSinkToFP, - kNuDataSinkToBuffer, - kNuDataSinkToVoid -} NuDataSinkType; - -typedef struct NuDataSinkCommon { - NuDataSinkType sinkType; - Boolean doExpand; /* expand file? */ - NuValue convertEOL; /* convert EOL? (req "expand") */ - uint32_t outCount; -} NuDataSinkCommon; - -union NuDataSink { - NuDataSinkType sinkType; - - NuDataSinkCommon common; - - struct { - NuDataSinkCommon common; - UNICHAR* pathnameUNI; /* file to open */ - UNICHAR fssep; - - /* temp storage; must be NULL except when processing in library */ - FILE* fp; - } toFile; - - struct { - NuDataSinkCommon common; - FILE* fp; - } toFP; - - struct { - NuDataSinkCommon common; - uint8_t* buffer; - uint32_t bufLen; /* max amount of data "buffer" holds */ - NuError stickyErr; - } toBuffer; -}; - - -/* - * =========================================================================== - * Function prototypes - * =========================================================================== - */ - -/* - * This is a little unpleasant. This blob of stuff gets stuffed in as - * the first arguments to Nu_ReportError, so we don't have to type them - * in every time we use the function. It would've been much easier to - * use a gcc-style varargs macro, but not everybody uses gcc. - * - * TODO: Visual C++ has vararg macros now; time to replace this. - */ -#ifdef HAS__FUNCTION__ -# define _FUNCTION_ __FUNCTION__ -#else -# define _FUNCTION_ "" -#endif - -#define NU_BLOB pArchive, __FILE__, __LINE__, _FUNCTION_, false -#define NU_BLOB_DEBUG pArchive, __FILE__, __LINE__, _FUNCTION_, true -#define NU_NILBLOB NULL, __FILE__, __LINE__, _FUNCTION_, false - -#ifdef DEBUG_MSGS -# define DebugShowError(err) \ - Nu_ReportError(pArchive, __FILE__, __LINE__, _FUNCTION_, \ - true, err, "(DEBUG)"); -#else -# define DebugShowError(err) ((void)0) -#endif - -/* - * The BailError macro serves two purposes. First, it's a convenient - * way to avoid typing, "if (err != kNuErrNone) goto bail;". Second, - * when the library is built with debugging enabled, it vitually gives - * us a stack trace of exiting functions. This makes it easier to debug - * problems sent in as screen dumps via e-mail. - */ -#define BailError(err) { \ - if ((err) != kNuErrNone) { \ - /* [should this be debug-only, or all the time?] */ \ - DebugShowError(err); \ - goto bail; \ - } \ - } -#define BailErrorQuiet(err) { \ - if ((err) != kNuErrNone) \ - goto bail; \ - } -#define BailNil(val) { \ - if ((val) == NULL) { \ - err = kNuErrUnexpectedNil; \ - BailError(err); \ - } \ - } -#define BailAlloc(val) { \ - if ((val) == NULL) { \ - err = kNuErrMalloc; \ - BailError(err); \ - } \ - } - - -/* - * Internal function prototypes and inline functions. - */ - -/* Archive.c */ -void Nu_MasterHeaderCopy(NuArchive* pArchive, NuMasterHeader* pDstHeader, - const NuMasterHeader* pSrcHeader); -NuError Nu_GetMasterHeader(NuArchive* pArchive, - const NuMasterHeader** ppMasterHeader); -NuRecordIdx Nu_GetNextRecordIdx(NuArchive* pArchive); -NuThreadIdx Nu_GetNextThreadIdx(NuArchive* pArchive); -NuError Nu_CopyWrapperToTemp(NuArchive* pArchive); -NuError Nu_UpdateWrapper(NuArchive* pArchive, FILE* fp); -NuError Nu_AdjustWrapperPadding(NuArchive* pArchive, FILE* fp); -NuError Nu_AllocCompressionBufferIFN(NuArchive* pArchive); -NuError Nu_StreamOpenRO(FILE* infp, NuArchive** ppArchive); -NuError Nu_OpenRO(const UNICHAR* archivePathnameUNI, NuArchive** ppArchive); -NuError Nu_OpenRW(const UNICHAR* archivePathnameUNI, - const UNICHAR* tempPathnameUNI, uint32_t flags, NuArchive** ppArchive); -NuError Nu_WriteMasterHeader(NuArchive* pArchive, FILE* fp, - NuMasterHeader* pMasterHeader); -NuError Nu_Close(NuArchive* pArchive); -NuError Nu_Abort(NuArchive* pArchive); -NuError Nu_RenameTempToArchive(NuArchive* pArchive); -NuError Nu_DeleteArchiveFile(NuArchive* pArchive); - -/* ArchiveIO.c */ -uint8_t Nu_ReadOneC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc); -uint8_t Nu_ReadOne(NuArchive* pArchive, FILE* fp); -void Nu_WriteOneC(NuArchive* pArchive, FILE* fp, uint8_t val, uint16_t* pCrc); -void Nu_WriteOne(NuArchive* pArchive, FILE* fp, uint8_t val); -uint16_t Nu_ReadTwoC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc); -uint16_t Nu_ReadTwo(NuArchive* pArchive, FILE* fp); -void Nu_WriteTwoC(NuArchive* pArchive, FILE* fp, uint16_t val, uint16_t* pCrc); -void Nu_WriteTwo(NuArchive* pArchive, FILE* fp, uint16_t val); -uint32_t Nu_ReadFourC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc); -uint32_t Nu_ReadFour(NuArchive* pArchive, FILE* fp); -void Nu_WriteFourC(NuArchive* pArchive, FILE* fp, uint32_t val, uint16_t* pCrc); -void Nu_WriteFour(NuArchive* pArchive, FILE* fp, uint32_t val); -NuDateTime Nu_ReadDateTimeC(NuArchive* pArchive, FILE* fp, uint16_t* pCrc); -NuDateTime Nu_ReadDateTime(NuArchive* pArchive, FILE* fp, uint16_t* pCrc); -void Nu_WriteDateTimeC(NuArchive* pArchive, FILE* fp, NuDateTime dateTime, - uint16_t* pCrc); -void Nu_WriteDateTime(NuArchive* pArchive, FILE* fp, NuDateTime dateTime); -void Nu_ReadBytesC(NuArchive* pArchive, FILE* fp, void* vbuffer, long count, - uint16_t* pCrc); -void Nu_ReadBytes(NuArchive* pArchive, FILE* fp, void* vbuffer, long count); -void Nu_WriteBytesC(NuArchive* pArchive, FILE* fp, const void* vbuffer, - long count, uint16_t* pCrc); -void Nu_WriteBytes(NuArchive* pArchive, FILE* fp, const void* vbuffer, - long count); -NuError Nu_HeaderIOFailed(NuArchive* pArchive, FILE* fp); -NuError Nu_SeekArchive(NuArchive* pArchive, FILE* fp, long offset, - int ptrname); -NuError Nu_RewindArchive(NuArchive* pArchive); - -/* Bzip2.c */ -NuError Nu_CompressBzip2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_ExpandBzip2(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc); - -/* Charset.c */ -size_t Nu_ConvertMORToUNI(const char* stringMOR, UNICHAR* bufUNI, - size_t bufSize); -UNICHAR* Nu_CopyMORToUNI(const char* stringMOR); -size_t Nu_ConvertUNIToMOR(const UNICHAR* stringUNI, char* bufMOR, - size_t bufSize); - -/* Compress.c */ -NuError Nu_CompressToArchive(NuArchive* pArchive, NuDataSource* pDataSource, - NuThreadID threadID, NuThreadFormat sourceFormat, - NuThreadFormat targetFormat, NuProgressData* progressData, FILE* dstFp, - NuThread* pThread); -NuError Nu_CopyPresizedToArchive(NuArchive* pArchive, - NuDataSource* pDataSource, NuThreadID threadID, FILE* dstFp, - NuThread* pThread, char** ppSavedCopy); - -/* Crc16.c */ -extern const uint16_t gNuCrc16Table[256]; -uint16_t Nu_CalcCRC16(uint16_t seed, const uint8_t* ptr, int count); -/* - * Update the CRC-16. - * - * _val (uint8_t) is the byte to add to the CRC. It's evaluated once. - * _crc (uint16_t) is the previous CRC. It's evaluated twice. - * Returns the updated CRC as a uint16_t. - */ -#define Nu_UpdateCRC16(_val, _crc) \ - (gNuCrc16Table[(((_crc) >> 8) & 0xff) ^ (_val)] ^ ((_crc) << 8)) - -/* Debug.c */ -#if defined(DEBUG_MSGS) || !defined(NDEBUG) -void Nu_DebugDumpAll(NuArchive* pArchive); -void Nu_DebugDumpThread(const NuThread* pThread); -#endif - -/* Deferred.c */ -NuError Nu_ThreadModAdd_New(NuArchive* pArchive, NuThreadID threadID, - NuThreadFormat threadFormat, NuDataSource* pDataSource, - NuThreadMod** ppThreadMod); -NuError Nu_ThreadModUpdate_New(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSource* pDataSource, NuThreadMod** ppThreadMod); -NuError Nu_ThreadModDelete_New(NuArchive* pArchive, NuThreadIdx threadIdx, - NuThreadID threadID, NuThreadMod** ppThreadMod); -void Nu_ThreadModFree(NuArchive* pArchive, NuThreadMod* pThreadMod); -NuError Nu_ThreadModAdd_FindByThreadID(const NuRecord* pRecord, - NuThreadID threadID, NuThreadMod** ppThreadMod); -void Nu_FreeThreadMods(NuArchive* pArchive, NuRecord* pRecord); -NuThreadMod* Nu_ThreadMod_FindByThreadIdx(const NuRecord* pRecord, - NuThreadIdx threadIdx); -NuError Nu_Flush(NuArchive* pArchive, uint32_t* pStatusFlags); - -/* Deflate.c */ -NuError Nu_CompressDeflate(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_ExpandDeflate(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc); - -/* Expand.c */ -NuError Nu_ExpandStream(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel); - -/* FileIO.c */ -void Nu_SetCurrentDateTime(NuDateTime* pDateTime); -Boolean Nu_IsOlder(const NuDateTime* pWhen1, const NuDateTime* pWhen2); -NuError Nu_OpenOutputFile(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, const UNICHAR* newPathnameUNI, UNICHAR newFssep, - FILE** pFp); -NuError Nu_CloseOutputFile(NuArchive* pArchive, const NuRecord* pRecord, - FILE* fp, const UNICHAR* pathnameUNI); -NuError Nu_OpenInputFile(NuArchive* pArchive, const UNICHAR* pathnameUNI, - Boolean openRsrc, FILE** pFp); -NuError Nu_DeleteFile(const UNICHAR* pathnameUNI); -NuError Nu_RenameFile(const UNICHAR* fromPathUNI, const UNICHAR* toPathUNI); -NuError Nu_FTell(FILE* fp, long* pOffset); -NuError Nu_FSeek(FILE* fp, long offset, int ptrname); -NuError Nu_FRead(FILE* fp, void* buf, size_t nbyte); -NuError Nu_FWrite(FILE* fp, const void* buf, size_t nbyte); -NuError Nu_CopyFileSection(NuArchive* pArchive, FILE* dstFp, FILE* srcFp, - long length); -NuError Nu_GetFileLength(NuArchive* pArchive, FILE* fp, long* pLength); -NuError Nu_TruncateOpenFile(FILE* fp, long length); - -/* Funnel.c */ -NuError Nu_ProgressDataInit_Compress(NuArchive* pArchive, - NuProgressData* pProgressData, const NuRecord* pRecord, - const UNICHAR* origPathnameUNI, const UNICHAR* pathnameUNI); -NuError Nu_ProgressDataInit_Expand(NuArchive* pArchive, - NuProgressData* pProgressData, const NuRecord* pRecord, - const UNICHAR* newPathnameUNI, UNICHAR newFssep, - const UNICHAR* origPathnameUNI, NuValue convertEOL); -NuError Nu_SendInitialProgress(NuArchive* pArchive, NuProgressData* pProgress); - -NuError Nu_FunnelNew(NuArchive* pArchive, NuDataSink* pDataSink, - NuValue convertEOL, NuValue convertEOLTo, NuProgressData* pProgress, - NuFunnel** ppFunnel); -NuError Nu_FunnelFree(NuArchive* pArchive, NuFunnel* pFunnel); -/*void Nu_FunnelSetMaxOutput(NuFunnel* pFunnel, uint32_t maxBytes);*/ -NuError Nu_FunnelWrite(NuArchive* pArchive, NuFunnel* pFunnel, - const uint8_t* buffer, uint32_t count); -NuError Nu_FunnelFlush(NuArchive* pArchive, NuFunnel* pFunnel); -NuError Nu_ProgressDataCompressPrep(NuArchive* pArchive, NuStraw* pStraw, - NuThreadFormat threadFormat, uint32_t sourceLen); -NuError Nu_ProgressDataExpandPrep(NuArchive* pArchive, NuFunnel* pFunnel, - const NuThread* pThread); -NuError Nu_FunnelSetProgressState(NuFunnel* pFunnel, NuProgressState state); -NuError Nu_FunnelSendProgressUpdate(NuArchive* pArchive, NuFunnel* pFunnel); -Boolean Nu_FunnelGetDoExpand(NuFunnel* pFunnel); - -NuError Nu_StrawNew(NuArchive* pArchive, NuDataSource* pDataSource, - NuProgressData* pProgress, NuStraw** ppStraw); -NuError Nu_StrawFree(NuArchive* pArchive, NuStraw* pStraw); -NuError Nu_StrawSetProgressState(NuStraw* pStraw, NuProgressState state); -NuError Nu_StrawSendProgressUpdate(NuArchive* pArchive, NuStraw* pStraw); -NuError Nu_StrawRead(NuArchive* pArchive, NuStraw* pStraw, uint8_t* buffer, - long len); -NuError Nu_StrawRewind(NuArchive* pArchive, NuStraw* pStraw); - -/* Lzc.c */ -NuError Nu_CompressLZC12(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_CompressLZC16(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_ExpandLZC(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc); - -/* Lzw.c */ -NuError Nu_CompressLZW1(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_CompressLZW2(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_ExpandLZW(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, - uint16_t* pThreadCrc); - -/* MiscUtils.c */ -/*extern const char* kNufxLibName;*/ -extern NuCallback gNuGlobalErrorMessageHandler; -const char* Nu_StrError(NuError err); -void Nu_ReportError(NuArchive* pArchive, const char* file, int line, - const char* function, Boolean isDebug, NuError err, - const UNICHAR* format, ...) - #if defined(__GNUC__) - __attribute__ ((format(printf, 7, 8))) - #endif - ; -#ifdef USE_DMALLOC /* want file and line numbers for calls */ -# define Nu_Malloc(archive, size) malloc(size) -# define Nu_Calloc(archive, size) calloc(1, size) -# define Nu_Realloc(archive, ptr, size) realloc(ptr, size) -# define Nu_Free(archive, ptr) (ptr != NULL ? free(ptr) : (void)0) -#else -void* Nu_Malloc(NuArchive* pArchive, size_t size); -void* Nu_Calloc(NuArchive* pArchive, size_t size); -void* Nu_Realloc(NuArchive* pArchive, void* ptr, size_t size); -void Nu_Free(NuArchive* pArchive, void* ptr); -#endif -NuResult Nu_InternalFreeCallback(NuArchive* pArchive, void* args); - -/* Record.c */ -void Nu_RecordAddThreadMod(NuRecord* pRecord, NuThreadMod* pThreadMod); -Boolean Nu_RecordIsEmpty(NuArchive* pArchive, const NuRecord* pRecord); -Boolean Nu_RecordSet_GetLoaded(const NuRecordSet* pRecordSet); -uint32_t Nu_RecordSet_GetNumRecords(const NuRecordSet* pRecordSet); -void Nu_RecordSet_SetNumRecords(NuRecordSet* pRecordSet, uint32_t val); -void Nu_RecordSet_IncNumRecords(NuRecordSet* pRecordSet); -NuRecord* Nu_RecordSet_GetListHead(const NuRecordSet* pRecordSet); -NuRecord** Nu_RecordSet_GetListHeadPtr(NuRecordSet* pRecordSet); -NuRecord* Nu_RecordSet_GetListTail(const NuRecordSet* pRecordSet); -Boolean Nu_RecordSet_IsEmpty(const NuRecordSet* pRecordSet); -NuError Nu_RecordSet_FreeAllRecords(NuArchive* pArchive, - NuRecordSet* pRecordSet); -NuError Nu_RecordSet_DeleteRecordPtr(NuArchive* pArchive, - NuRecordSet* pRecordSet, NuRecord** ppRecord); -NuError Nu_RecordSet_DeleteRecord(NuArchive* pArchive, NuRecordSet* pRecordSet, - NuRecord* pRecord); -NuError Nu_RecordSet_Clone(NuArchive* pArchive, NuRecordSet* pDstSet, - const NuRecordSet* pSrcSet); -NuError Nu_RecordSet_MoveAllRecords(NuArchive* pArchive, NuRecordSet* pDstSet, - NuRecordSet* pSrcSet); -NuError Nu_RecordSet_FindByIdx(const NuRecordSet* pRecordSet, NuRecordIdx rec, - NuRecord** ppRecord); -NuError Nu_RecordSet_FindByThreadIdx(NuRecordSet* pRecordSet, - NuThreadIdx threadIdx, NuRecord** ppRecord, NuThread** ppThread); -NuError Nu_RecordSet_ReplaceRecord(NuArchive* pArchive, NuRecordSet* pBadSet, - NuRecord* pBadRecord, NuRecordSet* pGoodSet, NuRecord** ppGoodRecord); -Boolean Nu_ShouldIgnoreBadCRC(NuArchive* pArchive, const NuRecord* pRecord, - NuError err); -NuError Nu_WriteRecordHeader(NuArchive* pArchive, NuRecord* pRecord, FILE* fp); -NuError Nu_GetTOCIfNeeded(NuArchive* pArchive); -NuError Nu_StreamContents(NuArchive* pArchive, NuCallback contentFunc); -NuError Nu_StreamExtract(NuArchive* pArchive); -NuError Nu_StreamTest(NuArchive* pArchive); -NuError Nu_Contents(NuArchive* pArchive, NuCallback contentFunc); -NuError Nu_Extract(NuArchive* pArchive); -NuError Nu_ExtractRecord(NuArchive* pArchive, NuRecordIdx recIdx); -NuError Nu_Test(NuArchive* pArchive); -NuError Nu_TestRecord(NuArchive* pArchive, NuRecordIdx recIdx); -NuError Nu_GetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecord** ppRecord); -NuError Nu_GetRecordIdxByName(NuArchive* pArchive, const char* nameMOR, - NuRecordIdx* pRecordIdx); -NuError Nu_GetRecordIdxByPosition(NuArchive* pArchive, uint32_t position, - NuRecordIdx* pRecordIdx); -NuError Nu_FindRecordForWriteByIdx(NuArchive* pArchive, NuRecordIdx recIdx, - NuRecord** ppFoundRecord); -NuError Nu_AddFile(NuArchive* pArchive, const UNICHAR* pathnameUNI, - const NuFileDetails* pFileDetails, Boolean fromRsrcFork, - NuRecordIdx* pRecordIdx); -NuError Nu_AddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails, - NuRecordIdx* pRecordIdx, NuRecord** ppRecord); -NuError Nu_Rename(NuArchive* pArchive, NuRecordIdx recIdx, - const char* pathnameMOR, char fssepMOR); -NuError Nu_SetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecordAttr* pRecordAttr); -NuError Nu_Delete(NuArchive* pArchive); -NuError Nu_DeleteRecord(NuArchive* pArchive, NuRecordIdx rec); - -/* SourceSink.c */ -NuError Nu_DataSourceFile_New(NuThreadFormat threadFormat, - uint32_t otherLen, const UNICHAR* pathnameUNI, Boolean isFromRsrcFork, - NuDataSource** ppDataSource); -NuError Nu_DataSourceFP_New(NuThreadFormat threadFormat, - uint32_t otherLen, FILE* fp, long offset, long length, - NuCallback fcloseFunc, NuDataSource** ppDataSource); -NuError Nu_DataSourceBuffer_New(NuThreadFormat threadFormat, - uint32_t otherLen, const uint8_t* buffer, long offset, long length, - NuCallback freeFunc, NuDataSource** ppDataSource); -NuDataSource* Nu_DataSourceCopy(NuDataSource* pDataSource); -NuError Nu_DataSourceFree(NuDataSource* pDataSource); -NuDataSourceType Nu_DataSourceGetType(const NuDataSource* pDataSource); -NuThreadFormat Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource); -uint32_t Nu_DataSourceGetDataLen(const NuDataSource* pDataSource); -uint32_t Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource); -void Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen); -uint16_t Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource); -void Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, uint16_t crc); -NuError Nu_DataSourcePrepareInput(NuArchive* pArchive, - NuDataSource* pDataSource); -void Nu_DataSourceUnPrepareInput(NuArchive* pArchive, - NuDataSource* pDataSource); -const char* Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource); -NuError Nu_DataSourceGetBlock(NuDataSource* pDataSource, uint8_t* buf, - uint32_t len); -NuError Nu_DataSourceRewind(NuDataSource* pDataSource); -NuError Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL, - const UNICHAR* pathnameUNI, UNICHAR fssep, NuDataSink** ppDataSink); -NuError Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp, - NuDataSink** ppDataSink); -NuError Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL, - uint8_t* buffer, uint32_t bufLen, NuDataSink** ppDataSink); -NuError Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL, - NuDataSink** ppDataSink); -NuError Nu_DataSinkFree(NuDataSink* pDataSink); -NuDataSinkType Nu_DataSinkGetType(const NuDataSink* pDataSink); -Boolean Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink); -NuValue Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink); -uint32_t Nu_DataSinkGetOutCount(const NuDataSink* pDataSink); -const char* Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink); -UNICHAR Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink); -FILE* Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink); -void Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp); -void Nu_DataSinkFile_Close(NuDataSink* pDataSink); -NuError Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uint8_t* buf, - uint32_t len); -NuError Nu_DataSinkGetError(NuDataSink* pDataSink); - -/* Squeeze.c */ -NuError Nu_CompressHuffmanSQ(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc); -NuError Nu_ExpandHuffmanSQ(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc); - -/* Thread.c */ -NuThread* Nu_GetThread(const NuRecord* pRecord, int idx); -void Nu_StripHiIfAllSet(char* str); -Boolean Nu_IsPresizedThreadID(NuThreadID threadID); -Boolean Nu_IsCompressibleThreadID(NuThreadID threadID); -Boolean Nu_ThreadHasCRC(uint16_t recordVersion, NuThreadID threadID); -NuError Nu_FindThreadByIdx(const NuRecord* pRecord, NuThreadIdx thread, - NuThread** ppThread); -NuError Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID, - NuThread** ppThread); -void Nu_CopyThreadContents(NuThread* pDstThread, const NuThread* pSrcThread); -NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, - uint16_t* pCrc); -NuError Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp, - uint16_t* pCrc); -NuError Nu_ComputeThreadData(NuArchive* pArchive, NuRecord* pRecord); -NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord,long numThreads); -NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread); -NuError Nu_SkipThread(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread); -NuError Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSink* pDataSink); -NuError Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord, - NuThreadID threadID); -NuError Nu_AddThread(NuArchive* pArchive, NuRecordIdx rec, NuThreadID threadID, - NuDataSource* pDataSource, NuThreadIdx* pThreadIdx); -NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSource* pDataSource, int32_t* pMaxLen); -NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx); - -/* Value.c */ -NuError Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue); -NuError Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value); -NuError Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr); -NuThreadFormat Nu_ConvertCompressValToFormat(NuArchive* pArchive, - NuValue compValue); - -/* Version.c */ -NuError Nu_GetVersion(int32_t* pMajorVersion, int32_t* pMinorVersion, - int32_t* pBugVersion, const char** ppBuildDate, const char** ppBuildFlags); - -#endif /*NUFXLIB_NUFXLIBPRIV_H*/ diff --git a/ciderpress/nufxlib/README.txt b/ciderpress/nufxlib/README.txt deleted file mode 100644 index 3e246ee..0000000 --- a/ciderpress/nufxlib/README.txt +++ /dev/null @@ -1,119 +0,0 @@ -NufxLib README, updated 2014/12/23 -http://www.nulib.com/ - -See "COPYING-LIB" for distribution restrictions. - - -UNIX -==== - -Run the "configure" script. Read through "INSTALL" if you haven't used -one of these before, especially if you want to use a specific compiler -or a particular set of compiler flags. - -You can disable specific compression methods with "--disable-METHOD" -(run "sh ./configure --help" to see the possible options). By default, -all methods are enabled except bzip2. - -Run "make depend" if you have makedepend, and then type "make". This will -build the library and all of the programs in the "samples" directory. -There are some useful programs in "samples", described in a README.txt -file there. In particular, you should run samples/test-basic to verify -that things are more or less working. - -If you want to install the library and header file into standard system -locations (usually /usr/local), run "make install". To learn how to -specify different locations, read the INSTALL document. - -There are some flags in "OPT" you may want to use. The "autoconf" default -for @CFLAGS@ is "-g -O2". - --DNDEBUG - Disable assert() calls and extra tests. This will speed things up, - but errors won't get caught until later on, making the root cause - harder to locate. - --DDEBUG_MSGS - Enable debug messages. This increases the size of the executable, - but shouldn't affect performance. When errors occur, more output is - produced. The "debug dump" feature is enabled by this flag. - --DDEBUG_VERBOSE - (Implicitly sets DEBUG_MSGS.) Spray lots of debugging output. - -If you want to do benchmarks, use "-O2 -DNDEBUG". The recommended -configuration is "-g -O2 -DDEBUG_MSGS", so that verbose debug output is -available when errors occur. - - -BeOS -==== - -This works just like the UNIX version, but certain defaults have been -changed. Running configure without arguments under BeOS is equivalent to: - - ./configure --prefix=/boot --includedir='${prefix}/develop/headers' - --libdir='${exec_prefix}/home/config/lib' --mandir='/tmp' - --bindir='${exec_prefix}/home/config/bin' - -If you're using BeOS/PPC, it will also do: - - CC=cc CFLAGS='-proc 603 -opt full' - - -Mac OS X -======== - -This works just like the UNIX version, but includes support for resource -forks and Finder file/aux types. - -Tested with Xcode v5.1.1 and Mac OS 10.8.5. - - -Win32 -===== - -If you're using an environment that supports "configure" scripts, such as -DJGPP, follow the UNIX instructions. - -NufxLib has been tested with Microsoft Visual C++ 12 (Visual Studio 2013). -To build NufxLib, run the "Visual Studio 2013 x86 Native Tools Command -Prompt" shortcut to get a shell. Change to the nufxlib directory, then: - - nmake -f makefile.msc - -When the build finishes, run "test-basic.exe" to confirm things are working. - -If you want to have zlib support enabled, you will need to have zlib.h, -zconf.h, and zlib.lib copied into the directory. See "makefile.msc" for -more details. - -The makefile builds NufxLib as a static library and as a DLL. - - -Other Notes -=========== - -If you want to use the library in a multithreaded application, you should -define "USE_REENTRANT_CALLS" to tell it to use reentrant versions of -certain library calls. This defines _REENTRANT, which causes Solaris to -add the appropriate goodies. (Seems to me you'd always want this on, but -for some reason Solaris makes you take an extra step, so I'm not going to -define it by default.) - -Originally, NufxLib / NuLib2 were intended to be usable natively on the -Apple IIgs, so some of the design decisions were influenced by the need -to minimize memory usage (e.g. being able to get a directory listing -without holding the entire directory in memory) and interact with GS/OS -(forked files have a single filename, files have type/auxtype). The IIgs -port was never started. - - -Legalese -======== - -NufxLib, a NuFX archive manipulation library. -Copyright (C) 2000-2014 by Andy McFadden, All Rights Reserved. - -See COPYING for license. - diff --git a/ciderpress/nufxlib/Record.c b/ciderpress/nufxlib/Record.c deleted file mode 100644 index 6c85684..0000000 --- a/ciderpress/nufxlib/Record.c +++ /dev/null @@ -1,2848 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Record-level operations. - */ -#include "NufxLibPriv.h" - - -/* - * Local constants. - */ -static const uint8_t kNufxID[kNufxIDLen] = { 0x4e, 0xf5, 0x46, 0xd8 }; - - -/* - * =========================================================================== - * Simple NuRecord stuff - * =========================================================================== - */ - -/* - * Initialize the contents of a NuRecord. The goal here is to init the - * things that a Nu_FreeRecordContents call will check, so that we don't - * end up trying to free garbage. No need to memset() the whole thing. - */ -static NuError Nu_InitRecordContents(NuArchive* pArchive, NuRecord* pRecord) -{ - Assert(pRecord != NULL); - - DebugFill(pRecord, sizeof(*pRecord)); - - pRecord->recOptionList = NULL; - pRecord->extraBytes = NULL; - pRecord->recFilenameMOR = NULL; - pRecord->threadFilenameMOR = NULL; - pRecord->newFilenameMOR = NULL; - pRecord->pThreads = NULL; - pRecord->pNext = NULL; - pRecord->pThreadMods = NULL; - pRecord->dirtyHeader = false; - pRecord->dropRecFilename = false; - pRecord->isBadMac = false; - - return kNuErrNone; -} - -/* - * Allocate and initialize a new NuRecord struct. - */ -static NuError Nu_RecordNew(NuArchive* pArchive, NuRecord** ppRecord) -{ - Assert(ppRecord != NULL); - - *ppRecord = Nu_Malloc(pArchive, sizeof(**ppRecord)); - if (*ppRecord == NULL) - return kNuErrMalloc; - - return Nu_InitRecordContents(pArchive, *ppRecord); -} - -/* - * Free anything allocated within a record. Doesn't try to free the record - * itself. - */ -static NuError Nu_FreeRecordContents(NuArchive* pArchive, NuRecord* pRecord) -{ - Assert(pRecord != NULL); - - Nu_Free(pArchive, pRecord->recOptionList); - Nu_Free(pArchive, pRecord->extraBytes); - Nu_Free(pArchive, pRecord->recFilenameMOR); - Nu_Free(pArchive, pRecord->threadFilenameMOR); - Nu_Free(pArchive, pRecord->newFilenameMOR); - Nu_Free(pArchive, pRecord->pThreads); - /* don't Free(pRecord->pNext)! */ - Nu_FreeThreadMods(pArchive, pRecord); - - (void) Nu_InitRecordContents(pArchive, pRecord); /* mark as freed */ - - return kNuErrNone; -} - -/* - * Free up a NuRecord struct. - */ -static NuError Nu_RecordFree(NuArchive* pArchive, NuRecord* pRecord) -{ - if (pRecord == NULL) - return kNuErrNone; - - (void) Nu_FreeRecordContents(pArchive, pRecord); - Nu_Free(pArchive, pRecord); - - return kNuErrNone; -} - -/* - * Copy a field comprised of a buffer and a length from one structure to - * another. It is assumed that the length value has already been copied. - */ -static NuError CopySizedField(NuArchive* pArchive, void* vppDst, - const void* vpSrc, uint32_t len) -{ - NuError err = kNuErrNone; - uint8_t** ppDst = vppDst; - const uint8_t* pSrc = vpSrc; - - Assert(ppDst != NULL); - - if (len) { - Assert(pSrc != NULL); - *ppDst = Nu_Malloc(pArchive, len); - BailAlloc(*ppDst); - memcpy(*ppDst, pSrc, len); - } else { - Assert(pSrc == NULL); - *ppDst = NULL; - } - -bail: - return err; -} - -/* - * Make a copy of a record. - */ -static NuError Nu_RecordCopy(NuArchive* pArchive, NuRecord** ppDst, - const NuRecord* pSrc) -{ - NuError err; - NuRecord* pDst; - - err = Nu_RecordNew(pArchive, ppDst); - BailError(err); - - /* copy all the static fields, then copy or blank the "hairy" parts */ - pDst = *ppDst; - memcpy(pDst, pSrc, sizeof(*pSrc)); - CopySizedField(pArchive, &pDst->recOptionList, pSrc->recOptionList, - pSrc->recOptionSize); - CopySizedField(pArchive, &pDst->extraBytes, pSrc->extraBytes, - pSrc->extraCount); - CopySizedField(pArchive, &pDst->recFilenameMOR, pSrc->recFilenameMOR, - pSrc->recFilenameLength == 0 ? 0 : pSrc->recFilenameLength+1); - CopySizedField(pArchive, &pDst->threadFilenameMOR, pSrc->threadFilenameMOR, - pSrc->threadFilenameMOR == NULL ? 0 : strlen(pSrc->threadFilenameMOR) +1); - CopySizedField(pArchive, &pDst->newFilenameMOR, pSrc->newFilenameMOR, - pSrc->newFilenameMOR == NULL ? 0 : strlen(pSrc->newFilenameMOR) +1); - CopySizedField(pArchive, &pDst->pThreads, pSrc->pThreads, - pSrc->recTotalThreads * sizeof(*pDst->pThreads)); - - /* now figure out what the filename is supposed to point at */ - if (pSrc->filenameMOR == pSrc->threadFilenameMOR) - pDst->filenameMOR = pDst->threadFilenameMOR; - else if (pSrc->filenameMOR == pSrc->recFilenameMOR) - pDst->filenameMOR = pDst->recFilenameMOR; - else if (pSrc->filenameMOR == pSrc->newFilenameMOR) - pDst->filenameMOR = pDst->newFilenameMOR; - else - pDst->filenameMOR = pSrc->filenameMOR; /* probably static kDefault value */ - - pDst->pNext = NULL; - - /* these only hold for copy from orig... may need to remove */ - Assert(pSrc->pThreadMods == NULL); - Assert(!pSrc->dirtyHeader); - -bail: - return err; -} - - -/* - * Add a ThreadMod to the list in the NuRecord. - * - * In general, the order is not significant. However, if we're adding - * a bunch of "add" threadMods for control threads to a record, their - * order might be important. So, we want to add the threadMod to the - * end of the list. - * - * I'm expecting these lists to be short, so walking down them is - * acceptable. We could do simple optimizations, like only preserving - * ordering for "add" threadMods, but even that seems silly. - */ -void Nu_RecordAddThreadMod(NuRecord* pRecord, NuThreadMod* pThreadMod) -{ - NuThreadMod* pScanThreadMod; - - Assert(pRecord != NULL); - Assert(pThreadMod != NULL); - - if (pRecord->pThreadMods == NULL) { - pRecord->pThreadMods = pThreadMod; - } else { - pScanThreadMod = pRecord->pThreadMods; - while (pScanThreadMod->pNext != NULL) - pScanThreadMod = pScanThreadMod->pNext; - - pScanThreadMod->pNext = pThreadMod; - } - - pThreadMod->pNext = NULL; -} - - -/* - * Decide if a record is empty. An empty record is one that will have no - * threads after all adds and deletes are processed. - * - * You can't delete something you just added or has been updated, and you - * can't update something that has been deleted, so any "add" or "update" - * items indicate that the thread isn't empty. - * - * You can't delete a thread more than once, or delete a thread that - * doesn't exist, so all we need to do is count up the number of current - * threads, subtract the number of deletes, and return "true" if the net - * result is zero. - */ -Boolean Nu_RecordIsEmpty(NuArchive* pArchive, const NuRecord* pRecord) -{ - const NuThreadMod* pThreadMod; - int numThreads; - - Assert(pRecord != NULL); - - numThreads = pRecord->recTotalThreads; - - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - switch (pThreadMod->entry.kind) { - case kNuThreadModAdd: - case kNuThreadModUpdate: - return false; - case kNuThreadModDelete: - numThreads--; - break; - case kNuThreadModUnknown: - default: - Assert(0); - return false; - } - - pThreadMod = pThreadMod->pNext; - } - - if (numThreads > 0) - return false; - else if (numThreads == 0) - return true; - else { - Assert(0); - Nu_ReportError(NU_BLOB, kNuErrInternal, - "Thread counting failed (%d)", numThreads); - return false; - } -} - - -/* - * =========================================================================== - * NuRecordSet functions - * =========================================================================== - */ - -/* - * Trivial getters and setters - */ - -Boolean Nu_RecordSet_GetLoaded(const NuRecordSet* pRecordSet) -{ - Assert(pRecordSet != NULL); - return pRecordSet->loaded; -} - -void Nu_RecordSet_SetLoaded(NuRecordSet* pRecordSet, Boolean val) -{ - pRecordSet->loaded = val; -} - -uint32_t Nu_RecordSet_GetNumRecords(const NuRecordSet* pRecordSet) -{ - return pRecordSet->numRecords; -} - -void Nu_RecordSet_SetNumRecords(NuRecordSet* pRecordSet, uint32_t val) -{ - pRecordSet->numRecords = val; -} - -void Nu_RecordSet_IncNumRecords(NuRecordSet* pRecordSet) -{ - pRecordSet->numRecords++; -} - -NuRecord* Nu_RecordSet_GetListHead(const NuRecordSet* pRecordSet) -{ - return pRecordSet->nuRecordHead; -} - -NuRecord** Nu_RecordSet_GetListHeadPtr(NuRecordSet* pRecordSet) -{ - return &pRecordSet->nuRecordHead; -} - -NuRecord* Nu_RecordSet_GetListTail(const NuRecordSet* pRecordSet) -{ - return pRecordSet->nuRecordTail; -} - - -/* - * Returns "true" if the record set has no records or hasn't ever been - * used. - */ -Boolean Nu_RecordSet_IsEmpty(const NuRecordSet* pRecordSet) -{ - if (!pRecordSet->loaded || pRecordSet->numRecords == 0) - return true; - - return false; -} - -/* - * Free the list of records, and reset the record sets to initial state. - */ -NuError Nu_RecordSet_FreeAllRecords(NuArchive* pArchive, - NuRecordSet* pRecordSet) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - NuRecord* pNextRecord; - - if (!pRecordSet->loaded) { - Assert(pRecordSet->nuRecordHead == NULL); - Assert(pRecordSet->nuRecordTail == NULL); - Assert(pRecordSet->numRecords == 0); - return kNuErrNone; - } - - DBUG(("+++ FreeAllRecords\n")); - pRecord = pRecordSet->nuRecordHead; - while (pRecord != NULL) { - pNextRecord = pRecord->pNext; - - err = Nu_RecordFree(pArchive, pRecord); - BailError(err); /* don't really expect this to fail */ - - pRecord = pNextRecord; - } - - pRecordSet->nuRecordHead = pRecordSet->nuRecordTail = NULL; - pRecordSet->numRecords = 0; - pRecordSet->loaded = false; - -bail: - return err; -} - - -/* - * Add a new record to the end of the list. - */ -static NuError Nu_RecordSet_AddRecord(NuRecordSet* pRecordSet, - NuRecord* pRecord) -{ - Assert(pRecordSet != NULL); - Assert(pRecord != NULL); - - /* if one is NULL, both must be NULL */ - Assert(pRecordSet->nuRecordHead == NULL || pRecordSet->nuRecordTail != NULL); - Assert(pRecordSet->nuRecordTail == NULL || pRecordSet->nuRecordHead != NULL); - - if (pRecordSet->nuRecordHead == NULL) { - /* empty list */ - pRecordSet->nuRecordHead = pRecordSet->nuRecordTail = pRecord; - pRecordSet->loaded = true; - Assert(!pRecordSet->numRecords); - } else { - pRecord->pNext = NULL; - pRecordSet->nuRecordTail->pNext = pRecord; - pRecordSet->nuRecordTail = pRecord; - } - - pRecordSet->numRecords++; - - return kNuErrNone; -} - - -/* - * Delete a record from the record set. Pass in a pointer to the pointer - * to the record (usually either the head pointer or another record's - * "pNext" pointer). - * - * (Should have a "heavy assert" mode where we verify that "ppRecord" - * actually has something to do with pRecordSet.) - */ -NuError Nu_RecordSet_DeleteRecordPtr(NuArchive* pArchive, - NuRecordSet* pRecordSet, NuRecord** ppRecord) -{ - NuError err; - NuRecord* pRecord; - - Assert(pRecordSet != NULL); - Assert(ppRecord != NULL); - Assert(*ppRecord != NULL); - - /* save a copy of the record we're freeing */ - pRecord = *ppRecord; - - /* update the pHead or pNext pointer */ - *ppRecord = (*ppRecord)->pNext; - pRecordSet->numRecords--; - - /* if we're deleting the tail, we have to find the "new" last entry */ - if (pRecord == pRecordSet->nuRecordTail) { - if (pRecordSet->nuRecordHead == NULL) { - /* this was the last entry; we're done */ - pRecordSet->nuRecordTail = NULL; - } else { - /* walk through the list... delete bottom-up will be slow! */ - pRecordSet->nuRecordTail = pRecordSet->nuRecordHead; - while (pRecordSet->nuRecordTail->pNext != NULL) - pRecordSet->nuRecordTail = pRecordSet->nuRecordTail->pNext; - } - } - - if (pRecordSet->numRecords) - Assert(pRecordSet->nuRecordHead!=NULL && pRecordSet->nuRecordTail!=NULL); - else - Assert(pRecordSet->nuRecordHead==NULL && pRecordSet->nuRecordTail==NULL); - - err = Nu_RecordFree(pArchive, pRecord); - return err; -} - -/* - * Delete a record from the record set. - */ -NuError Nu_RecordSet_DeleteRecord(NuArchive* pArchive, NuRecordSet* pRecordSet, - NuRecord* pRecord) -{ - NuError err; - NuRecord** ppRecord; - - ppRecord = Nu_RecordSet_GetListHeadPtr(pRecordSet); - Assert(ppRecord != NULL); - Assert(*ppRecord != NULL); - - /* look for the record, so we can update his neighbors */ - /* (this also ensures that the record really is in the set we think it is)*/ - while (*ppRecord) { - if (*ppRecord == pRecord) { - err = Nu_RecordSet_DeleteRecordPtr(pArchive, pRecordSet, ppRecord); - BailError(err); - goto bail; - } - - ppRecord = &((*ppRecord)->pNext); - } - - DBUG(("--- Nu_RecordSet_DeleteRecord failed\n")); - err = kNuErrNotFound; - -bail: - return err; -} - -/* - * Make a clone of a record set. This is used to create the "copy" record - * set out of the "orig" set. - */ -NuError Nu_RecordSet_Clone(NuArchive* pArchive, NuRecordSet* pDstSet, - const NuRecordSet* pSrcSet) -{ - NuError err = kNuErrNone; - const NuRecord* pSrcRecord; - NuRecord* pDstRecord; - - Assert(pDstSet != NULL); - Assert(pSrcSet != NULL); - Assert(Nu_RecordSet_GetLoaded(pDstSet) == false); - Assert(Nu_RecordSet_GetLoaded(pSrcSet) == true); - - DBUG(("--- Cloning record set\n")); - - Nu_RecordSet_SetLoaded(pDstSet, true); - - /* copy each record over */ - pSrcRecord = pSrcSet->nuRecordHead; - while (pSrcRecord != NULL) { - err = Nu_RecordCopy(pArchive, &pDstRecord, pSrcRecord); - BailError(err); - err = Nu_RecordSet_AddRecord(pDstSet, pDstRecord); - BailError(err); - - pSrcRecord = pSrcRecord->pNext; - } - - Assert(pDstSet->numRecords == pSrcSet->numRecords); - -bail: - if (err != kNuErrNone) { - Nu_RecordSet_FreeAllRecords(pArchive, pDstSet); - } - return err; -} - -/* - * Move all of the records from one record set to another. The records - * from "pSrcSet" are appended to "pDstSet". - * - * On completion, "pSrcSet" will be empty and "unloaded". - */ -NuError Nu_RecordSet_MoveAllRecords(NuArchive* pArchive, NuRecordSet* pDstSet, - NuRecordSet* pSrcSet) -{ - NuError err = kNuErrNone; - - Assert(pDstSet != NULL); - Assert(pSrcSet != NULL); - - /* move records over */ - if (Nu_RecordSet_GetNumRecords(pSrcSet)) { - Assert(pSrcSet->loaded); - Assert(pSrcSet->nuRecordHead != NULL); - Assert(pSrcSet->nuRecordTail != NULL); - if (pDstSet->nuRecordHead == NULL) { - /* empty dst list */ - Assert(pDstSet->nuRecordTail == NULL); - pDstSet->nuRecordHead = pSrcSet->nuRecordHead; - pDstSet->nuRecordTail = pSrcSet->nuRecordTail; - pDstSet->numRecords = pSrcSet->numRecords; - pDstSet->loaded = true; - } else { - /* append to dst list */ - Assert(pDstSet->loaded); - Assert(pDstSet->nuRecordTail != NULL); - pDstSet->nuRecordTail->pNext = pSrcSet->nuRecordHead; - pDstSet->nuRecordTail = pSrcSet->nuRecordTail; - pDstSet->numRecords += pSrcSet->numRecords; - } - } else { - /* no records in src set */ - Assert(pSrcSet->nuRecordHead == NULL); - Assert(pSrcSet->nuRecordTail == NULL); - - if (pSrcSet->loaded) - pDstSet->loaded = true; - } - - /* nuke all pointers in original list */ - pSrcSet->nuRecordHead = pSrcSet->nuRecordTail = NULL; - pSrcSet->numRecords = 0; - pSrcSet->loaded = false; - - return err; -} - - -/* - * Find a record in the list by index. - */ -NuError Nu_RecordSet_FindByIdx(const NuRecordSet* pRecordSet, - NuRecordIdx recIdx, NuRecord** ppRecord) -{ - NuRecord* pRecord; - - pRecord = pRecordSet->nuRecordHead; - while (pRecord != NULL) { - if (pRecord->recordIdx == recIdx) { - *ppRecord = pRecord; - return kNuErrNone; - } - - pRecord = pRecord->pNext; - } - - return kNuErrRecIdxNotFound; -} - - -/* - * Search for a specific thread in all records in the specified record set. - */ -NuError Nu_RecordSet_FindByThreadIdx(NuRecordSet* pRecordSet, - NuThreadIdx threadIdx, NuRecord** ppRecord, NuThread** ppThread) -{ - NuError err = kNuErrThreadIdxNotFound; - NuRecord* pRecord; - - pRecord = Nu_RecordSet_GetListHead(pRecordSet); - while (pRecord != NULL) { - err = Nu_FindThreadByIdx(pRecord, threadIdx, ppThread); - if (err == kNuErrNone) { - *ppRecord = pRecord; - break; - } - pRecord = pRecord->pNext; - } - - Assert(err != kNuErrNone || (*ppRecord != NULL && *ppThread != NULL)); - return err; -} - - -/* - * Compare two record filenames. This comes into play when looking for - * conflicts while adding records to an archive. - * - * Interesting issues: - * - some filesystems are case-sensitive, some aren't - * - the fssep may be different ('/', ':') for otherwise equivalent names - * - system-dependent conversions could resolve two different names to - * the same thing - * - * Some of these are out of our control. For now, I'm just doing a - * case-insensitive comparison, since the most interesting case for us is - * when the person is adding a data fork and a resource fork from the - * same file during the same operation. - * - * [ Could run both names through the pathname conversion callback first? - * Might be expensive. ] - * - * Returns an integer greater than, equal to, or less than 0, if the - * string pointed to by name1 is greater than, equal to, or less than - * the string pointed to by s2, respectively (i.e. same as strcmp). - */ -static int Nu_CompareRecordNames(const char* name1MOR, const char* name2MOR) -{ -#ifdef NU_CASE_SENSITIVE - return strcmp(name1MOR, name2MOR); -#else - return strcasecmp(name1MOR, name2MOR); -#endif -} - - -/* - * Find a record in the list by storageName. - */ -static NuError Nu_RecordSet_FindByName(const NuRecordSet* pRecordSet, - const char* nameMOR, NuRecord** ppRecord) -{ - NuRecord* pRecord; - - Assert(pRecordSet != NULL); - Assert(pRecordSet->loaded); - Assert(nameMOR != NULL); - Assert(ppRecord != NULL); - - pRecord = pRecordSet->nuRecordHead; - while (pRecord != NULL) { - if (Nu_CompareRecordNames(pRecord->filenameMOR, nameMOR) == 0) { - *ppRecord = pRecord; - return kNuErrNone; - } - - pRecord = pRecord->pNext; - } - - return kNuErrRecNameNotFound; -} - -/* - * Find a record in the list by storageName, starting from the end and - * searching backwards. - * - * Since we don't actually have a "prev" pointer in the record, we end - * up scanning the entire list and keeping the last match. If this - * causes a notable reduction in efficiency we'll have to fix this. - */ -static NuError Nu_RecordSet_ReverseFindByName(const NuRecordSet* pRecordSet, - const char* nameMOR, NuRecord** ppRecord) -{ - NuRecord* pRecord; - NuRecord* pFoundRecord = NULL; - - Assert(pRecordSet != NULL); - Assert(pRecordSet->loaded); - Assert(nameMOR != NULL); - Assert(ppRecord != NULL); - - pRecord = pRecordSet->nuRecordHead; - while (pRecord != NULL) { - if (Nu_CompareRecordNames(pRecord->filenameMOR, nameMOR) == 0) - pFoundRecord = pRecord; - - pRecord = pRecord->pNext; - } - - if (pFoundRecord != NULL) { - *ppRecord = pFoundRecord; - return kNuErrNone; - } - return kNuErrRecNameNotFound; -} - - -/* - * We have a copy of the record in the "copy" set, but we've decided - * (perhaps because the user elected to Skip a failed add) that we'd - * rather have the original. - * - * Delete the record from the "copy" set, clone the "orig" record, and - * insert the "orig" record into the same spot in the "copy" set. - * - * "ppNewRecord" will get a pointer to the newly-created clone. - */ -NuError Nu_RecordSet_ReplaceRecord(NuArchive* pArchive, NuRecordSet* pBadSet, - NuRecord* pBadRecord, NuRecordSet* pGoodSet, NuRecord** ppNewRecord) -{ - NuError err; - NuRecord* pGoodRecord; - NuRecord* pSiblingRecord; - NuRecord* pNewRecord = NULL; - - Assert(pArchive != NULL); - Assert(pBadSet != NULL); - Assert(pBadRecord != NULL); - Assert(pGoodSet != NULL); - Assert(ppNewRecord != NULL); - - /* - * Find a record in "pGoodSet" that has the same record index as - * the "bad" record. - */ - err = Nu_RecordSet_FindByIdx(pGoodSet, pBadRecord->recordIdx, - &pGoodRecord); - BailError(err); - - /* - * Clone the original. - */ - err = Nu_RecordCopy(pArchive, &pNewRecord, pGoodRecord); - BailError(err); - - /* - * Insert the new one into the "bad" record set, in the exact same - * position. - */ - pNewRecord->pNext = pBadRecord->pNext; - if (pBadSet->nuRecordTail == pBadRecord) - pBadSet->nuRecordTail = pNewRecord; - if (pBadSet->nuRecordHead == pBadRecord) - pBadSet->nuRecordHead = pNewRecord; - else { - /* find the record that points to pBadRecord */ - pSiblingRecord = pBadSet->nuRecordHead; - while (pSiblingRecord->pNext != pBadRecord && pSiblingRecord != NULL) - pSiblingRecord = pSiblingRecord->pNext; - - if (pSiblingRecord == NULL) { - /* looks like "pBadRecord" wasn't part of "pBadSet" after all */ - Assert(0); - err = kNuErrInternal; - goto bail; - } - - pSiblingRecord->pNext = pNewRecord; - } - - err = Nu_RecordFree(pArchive, pBadRecord); - BailError(err); - - *ppNewRecord = pNewRecord; - pNewRecord = NULL; /* don't free */ - -bail: - if (pNewRecord != NULL) - Nu_RecordFree(pArchive, pNewRecord); - return err; -} - - -/* - * =========================================================================== - * Assorted utility functions - * =========================================================================== - */ - -/* - * Ask the user if it's okay to ignore a bad CRC. If we can't ask the - * user, return "false". - */ -Boolean Nu_ShouldIgnoreBadCRC(NuArchive* pArchive, const NuRecord* pRecord, - NuError err) -{ - NuErrorStatus errorStatus; - NuResult result; - Boolean retval = false; - UNICHAR* pathnameUNI = NULL; - - Assert(pArchive->valIgnoreCRC == false); - - if (pArchive->errorHandlerFunc != NULL) { - errorStatus.operation = kNuOpTest; /* mostly accurate */ - errorStatus.err = err; - errorStatus.sysErr = 0; - errorStatus.message = NULL; - errorStatus.pRecord = pRecord; - errorStatus.pathnameUNI = NULL; - errorStatus.origPathname = NULL; - errorStatus.filenameSeparator = 0; - if (pRecord != NULL) { - pathnameUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); - errorStatus.pathnameUNI = pathnameUNI; - errorStatus.filenameSeparator = - NuGetSepFromSysInfo(pRecord->recFileSysInfo); - } - /*errorStatus.origArchiveTouched = false;*/ - errorStatus.canAbort = true; - errorStatus.canRetry = false; - errorStatus.canIgnore = true; - errorStatus.canSkip = false; - errorStatus.canRename = false; - errorStatus.canOverwrite = false; - - result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); - - switch (result) { - case kNuAbort: - goto bail; - case kNuIgnore: - retval = true; - goto bail; - case kNuSkip: - case kNuOverwrite: - case kNuRetry: - case kNuRename: - default: - Nu_ReportError(NU_BLOB, kNuErrSyntax, - "Wasn't expecting result %d here", result); - break; - } - } - -bail: - Nu_Free(pArchive, pathnameUNI); - return retval; -} - - -/* - * Read the next NuFX record from the current offset in the archive stream. - * This includes the record header and the thread header blocks. - * - * Pass in a NuRecord structure that will hold the data we read. - */ -static NuError Nu_ReadRecordHeader(NuArchive* pArchive, NuRecord* pRecord) -{ - NuError err = kNuErrNone; - uint16_t crc; - FILE* fp; - int bytesRead; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(pRecord->pThreads == NULL); - Assert(pRecord->pNext == NULL); - - fp = pArchive->archiveFp; - - pRecord->recordIdx = Nu_GetNextRecordIdx(pArchive); - - /* points to whichever filename storage we like best */ - pRecord->filenameMOR = NULL; - pRecord->fileOffset = pArchive->currentOffset; - - (void) Nu_ReadBytes(pArchive, fp, pRecord->recNufxID, kNufxIDLen); - if (memcmp(kNufxID, pRecord->recNufxID, kNufxIDLen) != 0) { - err = kNuErrRecHdrNotFound; - Nu_ReportError(NU_BLOB, kNuErrNone, - "Couldn't find start of next record"); - goto bail; - } - - /* - * Read the static fields. - */ - crc = 0; - pRecord->recHeaderCRC = Nu_ReadTwo(pArchive, fp); - pRecord->recAttribCount = Nu_ReadTwoC(pArchive, fp, &crc); - pRecord->recVersionNumber = Nu_ReadTwoC(pArchive, fp, &crc); - pRecord->recTotalThreads = Nu_ReadFourC(pArchive, fp, &crc); - pRecord->recFileSysID = Nu_ReadTwoC(pArchive, fp, &crc); - pRecord->recFileSysInfo = Nu_ReadTwoC(pArchive, fp, &crc); - pRecord->recAccess = Nu_ReadFourC(pArchive, fp, &crc); - pRecord->recFileType = Nu_ReadFourC(pArchive, fp, &crc); - pRecord->recExtraType = Nu_ReadFourC(pArchive, fp, &crc); - pRecord->recStorageType = Nu_ReadTwoC(pArchive, fp, &crc); - pRecord->recCreateWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); - pRecord->recModWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); - pRecord->recArchiveWhen = Nu_ReadDateTimeC(pArchive, fp, &crc); - bytesRead = 56; /* 4-byte 'NuFX' plus the above */ - - /* - * Do some sanity checks before we continue. - */ - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed reading record header"); - goto bail; - } - if (pRecord->recAttribCount > kNuReasonableAttribCount) { - err = kNuErrBadRecord; - Nu_ReportError(NU_BLOB, err, "Attrib count is huge (%u)", - pRecord->recAttribCount); - goto bail; - } - if (pRecord->recVersionNumber > kNuMaxRecordVersion) { - err = kNuErrBadRecord; - Nu_ReportError(NU_BLOB, err, "Unrecognized record version number (%u)", - pRecord->recVersionNumber); - goto bail; - } - if (pRecord->recTotalThreads > kNuReasonableTotalThreads) { - err = kNuErrBadRecord; - Nu_ReportError(NU_BLOB, err, "Unreasonable number of threads (%u)", - pRecord->recTotalThreads); - goto bail; - } - - /* - * Read the option list, if present. - */ - if (pRecord->recVersionNumber > 0) { - pRecord->recOptionSize = Nu_ReadTwoC(pArchive, fp, &crc); - bytesRead += 2; - - /* - * It appears GS/ShrinkIt is creating bad option lists, claiming - * 36 bytes of data when there's only room for 18. Since we don't - * really pay attention to the option list - */ - if (pRecord->recOptionSize + bytesRead > pRecord->recAttribCount -2) { - DBUG(("--- truncating option list from %d to %d\n", - pRecord->recOptionSize, - pRecord->recAttribCount -2 - bytesRead)); - if (pRecord->recAttribCount -2 > bytesRead) - pRecord->recOptionSize = pRecord->recAttribCount -2 - bytesRead; - else - pRecord->recOptionSize = 0; - } - - /* this is the older test, which rejected funky archives */ - if (pRecord->recOptionSize + bytesRead > pRecord->recAttribCount -2) { - /* option size exceeds the total attribute area */ - err = kNuErrBadRecord; - Nu_ReportError(NU_BLOB, kNuErrBadRecord, - "Option size (%u) exceeds attribs (%u,%u-2)", - pRecord->recOptionSize, bytesRead, - pRecord->recAttribCount); - goto bail; - } - - if (pRecord->recOptionSize) { - pRecord->recOptionList = Nu_Malloc(pArchive,pRecord->recOptionSize); - BailAlloc(pRecord->recOptionList); - (void) Nu_ReadBytesC(pArchive, fp, pRecord->recOptionList, - pRecord->recOptionSize, &crc); - bytesRead += pRecord->recOptionSize; - } - } else { - pRecord->recOptionSize = 0; - pRecord->recOptionList = NULL; - } - - /* last two bytes are the filename len; all else is "extra" */ - pRecord->extraCount = (pRecord->recAttribCount -2) - bytesRead; - Assert(pRecord->extraCount >= 0); - - /* - * Some programs (for example, NuLib) may leave extra junk in here. This - * is allowed by the archive spec. We may want to preserve it, so we - * allocate space for it and read it if it exists. - */ - if (pRecord->extraCount) { - pRecord->extraBytes = Nu_Malloc(pArchive, pRecord->extraCount); - BailAlloc(pRecord->extraBytes); - (void) Nu_ReadBytesC(pArchive, fp, pRecord->extraBytes, - pRecord->extraCount, &crc); - bytesRead += pRecord->extraCount; - } - - /* - * Read the in-record filename if one exists (likely in v0 records only). - */ - pRecord->recFilenameLength = Nu_ReadTwoC(pArchive, fp, &crc); - bytesRead += 2; - if (pRecord->recFilenameLength > kNuReasonableFilenameLen) { - err = kNuErrBadRecord; - Nu_ReportError(NU_BLOB, kNuErrBadRecord, "Filename length is huge (%u)", - pRecord->recFilenameLength); - goto bail; - } - if (pRecord->recFilenameLength) { - pRecord->recFilenameMOR = - Nu_Malloc(pArchive, pRecord->recFilenameLength +1); - BailAlloc(pRecord->recFilenameMOR); - (void) Nu_ReadBytesC(pArchive, fp, pRecord->recFilenameMOR, - pRecord->recFilenameLength, &crc); - pRecord->recFilenameMOR[pRecord->recFilenameLength] = '\0'; - - bytesRead += pRecord->recFilenameLength; - - Nu_StripHiIfAllSet(pRecord->recFilenameMOR); - - /* use the in-header one */ - pRecord->filenameMOR = pRecord->recFilenameMOR; - } - - /* - * Read the threads records. The data is included in the record header - * CRC, so we have to pass that in too. - */ - pRecord->fakeThreads = 0; - err = Nu_ReadThreadHeaders(pArchive, pRecord, &crc); - BailError(err); - - /* - * After all is said and done, did we read the file without errors, - * and does the CRC match? - */ - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed reading late record header"); - goto bail; - } - if (!pArchive->valIgnoreCRC && crc != pRecord->recHeaderCRC) { - if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadRHCRC)) { - err = kNuErrBadRHCRC; - Nu_ReportError(NU_BLOB, err, "Stored RH CRC=0x%04x, calc=0x%04x", - pRecord->recHeaderCRC, crc); - Nu_ReportError(NU_BLOB_DEBUG, kNuErrNone, - "--- Problematic record is id=%u", pRecord->recordIdx); - goto bail; - } - } - - /* - * Init or compute misc record fields. - */ - /* adjust "currentOffset" for the entire record header */ - pArchive->currentOffset += bytesRead; - pArchive->currentOffset += - (pRecord->recTotalThreads - pRecord->fakeThreads) * kNuThreadHeaderSize; - - pRecord->recHeaderLength = - bytesRead + pRecord->recTotalThreads * kNuThreadHeaderSize; - pRecord->recHeaderLength -= pRecord->fakeThreads * kNuThreadHeaderSize; - - err = Nu_ComputeThreadData(pArchive, pRecord); - BailError(err); - - /* check for "bad Mac" archives */ - if (pArchive->valHandleBadMac) { - if (pRecord->recFileSysInfo == '?' && - pRecord->recFileSysID == kNuFileSysMacMFS) - { - DBUG(("--- using 'bad mac' handling\n")); - pRecord->isBadMac = true; - pRecord->recFileSysInfo = ':'; - } - } - -bail: - if (err != kNuErrNone) - (void)Nu_FreeRecordContents(pArchive, pRecord); - return err; -} - - -/* - * Update the record's storageType if it looks like it needs it, based on - * the current set of threads. - * - * The rules we follow (stopping at the first match) are: - * - If there's a disk thread, leave it alone. Disk block size issues - * should already have been resolved. If we end up copying the same - * bogus block size we were given initially, that's fine. - * - If there's a resource fork, set the storageType to 5. - * - If there's a data fork, set the storageType to 1-3. - * - If there are no data-class threads at all, set the storageType to zero. - * - * This assumes that all updates have already been processed, i.e. there's - * no lingering add or delete threadMods. This only examines the thread - * array. - * - * NOTE: for data files (types 1, 2, and 3), the actual value may not match - * up what ProDOS would use, because this doesn't test for sparseness. - */ -static void Nu_UpdateStorageType(NuArchive* pArchive, NuRecord* pRecord) -{ - NuError err; - NuThread* pThread; - - err = Nu_FindThreadByID(pRecord, kNuThreadIDDiskImage, &pThread); - if (err == kNuErrNone) - goto bail; - - err = Nu_FindThreadByID(pRecord, kNuThreadIDRsrcFork, &pThread); - if (err == kNuErrNone) { - DBUG(("--- setting storageType to %d (was %d)\n", kNuStorageExtended, - pRecord->recStorageType)); - pRecord->recStorageType = kNuStorageExtended; - goto bail; - } - - err = Nu_FindThreadByID(pRecord, kNuThreadIDDataFork, &pThread); - if (err == kNuErrNone) { - int newType; - if (pThread->actualThreadEOF <= 512) - newType = kNuStorageSeedling; - else if (pThread->actualThreadEOF < 131072) - newType = kNuStorageSapling; - else - newType = kNuStorageTree; - DBUG(("--- setting storageType to %d (was %d)\n", newType, - pRecord->recStorageType)); - pRecord->recStorageType = newType; - goto bail; - } - - DBUG(("--- no stuff here, setting storageType to %d (was %d)\n", - kNuStorageUnknown, pRecord->recStorageType)); - pRecord->recStorageType = kNuStorageUnknown; - -bail: - return; -} - -/* - * Write the record header to the current offset of the specified file. - * This includes writing all of the thread headers. - * - * We don't "promote" records to newer versions, because that might - * require expanding and CRCing data threads. Instead, we write the - * record in a manner appropriate for the version. - * - * As a side effect, this may update the storageType to something appropriate. - * - * The position of the file pointer on exit is undefined. The position - * past the end of the record will be stored in pArchive->currentOffset. - */ -NuError Nu_WriteRecordHeader(NuArchive* pArchive, NuRecord* pRecord, FILE* fp) -{ - NuError err = kNuErrNone; - uint16_t crc; - long crcOffset; - int bytesWritten; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(fp != NULL); - - /* - * Before we get started, let's make sure the storageType makes sense - * for this record. - */ - Nu_UpdateStorageType(pArchive, pRecord); - - DBUG(("--- Writing record header (v=%d)\n", pRecord->recVersionNumber)); - - (void) Nu_WriteBytes(pArchive, fp, pRecord->recNufxID, kNufxIDLen); - err = Nu_FTell(fp, &crcOffset); - BailError(err); - - /* - * Write the static fields. - */ - crc = 0; - Nu_WriteTwo(pArchive, fp, 0); /* crc -- come back later */ - Nu_WriteTwoC(pArchive, fp, pRecord->recAttribCount, &crc); - Nu_WriteTwoC(pArchive, fp, pRecord->recVersionNumber, &crc); - Nu_WriteFourC(pArchive, fp, pRecord->recTotalThreads, &crc); - Nu_WriteTwoC(pArchive, fp, (uint16_t)pRecord->recFileSysID, &crc); - Nu_WriteTwoC(pArchive, fp, pRecord->recFileSysInfo, &crc); - Nu_WriteFourC(pArchive, fp, pRecord->recAccess, &crc); - Nu_WriteFourC(pArchive, fp, pRecord->recFileType, &crc); - Nu_WriteFourC(pArchive, fp, pRecord->recExtraType, &crc); - Nu_WriteTwoC(pArchive, fp, pRecord->recStorageType, &crc); - Nu_WriteDateTimeC(pArchive, fp, pRecord->recCreateWhen, &crc); - Nu_WriteDateTimeC(pArchive, fp, pRecord->recModWhen, &crc); - Nu_WriteDateTimeC(pArchive, fp, pRecord->recArchiveWhen, &crc); - bytesWritten = 56; /* 4-byte 'NuFX' plus the above */ - - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed writing record header"); - goto bail; - } - - /* - * Write the option list, if present. - */ - if (pRecord->recVersionNumber > 0) { - Nu_WriteTwoC(pArchive, fp, pRecord->recOptionSize, &crc); - bytesWritten += 2; - - if (pRecord->recOptionSize) { - Nu_WriteBytesC(pArchive, fp, pRecord->recOptionList, - pRecord->recOptionSize, &crc); - bytesWritten += pRecord->recOptionSize; - } - } - - /* - * Preserve whatever miscellaneous junk was left in here by the last guy. - * We don't know what this is or why it's here, but who knows, maybe - * it's important. - * - * Besides, if we don't, we'll have to go back and fix the attrib count. - */ - if (pRecord->extraCount) { - Nu_WriteBytesC(pArchive, fp, pRecord->extraBytes, pRecord->extraCount, - &crc); - bytesWritten += pRecord->extraCount; - } - - /* - * If the record has a filename in the header, write it, unless - * recent changes have inspired us to drop the name from the header. - * - * Records that begin with no filename will have a default one - * stuffed in, so it's possible for pRecord->filename to be set - * already even if there wasn't one in the record. (In such cases, - * we don't write a name.) - */ - if (pRecord->recFilenameLength && !pRecord->dropRecFilename) { - Nu_WriteTwoC(pArchive, fp, pRecord->recFilenameLength, &crc); - bytesWritten += 2; - Nu_WriteBytesC(pArchive, fp, pRecord->recFilenameMOR, - pRecord->recFilenameLength, &crc); - } else { - Nu_WriteTwoC(pArchive, fp, 0, &crc); - bytesWritten += 2; - } - - /* make sure we are where we thought we would be */ - if (bytesWritten != pRecord->recAttribCount) { - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, kNuErrNone, - "Didn't write what was expected (%d vs %d)", - bytesWritten, pRecord->recAttribCount); - goto bail; - } - - /* write the thread headers, and zero out "fake" thread count */ - err = Nu_WriteThreadHeaders(pArchive, pRecord, fp, &crc); - BailError(err); - - /* get the current file offset, for some computations later */ - err = Nu_FTell(fp, &pArchive->currentOffset); - BailError(err); - - /* go back and fill in the CRC */ - pRecord->recHeaderCRC = crc; - err = Nu_FSeek(fp, crcOffset, SEEK_SET); - BailError(err); - Nu_WriteTwo(pArchive, fp, pRecord->recHeaderCRC); - - /* - * All okay? - */ - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed writing late record header"); - goto bail; - } - - /* - * Update values for misc record fields. - */ - Assert(pRecord->fakeThreads == 0); - pRecord->recHeaderLength = - bytesWritten + pRecord->recTotalThreads * kNuThreadHeaderSize; - pRecord->recHeaderLength -= pRecord->fakeThreads * kNuThreadHeaderSize; - - err = Nu_ComputeThreadData(pArchive, pRecord); - BailError(err); - -bail: - return err; -} - - -/* - * Prepare for a "walk" through the records. This is useful for the - * "read the TOC as you go" method of archive use. - */ -static NuError Nu_RecordWalkPrepare(NuArchive* pArchive, NuRecord** ppRecord) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(ppRecord != NULL); - - DBUG(("--- walk prep\n")); - - *ppRecord = NULL; - - if (!pArchive->haveToc) { - /* might have tried and aborted earlier, rewind to start of records */ - err = Nu_RewindArchive(pArchive); - BailError(err); - } - -bail: - return err; -} - -/* - * Get the next record from the "orig" set in the archive. - * - * On entry, pArchive->archiveFp must point at the start of the next - * record. On exit, it will point past the end of the record (headers and - * all data) that we just read. - * - * If we have the TOC, we just pull it out of the structure. If we don't, - * we read it from the archive file, and add it to the TOC being - * constructed. - */ -static NuError Nu_RecordWalkGetNext(NuArchive* pArchive, NuRecord** ppRecord) -{ - NuError err = kNuErrNone; - - Assert(pArchive != NULL); - Assert(ppRecord != NULL); - - /*DBUG(("--- walk toc=%d\n", pArchive->haveToc));*/ - - if (pArchive->haveToc) { - if (*ppRecord == NULL) - *ppRecord = Nu_RecordSet_GetListHead(&pArchive->origRecordSet); - else - *ppRecord = (*ppRecord)->pNext; - } else { - *ppRecord = NULL; /* so we don't try to free it on exit */ - - /* allocate and fill in a new record */ - err = Nu_RecordNew(pArchive, ppRecord); - BailError(err); - - /* read data from archive file */ - err = Nu_ReadRecordHeader(pArchive, *ppRecord); - BailError(err); - err = Nu_ScanThreads(pArchive, *ppRecord, (*ppRecord)->recTotalThreads); - BailError(err); - - DBUG(("--- Found record '%s'\n", (*ppRecord)->filenameMOR)); - - /* add to list */ - err = Nu_RecordSet_AddRecord(&pArchive->origRecordSet, *ppRecord); - BailError(err); - } - -bail: - if (err != kNuErrNone && !pArchive->haveToc) { - /* on failure, free whatever we allocated */ - Nu_RecordFree(pArchive, *ppRecord); - *ppRecord = NULL; - } - return err; -} - -/* - * Finish off a successful record walk by noting that we now have a - * full table of contents. On an unsuccessful walk, blow away the TOC - * if we don't have all of it. - */ -static NuError Nu_RecordWalkFinish(NuArchive* pArchive, NuError walkErr) -{ - if (pArchive->haveToc) - return kNuErrNone; - - if (walkErr == kNuErrNone) { - pArchive->haveToc = true; - /* mark as loaded, even if there weren't any entries (e.g. new arc) */ - Nu_RecordSet_SetLoaded(&pArchive->origRecordSet, true); - return kNuErrNone; - } else { - pArchive->haveToc = false; /* redundant */ - return Nu_RecordSet_FreeAllRecords(pArchive, &pArchive->origRecordSet); - } -} - - -/* - * If we don't have the complete record listing from the archive in - * the "orig" record set, go get it. - * - * Uses the "record walk" functions, because they're there. - */ -NuError Nu_GetTOCIfNeeded(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - uint32_t count; - - Assert(pArchive != NULL); - - if (pArchive->haveToc) - goto bail; - - DBUG(("--- GetTOCIfNeeded\n")); - - err = Nu_RecordWalkPrepare(pArchive, &pRecord); - BailError(err); - - count = pArchive->masterHeader.mhTotalRecords; - while (count--) { - err = Nu_RecordWalkGetNext(pArchive, &pRecord); - BailError(err); - } - -bail: - (void) Nu_RecordWalkFinish(pArchive, err); - return err; -} - - - -/* - * =========================================================================== - * Streaming read-only operations - * =========================================================================== - */ - -/* - * Run through the entire archive, pulling out the header bits, skipping - * over the data bits, and calling "contentFunc" for each record. - */ -NuError Nu_StreamContents(NuArchive* pArchive, NuCallback contentFunc) -{ - NuError err = kNuErrNone; - NuRecord tmpRecord; - NuResult result; - uint32_t count; - - if (contentFunc == NULL) { - err = kNuErrInvalidArg; - goto bail; - } - - Nu_InitRecordContents(pArchive, &tmpRecord); - count = pArchive->masterHeader.mhTotalRecords; - - while (count--) { - err = Nu_ReadRecordHeader(pArchive, &tmpRecord); - BailError(err); - err = Nu_ScanThreads(pArchive, &tmpRecord, tmpRecord.recTotalThreads); - BailError(err); - - /*Nu_DebugDumpRecord(&tmpRecord); - printf("\n");*/ - - /* let them display the contents */ - result = (*contentFunc)(pArchive, &tmpRecord); - if (result == kNuAbort) { - err = kNuErrAborted; - goto bail; - } - - /* dispose of the entry */ - (void) Nu_FreeRecordContents(pArchive, &tmpRecord); - (void) Nu_InitRecordContents(pArchive, &tmpRecord); - } - -bail: - (void) Nu_FreeRecordContents(pArchive, &tmpRecord); - return err; -} - - -/* - * If we're trying to be compatible with ShrinkIt, and we tried to extract - * a record that had nothing in it but comments and filenames, then we need - * to create a zero-byte data file. - * - * GS/ShrinkIt v1.1 has a bug that causes it to store zero-byte data files - * (and, for that matter, zero-byte resource forks) without a thread header. - * It isn't able to extract them. This isn't so much a compatibility - * thing as it is a bug-workaround thing. - * - * The record's storage type should tell us if it was an extended file or - * a plain file. Not really important when extracting, but if we want - * to recreate the original we need to re-add the resource fork so - * NufxLib knows to make it an extended file. - */ -static NuError Nu_FakeZeroExtract(NuArchive* pArchive, NuRecord* pRecord, - int threadKind) -{ - NuError err; - NuThread fakeThread; - - Assert(pRecord != NULL); - - DBUG(("--- found empty record, creating zero-byte file (kind=0x%04x)\n", - threadKind)); - fakeThread.thThreadClass = kNuThreadClassData; - fakeThread.thThreadFormat = kNuThreadFormatUncompressed; - fakeThread.thThreadKind = threadKind; - fakeThread.thThreadCRC = kNuInitialThreadCRC; - fakeThread.thThreadEOF = 0; - fakeThread.thCompThreadEOF = 0; - - fakeThread.threadIdx = (NuThreadIdx)-1; /* shouldn't matter */ - fakeThread.actualThreadEOF = 0; - fakeThread.fileOffset = 0; /* shouldn't matter */ - fakeThread.used = false; - - err = Nu_ExtractThreadBulk(pArchive, pRecord, &fakeThread); - if (err == kNuErrSkipped) - err = Nu_SkipThread(pArchive, pRecord, &fakeThread); - - return err; -} - - -/* - * Run through the entire archive, extracting the contents. - */ -NuError Nu_StreamExtract(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - NuRecord tmpRecord; - Boolean needFakeData, needFakeRsrc; - uint32_t count; - long idx; - - /* reset this just to be safe */ - pArchive->lastDirCreatedUNI = NULL; - - Nu_InitRecordContents(pArchive, &tmpRecord); - count = pArchive->masterHeader.mhTotalRecords; - - while (count--) { - /* - * Read the record header (which includes the thread header blocks). - */ - err = Nu_ReadRecordHeader(pArchive, &tmpRecord); - BailError(err); - - /* - * We may need to pull the filename out of a thread, but we don't - * want to blow past any data while we do it. There's no really - * good way to deal with this, so we just assume that all NuFX - * applications are nice and put the filename thread first. - */ - for (idx = 0; idx < (long)tmpRecord.recTotalThreads; idx++) { - const NuThread* pThread = Nu_GetThread(&tmpRecord, idx); - - if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) - == kNuThreadIDFilename) - { - break; - } - } - /* if we have fn, read it; either way, leave idx pointing at next */ - if (idx < (long)tmpRecord.recTotalThreads) { - idx++; /* want count, not index */ - err = Nu_ScanThreads(pArchive, &tmpRecord, idx); - BailError(err); - } else - idx = 0; - if (tmpRecord.filenameMOR == NULL) { - Nu_ReportError(NU_BLOB, kNuErrNone, - "Couldn't find filename in record"); - err = kNuErrBadRecord; - goto bail; - } - - /*Nu_DebugDumpRecord(&tmpRecord); - printf("\n");*/ - - needFakeData = true; - needFakeRsrc = (tmpRecord.recStorageType == kNuStorageExtended); - - /* extract all relevant (remaining) threads */ - pArchive->lastFileCreatedUNI = NULL; - for ( ; idx < (long)tmpRecord.recTotalThreads; idx++) { - const NuThread* pThread = Nu_GetThread(&tmpRecord, idx); - - if (pThread->thThreadClass == kNuThreadClassData) { - if (pThread->thThreadKind == kNuThreadKindDataFork) { - needFakeData = false; - } else if (pThread->thThreadKind == kNuThreadKindRsrcFork) { - needFakeRsrc = false; - } else if (pThread->thThreadKind == kNuThreadKindDiskImage) { - /* needFakeRsrc shouldn't be set, but clear anyway */ - needFakeData = needFakeRsrc = false; - } - err = Nu_ExtractThreadBulk(pArchive, &tmpRecord, pThread); - if (err == kNuErrSkipped) { - err = Nu_SkipThread(pArchive, &tmpRecord, pThread); - BailError(err); - } else if (err != kNuErrNone) - goto bail; - } else { - DBUG(("IGNORING 0x%08lx from '%s'\n", - NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), - tmpRecord.filename)); - if (NuGetThreadID(pThread) != kNuThreadIDComment && - NuGetThreadID(pThread) != kNuThreadIDFilename) - { - /* unknown stuff in record, skip thread fakery */ - needFakeData = needFakeRsrc = false; - } - err = Nu_SkipThread(pArchive, &tmpRecord, pThread); - BailError(err); - } - } - - /* - * As in Nu_ExtractRecordByPtr, we need to synthesize empty forks for - * cases where GSHK omitted the data thread entirely. - */ - Assert(!pArchive->valMaskDataless || (!needFakeData && !needFakeRsrc)); - if (needFakeData) { - err = Nu_FakeZeroExtract(pArchive, &tmpRecord, - kNuThreadKindDataFork); - BailError(err); - } - if (needFakeRsrc) { - err = Nu_FakeZeroExtract(pArchive, &tmpRecord, - kNuThreadKindRsrcFork); - BailError(err); - } - - /* dispose of the entry */ - (void) Nu_FreeRecordContents(pArchive, &tmpRecord); - (void) Nu_InitRecordContents(pArchive, &tmpRecord); - } - -bail: - (void) Nu_FreeRecordContents(pArchive, &tmpRecord); - return err; -} - -/* - * Test the contents of an archive. Works just like extraction, but we - * don't store anything. - */ -NuError Nu_StreamTest(NuArchive* pArchive) -{ - NuError err; - - pArchive->testMode = true; - err = Nu_StreamExtract(pArchive); - pArchive->testMode = false; - return err; -} - - -/* - * =========================================================================== - * Non-streaming read-only operations - * =========================================================================== - */ - -/* - * Shove the archive table of contents through the callback function. - * - * This only walks through the "orig" list, so it does not reflect the - * results of un-flushed changes. - */ -NuError Nu_Contents(NuArchive* pArchive, NuCallback contentFunc) -{ - NuError err = kNuErrNone; - NuRecord* pRecord; - NuResult result; - uint32_t count; - - if (contentFunc == NULL) { - err = kNuErrInvalidArg; - goto bail; - } - - err = Nu_RecordWalkPrepare(pArchive, &pRecord); - BailError(err); - - count = pArchive->masterHeader.mhTotalRecords; - while (count--) { - err = Nu_RecordWalkGetNext(pArchive, &pRecord); - BailError(err); - - Assert(pRecord->filenameMOR != NULL); - result = (*contentFunc)(pArchive, pRecord); - if (result == kNuAbort) { - err = kNuErrAborted; - goto bail; - } - } - -bail: - (void) Nu_RecordWalkFinish(pArchive, err); - return err; -} - - -/* - * Extract all interesting threads from a record, given a NuRecord pointer - * into the archive data structure. - * - * This assumes random access, so it can't be used in streaming mode. - */ -static NuError Nu_ExtractRecordByPtr(NuArchive* pArchive, NuRecord* pRecord) -{ - NuError err = kNuErrNone; - Boolean needFakeData, needFakeRsrc; - uint32_t idx; - - needFakeData = true; - needFakeRsrc = (pRecord->recStorageType == kNuStorageExtended); - - Assert(!Nu_IsStreaming(pArchive)); /* we don't skip things we don't read */ - Assert(pRecord != NULL); - - /* extract all relevant threads */ - pArchive->lastFileCreatedUNI = NULL; - for (idx = 0; idx < pRecord->recTotalThreads; idx++) { - const NuThread* pThread = Nu_GetThread(pRecord, idx); - - if (pThread->thThreadClass == kNuThreadClassData) { - if (pThread->thThreadKind == kNuThreadKindDataFork) { - needFakeData = false; - } else if (pThread->thThreadKind == kNuThreadKindRsrcFork) { - needFakeRsrc = false; - } else if (pThread->thThreadKind == kNuThreadKindDiskImage) { - /* needFakeRsrc shouldn't be set, but clear anyway */ - needFakeData = needFakeRsrc = false; - } - err = Nu_ExtractThreadBulk(pArchive, pRecord, pThread); - if (err == kNuErrSkipped) { - err = Nu_SkipThread(pArchive, pRecord, pThread); - BailError(err); - } else if (err != kNuErrNone) - goto bail; - } else { - if (NuGetThreadID(pThread) != kNuThreadIDComment && - NuGetThreadID(pThread) != kNuThreadIDFilename) - { - /* - * This record has a thread we don't recognize. Disable - * the thread fakery to avoid doing anything weird -- we - * should only need to create zero-length files for - * simple file records. - */ - needFakeData = needFakeRsrc = false; - } - DBUG(("IGNORING 0x%08lx from '%s'\n", - NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), - pRecord->filenameMOR)); - } - } - - /* - * GSHK creates empty threads for zero-length forks. It doesn't always - * handle them correctly when extracting, so it appears this behavior - * may not be intentional. - * - * We need to create an empty file for whichever forks are missing. - * Could be the data fork, resource fork, or both. The only way to - * know what's expected is to examine the file's storage type. - * - * If valMaskDataless is enabled, this won't fire, because we will have - * "forged" the appropriate threads. - * - * Note there's another one of these below, in Nu_StreamExtract. - */ - Assert(!pArchive->valMaskDataless || (!needFakeData && !needFakeRsrc)); - if (needFakeData) { - err = Nu_FakeZeroExtract(pArchive, pRecord, kNuThreadKindDataFork); - BailError(err); - } - if (needFakeRsrc) { - err = Nu_FakeZeroExtract(pArchive, pRecord, kNuThreadKindRsrcFork); - BailError(err); - } - -bail: - return err; -} - - -/* - * Extract a big buncha files. - */ -NuError Nu_Extract(NuArchive* pArchive) -{ - NuError err; - NuRecord* pRecord = NULL; - uint32_t count; - long offset; - - /* reset this just to be safe */ - pArchive->lastDirCreatedUNI = NULL; - - err = Nu_RecordWalkPrepare(pArchive, &pRecord); - BailError(err); - - count = pArchive->masterHeader.mhTotalRecords; - while (count--) { - /* read the record and threads if we don't have them yet */ - err = Nu_RecordWalkGetNext(pArchive, &pRecord); - BailError(err); - - if (!pArchive->haveToc) { - /* remember where the end of the record is */ - err = Nu_FTell(pArchive->archiveFp, &offset); - BailError(err); - } - - /* extract one or more threads */ - err = Nu_ExtractRecordByPtr(pArchive, pRecord); - BailError(err); - - if (!pArchive->haveToc) { - /* line us back up so RecordWalkGetNext can read the record hdr */ - err = Nu_FSeek(pArchive->archiveFp, offset, SEEK_SET); - BailError(err); - } - } - -bail: - (void) Nu_RecordWalkFinish(pArchive, err); - return err; -} - - -/* - * Extract a single record. - */ -NuError Nu_ExtractRecord(NuArchive* pArchive, NuRecordIdx recIdx) -{ - NuError err; - NuRecord* pRecord; - - if (Nu_IsStreaming(pArchive)) - return kNuErrUsage; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* find the correct record by index */ - err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recIdx, &pRecord); - BailError(err); - Assert(pRecord != NULL); - - /* extract whatever looks promising */ - err = Nu_ExtractRecordByPtr(pArchive, pRecord); - BailError(err); - -bail: - return err; -} - - -/* - * Test the contents of an archive. Works just like extraction, but we - * don't store anything. - */ -NuError Nu_Test(NuArchive* pArchive) -{ - NuError err; - - pArchive->testMode = true; - err = Nu_Extract(pArchive); - pArchive->testMode = false; - return err; -} - -/* - * Test a single record. - */ -NuError Nu_TestRecord(NuArchive* pArchive, NuRecordIdx recIdx) -{ - NuError err; - NuRecord* pRecord; - - if (Nu_IsStreaming(pArchive)) - return kNuErrUsage; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* find the correct record by index */ - err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recIdx, &pRecord); - BailError(err); - Assert(pRecord != NULL); - - /* extract whatever looks promising */ - pArchive->testMode = true; - err = Nu_ExtractRecordByPtr(pArchive, pRecord); - pArchive->testMode = false; - BailError(err); - -bail: - return err; -} - - -/* - * Return a pointer to a NuRecord. - * - * This pulls the record out of the "orig" set, so it will work even - * for records that have been deleted. It will not reflect changes - * made by previous "write" calls, not even SetRecordAttr. - */ -NuError Nu_GetRecord(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecord** ppRecord) -{ - NuError err; - - if (recordIdx == 0 || ppRecord == NULL) - return kNuErrInvalidArg; - - if (Nu_IsStreaming(pArchive)) - return kNuErrUsage; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recordIdx, - (NuRecord**)ppRecord); - if (err == kNuErrNone) { - Assert(*ppRecord != NULL); - } - /* fall through with error */ - -bail: - return err; -} - -/* - * Find the recordIdx of a record by storage name. - */ -NuError Nu_GetRecordIdxByName(NuArchive* pArchive, const char* nameMOR, - NuRecordIdx* pRecordIdx) -{ - NuError err; - NuRecord* pRecord = NULL; - - if (pRecordIdx == NULL) - return kNuErrInvalidArg; - - if (Nu_IsStreaming(pArchive)) - return kNuErrUsage; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - err = Nu_RecordSet_FindByName(&pArchive->origRecordSet, nameMOR, &pRecord); - if (err == kNuErrNone) { - Assert(pRecord != NULL); - *pRecordIdx = pRecord->recordIdx; - } - /* fall through with error */ - -bail: - return err; -} - -/* - * Find the recordIdx of a record by zero-based position. - */ -NuError Nu_GetRecordIdxByPosition(NuArchive* pArchive, uint32_t position, - NuRecordIdx* pRecordIdx) -{ - NuError err; - const NuRecord* pRecord; - - if (pRecordIdx == NULL) - return kNuErrInvalidArg; - - if (Nu_IsStreaming(pArchive)) - return kNuErrUsage; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - if (position >= Nu_RecordSet_GetNumRecords(&pArchive->origRecordSet)) { - err = kNuErrRecordNotFound; - goto bail; - } - - pRecord = Nu_RecordSet_GetListHead(&pArchive->origRecordSet); - while (position--) { - Assert(pRecord->pNext != NULL); - pRecord = pRecord->pNext; - } - - *pRecordIdx = pRecord->recordIdx; - -bail: - return err; -} - - -/* - * =========================================================================== - * Read/write record operations (add, delete) - * =========================================================================== - */ - -/* - * Find an existing record somewhere in the archive. If the "copy" set - * exists it will be searched. If not, the "orig" set is searched, and - * if an entry is found a "copy" set will be created. - * - * The goal is to always return something from the "copy" set, which we - * could do easily by just creating the "copy" set and then searching in - * it. However, we don't want to create the "copy" set if we don't have - * to, so we search "orig" if "copy" doesn't exist yet. - * - * The record returned will always be from the "copy" set. An error result - * is returned if the record isn't found. - */ -NuError Nu_FindRecordForWriteByIdx(NuArchive* pArchive, NuRecordIdx recIdx, - NuRecord** ppFoundRecord) -{ - NuError err; - - Assert(pArchive != NULL); - Assert(ppFoundRecord != NULL); - - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - err = Nu_RecordSet_FindByIdx(&pArchive->copyRecordSet, recIdx, - ppFoundRecord); - } else { - Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); - err = Nu_RecordSet_FindByIdx(&pArchive->origRecordSet, recIdx, - ppFoundRecord); - *ppFoundRecord = NULL; /* can't delete from here */ - } - BailErrorQuiet(err); - - /* - * The record exists. If we were looking in the "orig" set, we have - * to create a "copy" set and return it from there. - */ - if (*ppFoundRecord == NULL) { - err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, - &pArchive->origRecordSet); - BailError(err); - err = Nu_RecordSet_FindByIdx(&pArchive->copyRecordSet, recIdx, - ppFoundRecord); - Assert(err == kNuErrNone && *ppFoundRecord != NULL); /* must succeed */ - BailError(err); - } - -bail: - return err; -} - - -/* - * Deal with the situation where we're trying to add a record with the - * same name as an existing record. The existing record can't be in the - * "new" list (that's handled differently) and can't already have been - * deleted. - * - * This will either delete the existing record or return with an error. - * - * If we decide to delete the record, and the "orig" record set was - * passed in, then the record will be deleted from the "copy" set (which - * will be created only if necessary). - */ -static NuError Nu_HandleAddDuplicateRecord(NuArchive* pArchive, - NuRecordSet* pRecordSet, NuRecord* pRecord, - const NuFileDetails* pFileDetails) -{ - NuError err = kNuErrNone; - NuErrorStatus errorStatus; - NuResult result; - - Assert(pRecordSet == &pArchive->origRecordSet || - pRecordSet == &pArchive->copyRecordSet); - Assert(pRecord != NULL); - Assert(pFileDetails != NULL); - Assert(pArchive->valAllowDuplicates == false); - - /* - * If "only update older" is set, check the dates. Reject the - * request if the archived file isn't older than the new file. This - * tells the application that the request was rejected, but it's - * okay for them to move on to the next file. - */ - if (pArchive->valOnlyUpdateOlder) { - if (!Nu_IsOlder(&pRecord->recModWhen, &pFileDetails->modWhen)) - return kNuErrNotNewer; - } - - /* - * The file exists when it shouldn't. Decide what to do, based - * on the options configured by the application. - * - * If they "might" allow overwrites, and they have an error-handling - * callback defined, call that to find out what they want to do - * here. Options include skipping or overwriting the record. - * - * We don't currently allow renaming of records, though I suppose we - * could. - */ - switch (pArchive->valHandleExisting) { - case kNuMaybeOverwrite: - if (pArchive->errorHandlerFunc != NULL) { - errorStatus.operation = kNuOpAdd; - errorStatus.err = kNuErrRecordExists; - errorStatus.sysErr = 0; - errorStatus.message = NULL; - errorStatus.pRecord = pRecord; - UNICHAR* pathnameUNI = - Nu_CopyMORToUNI(pFileDetails->storageNameMOR); - errorStatus.pathnameUNI = pathnameUNI; - errorStatus.origPathname = pFileDetails->origName; - errorStatus.filenameSeparator = - NuGetSepFromSysInfo(pFileDetails->fileSysInfo); - /*errorStatus.origArchiveTouched = false;*/ - errorStatus.canAbort = true; - errorStatus.canRetry = false; - errorStatus.canIgnore = false; - errorStatus.canSkip = true; - errorStatus.canRename = false; - errorStatus.canOverwrite = true; - - result = (*pArchive->errorHandlerFunc)(pArchive, &errorStatus); - Nu_Free(pArchive, pathnameUNI); - - switch (result) { - case kNuAbort: - err = kNuErrAborted; - goto bail; - case kNuSkip: - err = kNuErrSkipped; - goto bail; - case kNuOverwrite: - break; /* fall back into main code */ - case kNuRetry: - case kNuRename: - case kNuIgnore: - default: - err = kNuErrSyntax; - Nu_ReportError(NU_BLOB, err, - "Wasn't expecting result %d here", result); - goto bail; - } - } else { - /* no error handler, treat like NeverOverwrite */ - err = kNuErrSkipped; - goto bail; - } - break; - case kNuNeverOverwrite: - err = kNuErrSkipped; - goto bail; - case kNuMustOverwrite: - case kNuAlwaysOverwrite: - /* fall through to record deletion */ - break; - default: - Assert(0); - err = kNuErrInternal; - goto bail; - } - - err = kNuErrNone; - - /* - * We're going to overwrite the existing record. To do this, we have - * to start by deleting it from the "copy" list. - * - * If the copy set doesn't yet exist, we have to create it and find - * the record in the new set. - */ - if (pRecordSet == &pArchive->origRecordSet) { - Assert(!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)); - err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, - &pArchive->origRecordSet); - BailError(err); - - err = Nu_RecordSet_FindByIdx(&pArchive->copyRecordSet, - pRecord->recordIdx, &pRecord); - Assert(err == kNuErrNone && pRecord != NULL); /* must succeed */ - BailError(err); - } - - DBUG(("+++ deleting record %ld\n", pRecord->recordIdx)); - err = Nu_RecordSet_DeleteRecord(pArchive,&pArchive->copyRecordSet, pRecord); - BailError(err); - -bail: - return err; -} - -/* - * Create a new record, filling in most of the blanks from "pFileDetails". - * - * The filename in pFileDetails->storageName will be remembered. If no - * filename thread is added to this record before the next Flush call, a - * filename thread will be generated from this name. - * - * This always creates a "version 3" record, regardless of what else is - * in the archive. The filename is always in a thread. - * - * On success, the NuRecordIdx of the newly-created record will be placed - * in "*pRecordIdx", and the NuThreadIdx of the filename thread will be - * placed in "*pThreadIdx". If "*ppNewRecord" is non-NULL, it gets a pointer - * to the newly-created record (this isn't part of the external interface). - */ -NuError Nu_AddRecord(NuArchive* pArchive, const NuFileDetails* pFileDetails, - NuRecordIdx* pRecordIdx, NuRecord** ppNewRecord) -{ - NuError err; - NuRecord* pNewRecord = NULL; - - if (pFileDetails == NULL || pFileDetails->storageNameMOR == NULL || - pFileDetails->storageNameMOR[0] == '\0' || - NuGetSepFromSysInfo(pFileDetails->fileSysInfo) == 0) - /* pRecordIdx may be NULL */ - /* ppNewRecord may be NULL */ - { - err = kNuErrInvalidArg; - goto bail; - } - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* NuFX spec forbids leading fssep chars */ - if (pFileDetails->storageNameMOR[0] == - NuGetSepFromSysInfo(pFileDetails->fileSysInfo)) - { - err = kNuErrLeadingFssep; - goto bail; - } - - /* - * If requested, look for an existing record. Look in the "copy" - * list if we have it (so we don't complain if they've already deleted - * the record), or in the "orig" list if we don't. Look in the "new" - * list to see if it clashes with something we've just added. - * - * If this is a brand-new archive, there won't be an "orig" list - * either. - */ - if (!pArchive->valAllowDuplicates) { - NuRecordSet* pRecordSet; - NuRecord* pFoundRecord; - - pRecordSet = &pArchive->copyRecordSet; - if (!Nu_RecordSet_GetLoaded(pRecordSet)) - pRecordSet = &pArchive->origRecordSet; - Assert(Nu_RecordSet_GetLoaded(pRecordSet)); - err = Nu_RecordSet_FindByName(pRecordSet, pFileDetails->storageNameMOR, - &pFoundRecord); - if (err == kNuErrNone) { - /* handle the existing record */ - DBUG(("--- Duplicate record found (%06ld) '%s'\n", - pFoundRecord->recordIdx, pFoundRecord->filenameMOR)); - err = Nu_HandleAddDuplicateRecord(pArchive, pRecordSet, - pFoundRecord, pFileDetails); - if (err != kNuErrNone) { - /* for whatever reason, we're not replacing it */ - DBUG(("--- Returning err=%d\n", err)); - goto bail; - } - } else { - /* if we *must* replace an existing file, we fail now */ - if (pArchive->valHandleExisting == kNuMustOverwrite) { - DBUG(("+++ can't freshen nonexistent '%s'\n", - pFileDetails->storageName)); - err = kNuErrDuplicateNotFound; - goto bail; - } - } - - if (Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) { - err = Nu_RecordSet_FindByName(&pArchive->newRecordSet, - pFileDetails->storageNameMOR, &pFoundRecord); - if (err == kNuErrNone) { - /* we can't delete from the "new" list, so return an error */ - err = kNuErrRecordExists; - goto bail; - } - } - - /* clear "err" so we can continue */ - err = kNuErrNone; - } - - /* - * Prepare the new record structure. - */ - err = Nu_RecordNew(pArchive, &pNewRecord); - BailError(err); - (void) Nu_InitRecordContents(pArchive, pNewRecord); - memcpy(pNewRecord->recNufxID, kNufxID, kNufxIDLen); - /*pNewRecord->recHeaderCRC*/ - /*pNewRecord->recAttribCount*/ - pNewRecord->recVersionNumber = kNuOurRecordVersion; - pNewRecord->recTotalThreads = 0; - pNewRecord->recFileSysID = pFileDetails->fileSysID; - pNewRecord->recFileSysInfo = pFileDetails->fileSysInfo; - pNewRecord->recAccess = pFileDetails->access; - pNewRecord->recFileType = pFileDetails->fileType; - pNewRecord->recExtraType = pFileDetails->extraType; - pNewRecord->recStorageType = pFileDetails->storageType; - pNewRecord->recCreateWhen = pFileDetails->createWhen; - pNewRecord->recModWhen = pFileDetails->modWhen; - pNewRecord->recArchiveWhen = pFileDetails->archiveWhen; - pNewRecord->recOptionSize = 0; - pNewRecord->extraCount = 0; - pNewRecord->recFilenameLength = 0; - - pNewRecord->recordIdx = Nu_GetNextRecordIdx(pArchive); - pNewRecord->threadFilenameMOR = NULL; - pNewRecord->newFilenameMOR = strdup(pFileDetails->storageNameMOR); - pNewRecord->filenameMOR = pNewRecord->newFilenameMOR; - pNewRecord->recHeaderLength = -1; - pNewRecord->totalCompLength = 0; - pNewRecord->fakeThreads = 0; - pNewRecord->fileOffset = -1; - - /* - * Add it to the "new" record set. - */ - err = Nu_RecordSet_AddRecord(&pArchive->newRecordSet, pNewRecord); - BailError(err); - - /* return values */ - if (pRecordIdx != NULL) - *pRecordIdx = pNewRecord->recordIdx; - if (ppNewRecord != NULL) - *ppNewRecord = pNewRecord; - -bail: - return err; -} - - -/* - * Add a new "add file" thread mod to the specified record. - * - * The caller should have already verified that there isn't another - * "add file" thread mod with the same ThreadID. - */ -static NuError Nu_AddFileThreadMod(NuArchive* pArchive, NuRecord* pRecord, - const UNICHAR* pathnameUNI, const NuFileDetails* pFileDetails, - Boolean fromRsrcFork) -{ - NuError err; - NuThreadFormat threadFormat; - NuDataSource* pDataSource = NULL; - NuThreadMod* pThreadMod = NULL; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(pathnameUNI != NULL); - Assert(pFileDetails != NULL); - Assert(fromRsrcFork == true || fromRsrcFork == false); - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - - /* decide if this should be compressed; we know source isn't */ - if (Nu_IsCompressibleThreadID(pFileDetails->threadID)) - threadFormat = Nu_ConvertCompressValToFormat(pArchive, - pArchive->valDataCompression); - else - threadFormat = kNuThreadFormatUncompressed; - - /* create a data source for this file, which is assumed uncompressed */ - err = Nu_DataSourceFile_New(kNuThreadFormatUncompressed, 0, - pathnameUNI, fromRsrcFork, &pDataSource); - BailError(err); - - /* create a new ThreadMod */ - err = Nu_ThreadModAdd_New(pArchive, pFileDetails->threadID, threadFormat, - pDataSource, &pThreadMod); - BailError(err); - Assert(pThreadMod != NULL); - /*pDataSource = NULL;*/ /* ThreadModAdd_New makes a copy */ - - /* add the thread mod to the record */ - Nu_RecordAddThreadMod(pRecord, pThreadMod); - pThreadMod = NULL; /* don't free on exit */ - -bail: - if (pDataSource != NULL) - Nu_DataSourceFree(pDataSource); - if (pThreadMod != NULL) - Nu_ThreadModFree(pArchive, pThreadMod); - return err; -} - -/* - * Make note of a file to add. This goes beyond AddRecord and AddThread - * calls by searching the list of newly-added files for matching pairs - * of data and rsrc forks. This is independent of the "overwrite existing - * files" feature. The comparison is made based on storageName. - * - * "fromRsrcFork" tells us how to open the source file, not what type - * of thread the file should be stored as. - * - * If "pRecordIdx" is non-NULL, it will receive the newly assigned recordID. - */ -NuError Nu_AddFile(NuArchive* pArchive, const UNICHAR* pathnameUNI, - const NuFileDetails* pFileDetails, Boolean fromRsrcFork, - NuRecordIdx* pRecordIdx) -{ - NuError err = kNuErrNone; - NuRecordIdx recordIdx = 0; - NuRecord* pRecord; - - if (pathnameUNI == NULL || pFileDetails == NULL || - !(fromRsrcFork == true || fromRsrcFork == false)) - { - return kNuErrInvalidArg; - } - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - if (pFileDetails->storageNameMOR == NULL) { - err = kNuErrInvalidArg; - Nu_ReportError(NU_BLOB, err, "Must specify storageName"); - goto bail; - } - if (pFileDetails->storageNameMOR[0] == - NuGetSepFromSysInfo(pFileDetails->fileSysInfo)) - { - err = kNuErrLeadingFssep; - goto bail; - } - - DBUG(("+++ ADDING '%s' (%s) 0x%02lx 0x%04lx threadID=0x%08lx\n", - pathnameUNI, pFileDetails->storageName, pFileDetails->fileType, - pFileDetails->extraType, pFileDetails->threadID)); - - /* - * See if there's another record among the "new additions" with the - * same storageName and compatible threads. - * - * If found, add a new thread in that record. If an incompatibility - * exists (same fork already present, disk image is there, etc), either - * create a new record or return with an error. - * - * We want to search from the *end* of the "new" list, so that if - * duplicates are allowed we find the entry most likely to be paired - * up with the fork currently being added. - */ - if (Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) { - NuRecord* pNewRecord; - - err = Nu_RecordSet_ReverseFindByName(&pArchive->newRecordSet, - pFileDetails->storageNameMOR, &pNewRecord); - if (err == kNuErrNone) { - /* is it okay to add it here? */ - err = Nu_OkayToAddThread(pArchive, pNewRecord, - pFileDetails->threadID); - - if (err == kNuErrNone) { - /* okay to add it to this record */ - DBUG((" attaching to existing record %06ld\n", - pNewRecord->recordIdx)); - err = Nu_AddFileThreadMod(pArchive, pNewRecord, pathnameUNI, - pFileDetails, fromRsrcFork); - BailError(err); - recordIdx = pNewRecord->recordIdx; - goto bail; /* we're done! */ - } - - err = kNuErrNone; /* go a little farther */ - - /* - * We found a brand-new record with the same name, but we - * can't add this fork to that record. We can't delete the - * item from the "new" list, so we can ignore HandleExisting. - * If we don't allow duplicates, return an error; if we do, - * then just continue with the normal processing path. - */ - if (!pArchive->valAllowDuplicates) { - DBUG(("+++ found matching record in new list, no dups\n")); - err = kNuErrRecordExists; - goto bail; - } - - } else if (err == kNuErrRecNameNotFound) { - /* no match in "new" list, fall through to normal processing */ - err = kNuErrNone; - } else { - /* general failure */ - goto bail; - } - } - - /* - * Wasn't found, invoke Nu_AddRecord. This will search through the - * existing records, using the "allow duplicates" flag to cope with - * any matches it finds. On success, we should have a brand-new record - * to play with. - */ - err = Nu_AddRecord(pArchive, pFileDetails, &recordIdx, &pRecord); - BailError(err); - DBUG(("--- Added new record %06ld\n", recordIdx)); - - /* - * Got the record, now add a data file thread. - */ - err = Nu_AddFileThreadMod(pArchive, pRecord, pathnameUNI, pFileDetails, - fromRsrcFork); - BailError(err); - -bail: - if (err == kNuErrNone && pRecordIdx != NULL) - *pRecordIdx = recordIdx; - - return err; -} - - -/* - * Rename a record. There are three situations: - * - * (1) Record has the filename in a thread, and the field has enough - * room to hold the new name. For this case we add an "update" threadMod - * with the new data. - * (2) Record has the filename in a thread, and there is not enough room - * to hold the new name. Here, we add a "delete" threadMod for the - * existing filename, and add an "add" threadMod for the new. - * (3) Record stores the filename in the header. We zero out the filename - * and add a filename thread. - * - * We don't actually check to see if the filename is changing. If you - * want to rename something to the same thing, go right ahead. (This - * provides a way for applications to "filter" records that have filenames - * in the headers instead of a thread.) - * - * BUG: we shouldn't allow a disk image to be renamed to have a complex - * path name (e.g. "dir1:dir2:foo"). However, we may not be able to catch - * that here depending on pending operations. - * - * We might also want to screen out trailing fssep chars, though the NuFX - * spec doesn't say they're illegal. - */ -NuError Nu_Rename(NuArchive* pArchive, NuRecordIdx recIdx, - const char* pathnameMOR, char fssepMOR) -{ - NuError err; - NuRecord* pRecord; - NuThread* pFilenameThread; - const NuThreadMod* pThreadMod; - NuThreadMod* pNewThreadMod = NULL; - NuDataSource* pDataSource = NULL; - long requiredCapacity, existingCapacity, newCapacity; - Boolean doDelete, doAdd, doUpdate; - - if (recIdx == 0 || pathnameMOR == NULL || pathnameMOR[0] == '\0' || - fssepMOR == '\0') - { - return kNuErrInvalidArg; - } - - if (pathnameMOR[0] == fssepMOR) { - err = kNuErrLeadingFssep; - Nu_ReportError(NU_BLOB, err, "rename path"); - goto bail; - } - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* find the record in the "copy" set */ - err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); - BailError(err); - Assert(pRecord != NULL); - - /* look for a filename thread */ - err = Nu_FindThreadByID(pRecord, kNuThreadIDFilename, &pFilenameThread); - - if (err != kNuErrNone) - pFilenameThread = NULL; - else if (err == kNuErrNone && pRecord->pThreadMods) { - /* found a thread, check to see if it has been deleted (or modifed) */ - Assert(pFilenameThread != NULL); - pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, - pFilenameThread->threadIdx); - if (pThreadMod != NULL) { - DBUG(("--- tried to modify threadIdx %ld, which has already been\n", - pFilenameThread->threadIdx)); - err = kNuErrModThreadChange; - goto bail; - } - } - - /* - * Looks like we're okay so far. Figure out what to do. - */ - doDelete = doAdd = doUpdate = false; - newCapacity = existingCapacity = 0; - requiredCapacity = strlen(pathnameMOR); - - if (pFilenameThread != NULL) { - existingCapacity = pFilenameThread->thCompThreadEOF; - if (existingCapacity >= requiredCapacity) { - doUpdate = true; - newCapacity = existingCapacity; - } else { - doDelete = doAdd = true; - /* make sure they have a few bytes of leeway */ - /*newCapacity = (requiredCapacity + kNuDefaultFilenameThreadSize) & - (~(kNuDefaultFilenameThreadSize-1));*/ - newCapacity = requiredCapacity + 8; - } - } else { - doAdd = true; - /*newCapacity = (requiredCapacity + kNuDefaultFilenameThreadSize) & - (~(kNuDefaultFilenameThreadSize-1));*/ - newCapacity = requiredCapacity + 8; - } - - Assert(doAdd || doDelete || doUpdate); - Assert(doDelete == false || doAdd == true); - - /* create a data source for the filename, if needed */ - if (doAdd || doUpdate) { - Assert(newCapacity); - err = Nu_DataSourceBuffer_New(kNuThreadFormatUncompressed, - newCapacity, (const uint8_t*)strdup(pathnameMOR), 0, - requiredCapacity /*(strlen)*/, Nu_InternalFreeCallback, - &pDataSource); - BailError(err); - } - - if (doDelete) { - err = Nu_ThreadModDelete_New(pArchive, pFilenameThread->threadIdx, - kNuThreadIDFilename, &pNewThreadMod); - BailError(err); - Nu_RecordAddThreadMod(pRecord, pNewThreadMod); - pNewThreadMod = NULL; /* successful, don't free */ - } - - if (doAdd) { - err = Nu_ThreadModAdd_New(pArchive, kNuThreadIDFilename, - kNuThreadFormatUncompressed, pDataSource, &pNewThreadMod); - BailError(err); - /*pDataSource = NULL;*/ /* ThreadModAdd_New makes a copy */ - Nu_RecordAddThreadMod(pRecord, pNewThreadMod); - pNewThreadMod = NULL; /* successful, don't free */ - } - - if (doUpdate) { - err = Nu_ThreadModUpdate_New(pArchive, pFilenameThread->threadIdx, - pDataSource, &pNewThreadMod); - BailError(err); - /*pDataSource = NULL;*/ /* ThreadModAdd_New makes a copy */ - Nu_RecordAddThreadMod(pRecord, pNewThreadMod); - pNewThreadMod = NULL; /* successful, don't free */ - } - - DBUG(("--- renaming '%s' to '%s' with delete=%d add=%d update=%d\n", - pRecord->filenameMOR, pathnameMOR, doDelete, doAdd, doUpdate)); - - /* - * Update the fssep, if necessary. (This is slightly silly -- we - * have to rewrite the record header anyway since we're changing - * threads around.) - */ - if (NuGetSepFromSysInfo(pRecord->recFileSysInfo) != fssepMOR) { - DBUG(("--- and updating the fssep\n")); - pRecord->recFileSysInfo = NuSetSepInSysInfo(pRecord->recFileSysInfo, - fssepMOR); - pRecord->dirtyHeader = true; - } - - /* if we had a header filename, mark it for oblivion */ - if (pFilenameThread == NULL) { - DBUG(("+++ rename gonna drop the filename\n")); - pRecord->dropRecFilename = true; - } - -bail: - Nu_ThreadModFree(pArchive, pNewThreadMod); - Nu_DataSourceFree(pDataSource); - return err; -} - - -/* - * Update a record's attributes with the contents of pRecordAttr. - */ -NuError Nu_SetRecordAttr(NuArchive* pArchive, NuRecordIdx recordIdx, - const NuRecordAttr* pRecordAttr) -{ - NuError err; - NuRecord* pRecord; - - if (pRecordAttr == NULL) - return kNuErrInvalidArg; - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* pull the record out of the "copy" set */ - err = Nu_FindRecordForWriteByIdx(pArchive, recordIdx, &pRecord); - BailError(err); - - Assert(pRecord != NULL); - pRecord->recFileSysID = pRecordAttr->fileSysID; - /*pRecord->recFileSysInfo = pRecordAttr->fileSysInfo;*/ - pRecord->recAccess = pRecordAttr->access; - pRecord->recFileType = pRecordAttr->fileType; - pRecord->recExtraType = pRecordAttr->extraType; - pRecord->recCreateWhen = pRecordAttr->createWhen; - pRecord->recModWhen = pRecordAttr->modWhen; - pRecord->recArchiveWhen = pRecordAttr->archiveWhen; - pRecord->dirtyHeader = true; - -bail: - return err; -} - - -/* - * Bulk-delete several records, using the selection filter callback. - */ -NuError Nu_Delete(NuArchive* pArchive) -{ - NuError err; - NuSelectionProposal selProposal; - NuRecord* pNextRecord; - NuRecord* pRecord; - NuResult result; - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* - * If we don't yet have a copy set, make one. - */ - if (!Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, - &pArchive->origRecordSet); - BailError(err); - } - - /* - * Run through the copy set. This is different from most other - * operations, which run through the "orig" set. However, since - * we're not interested in allowing the user to delete things that - * have already been deleted, we might as well use this set. - */ - pNextRecord = Nu_RecordSet_GetListHead(&pArchive->copyRecordSet); - while (pNextRecord != NULL) { - pRecord = pNextRecord; - pNextRecord = pRecord->pNext; - - /* - * Deletion of modified records (thread adds, deletes, or updates) - * isn't allowed. There's no point in showing the record to the - * user. - */ - if (pRecord->pThreadMods != NULL) { - DBUG(("+++ Skipping delete on a modified record\n")); - continue; - } - - /* - * If a selection filter is defined, allow the user the opportunity - * to select which files will be deleted, or abort the entire - * operation. - */ - if (pArchive->selectionFilterFunc != NULL) { - selProposal.pRecord = pRecord; - selProposal.pThread = pRecord->pThreads; /* doesn't matter */ - result = (*pArchive->selectionFilterFunc)(pArchive, &selProposal); - - if (result == kNuSkip) - continue; - if (result == kNuAbort) { - err = kNuErrAborted; - goto bail; - } - } - - /* - * Do we want to allow this? (Same test as for DeleteRecord.) - */ - if (pRecord->pThreadMods != NULL || pRecord->dirtyHeader) { - DBUG(("--- Tried to delete a modified record\n")); - err = kNuErrModRecChange; - goto bail; - } - - err = Nu_RecordSet_DeleteRecord(pArchive, &pArchive->copyRecordSet, - pRecord); - BailError(err); - } - -bail: - return err; -} - -/* - * Delete an entire record. - */ -NuError Nu_DeleteRecord(NuArchive* pArchive, NuRecordIdx recIdx) -{ - NuError err; - NuRecord* pRecord; - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); - BailError(err); - - /* - * Deletion of modified records (thread adds, deletes, or updates) isn't - * allowed. It probably wouldn't be hard to handle, but it's pointless. - * Preventing the action maintains our general semantics of disallowing - * conflicting actions on the same object. - * - * We also block it if the header is dirty (e.g. they changed the - * record's filetype). This isn't necessary for correct operation, - * but again it maintains the semantics. - */ - if (pRecord->pThreadMods != NULL || pRecord->dirtyHeader) { - DBUG(("--- Tried to delete a modified record\n")); - err = kNuErrModRecChange; - goto bail; - } - - err = Nu_RecordSet_DeleteRecord(pArchive,&pArchive->copyRecordSet, pRecord); - BailError(err); - -bail: - return err; -} - diff --git a/ciderpress/nufxlib/SourceSink.c b/ciderpress/nufxlib/SourceSink.c deleted file mode 100644 index e5d6313..0000000 --- a/ciderpress/nufxlib/SourceSink.c +++ /dev/null @@ -1,858 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Implementation of DataSource and DataSink objects. - */ -#include "NufxLibPriv.h" - - - -/* - * =========================================================================== - * NuDataSource - * =========================================================================== - */ - -/* - * Allocate a new DataSource structure. - */ -static NuError Nu_DataSourceNew(NuDataSource** ppDataSource) -{ - Assert(ppDataSource != NULL); - - *ppDataSource = Nu_Malloc(NULL, sizeof(**ppDataSource)); - if (*ppDataSource == NULL) - return kNuErrMalloc; - - (*ppDataSource)->sourceType = kNuDataSourceUnknown; - - return kNuErrNone; -} - - -/* - * Make a copy of a DataSource. Actually just increments a reference count. - * - * What we *really* want to be doing is copying the structure (since we - * can't guarantee copy-on-write semantics for the fields without adding - * more stuff) and refcounting the underlying resource, so that "auto-free" - * semantics work out right. - * - * We're okay for now, since for the most part we only do work on one - * copy of each. (I wish I could remember why this copying thing was - * needed in the first place.) Buffer sources are a little scary since - * they include a "curOffset" value. - * - * Returns NULL on error. - */ -NuDataSource* Nu_DataSourceCopy(NuDataSource* pDataSource) -{ - Assert(pDataSource->common.refCount >= 1); - pDataSource->common.refCount++; - return pDataSource; - -#if 0 /* we used to copy them -- very bad idea */ - NuDataSource* pNewDataSource; - - Assert(pDataSource != NULL); - - if (Nu_DataSourceNew(&pNewDataSource) != kNuErrNone) - return NULL; - Assert(pNewDataSource != NULL); - - /* this gets most of it */ - memcpy(pNewDataSource, pDataSource, sizeof(*pNewDataSource)); - - /* copy anything we're sure to free up */ - if (pDataSource->sourceType == kNuDataSourceFromFile) { - Assert(pDataSource->fromFile.fp == NULL); /* does this matter? */ - pNewDataSource->fromFile.pathname = - strdup(pDataSource->fromFile.pathname); - } - - /* don't let the original free up the resources */ - if (pDataSource->common.doClose) { - DBUG(("--- clearing doClose on source-copy of data source\n")); - pDataSource->common.doClose = false; - } - - return pNewDataSource; -#endif -} - - -/* - * Free a data source structure, and any type-specific elements. - */ -NuError Nu_DataSourceFree(NuDataSource* pDataSource) -{ - if (pDataSource == NULL) - return kNuErrNone; - - Assert(pDataSource->common.refCount > 0); - if (pDataSource->common.refCount > 1) { - pDataSource->common.refCount--; - return kNuErrNone; - } - - switch (pDataSource->sourceType) { - case kNuDataSourceFromFile: - Nu_Free(NULL, pDataSource->fromFile.pathnameUNI); - if (pDataSource->fromFile.fp != NULL) { - fclose(pDataSource->fromFile.fp); - pDataSource->fromFile.fp = NULL; - } - break; - case kNuDataSourceFromFP: - if (pDataSource->fromFP.fcloseFunc != NULL && - pDataSource->fromFP.fp != NULL) - { - (*pDataSource->fromFP.fcloseFunc)(NULL, pDataSource->fromFP.fp); - pDataSource->fromFP.fp = NULL; - } - break; - case kNuDataSourceFromBuffer: - if (pDataSource->fromBuffer.freeFunc != NULL) { - (*pDataSource->fromBuffer.freeFunc)(NULL, - (void*)pDataSource->fromBuffer.buffer); - pDataSource->fromBuffer.buffer = NULL; - } - break; - case kNuDataSourceUnknown: - break; - default: - Assert(0); - return kNuErrInternal; - } - - Nu_Free(NULL, pDataSource); - return kNuErrNone; -} - - -/* - * Create a data source for an unopened file. - */ -NuError Nu_DataSourceFile_New(NuThreadFormat threadFormat, uint32_t otherLen, - const UNICHAR* pathnameUNI, Boolean isFromRsrcFork, - NuDataSource** ppDataSource) -{ - NuError err; - - if (pathnameUNI == NULL || - !(isFromRsrcFork == true || isFromRsrcFork == false) || - ppDataSource == NULL) - { - return kNuErrInvalidArg; - } - - err = Nu_DataSourceNew(ppDataSource); - BailErrorQuiet(err); - - (*ppDataSource)->common.sourceType = kNuDataSourceFromFile; - (*ppDataSource)->common.threadFormat = threadFormat; - (*ppDataSource)->common.rawCrc = 0; - (*ppDataSource)->common.dataLen = 0; /* to be filled in later */ - (*ppDataSource)->common.otherLen = otherLen; - (*ppDataSource)->common.refCount = 1; - - (*ppDataSource)->fromFile.pathnameUNI = strdup(pathnameUNI); - (*ppDataSource)->fromFile.fromRsrcFork = isFromRsrcFork; - (*ppDataSource)->fromFile.fp = NULL; /* to be filled in later */ - -bail: - return err; -} - - -/* - * Create a data source for an open file at a specific offset. The FILE* - * must be seekable. - */ -NuError Nu_DataSourceFP_New(NuThreadFormat threadFormat, uint32_t otherLen, - FILE* fp, long offset, long length, NuCallback fcloseFunc, - NuDataSource** ppDataSource) -{ - NuError err; - - if (fp == NULL || offset < 0 || length < 0 || - ppDataSource == NULL) - { - return kNuErrInvalidArg; - } - - if (otherLen && otherLen < (uint32_t)length) { - DBUG(("--- rejecting FP len=%ld other=%ld\n", length, otherLen)); - err = kNuErrPreSizeOverflow; - goto bail; - } - - err = Nu_DataSourceNew(ppDataSource); - BailErrorQuiet(err); - - (*ppDataSource)->common.sourceType = kNuDataSourceFromFP; - (*ppDataSource)->common.threadFormat = threadFormat; - (*ppDataSource)->common.rawCrc = 0; - (*ppDataSource)->common.dataLen = length; - (*ppDataSource)->common.otherLen = otherLen; - (*ppDataSource)->common.refCount = 1; - - (*ppDataSource)->fromFP.fp = fp; - (*ppDataSource)->fromFP.offset = offset; - (*ppDataSource)->fromFP.fcloseFunc = fcloseFunc; - -bail: - return err; -} - - -/* - * Create a data source for a buffer. - * - * We allow "buffer" to be NULL so long as "offset" and "length" are also - * NULL. This is useful for creating empty pre-sized buffers, such as - * blank comment fields. - */ -NuError Nu_DataSourceBuffer_New(NuThreadFormat threadFormat, uint32_t otherLen, - const uint8_t* buffer, long offset, long length, NuCallback freeFunc, - NuDataSource** ppDataSource) -{ - NuError err; - - if (offset < 0 || length < 0 || ppDataSource == NULL) - return kNuErrInvalidArg; - if (buffer == NULL && (offset != 0 || length != 0)) - return kNuErrInvalidArg; - - if (buffer == NULL) { - DBUG(("+++ zeroing freeFunc for empty-buffer DataSource\n")); - freeFunc = NULL; - } - - if (otherLen && otherLen < (uint32_t)length) { - DBUG(("--- rejecting buffer len=%ld other=%ld\n", length, otherLen)); - err = kNuErrPreSizeOverflow; - goto bail; - } - - err = Nu_DataSourceNew(ppDataSource); - BailErrorQuiet(err); - - (*ppDataSource)->common.sourceType = kNuDataSourceFromBuffer; - (*ppDataSource)->common.threadFormat = threadFormat; - (*ppDataSource)->common.rawCrc = 0; - (*ppDataSource)->common.dataLen = length; - (*ppDataSource)->common.otherLen = otherLen; - (*ppDataSource)->common.refCount = 1; - - (*ppDataSource)->fromBuffer.buffer = buffer; - (*ppDataSource)->fromBuffer.offset = offset; - (*ppDataSource)->fromBuffer.curOffset = offset; - (*ppDataSource)->fromBuffer.curDataLen = length; - (*ppDataSource)->fromBuffer.freeFunc = freeFunc; - -bail: - return err; -} - - -/* - * Get the type of a NuDataSource. - */ -NuDataSourceType Nu_DataSourceGetType(const NuDataSource* pDataSource) -{ - Assert(pDataSource != NULL); - return pDataSource->sourceType; -} - -/* - * Get the threadFormat for a data source. - */ -NuThreadFormat Nu_DataSourceGetThreadFormat(const NuDataSource* pDataSource) -{ - Assert(pDataSource != NULL); - return pDataSource->common.threadFormat; -} - -/* - * Get "dataLen" from a dataSource. - */ -uint32_t Nu_DataSourceGetDataLen(const NuDataSource* pDataSource) -{ - Assert(pDataSource != NULL); - - if (pDataSource->sourceType == kNuDataSourceFromFile) { - /* dataLen can only be valid if file has been opened */ - Assert(pDataSource->fromFile.fp != NULL); - } - - return pDataSource->common.dataLen; -} - -/* - * Get "otherLen" from a dataSource. - */ -uint32_t Nu_DataSourceGetOtherLen(const NuDataSource* pDataSource) -{ - Assert(pDataSource != NULL); - return pDataSource->common.otherLen; -} - -/* - * Change the "otherLen" value. - */ -void Nu_DataSourceSetOtherLen(NuDataSource* pDataSource, long otherLen) -{ - Assert(pDataSource != NULL && otherLen > 0); - pDataSource->common.otherLen = otherLen; -} - - -/* - * Get the "raw CRC" value. - */ -uint16_t Nu_DataSourceGetRawCrc(const NuDataSource* pDataSource) -{ - Assert(pDataSource != NULL); - return pDataSource->common.rawCrc; -} - -/* - * Set the "raw CRC" value. You would want to do this if the input was - * already-compressed data, and you wanted to propagate the thread CRC. - */ -void Nu_DataSourceSetRawCrc(NuDataSource* pDataSource, uint16_t crc) -{ - Assert(pDataSource != NULL); - pDataSource->common.rawCrc = crc; -} - - -/* - * Prepare a data source for action. - */ -NuError Nu_DataSourcePrepareInput(NuArchive* pArchive, - NuDataSource* pDataSource) -{ - NuError err = kNuErrNone; - FILE* fileFp = NULL; - - /* - * Doesn't apply to buffer sources. - */ - if (Nu_DataSourceGetType(pDataSource) == kNuDataSourceFromBuffer) - goto bail; - - /* - * FP sources can be used several times, so we need to seek them - * to the correct offset before we begin. - */ - if (Nu_DataSourceGetType(pDataSource) == kNuDataSourceFromFP) { - err = Nu_FSeek(pDataSource->fromFP.fp, pDataSource->fromFP.offset, - SEEK_SET); - goto bail; /* return this err */ - } - - /* - * We're adding from a file on disk. Open it. - */ - err = Nu_OpenInputFile(pArchive, - pDataSource->fromFile.pathnameUNI, - pDataSource->fromFile.fromRsrcFork, &fileFp); - BailError(err); - - Assert(fileFp != NULL); - pDataSource->fromFile.fp = fileFp; - err = Nu_GetFileLength(pArchive, fileFp, - (long*)&pDataSource->common.dataLen); - BailError(err); - - if (pDataSource->common.otherLen && - pDataSource->common.otherLen < pDataSource->common.dataLen) - { - DBUG(("--- Uh oh, looks like file len is too small for presized\n")); - } - -bail: - return err; -} - - -/* - * Un-prepare a data source. This really only affects "file" sources, and - * is only here so we don't end up with 200+ FILE* structures hanging around. - * If we don't do this, the first resource we're likely to run out of is - * file descriptors. - * - * It's not necessary to do this in all error cases -- the DataSource "Free" - * call will take care of this eventually -- but for normal operation on - * a large number of files, it's vital. - */ -void Nu_DataSourceUnPrepareInput(NuArchive* pArchive, NuDataSource* pDataSource) -{ - if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile) - return; - - if (pDataSource->fromFile.fp != NULL) { - fclose(pDataSource->fromFile.fp); - pDataSource->fromFile.fp = NULL; - pDataSource->common.dataLen = 0; - } -} - - -/* - * Get the pathname from a "from-file" dataSource. Returned string is UTF-8. - */ -const char* Nu_DataSourceFile_GetPathname(NuDataSource* pDataSource) -{ - Assert(pDataSource != NULL); - Assert(pDataSource->sourceType == kNuDataSourceFromFile); - Assert(pDataSource->fromFile.pathnameUNI != NULL); - - return pDataSource->fromFile.pathnameUNI; -} - - -/* - * Read a block of data from a dataSource. - */ -NuError Nu_DataSourceGetBlock(NuDataSource* pDataSource, uint8_t* buf, - uint32_t len) -{ - NuError err; - - Assert(pDataSource != NULL); - Assert(buf != NULL); - Assert(len > 0); - - switch (pDataSource->sourceType) { - case kNuDataSourceFromFile: - Assert(pDataSource->fromFile.fp != NULL); - err = Nu_FRead(pDataSource->fromFile.fp, buf, len); - if (feof(pDataSource->fromFile.fp)) - Nu_ReportError(NU_NILBLOB, err, "EOF hit unexpectedly"); - return err; - - case kNuDataSourceFromFP: - err = Nu_FRead(pDataSource->fromFP.fp, buf, len); - if (feof(pDataSource->fromFP.fp)) - Nu_ReportError(NU_NILBLOB, err, "EOF hit unexpectedly"); - return err; - - case kNuDataSourceFromBuffer: - if ((long)len > pDataSource->fromBuffer.curDataLen) { - /* buffer underrun */ - return kNuErrBufferUnderrun; - } - memcpy(buf, - pDataSource->fromBuffer.buffer + pDataSource->fromBuffer.curOffset, - len); - pDataSource->fromBuffer.curOffset += len; - pDataSource->fromBuffer.curDataLen -= len; - return kNuErrNone; - - default: - Assert(false); - return kNuErrInternal; - } -} - - -/* - * Rewind a data source to the start of its input. - */ -NuError Nu_DataSourceRewind(NuDataSource* pDataSource) -{ - NuError err; - - Assert(pDataSource != NULL); - - switch (pDataSource->sourceType) { - case kNuDataSourceFromFile: - Assert(pDataSource->fromFile.fp != NULL); - err = Nu_FSeek(pDataSource->fromFile.fp, 0, SEEK_SET); - break; /* fall through with error */ - case kNuDataSourceFromFP: - err = Nu_FSeek(pDataSource->fromFP.fp, pDataSource->fromFP.offset, - SEEK_SET); - break; /* fall through with error */ - case kNuDataSourceFromBuffer: - pDataSource->fromBuffer.curOffset = pDataSource->fromBuffer.offset; - pDataSource->fromBuffer.curDataLen = pDataSource->common.dataLen; - err = kNuErrNone; - break; - default: - Assert(false); - err = kNuErrInternal; - } - - return err; -} - - -/* - * =========================================================================== - * NuDataSink - * =========================================================================== - */ - -/* - * Allocate a new DataSink structure. - */ -static NuError Nu_DataSinkNew(NuDataSink** ppDataSink) -{ - Assert(ppDataSink != NULL); - - *ppDataSink = Nu_Malloc(NULL, sizeof(**ppDataSink)); - if (*ppDataSink == NULL) - return kNuErrMalloc; - - (*ppDataSink)->sinkType = kNuDataSinkUnknown; - - return kNuErrNone; -} - - -/* - * Free a data sink structure, and any type-specific elements. - */ -NuError Nu_DataSinkFree(NuDataSink* pDataSink) -{ - if (pDataSink == NULL) - return kNuErrNone; - - switch (pDataSink->sinkType) { - case kNuDataSinkToFile: - Nu_DataSinkFile_Close(pDataSink); - Nu_Free(NULL, pDataSink->toFile.pathnameUNI); - break; - case kNuDataSinkToFP: - break; - case kNuDataSinkToBuffer: - break; - case kNuDataSinkToVoid: - break; - case kNuDataSinkUnknown: - break; - default: - Assert(0); - return kNuErrInternal; - } - - Nu_Free(NULL, pDataSink); - return kNuErrNone; -} - - -/* - * Create a data sink for an unopened file. - */ -NuError Nu_DataSinkFile_New(Boolean doExpand, NuValue convertEOL, - const UNICHAR* pathnameUNI, UNICHAR fssep, NuDataSink** ppDataSink) -{ - NuError err; - - if ((doExpand != true && doExpand != false) || - (convertEOL != kNuConvertOff && convertEOL != kNuConvertOn && - convertEOL != kNuConvertAuto) || - pathnameUNI == NULL || - fssep == 0 || - ppDataSink == NULL) - { - return kNuErrInvalidArg; - } - - err = Nu_DataSinkNew(ppDataSink); - BailErrorQuiet(err); - - (*ppDataSink)->common.sinkType = kNuDataSinkToFile; - (*ppDataSink)->common.doExpand = doExpand; - if (doExpand) - (*ppDataSink)->common.convertEOL = convertEOL; - else - (*ppDataSink)->common.convertEOL = kNuConvertOff; - (*ppDataSink)->common.outCount = 0; - (*ppDataSink)->toFile.pathnameUNI = strdup(pathnameUNI); - (*ppDataSink)->toFile.fssep = fssep; - - (*ppDataSink)->toFile.fp = NULL; - -bail: - return err; -} - - -/* - * Create a data sink based on a file pointer. - */ -NuError Nu_DataSinkFP_New(Boolean doExpand, NuValue convertEOL, FILE* fp, - NuDataSink** ppDataSink) -{ - NuError err; - - if ((doExpand != true && doExpand != false) || - (convertEOL != kNuConvertOff && convertEOL != kNuConvertOn && - convertEOL != kNuConvertAuto) || - fp == NULL || - ppDataSink == NULL) - { - return kNuErrInvalidArg; - } - - err = Nu_DataSinkNew(ppDataSink); - BailErrorQuiet(err); - - (*ppDataSink)->common.sinkType = kNuDataSinkToFP; - (*ppDataSink)->common.doExpand = doExpand; - if (doExpand) - (*ppDataSink)->common.convertEOL = convertEOL; - else - (*ppDataSink)->common.convertEOL = kNuConvertOff; - (*ppDataSink)->common.outCount = 0; - (*ppDataSink)->toFP.fp = fp; - -bail: - return err; -} - - -/* - * Create a data sink for a buffer in memory. - */ -NuError Nu_DataSinkBuffer_New(Boolean doExpand, NuValue convertEOL, - uint8_t* buffer, uint32_t bufLen, NuDataSink** ppDataSink) -{ - NuError err; - - if ((doExpand != true && doExpand != false) || - (convertEOL != kNuConvertOff && convertEOL != kNuConvertOn && - convertEOL != kNuConvertAuto) || - buffer == NULL || - bufLen == 0 || - ppDataSink == NULL) - { - return kNuErrInvalidArg; - } - - err = Nu_DataSinkNew(ppDataSink); - BailErrorQuiet(err); - - (*ppDataSink)->common.sinkType = kNuDataSinkToBuffer; - (*ppDataSink)->common.doExpand = doExpand; - if (doExpand) - (*ppDataSink)->common.convertEOL = convertEOL; - else - (*ppDataSink)->common.convertEOL = kNuConvertOff; - (*ppDataSink)->common.convertEOL = convertEOL; - (*ppDataSink)->common.outCount = 0; - (*ppDataSink)->toBuffer.buffer = buffer; - (*ppDataSink)->toBuffer.bufLen = bufLen; - (*ppDataSink)->toBuffer.stickyErr = kNuErrNone; - -bail: - return err; -} - - -/* - * Create a data sink that goes nowhere. - */ -NuError Nu_DataSinkVoid_New(Boolean doExpand, NuValue convertEOL, - NuDataSink** ppDataSink) -{ - NuError err; - - Assert(doExpand == true || doExpand == false); - Assert(ppDataSink != NULL); - - err = Nu_DataSinkNew(ppDataSink); - BailErrorQuiet(err); - - (*ppDataSink)->common.sinkType = kNuDataSinkToVoid; - (*ppDataSink)->common.doExpand = doExpand; - (*ppDataSink)->common.convertEOL = convertEOL; - (*ppDataSink)->common.outCount = 0; - -bail: - return err; -} - - -/* - * Get the type of a NuDataSink. - */ -NuDataSinkType Nu_DataSinkGetType(const NuDataSink* pDataSink) -{ - Assert(pDataSink != NULL); - return pDataSink->sinkType; -} - - -/* - * Return the "doExpand" parameter from any kind of sink. - */ -Boolean Nu_DataSinkGetDoExpand(const NuDataSink* pDataSink) -{ - return pDataSink->common.doExpand; -} - -/* - * Return the "convertEOL" parameter from any kind of sink. - */ -NuValue Nu_DataSinkGetConvertEOL(const NuDataSink* pDataSink) -{ - return pDataSink->common.convertEOL; -} - -/* - * Return the #of bytes written to the sink. - */ -uint32_t Nu_DataSinkGetOutCount(const NuDataSink* pDataSink) -{ - return pDataSink->common.outCount; -} - - -/* - * Get "pathname" from a to-file sink. Returned string is UTF-8. - */ -const char* Nu_DataSinkFile_GetPathname(const NuDataSink* pDataSink) -{ - Assert(pDataSink != NULL); - Assert(pDataSink->sinkType == kNuDataSinkToFile); - - return pDataSink->toFile.pathnameUNI; -} - -/* - * Get "fssep" from a to-file sink. - */ -UNICHAR Nu_DataSinkFile_GetFssep(const NuDataSink* pDataSink) -{ - Assert(pDataSink != NULL); - Assert(pDataSink->sinkType == kNuDataSinkToFile); - - return pDataSink->toFile.fssep; -} - -/* - * Get the "fp" for a file sink. - */ -FILE* Nu_DataSinkFile_GetFP(const NuDataSink* pDataSink) -{ - Assert(pDataSink != NULL); - Assert(pDataSink->sinkType == kNuDataSinkToFile); - - return pDataSink->toFile.fp; -} - -/* - * Set the "fp" for a file sink. - */ -void Nu_DataSinkFile_SetFP(NuDataSink* pDataSink, FILE* fp) -{ - Assert(pDataSink != NULL); - Assert(pDataSink->sinkType == kNuDataSinkToFile); - - pDataSink->toFile.fp = fp; -} - -/* - * Close a to-file sink. - */ -void Nu_DataSinkFile_Close(NuDataSink* pDataSink) -{ - Assert(pDataSink != NULL); - - if (pDataSink->toFile.fp != NULL) { - fclose(pDataSink->toFile.fp); - pDataSink->toFile.fp = NULL; - } -} - - -/* - * Write a block of data to a DataSink. - */ -NuError Nu_DataSinkPutBlock(NuDataSink* pDataSink, const uint8_t* buf, - uint32_t len) -{ - NuError err; - - Assert(pDataSink != NULL); - Assert(buf != NULL); - Assert(len > 0); - - switch (pDataSink->sinkType) { - case kNuDataSinkToFile: - Assert(pDataSink->toFile.fp != NULL); - err = Nu_FWrite(pDataSink->toFile.fp, buf, len); - if (err != kNuErrNone) - return err; - break; - case kNuDataSinkToFP: - Assert(pDataSink->toFP.fp != NULL); - err = Nu_FWrite(pDataSink->toFP.fp, buf, len); - if (err != kNuErrNone) - return err; - break; - case kNuDataSinkToBuffer: - if (len > pDataSink->toBuffer.bufLen) { - /* buffer overrun; set a "sticky" error, like FILE* does */ - err = kNuErrBufferOverrun; - pDataSink->toBuffer.stickyErr = err; - return err; - } - memcpy(pDataSink->toBuffer.buffer, buf, len); - pDataSink->toBuffer.buffer += len; - pDataSink->toBuffer.bufLen -= len; - break; - case kNuDataSinkToVoid: - /* do nothing */ - break; - default: - Assert(false); - return kNuErrInternal; - } - pDataSink->common.outCount += len; - return kNuErrNone; -} - - -/* - * Figure out if one of our earlier writes has failed. - */ -NuError Nu_DataSinkGetError(NuDataSink* pDataSink) -{ - NuError err = kNuErrNone; - - Assert(pDataSink != NULL); - - switch (pDataSink->sinkType) { - case kNuDataSinkToFile: - if (ferror(pDataSink->toFile.fp)) - err = kNuErrFileWrite; - break; - case kNuDataSinkToFP: - if (ferror(pDataSink->toFP.fp)) - err = kNuErrFileWrite; - break; - case kNuDataSinkToBuffer: - err = pDataSink->toBuffer.stickyErr; - break; - case kNuDataSinkToVoid: - /* do nothing */ - break; - default: - Assert(false); - err = kNuErrInternal; - break; - } - - return err; -} - diff --git a/ciderpress/nufxlib/Squeeze.c b/ciderpress/nufxlib/Squeeze.c deleted file mode 100644 index a4c339f..0000000 --- a/ciderpress/nufxlib/Squeeze.c +++ /dev/null @@ -1,1131 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Huffman/RLE "squeeze" compression, based on SQ/USQ. This format is - * listed in the NuFX documentation, but to my knowledge has never - * actually been used (until now). Neither P8 ShrinkIt v3.4 nor II Unshrink - * handle the format correctly, so this is really only useful as an - * experiment. - * - * The algorithm appears to date back to the CP/M days. This implementation - * is based on "xsq"/"xusq" v1.7u by Richard Greenlaw (from December 1982). - * The code was also present in ARC v5.x. - * - * The "nusq.c" implementation found in NuLib was by Marcel J.E. Mol, - * who got it from Don Elton's sq3/usq2 programs for the Apple II. - * - * The SQ file format begins with this: - * +00 magic number (0xff76) - * +02 checksum on uncompressed data - * +04 filename, ending with \0 - * The NuFX format skips the above, starting immediately after it: - * +00 node count - * +02 node value array [node count], two bytes each - * +xx data immediately follows array - * - * NuFX drops the magic number, checksum, and filename from the header, - * since (with v3 records) all three are redundant. You can enable this - * if you want to experiment with SQ-compatible output. - */ -#include "NufxLibPriv.h" - -#ifdef ENABLE_SQ - -/* if this is defined, create and unpack the full SQ header (debugging only) */ -/* #define FULL_SQ_HEADER */ - - -#define kNuSQMagic 0xff76 /* magic value for file header */ -#define kNuSQRLEDelim 0x90 /* RLE delimiter */ -#define kNuSQEOFToken 256 /* distinguished stop symbol */ -#define kNuSQNumVals 257 /* 256 symbols + stop */ - - -/* - * =========================================================================== - * Compression - * =========================================================================== - */ - -#define kNuSQNoChild (-1) /* indicates end of path through tree */ -#define kNuSQNumNodes (kNuSQNumVals + kNuSQNumVals -1) -#define kNuSQMaxCount 65535 /* max value you can store in 16 bits */ - -/* states for the RLE encoding */ -typedef enum { - kNuSQRLEStateUnknown = 0, - - kNuSQRLEStateNoHist, /* nothing yet */ - kNuSQRLEStateSentChar, /* lastchar set, no lookahead yet */ - kNuSQRLEStateSendNewC, /* found run of two, send 2nd w/o DLE */ - kNuSQRLEStateSendCnt, /* newchar set, DLE sent, send count next */ -} NuSQRLEState; - -/* nodes in the Huffman encoding tree */ -typedef struct EncTreeNode { - int weight; /* #of appearances */ - int tdepth; /* length on longest path in tree */ - int lchild, rchild; /* indexes to next level */ -} EncTreeNode; - -/* - * State during compression. - */ -typedef struct SQState { - NuArchive* pArchive; - int doCalcCRC; /* boolean; if set, compute CRC on input */ - uint16_t crc; - - NuStraw* pStraw; - long uncompRemaining; - - #ifdef FULL_SQ_HEADER - uint16_t checksum; - #endif - - /* - * RLE state stuff. - */ - NuSQRLEState rleState; - int lastSym; - int likeCount; - - /* - * Huffman state stuff. - */ - EncTreeNode node[kNuSQNumNodes]; - - int treeHead; /* index to head node of final tree */ - - /* encoding table */ - int codeLen[kNuSQNumVals]; /* number of bits in code for symbol N */ - uint16_t code[kNuSQNumVals]; /* bits for symbol N (first bit in lsb) */ - uint16_t tmpCode; /* temporary code value */ -} SQState; - - -/* - * Get the next byte from the input straw. Also updates the checksum - * and SQ CRC, if "doCalcCRC" is set to true. - * - * This isn't exactly fast, but then this isn't exactly a fast algorithm, - * and there's not much point in optimizing something that isn't going - * to get used much. - * - * Returns kNuSQEOFToken as the value when we're out of data. - */ -static NuError Nu_SQGetcCRC(SQState* pSqState, int* pSym) -{ - NuError err; - uint8_t c; - - if (!pSqState->uncompRemaining) { - *pSym = kNuSQEOFToken; - return kNuErrNone; - } - - err = Nu_StrawRead(pSqState->pArchive, pSqState->pStraw, &c, 1); - if (err == kNuErrNone) { - if (pSqState->doCalcCRC) { - #ifdef FULL_SQ_HEADER - pSqState->checksum += c; - #endif - pSqState->crc = Nu_CalcCRC16(pSqState->crc, &c, 1); - } - *pSym = c; - pSqState->uncompRemaining--; - } - - return err; -} - -/* - * Get the next byte from the post-RLE input stream. - * - * Returns kNuSQEOFToken in "*pSum" when we reach the end of the input. - */ -static NuError Nu_SQGetcRLE(SQState* pSqState, int* pSym) -{ - NuError err = kNuErrNone; - int likeCount, newSym; - - switch (pSqState->rleState) { - case kNuSQRLEStateNoHist: - /* No relevant history */ - pSqState->rleState = kNuSQRLEStateSentChar; - err = Nu_SQGetcCRC(pSqState, pSym); - pSqState->lastSym = *pSym; - break; - - case kNuSQRLEStateSentChar: - /* lastChar is set, need lookahead */ - switch (pSqState->lastSym) { - case kNuSQRLEDelim: - /* send all DLEs escaped; note this is horrible for a run of DLEs */ - pSqState->rleState = kNuSQRLEStateNoHist; - *pSym = 0; /* zero len is how we define an escaped DLE */ - break; - case kNuSQEOFToken: - *pSym = kNuSQEOFToken; - break; - default: - /* - * Try for a run, using the character we previous read as - * the base. Thus, if the next character we read matches, - * we have a run of two. The count describes the total - * length of the run, including the character we've already - * emitted. - */ - likeCount = 0; - do { - likeCount++; - err = Nu_SQGetcCRC(pSqState, &newSym); - if (err != kNuErrNone) - goto bail; - } while (newSym == pSqState->lastSym && likeCount < 255); - - switch (likeCount) { - case 1: - /* not a run, return first one we got */ - pSqState->lastSym = newSym; - *pSym = newSym; - break; - case 2: - /* not long enough for run; return second one next time thru */ - pSqState->rleState = kNuSQRLEStateSendNewC; - *pSym = pSqState->lastSym; /* 1st new one */ - pSqState->lastSym = newSym; /* 2nd new one */ - break; - default: - pSqState->rleState = kNuSQRLEStateSendCnt; - pSqState->likeCount = likeCount; - pSqState->lastSym = newSym; /* 1st one after the run */ - *pSym = kNuSQRLEDelim; - break; - } - } - break; - - case kNuSQRLEStateSendNewC: - /* send first char past a run of two */ - pSqState->rleState = kNuSQRLEStateSentChar; - *pSym = pSqState->lastSym; - break; - - case kNuSQRLEStateSendCnt: - /* Sent DLE for repeat sequence, send count */ - pSqState->rleState = kNuSQRLEStateSendNewC; - *pSym = pSqState->likeCount; - break; - - default: - { - NuArchive* pArchive = pSqState->pArchive; - - err = kNuErrInternal; - Nu_ReportError(NU_BLOB, err, "invalid state %d in SQ RLE encode", - pSqState->rleState); - break; - } - } - -bail: - return err; -} - - -/* - * Comment from xsq.c: - * - * This translation uses the Huffman algorithm to develop a - * binary tree representing the decoding information for - * a variable length bit string code for each input value. - * Each string's length is in inverse proportion to its - * frequency of appearance in the incoming data stream. - * The encoding table is derived from the decoding table. - * - * The range of valid values into the Huffman algorithm are - * the values of a byte stored in an integer plus the special - * endfile value chosen to be an adjacent value. Overall, 0-SPEOF. - * - * The "node" array of structures contains the nodes of the - * binary tree. The first NUMVALS nodes are the leaves of the - * tree and represent the values of the data bytes being - * encoded and the special endfile, SPEOF. - * The remaining nodes become the internal nodes of the tree. - * - * In the original design it was believed that - * a Huffman code would fit in the same number of - * bits that will hold the sum of all the counts. - * That was disproven by a user's file and was a rare but - * infamous bug. This version attempts to choose among equally - * weighted subtrees according to their maximum depths to avoid - * unnecessarily long codes. In case that is not sufficient - * to guarantee codes <= 16 bits long, we initially scale - * the counts so the total fits in an unsigned integer, but - * if codes longer than 16 bits are generated the counts are - * rescaled to a lower ceiling and code generation is retried. - */ - -/* - * Return the greater of two integers. - */ -static int Nu_SQMax(int a, int b) -{ - if (a > b) - return a; - else - return b; -} - -/* - * Compare two trees, if a > b return true, else return false. - * Priority is given to weight, then depth. "a" and "b" are heaps, - * so we only need to look at the root element. - */ -static int Nu_SQCmpTrees(SQState* pSqState, int a, int b) -{ - if (pSqState->node[a].weight > pSqState->node[b].weight) - return true; - if (pSqState->node[a].weight == pSqState->node[b].weight) - if (pSqState->node[a].tdepth > pSqState->node[b].tdepth) - return true; - return false; -} - -/* - * heap() and adjust() maintain a list of binary trees as a - * heap with the top indexing the binary tree on the list - * which has the least weight or, in case of equal weights, - * least depth in its longest path. The depth part is not - * strictly necessary, but tends to avoid long codes which - * might provoke rescaling. - */ - -/* - * Recursively make a heap from a heap with a new top. - */ -static void Nu_SQHeapAdjust(SQState* pSqState, int list[], int top, int bottom) -{ - int k, temp; - - k = 2 * top + 1; /* left child of top */ - temp = list[top]; /* remember root node of top tree */ - if (k <= bottom) { - if (k < bottom && Nu_SQCmpTrees(pSqState, list[k], list[k + 1])) - k++; - - /* k indexes "smaller" child (in heap of trees) of top */ - /* now make top index "smaller" of old top and smallest child */ - if (Nu_SQCmpTrees(pSqState, temp, list[k])) { - list[top] = list[k]; - list[k] = temp; - /* Make the changed list a heap */ - Nu_SQHeapAdjust(pSqState, list, k, bottom); /*recursive*/ - } - } -} - -/* - * Create a heap. - */ -static void Nu_SQHeap(SQState* pSqState, int list[], int length) -{ - int i; - - for (i = (length - 2) / 2; i >= 0; i--) - Nu_SQHeapAdjust(pSqState, list, i, length - 1); -} - - -/* - * Build the encoding tree. - * - * HUFFMAN ALGORITHM: develops the single element trees - * into a single binary tree by forming subtrees rooted in - * interior nodes having weights equal to the sum of weights of all - * their descendents and having depth counts indicating the - * depth of their longest paths. - * - * When all trees have been formed into a single tree satisfying - * the heap property (on weight, with depth as a tie breaker) - * then the binary code assigned to a leaf (value to be encoded) - * is then the series of left (0) and right (1) - * paths leading from the root to the leaf. - * Note that trees are removed from the heaped list by - * moving the last element over the top element and - * reheaping the shorter list. - */ -static void Nu_SQBuildTree(SQState* pSqState, int list[], int len) -{ - int freenode; /* next free node in tree */ - EncTreeNode* frnp; /* free node pointer */ - int lch, rch; /* temporaries for left, right children */ - - /* - * Initialize index to next available (non-leaf) node. - * Lower numbered nodes correspond to leaves (data values). - */ - freenode = kNuSQNumVals; - - while (len > 1) { - /* - * Take from list two btrees with least weight - * and build an interior node pointing to them. - * This forms a new tree. - */ - lch = list[0]; /* This one will be left child */ - - /* delete top (least) tree from the list of trees */ - list[0] = list[--len]; - Nu_SQHeapAdjust(pSqState, list, 0, len - 1); - - /* Take new top (least) tree. Reuse list slot later */ - rch = list[0]; /* This one will be right child */ - - /* - * Form new tree from the two least trees using - * a free node as root. Put the new tree in the list. - */ - frnp = &pSqState->node[freenode]; /* address of next free node */ - list[0] = freenode++; /* put at top for now */ - frnp->lchild = lch; - frnp->rchild = rch; - frnp->weight = - pSqState->node[lch].weight + pSqState->node[rch].weight; - frnp->tdepth = 1 + Nu_SQMax(pSqState->node[lch].tdepth, - pSqState->node[rch].tdepth); - - /* reheap list to get least tree at top*/ - Nu_SQHeapAdjust(pSqState, list, 0, len - 1); - } - - pSqState->treeHead = list[0]; /* head of final tree */ -} - - -/* - * Recursive routine to walk the indicated subtree and level - * and maintain the current path code in bstree. When a leaf - * is found the entire code string and length are put into - * the encoding table entry for the leaf's data value . - * - * Returns zero on success, nonzero if codes are too long. - */ -static int Nu_SQBuildEncTable(SQState* pSqState, int level, int root) -{ - int l, r; - - l = pSqState->node[root].lchild; - r = pSqState->node[root].rchild; - - if (l == kNuSQNoChild && r == kNuSQNoChild) { - /* Leaf. Previous path determines bit string - * code of length level (bits 0 to level - 1). - * Ensures unused code bits are zero. - */ - pSqState->codeLen[root] = level; - pSqState->code[root] = - pSqState->tmpCode & (((uint16_t)~0) >> (16 - level)); - return (level > 16) ? -1 : 0; - } else { - if (l != kNuSQNoChild) { - /* Clear path bit and continue deeper */ - pSqState->tmpCode &= ~(1 << level); - /* NOTE RECURSION */ - if (Nu_SQBuildEncTable(pSqState, level + 1, l) != 0) - return -1; - } - if (r != kNuSQNoChild) { - /* Set path bit and continue deeper */ - pSqState->tmpCode |= 1 << level; - /* NOTE RECURSION */ - if (Nu_SQBuildEncTable(pSqState, level + 1, r) != 0) - return -1; - } - } - - return 0; /* if we got here we're ok so far */ -} - - -/* - * The count of number of occurrances of each input value - * have already been prevented from exceeding MAXCOUNT. - * Now we must scale them so that their sum doesn't exceed - * ceiling and yet no non-zero count can become zero. - * This scaling prevents errors in the weights of the - * interior nodes of the Huffman tree and also ensures that - * the codes will fit in an unsigned integer. Rescaling is - * used if necessary to limit the code length. - */ -static void Nu_SQScale(SQState* pSqState, int ceiling) -{ - int i; - int wt, ovflw, divisor; - uint16_t sum; - int increased; /* flag */ - - do { - for (i = sum = ovflw = 0; i < kNuSQNumVals; i++) { - if (pSqState->node[i].weight > (ceiling - sum)) - ovflw++; - sum += pSqState->node[i].weight; - } - - divisor = ovflw + 1; /* use the high 16 bits of the sum */ - - /* Ensure no non-zero values are lost */ - increased = false; - for (i = 0; i < kNuSQNumVals; i++) { - wt = pSqState->node[i].weight; - if (wt < divisor && wt != 0) { - /* Don't fail to provide a code if it's used at all */ - pSqState->node[i].weight = divisor; - increased = true; - } - } - } while(increased); - - /* scaling factor choosen and minimums are set; now do the downscale */ - if (divisor > 1) { - for (i = 0; i < kNuSQNumVals; i++) - pSqState->node[i].weight /= divisor; - } -} - -/* - * Build a frequency table from the post-RLE input stream, then generate - * an encoding tree from the results. - */ -static NuError Nu_SQComputeHuffTree(SQState* pSqState) -{ - NuError err = kNuErrNone; - int btreeList[kNuSQNumVals]; /* list of intermediate binary trees */ - int listLen; /* length of btreeList */ - int ceiling; /* limit for scaling */ - int i, sym, result; - - /* init tree */ - for (i = 0; i < kNuSQNumNodes; i++) { - pSqState->node[i].weight = 0; - pSqState->node[i].tdepth = 0; - pSqState->node[i].lchild = kNuSQNoChild; - pSqState->node[i].rchild = kNuSQNoChild; - } - - DBUG(("+++ SQ scanning...\n")); - - do { - int* pWeight; - - err = Nu_SQGetcRLE(pSqState, &sym); - if (err != kNuErrNone) - goto bail; - - Assert(sym >= 0 && sym <= kNuSQEOFToken); - pWeight = &pSqState->node[(unsigned)sym].weight; - if (*pWeight != kNuSQMaxCount) - (*pWeight)++; - } while (sym != kNuSQEOFToken); - - DBUG(("+++ SQ generating tree...\n")); - - ceiling = kNuSQMaxCount; - - do { - if (ceiling != kNuSQMaxCount) { - DBUG(("+++ SQ rescaling\n")); - } - - /* pick a divisor and scale everything to fit in "ceiling" */ - Nu_SQScale(pSqState, ceiling); - - ceiling /= 2; /* in case we need to rescale */ - - /* - * Build list of single node binary trees having - * leaves for the input values with non-zero counts - */ - for (i = listLen = 0; i < kNuSQNumVals; i++) { - if (pSqState->node[i].weight != 0) { - pSqState->node[i].tdepth = 0; - btreeList[listLen++] = i; - } - } - - /* - * Arrange list of trees into a heap with the entry - * indexing the node with the least weight a the top. - */ - Nu_SQHeap(pSqState, btreeList, listLen); - - /* convert the list of trees to a single decoding tree */ - Nu_SQBuildTree(pSqState, btreeList, listLen); - - /* initialize encoding table */ - for (i = 0; i < kNuSQNumVals; i++) - pSqState->codeLen[i] = 0; - - /* - * Recursively build the encoding table; returns non-zero (failure) - * if any code is > 16 bits long. - */ - result = Nu_SQBuildEncTable(pSqState, 0, pSqState->treeHead); - } while (result != 0); - -#if 0 -{ - int jj; - printf("init_huff\n"); - for (jj = 0; jj < kNuSQNumNodes; jj++) { - printf("NODE %d: w=%d d=%d l=%d r=%d\n", jj, - pSqState->node[jj].weight, - pSqState->node[jj].tdepth, - pSqState->node[jj].lchild, - pSqState->node[jj].rchild); - } -} -#endif - -bail: - return err; -} - - -/* - * Compress data from input to output, using the values in the "code" - * and "codeLen" arrays. - */ -static NuError Nu_SQCompressInput(SQState* pSqState, FILE* fp, - long* pCompressedLen) -{ - NuError err = kNuErrNone; - int sym = kNuSQEOFToken-1; - uint32_t bits, code; /* must hold at least 23 bits */ - int codeLen, gotbits; - long compressedLen; - - DBUG(("+++ SQ compressing\n")); - - Assert(sizeof(bits) >= 4); - compressedLen = *pCompressedLen; - - bits = 0; - gotbits = 0; - while (sym != kNuSQEOFToken) { - err = Nu_SQGetcRLE(pSqState, &sym); - if (err != kNuErrNone) - goto bail; - - code = pSqState->code[sym]; - codeLen = pSqState->codeLen[sym]; - - bits |= code << gotbits; - gotbits += codeLen; - - /* if we have more than a byte, output it */ - while (gotbits > 7) { - putc(bits & 0xff, fp); - compressedLen++; - bits >>= 8; - gotbits -= 8; - } - } - - if (gotbits) { - Assert(gotbits < 8); - putc(bits & 0xff, fp); - compressedLen++; - } - -bail: - *pCompressedLen = compressedLen; - return err; -} - - -/* - * Write a 16-bit value in little-endian order. - */ -static NuError Nu_SQWriteShort(FILE* outfp, short val) -{ - NuError err; - uint8_t tmpc; - - tmpc = val & 0xff; - err = Nu_FWrite(outfp, &tmpc, 1); - if (err != kNuErrNone) - goto bail; - tmpc = (val >> 8) & 0xff; - err = Nu_FWrite(outfp, &tmpc, 1); - if (err != kNuErrNone) - goto bail; - -bail: - return err; -} - -/* - * Compress "srcLen" bytes into SQ format, from "pStraw" to "fp". - * - * This requires two passes through the input. - * - * Bit of trivia: "sq3" on the Apple II self-destructs if you hand - * it an empty file. "xsq" works fine, creating an empty tree that - * "xusq" unpacks. - */ -NuError Nu_CompressHuffmanSQ(NuArchive* pArchive, NuStraw* pStraw, FILE* fp, - uint32_t srcLen, uint32_t* pDstLen, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - SQState sqState; - long compressedLen; - int i, j, numNodes; - - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - - sqState.pArchive = pArchive; - sqState.crc = 0; - if (pCrc == NULL) { - sqState.doCalcCRC = false; - } else { - sqState.doCalcCRC = true; - sqState.crc = *pCrc; - } - - #ifdef FULL_SQ_HEADER - sqState.checksum = 0; - #endif - - /* - * Pass 1: analysis. Perform a frequency analysis on the post-RLE - * input file. This will calculate the file CRCs as a side effect. - */ - sqState.rleState = kNuSQRLEStateNoHist; - sqState.uncompRemaining = srcLen; - sqState.pStraw = pStraw; - (void) Nu_StrawSetProgressState(pStraw, kNuProgressAnalyzing); - - err = Nu_SQComputeHuffTree(&sqState); - BailError(err); - - if (pCrc != NULL) - *pCrc = sqState.crc; - - /* - * Pass 2: compression. Using the encoding tree we computed, - * compress the input with RLE and Huffman. Start by writing - * the file header and rewinding the input file. - */ - sqState.doCalcCRC = false; /* don't need to re-compute */ - sqState.rleState = kNuSQRLEStateNoHist; /* reset */ - compressedLen = 0; - - /* rewind for next pass */ - (void) Nu_StrawSetProgressState(pStraw, kNuProgressCompressing); - err = Nu_StrawRewind(pArchive, pStraw); - BailError(err); - sqState.uncompRemaining = srcLen; - - #ifdef FULL_SQ_HEADER - /* write file header */ - err = Nu_SQWriteShort(fp, kNuSQMagic); - BailError(err); - compressedLen += 2; - - err = Nu_SQWriteShort(fp, sqState.checksum); - BailError(err); - compressedLen += 2; - - { - static const char fakename[] = "s.qqq"; - err = Nu_FWrite(fp, fakename, sizeof(fakename)); - BailError(err); - compressedLen += sizeof(fakename); - } - #endif - - /* - * Original description: - * Write out a simplified decoding tree. Only the interior - * nodes are written. When a child is a leaf index - * (representing a data value) it is recoded as - * -(index + 1) to distinguish it from interior indexes - * which are recoded as positive indexes in the new tree. - * Note that this tree will be empty for an empty file. - */ - if (sqState.treeHead < kNuSQNumVals) - numNodes = 0; - else - numNodes = sqState.treeHead - (kNuSQNumVals - 1); - err = Nu_SQWriteShort(fp, (short) numNodes); - BailError(err); - compressedLen += 2; - - for (i = sqState.treeHead, j = 0; j < numNodes; j++, i--) { - int l, r; - - l = sqState.node[i].lchild; - r = sqState.node[i].rchild; - l = l < kNuSQNumVals ? -(l + 1) : sqState.treeHead - l; - r = r < kNuSQNumVals ? -(r + 1) : sqState.treeHead - r; - err = Nu_SQWriteShort(fp, (short) l); - BailError(err); - err = Nu_SQWriteShort(fp, (short) r); - BailError(err); - compressedLen += 4; - - /*DBUG(("TREE %d: %d %d\n", j, l, r));*/ - } - - /* - * Convert the input to RLE/Huffman. - */ - err = Nu_SQCompressInput(&sqState, fp, &compressedLen); - BailError(err); - - /* - * Done! - */ - *pDstLen = compressedLen; - -bail: - return err; -} - - -/* - * =========================================================================== - * Expansion - * =========================================================================== - */ - -/* - * State during uncompression. - */ -typedef struct USQState { - uint32_t dataInBuffer; - uint8_t* dataPtr; - int bitPosn; - int bits; - - /* - * Decoding tree; first "nodeCount" values are populated. Positive - * values are indicies to another node in the tree, negative values - * are literals (+1 because "negative zero" doesn't work well). - */ - int nodeCount; - struct { - short child[2]; /* left/right kids, must be signed 16-bit */ - } decTree[kNuSQNumVals-1]; -} USQState; - - -/* - * Decode the next symbol from the Huffman stream. - */ -static NuError Nu_USQDecodeHuffSymbol(USQState* pUsqState, int* pVal) -{ - short val = 0; - int bits, bitPosn; - - bits = pUsqState->bits; /* local copy */ - bitPosn = pUsqState->bitPosn; - - do { - if (++bitPosn > 7) { - /* grab the next byte and use that */ - bits = *pUsqState->dataPtr++; - bitPosn = 0; - if (!pUsqState->dataInBuffer--) - return kNuErrBufferUnderrun; - - val = pUsqState->decTree[val].child[1 & bits]; - } else { - /* still got bits; shift right and use it */ - val = pUsqState->decTree[val].child[1 & (bits >>= 1)]; - } - } while (val >= 0); - - /* val is negative literal; add one to make it zero-based then negate it */ - *pVal = -(val + 1); - - pUsqState->bits = bits; - pUsqState->bitPosn = bitPosn; - - return kNuErrNone; -} - - -/* - * Read two bytes of signed data out of the buffer. - */ -static inline NuError Nu_USQReadShort(USQState* pUsqState, short* pShort) -{ - if (pUsqState->dataInBuffer < 2) - return kNuErrBufferUnderrun; - - *pShort = *pUsqState->dataPtr++; - *pShort |= (*pUsqState->dataPtr++) << 8; - pUsqState->dataInBuffer -= 2; - - return kNuErrNone; -} - -/* - * Expand "SQ" format. - * - * Because we have a stop symbol, knowing the uncompressed length of - * the file is not essential. - */ -NuError Nu_ExpandHuffmanSQ(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread, FILE* infp, NuFunnel* pFunnel, uint16_t* pCrc) -{ - NuError err = kNuErrNone; - USQState usqState; - uint32_t compRemaining, getSize; -#ifdef FULL_SQ_HEADER - uint16_t magic, fileChecksum, checksum; -#endif - short nodeCount; - int i, inrep; - uint8_t lastc = 0; - - err = Nu_AllocCompressionBufferIFN(pArchive); - if (err != kNuErrNone) - return err; - Assert(pArchive->compBuf != NULL); - - usqState.dataInBuffer = 0; - usqState.dataPtr = pArchive->compBuf; - usqState.bits = usqState.bitPosn = 0; - - compRemaining = pThread->thCompThreadEOF; -#ifdef FULL_SQ_HEADER - if (compRemaining < 8) -#else - if (compRemaining < 3) -#endif - { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, "thread too short to be valid SQ data"); - goto bail; - } - - getSize = compRemaining; - if (getSize > kNuGenCompBufSize) - getSize = kNuGenCompBufSize; - - /* - * Grab a big chunk. "compRemaining" is the amount of compressed - * data left in the file, usqState.dataInBuffer is the amount of - * compressed data left in the buffer. - */ - err = Nu_FRead(infp, usqState.dataPtr, getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, - "failed reading compressed data (%u bytes)", getSize); - goto bail; - } - usqState.dataInBuffer += getSize; - compRemaining -= getSize; - - /* - * Read the header. We assume that the header is less than - * kNuGenCompBufSize bytes, which is pretty fair since the buffer is - * currently 20x larger than the longest possible header (sq allowed - * 300+ for the filename, plus 257*2 for the tree, plus misc). - */ - Assert(kNuGenCompBufSize > 1200); -#ifdef FULL_SQ_HEADER - err = Nu_USQReadShort(&usqState, &magic); - BailError(err); - if (magic != kNuSQMagic) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, "bad magic number in SQ block"); - goto bail; - } - - err = Nu_USQReadShort(&usqState, &fileChecksum); - BailError(err); - - checksum = 0; - - while (*usqState.dataPtr++ != '\0') - usqState.dataInBuffer--; - usqState.dataInBuffer--; -#endif - - err = Nu_USQReadShort(&usqState, &nodeCount); - BailError(err); - if (nodeCount < 0 || nodeCount >= kNuSQNumVals) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, "invalid decode tree in SQ (%d nodes)", - nodeCount); - goto bail; - } - usqState.nodeCount = nodeCount; - - /* initialize for possibly empty tree (only happens on an empty file) */ - usqState.decTree[0].child[0] = -(kNuSQEOFToken+1); - usqState.decTree[0].child[1] = -(kNuSQEOFToken+1); - - /* read the nodes, ignoring "read errors" until we're done */ - for (i = 0; i < nodeCount; i++) { - err = Nu_USQReadShort(&usqState, &usqState.decTree[i].child[0]); - err = Nu_USQReadShort(&usqState, &usqState.decTree[i].child[1]); - } - if (err != kNuErrNone) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, "SQ data looks truncated at tree"); - goto bail; - } - - usqState.bitPosn = 99; /* force an immediate read */ - - /* - * Start pulling data out of the file. We have to Huffman-decode - * the input, and then feed that into an RLE expander. - * - * A completely lopsided (and broken) Huffman tree could require - * 256 tree descents, so we want to try to ensure we have at least 256 - * bits in the buffer. Otherwise, we could get a false buffer underrun - * indication back from DecodeHuffSymbol. - * - * The SQ sources actually guarantee that a code will fit entirely - * in 16 bits, but there's no reason not to use the larger value. - */ - inrep = false; - while (1) { - int val; - - if (usqState.dataInBuffer < 65 && compRemaining) { - /* - * Less than 256 bits, but there's more in the file. - * - * First thing we do is slide the old data to the start of - * the buffer. - */ - if (usqState.dataInBuffer) { - Assert(pArchive->compBuf != usqState.dataPtr); - memmove(pArchive->compBuf, usqState.dataPtr, - usqState.dataInBuffer); - } - usqState.dataPtr = pArchive->compBuf; - - /* - * Next we read as much as we can. - */ - if (kNuGenCompBufSize - usqState.dataInBuffer < compRemaining) - getSize = kNuGenCompBufSize - usqState.dataInBuffer; - else - getSize = compRemaining; - - err = Nu_FRead(infp, usqState.dataPtr + usqState.dataInBuffer, - getSize); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, - "failed reading compressed data (%u bytes)", getSize); - goto bail; - } - usqState.dataInBuffer += getSize; - compRemaining -= getSize; - - Assert(compRemaining < 32767*65536); - Assert(usqState.dataInBuffer <= kNuGenCompBufSize); - } - - err = Nu_USQDecodeHuffSymbol(&usqState, &val); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "failed decoding huff symbol"); - goto bail; - } - - if (val == kNuSQEOFToken) - break; - - /* - * Feed the symbol into the RLE decoder. - */ - if (inrep) { - /* - * Last char was RLE delim, handle this specially. We use - * --val instead of val-- because we already emitted the - * first occurrence of the char (right before the RLE delim). - */ - if (val == 0) { - /* special case -- just an escaped RLE delim */ - lastc = kNuSQRLEDelim; - val = 2; - } - while (--val) { - if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, &lastc, 1); - err = Nu_FunnelWrite(pArchive, pFunnel, &lastc, 1); - #ifdef FULL_SQ_HEADER - checksum += lastc; - #endif - } - inrep = false; - } else { - /* last char was ordinary */ - if (val == kNuSQRLEDelim) { - /* set a flag and catch the count the next time around */ - inrep = true; - } else { - lastc = val; - if (pCrc != NULL) - *pCrc = Nu_CalcCRC16(*pCrc, &lastc, 1); - err = Nu_FunnelWrite(pArchive, pFunnel, &lastc, 1); - #ifdef FULL_SQ_HEADER - checksum += lastc; - #endif - } - } - - } - - if (inrep) { - err = kNuErrBadData; - Nu_ReportError(NU_BLOB, err, - "got stop symbol when run length expected"); - goto bail; - } - - #ifdef FULL_SQ_HEADER - /* verify the checksum stored in the SQ file */ - if (checksum != fileChecksum && !pArchive->valIgnoreCRC) { - if (!Nu_ShouldIgnoreBadCRC(pArchive, pRecord, kNuErrBadDataCRC)) { - err = kNuErrBadDataCRC; - Nu_ReportError(NU_BLOB, err, "expected 0x%04x, got 0x%04x (SQ)", - fileChecksum, checksum); - (void) Nu_FunnelFlush(pArchive, pFunnel); - goto bail; - } - } else { - DBUG(("--- SQ checksums match (0x%04x)\n", checksum)); - } - #endif - - /* - * SQ2 adds an extra 0xff to the end, xsq doesn't. In any event, it - * appears that having an extra byte at the end is okay. - */ - if (usqState.dataInBuffer > 1) { - DBUG(("--- Found %ld bytes following compressed data (compRem=%ld)\n", - usqState.dataInBuffer, compRemaining)); - Nu_ReportError(NU_BLOB, kNuErrNone, "(Warning) unexpected fluff (%u)", - usqState.dataInBuffer); - } - -bail: - return err; -} - -#endif /*ENABLE_SQ*/ diff --git a/ciderpress/nufxlib/SysDefs.h b/ciderpress/nufxlib/SysDefs.h deleted file mode 100644 index aad4713..0000000 --- a/ciderpress/nufxlib/SysDefs.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * External type definitions and function prototypes. - */ -#ifndef NUFXLIB_SYSDEFS_H -#define NUFXLIB_SYSDEFS_H - -#ifdef HAVE_CONFIG_H -# include -#endif - -#ifdef DEBUG_VERBOSE -# define DEBUG_MSGS -#endif - -/* these should exist everywhere */ -#include -#include -#include -#include -#include -#include - -/* basic Win32 stuff -- info-zip has much more complete defs */ -#if defined(_WIN32) || defined(MSDOS) -# define WINDOWS_LIKE - -# ifndef HAVE_CONFIG_H -# define HAVE_FCNTL_H -# define HAVE_MALLOC_H -# define HAVE_STDLIB_H -# define HAVE_SYS_STAT_H -# undef HAVE_SYS_TIME_H -# define HAVE_SYS_TYPES_H -# undef HAVE_UNISTD_H -# undef HAVE_UTIME_H -# define HAVE_SYS_UTIME_H -# define HAVE_WINDOWS_H -# define HAVE_FDOPEN -# undef HAVE_FTRUNCATE -# define HAVE_MEMMOVE -# undef HAVE_MKSTEMP -# define HAVE_MKTIME -# define HAVE_SNPRINTF -# undef HAVE_STRCASECMP -# undef HAVE_STRNCASECMP -# define HAVE_STRERROR -# define HAVE_STRTOUL -# define HAVE_VSNPRINTF -# define SNPRINTF_DECLARED -# define VSNPRINTF_DECLARED -# define SPRINTF_RETURNS_INT -# define inline /*Visual C++6.0 can't inline ".c" files*/ -# define mode_t int -# define ENABLE_SQ -# define ENABLE_LZW -# define ENABLE_LZC -/*# define ENABLE_DEFLATE*/ -/*# define ENABLE_BZIP2*/ -# endif - -# include -# include -# define FOPEN_WANTS_B -# define HAVE_CHSIZE -# if _MSC_VER < 1900 /* no snprintf until Visual Studio 2015 */ -# define snprintf _snprintf -# define vsnprintf _vsnprintf -# endif - -#endif - -#ifdef HAVE_MALLOC_H -# include -#endif -#ifdef HAVE_STDLIB_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef HAVE_SYS_TIME_H -# include -#endif -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif -#ifdef HAVE_UTIME_H -# include -#endif -#ifdef HAVE_SYS_UTIME_H -# include -#endif - -#if defined(WINDOWS_LIKE) -# ifndef F_OK -# define F_OK 0 /* was 02 in <= v1.1.0 */ -# endif -#endif - -#if defined(__APPLE__) && defined(__MACH__) /* OS X */ -# define MAC_LIKE -# define UNIX_LIKE -#endif - -#if defined(__unix__) || defined(__unix) || defined(__BEOS__) || \ - defined(__hpux) || defined(_AIX) -# define UNIX_LIKE /* standardize */ -#endif - -#if defined(UNIX_LIKE) -# ifdef USE_REENTRANT_CALLS -# define _REENTRANT /* Solaris 2.x convention */ -# endif -#endif - -/* not currently using filesystem resource forks */ -//#if defined(__ORCAC__) || defined(MAC_LIKE) -//# define HAS_RESOURCE_FORKS -//#endif - -/* __FUNCTION__ was missing from BeOS __MWERKS__, and might be gcc-only */ -#ifdef __GNUC__ -# define HAS__FUNCTION__ -#endif - -#if defined(__linux__) -# define HAS_MALLOC_CHECK_ -#endif - -#endif /*NUFXLIB_SYSDEFS_H*/ diff --git a/ciderpress/nufxlib/Thread.c b/ciderpress/nufxlib/Thread.c deleted file mode 100644 index 6480bfd..0000000 --- a/ciderpress/nufxlib/Thread.c +++ /dev/null @@ -1,1381 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Thread-level operations. - */ -#include "NufxLibPriv.h" - - -/* - * =========================================================================== - * Utils - * =========================================================================== - */ - -/* - * Returns thread N, or NULL if the index is invalid. - */ -NuThread* Nu_GetThread(const NuRecord* pRecord, int idx) -{ - if (idx >= (int)pRecord->recTotalThreads) - return NULL; - else - return &pRecord->pThreads[idx]; -} - -/* - * ShrinkIt v3.0.0 had a bug where the filename thread would get created - * with the high bits set. We want to undo that without stomping on - * filenames that just happen to have a fancy character in them. If all - * of the high bits are set, assume it's a "defective" name and clear - * them all. If some aren't set, assume it's just a fancy filename. - * - * This high-bit-ism was also done for disk archives by most older versions - * of ShrinkIt. - */ -void Nu_StripHiIfAllSet(char* str) -{ - uint8_t* cp; - - for (cp = (uint8_t*)str; *cp != '\0'; cp++) - if (!(*cp & 0x80)) - return; - - for (cp = (uint8_t*)str; *cp != '\0'; cp++) - *cp &= 0x7f; -} - - -/* - * Decide if a thread is pre-sized (i.e. has a fixed maximum size with a - * lesser amount of uncompressed data within) based on the threadID. - */ -Boolean Nu_IsPresizedThreadID(NuThreadID threadID) -{ - if (threadID == kNuThreadIDFilename || threadID == kNuThreadIDComment) - return true; - else - return false; -} - - -/* - * Return an indication of whether the type of thread specified by ThreadID - * should ever be compressed. Right now, that's only data-class threads. - */ -Boolean Nu_IsCompressibleThreadID(NuThreadID threadID) -{ - if (NuThreadIDGetClass(threadID) == kNuThreadClassData) - return true; - else - return false; -} - - -/* - * Decide if the thread has a CRC, based on the record version and the - * threadID. - */ -Boolean Nu_ThreadHasCRC(uint16_t recordVersion, NuThreadID threadID) -{ - return recordVersion >= 3 && - NuThreadIDGetClass(threadID) == kNuThreadClassData; -} - - -/* - * Search through a given NuRecord for the specified thread. - */ -NuError Nu_FindThreadByIdx(const NuRecord* pRecord, NuThreadIdx thread, - NuThread** ppThread) -{ - NuThread* pThread; - int idx; - - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - if (pThread->threadIdx == thread) { - *ppThread = pThread; - return kNuErrNone; - } - } - - return kNuErrThreadIdxNotFound; -} - - -/* - * Search through a given NuRecord for the first thread with a matching - * threadID. - */ -NuError Nu_FindThreadByID(const NuRecord* pRecord, NuThreadID threadID, - NuThread** ppThread) -{ - NuThread* pThread; - int idx; - - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - if (NuGetThreadID(pThread) == threadID) { - *ppThread = pThread; - return kNuErrNone; - } - } - - return kNuErrThreadIDNotFound; -} - - -/* - * Copy the contents of a NuThread. - */ -void Nu_CopyThreadContents(NuThread* pDstThread, const NuThread* pSrcThread) -{ - Assert(pDstThread != NULL); - Assert(pSrcThread != NULL); - - memcpy(pDstThread, pSrcThread, sizeof(*pDstThread)); -} - - -/* - * =========================================================================== - * Reading threads from the archive - * =========================================================================== - */ - -/* - * Read a single thread header from the archive. - */ -static NuError Nu_ReadThreadHeader(NuArchive* pArchive, NuThread* pThread, - uint16_t* pCrc) -{ - FILE* fp; - - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(pCrc != NULL); - - fp = pArchive->archiveFp; - - pThread->thThreadClass = Nu_ReadTwoC(pArchive, fp, pCrc); - pThread->thThreadFormat = Nu_ReadTwoC(pArchive, fp, pCrc); - pThread->thThreadKind = Nu_ReadTwoC(pArchive, fp, pCrc); - pThread->thThreadCRC = Nu_ReadTwoC(pArchive, fp, pCrc); - pThread->thThreadEOF = Nu_ReadFourC(pArchive, fp, pCrc); - pThread->thCompThreadEOF = Nu_ReadFourC(pArchive, fp, pCrc); - - pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); - pThread->actualThreadEOF = 0; /* fix me later */ - pThread->fileOffset = -1; /* mark as invalid */ - pThread->used = 0xcfcf; /* init to invalid value */ - - return Nu_HeaderIOFailed(pArchive, fp); -} - -/* - * Read the threads from the current archive file position. - * - * The storage for the threads is allocated here, in one block. We could - * have used a linked list like NuLib, but that doesn't really provide any - * benefit for us, and adds complexity. - */ -NuError Nu_ReadThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, - uint16_t* pCrc) -{ - NuError err = kNuErrNone; - NuThread* pThread; - long count; - Boolean needFakeData, needFakeRsrc; - - needFakeData = true; - needFakeRsrc = (pRecord->recStorageType == kNuStorageExtended); - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - Assert(pCrc != NULL); - - if (!pRecord->recTotalThreads) { - /* not sure if this is reasonable, but we can handle it */ - DBUG(("--- WEIRD: no threads in the record?\n")); - goto bail; - } - - pRecord->pThreads = Nu_Malloc(pArchive, - pRecord->recTotalThreads * sizeof(NuThread)); - BailAlloc(pRecord->pThreads); - - count = pRecord->recTotalThreads; - pThread = pRecord->pThreads; - while (count--) { - err = Nu_ReadThreadHeader(pArchive, pThread, pCrc); - BailError(err); - - if (pThread->thThreadClass == kNuThreadClassData) { - if (pThread->thThreadKind == kNuThreadKindDataFork) { - needFakeData = false; - } else if (pThread->thThreadKind == kNuThreadKindRsrcFork) { - needFakeRsrc = false; - } else if (pThread->thThreadKind == kNuThreadKindDiskImage) { - /* needFakeRsrc shouldn't be set, but clear anyway */ - needFakeData = needFakeRsrc = false; - } - } - - /* - * Some versions of ShrinkIt write an invalid thThreadEOF for disks, - * so we have to figure out what it's supposed to be. - */ - if (NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == - kNuThreadIDDiskImage) - { - if (pRecord->recStorageType <= 13) { - /* supposed to be block size, but SHK v3.0.1 stored it wrong */ - pThread->actualThreadEOF = pRecord->recExtraType * 512; - - } else if (pRecord->recStorageType == 256 && - pRecord->recExtraType == 280 && - pRecord->recFileSysID == kNuFileSysDOS33) - { - /* - * Fix for less-common ShrinkIt problem: looks like an old - * version of GS/ShrinkIt used 256 as the block size when - * compressing DOS 3.3 images from 5.25" disks. If that - * appears to be the case here, crank up the block size. - */ - DBUG(("--- no such thing as a 70K disk image!\n")); - pThread->actualThreadEOF = pRecord->recExtraType * 512; - - } else { - pThread->actualThreadEOF = - pRecord->recExtraType * pRecord->recStorageType; - } - } else { - pThread->actualThreadEOF = pThread->thThreadEOF; - } - - pThread->used = false; - pThread++; - } - - /* - * If "mask threadless" is set, create "fake" threads with empty - * data and resource forks as needed. - */ - if ((needFakeData || needFakeRsrc) && pArchive->valMaskDataless) { - int firstNewThread = pRecord->recTotalThreads; - - if (needFakeData) { - pRecord->recTotalThreads++; - pRecord->fakeThreads++; - } - if (needFakeRsrc) { - pRecord->recTotalThreads++; - pRecord->fakeThreads++; - } - - pRecord->pThreads = Nu_Realloc(pArchive, pRecord->pThreads, - pRecord->recTotalThreads * sizeof(NuThread)); - BailAlloc(pRecord->pThreads); - - pThread = pRecord->pThreads + firstNewThread; - - if (needFakeData) { - pThread->thThreadClass = kNuThreadClassData; - pThread->thThreadFormat = kNuThreadFormatUncompressed; - pThread->thThreadKind = kNuThreadKindDataFork; - pThread->thThreadCRC = kNuInitialThreadCRC; - pThread->thThreadEOF = 0; - pThread->thCompThreadEOF = 0; - pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); - pThread->actualThreadEOF = 0; - pThread->fileOffset = -99999999; - pThread->used = false; - pThread++; - } - if (needFakeRsrc) { - pThread->thThreadClass = kNuThreadClassData; - pThread->thThreadFormat = kNuThreadFormatUncompressed; - pThread->thThreadKind = kNuThreadKindRsrcFork; - pThread->thThreadCRC = kNuInitialThreadCRC; - pThread->thThreadEOF = 0; - pThread->thCompThreadEOF = 0; - pThread->threadIdx = Nu_GetNextThreadIdx(pArchive); - pThread->actualThreadEOF = 0; - pThread->fileOffset = -99999999; - pThread->used = false; - } - } - -bail: - return err; -} - - -/* - * Write a single thread header to the archive. - */ -static NuError Nu_WriteThreadHeader(NuArchive* pArchive, - const NuThread* pThread, FILE* fp, uint16_t* pCrc) -{ - Assert(pArchive != NULL); - Assert(pThread != NULL); - Assert(fp != NULL); - Assert(pCrc != NULL); - - Nu_WriteTwoC(pArchive, fp, pThread->thThreadClass, pCrc); - Nu_WriteTwoC(pArchive, fp, (uint16_t)pThread->thThreadFormat, pCrc); - Nu_WriteTwoC(pArchive, fp, pThread->thThreadKind, pCrc); - Nu_WriteTwoC(pArchive, fp, pThread->thThreadCRC, pCrc); - Nu_WriteFourC(pArchive, fp, pThread->thThreadEOF, pCrc); - Nu_WriteFourC(pArchive, fp, pThread->thCompThreadEOF, pCrc); - - return Nu_HeaderIOFailed(pArchive, fp); -} - -/* - * Write the thread headers for the record at the current file position. - * - * Note this doesn't care whether a thread was "fake" or not. In - * effect, we promote all threads to "real" status. We update the - * "fake" count in pRecord accordingly. - */ -NuError Nu_WriteThreadHeaders(NuArchive* pArchive, NuRecord* pRecord, FILE* fp, - uint16_t* pCrc) -{ - NuError err = kNuErrNone; - NuThread* pThread; - int idx; - - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - err = Nu_WriteThreadHeader(pArchive, pThread, fp, pCrc); - BailError(err); - } - - if (pRecord->fakeThreads != 0) { - DBUG(("+++ promoting %ld fake threads to real\n",pRecord->fakeThreads)); - pRecord->fakeThreads = 0; - } - -bail: - return err; -} - - -/* - * Compute miscellaneous thread information, like total size and file - * offsets. Some values (like file offsets) will not be useful for - * streaming archives. - * - * Requires that the pArchive->currentOffset be set to the offset - * immediately after the last of the thread headers. - */ -NuError Nu_ComputeThreadData(NuArchive* pArchive, NuRecord* pRecord) -{ - NuThread* pThread; - long fileOffset, count; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - - /*pRecord->totalLength = 0;*/ - pRecord->totalCompLength = 0; - - fileOffset = pArchive->currentOffset; - - count = pRecord->recTotalThreads; - pThread = pRecord->pThreads; - while (count--) { - pThread->fileOffset = fileOffset; - - /*pRecord->totalLength += pThread->thThreadEOF;*/ - pRecord->totalCompLength += pThread->thCompThreadEOF; - fileOffset += pThread->thCompThreadEOF; - - pThread++; - } - - return kNuErrNone; -} - - -/* - * Skip past some or all of the thread data in the archive. For file - * archives, we scan all the threads, but for streaming archives we only - * want to scan up to the filename thread. (If the filename thread comes - * after one of the data threads, we have a problem!) - * - * The tricky part here is that we don't want to skip over a filename - * thread. We actually want to read it in, so that we have something to - * show to the application. (Someday I'll get AndyN for putting me - * through this...) - */ -NuError Nu_ScanThreads(NuArchive* pArchive, NuRecord* pRecord, long numThreads) -{ - NuError err = kNuErrNone; - NuThread* pThread; - FILE* fp; - - Assert(pArchive != NULL); - Assert(pRecord != NULL); - - fp = pArchive->archiveFp; - - Assert(numThreads <= (long)pRecord->recTotalThreads); - - pThread = pRecord->pThreads; - while (numThreads--) { - if (pRecord->threadFilenameMOR == NULL && - NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind) == - kNuThreadIDFilename) - { - /* it's the first filename thread, read the whole thing */ - if (pThread->thCompThreadEOF > kNuReasonableFilenameLen) { - err = kNuErrBadRecord; - Nu_ReportError(NU_BLOB, err, "Bad thread filename len (%u)", - pThread->thCompThreadEOF); - goto bail; - } - pRecord->threadFilenameMOR = Nu_Malloc(pArchive, - pThread->thCompThreadEOF +1); - BailAlloc(pRecord->threadFilenameMOR); - - /* note there is no CRC on a filename thread */ - (void) Nu_ReadBytes(pArchive, fp, pRecord->threadFilenameMOR, - pThread->thCompThreadEOF); - if ((err = Nu_HeaderIOFailed(pArchive, fp)) != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Failed reading filename thread"); - goto bail; - } - - /* null-terminate on the actual len, not the buffer len */ - pRecord->threadFilenameMOR[pThread->thThreadEOF] = '\0'; - - Nu_StripHiIfAllSet(pRecord->threadFilenameMOR); - - /* prefer this one over the record one, but only one should exist */ - if (pRecord->filenameMOR != NULL) { - DBUG(("--- HEY: got record filename and thread filename\n")); - } - pRecord->filenameMOR = pRecord->threadFilenameMOR; - - } else { - /* not a filename (or not first filename), skip past it */ - err = Nu_SeekArchive(pArchive, pArchive->archiveFp, - pThread->thCompThreadEOF, SEEK_CUR); - BailError(err); - } - - pThread++; - } - - /* - * Should've had one by now. Supposedly, older versions of ShrinkIt - * wouldn't prompt for a disk image name on DOS 3.3 volumes, so you'd - * end up with a disk image that had no name attached. This will tend - * to confuse things, so we go ahead and give it a name. - */ - if (pRecord->filenameMOR == NULL) { - DBUG(("+++ no filename found, using default record name\n")); - pRecord->filenameMOR = kNuDefaultRecordName; - } - - pArchive->currentOffset += pRecord->totalCompLength; - - if (!Nu_IsStreaming(pArchive)) { - Assert(pArchive->currentOffset == ftell(pArchive->archiveFp)); - } - -bail: - return err; -} - - -/* - * Skip the thread. This only has meaning for streaming archives, and - * assumes that the file pointer is set to the start of the thread's data - * already. - */ -NuError Nu_SkipThread(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread) -{ - NuError err; - - if (!Nu_IsStreaming(pArchive)) /* for debugging */ - return kNuErrNone; /* for debugging */ - Assert(Nu_IsStreaming(pArchive)); - - err = Nu_SeekArchive(pArchive, pArchive->archiveFp, - pThread->thCompThreadEOF, SEEK_CUR); - return err; -} - - -/* - * =========================================================================== - * Extract - * =========================================================================== - */ - -/* - * Extract the thread to the specified file pointer. - * - * If the archive is a stream, the stream must be positioned at the - * start of pThread's data. If not, it will be seeked first. - */ -static NuError Nu_ExtractThreadToDataSink(NuArchive* pArchive, - const NuRecord* pRecord, const NuThread* pThread, - NuProgressData* pProgress, NuDataSink* pDataSink) -{ - NuError err; - NuFunnel* pFunnel = NULL; - - /* if it's not a stream, seek to the appropriate spot in the file */ - if (!Nu_IsStreaming(pArchive)) { - err = Nu_SeekArchive(pArchive, pArchive->archiveFp, - pThread->fileOffset, SEEK_SET); - if (err != kNuErrNone) { - Nu_ReportError(NU_BLOB, err, "Unable to seek input to %ld", - pThread->fileOffset); - goto bail; - } - } - - /* - * Set up an output funnel to write to. - */ - err = Nu_FunnelNew(pArchive, pDataSink, Nu_DataSinkGetConvertEOL(pDataSink), - pArchive->valEOL, pProgress, &pFunnel); - BailError(err); - - /* - * Write it. - */ - err = Nu_ExpandStream(pArchive, pRecord, pThread, pArchive->archiveFp, - pFunnel); - if (err != kNuErrNone) { - if (err != kNuErrSkipped && err != kNuErrAborted) - Nu_ReportError(NU_BLOB, err, "ExpandStream failed"); - goto bail; - } - -bail: - (void) Nu_FunnelFree(pArchive, pFunnel); - return err; -} - - -/* - * Extract the specified thread to "pDataSink". If the sink is to a file, - * this will take care of opening (and, if appropriate, creating) the file. - * - * If we're operating on a streaming archive, the file pointer must be - * positioned at the start of the thread's data. If not, it will be - * seeked appropriately. - * - * This calls the "should we extract" and "what pathname should we use" - * filters for every thread, which means we can reject specific kinds - * of forks and/or give them different names. This is a good thing. - */ -static NuError Nu_ExtractThreadCommon(NuArchive* pArchive, - const NuRecord* pRecord, const NuThread* pThread, NuDataSink* pDataSink) -{ - NuError err = kNuErrNone; - NuSelectionProposal selProposal; - NuPathnameProposal pathProposal; - NuProgressData progressData; - NuProgressData* pProgressData; - NuDataSink* pOrigDataSink; - UNICHAR* newPathStorageUNI = NULL; - UNICHAR* recFilenameStorageUNI = NULL; - const UNICHAR* newPathnameUNI; - NuResult result; - uint8_t newFssep; - Boolean doFreeSink = false; - - Assert(pRecord != NULL); - Assert(pThread != NULL); - Assert(pDataSink != NULL); - - memset(&progressData, 0, sizeof(progressData)); - pProgressData = NULL; - - /* - * If we're just trying to verify the archive contents, create a - * data sink that goes nowhere at all. - */ - if (pArchive->testMode) { - err = Nu_DataSinkVoid_New( - Nu_DataSinkGetDoExpand(pDataSink), - Nu_DataSinkGetConvertEOL(pDataSink), - &pDataSink); - BailError(err); - doFreeSink = true; - } - - pOrigDataSink = pDataSink; /* save a copy for the "retry" loop */ - - /* - * Decide if we want to extract this thread. This is mostly for - * use by the "bulk" extract, not the per-thread extract, but it - * still applies if they so desire. - */ - if (pArchive->selectionFilterFunc != NULL) { - selProposal.pRecord = pRecord; - selProposal.pThread = pThread; - result = (*pArchive->selectionFilterFunc)(pArchive, &selProposal); - - if (result == kNuSkip) - return Nu_SkipThread(pArchive, pRecord, pThread); - if (result == kNuAbort) { - err = kNuErrAborted; - goto bail; - } - } - - newPathnameUNI = NULL; - newFssep = 0; - - recFilenameStorageUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); - -retry_name: - if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { - /* - * We're extracting. Figure out the name of the file to write it to. - * If they want to use the sleazy FILE* back door, create a new - * data sink and use that instead. - * - * Start by resetting everything to defaults, in case this isn't - * our first time through the "rename" loop. - */ - newPathnameUNI = Nu_DataSinkFile_GetPathname(pDataSink); - newFssep = Nu_DataSinkFile_GetFssep(pDataSink); - pDataSink = pOrigDataSink; - - /* if they don't have a pathname func defined, we just use default */ - if (pArchive->outputPathnameFunc != NULL) { - pathProposal.pathnameUNI = recFilenameStorageUNI; - pathProposal.filenameSeparator = - NuGetSepFromSysInfo(pRecord->recFileSysInfo); - pathProposal.pRecord = pRecord; - pathProposal.pThread = pThread; - pathProposal.newPathnameUNI = NULL; - pathProposal.newFilenameSeparator = '\0'; - /*pathProposal.newStorage = (NuThreadID)-1;*/ - pathProposal.newDataSink = NULL; - - result = (*pArchive->outputPathnameFunc)(pArchive, &pathProposal); - - if (result == kNuSkip) - return Nu_SkipThread(pArchive, pRecord, pThread); - if (result == kNuAbort) { - err = kNuErrAborted; - goto bail; - } - - /* we don't own this string, so make a copy */ - if (pathProposal.newPathnameUNI != NULL) { - Nu_Free(pArchive, newPathStorageUNI); - newPathStorageUNI = strdup(pathProposal.newPathnameUNI); - newPathnameUNI = newPathStorageUNI; - } else { - newPathnameUNI = NULL; - } - if (pathProposal.newFilenameSeparator != '\0') - newFssep = pathProposal.newFilenameSeparator; - - /* if they want to send this somewhere else, let them */ - if (pathProposal.newDataSink != NULL) - pDataSink = pathProposal.newDataSink; - } - - /* at least one of these must be set */ - Assert(!(newPathnameUNI == NULL && pathProposal.newDataSink == NULL)); - } - - /* - * Prepare the progress data if this is a data thread. - */ - if (newPathnameUNI == NULL) { - /* using a data sink; get the pathname out of the record */ - newPathnameUNI = recFilenameStorageUNI; - newFssep = NuGetSepFromSysInfo(pRecord->recFileSysInfo); - } - if (pThread->thThreadClass == kNuThreadClassData) { - pProgressData = &progressData; - err = Nu_ProgressDataInit_Expand(pArchive, pProgressData, pRecord, - newPathnameUNI, newFssep, recFilenameStorageUNI, - Nu_DataSinkGetConvertEOL(pOrigDataSink)); - BailError(err); - - /* send initial progress so they see the right name if "open" fails */ - pProgressData->state = kNuProgressOpening; - err = Nu_SendInitialProgress(pArchive, pProgressData); - BailError(err); - } - - if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { - /* - * We're extracting to a file. Open it, creating it if necessary and - * allowed. - */ - FILE* fileFp = NULL; - - err = Nu_OpenOutputFile(pArchive, pRecord, pThread, newPathnameUNI, - newFssep, &fileFp); - if (err == kNuErrRename) { - /* they want to rename; the OutputPathname callback handles this */ - Nu_Free(pArchive, newPathStorageUNI); - newPathStorageUNI = NULL; - /* reset these just to be careful */ - newPathnameUNI = NULL; - fileFp = NULL; - goto retry_name; - } else if (err != kNuErrNone) { - goto bail; - } - - Assert(fileFp != NULL); - (void) Nu_DataSinkFile_SetFP(pDataSink, fileFp); - - DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to '%s'\n", - NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), - pRecord->filename, pThread->fileOffset, newPathname)); - } else { - DBUG(("+++ EXTRACTING 0x%08lx from '%s' at offset %0ld to sink\n", - NuMakeThreadID(pThread->thThreadClass, pThread->thThreadKind), - pRecord->filename, pThread->fileOffset)); - } - - /* extract to the file */ - err = Nu_ExtractThreadToDataSink(pArchive, pRecord, pThread, - pProgressData, pDataSink); - BailError(err); - - if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) { - /* - * Close the file, adjusting the modification date and access - * permissions as appropriate. - */ - err = Nu_CloseOutputFile(pArchive, pRecord, - Nu_DataSinkFile_GetFP(pDataSink), newPathnameUNI); - Nu_DataSinkFile_SetFP(pDataSink, NULL); - BailError(err); - } - -bail: - if (err != kNuErrNone && pProgressData != NULL) { - /* send a final progress message, indicating failure */ - if (err == kNuErrSkipped) - pProgressData->state = kNuProgressSkipped; - else if (err == kNuErrAborted) - pProgressData->state = kNuProgressAborted; - else - pProgressData->state = kNuProgressFailed; - (void) Nu_SendInitialProgress(pArchive, pProgressData); - } - - /* if this was an ordinary file, and it's still open, close it */ - if (Nu_DataSinkGetType(pDataSink) == kNuDataSinkToFile) - Nu_DataSinkFile_Close(pDataSink); - - Nu_Free(pArchive, newPathStorageUNI); - Nu_Free(pArchive, recFilenameStorageUNI); - - if (doFreeSink) - Nu_DataSinkFree(pDataSink); - return err; -} - -/* - * Extract a thread from the archive as part of a "bulk" extract operation. - * - * Streaming archives must be properly positioned. - */ -NuError Nu_ExtractThreadBulk(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread) -{ - NuError err; - NuDataSink* pDataSink = NULL; - UNICHAR* recFilenameStorageUNI = NULL; - NuValue eolConv; - - /* - * Create a file data sink for the file. We use whatever EOL conversion - * is set as the default for the entire archive. (If you want to - * specify your own EOL conversion for each individual file, you will - * need to extract them individually, creating a data sink for each.) - * - * One exception: we turn EOL conversion off for disk image threads. - * It's *very* unlikely this would be desirable, and could be a problem - * if the user is extracting a collection of disks and files. - */ - eolConv = pArchive->valConvertExtractedEOL; - if (NuGetThreadID(pThread) == kNuThreadIDDiskImage) - eolConv = kNuConvertOff; - recFilenameStorageUNI = Nu_CopyMORToUNI(pRecord->filenameMOR); - err = Nu_DataSinkFile_New(true, eolConv, recFilenameStorageUNI, - NuGetSepFromSysInfo(pRecord->recFileSysInfo), &pDataSink); - BailError(err); - - err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink); - BailError(err); - -bail: - if (pDataSink != NULL) { - NuError err2 = Nu_DataSinkFree(pDataSink); - if (err == kNuErrNone) - err = err2; - } - Nu_Free(pArchive, recFilenameStorageUNI); - - return err; -} - - -/* - * Extract a thread, given the IDs and a data sink. - */ -NuError Nu_ExtractThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSink* pDataSink) -{ - NuError err; - NuRecord* pRecord; - NuThread* pThread; - - if (Nu_IsStreaming(pArchive)) - return kNuErrUsage; - if (threadIdx == 0 || pDataSink == NULL) - return kNuErrInvalidArg; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* find the correct record and thread by index */ - err = Nu_RecordSet_FindByThreadIdx(&pArchive->origRecordSet, threadIdx, - &pRecord, &pThread); - BailError(err); - Assert(pRecord != NULL); - - /* extract away */ - err = Nu_ExtractThreadCommon(pArchive, pRecord, pThread, pDataSink); - BailError(err); - -bail: - return err; -} - - -/* - * =========================================================================== - * Add/update/delete - * =========================================================================== - */ - -/* - * Verify that a conflicting thread with the specified threadID does not - * exist in this record, now or in the future. - * - * The set of interesting threads is equal to the current threads, minus - * any that have been deleted, plus any that have been added already. - * - * If a matching threadID is found, this returns an error. - */ -static NuError Nu_FindNoFutureThread(NuArchive* pArchive, - const NuRecord* pRecord, NuThreadID threadID) -{ - NuError err = kNuErrNone; - const NuThread* pThread; - const NuThreadMod* pThreadMod; - int idx; - - /* - * Start by scanning the existing threads (if any). - */ - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - if (NuGetThreadID(pThread) == threadID) { - /* found a match, see if it has been deleted */ - pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, - pThread->threadIdx); - if (pThreadMod != NULL && - pThreadMod->entry.kind == kNuThreadModDelete) - { - /* it's deleted, ignore it */ - continue; - } - DBUG(("--- found existing thread matching 0x%08lx\n", threadID)); - err = kNuErrThreadAdd; - goto bail; - } - } - - /* - * Now look for "add" threadMods with a matching threadID. - */ - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - if (pThreadMod->entry.kind == kNuThreadModAdd && - pThreadMod->entry.add.threadID == threadID) - { - DBUG(("--- found 'add' threadMod matching 0x%08lx\n", threadID)); - err = kNuErrThreadAdd; - goto bail; - } - - pThreadMod = pThreadMod->pNext; - } - -bail: - return err; -} - -/* - * Like Nu_FindNoFutureThread, but tests against a whole class. - */ -static NuError Nu_FindNoFutureThreadClass(NuArchive* pArchive, - const NuRecord* pRecord, long threadClass) -{ - NuError err = kNuErrNone; - const NuThread* pThread; - const NuThreadMod* pThreadMod; - int idx; - - /* - * Start by scanning the existing threads (if any). - */ - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = Nu_GetThread(pRecord, idx); - Assert(pThread != NULL); - - if (pThread->thThreadClass == threadClass) { - /* found a match, see if it has been deleted */ - pThreadMod = Nu_ThreadMod_FindByThreadIdx(pRecord, - pThread->threadIdx); - if (pThreadMod != NULL && - pThreadMod->entry.kind == kNuThreadModDelete) - { - /* it's deleted, ignore it */ - continue; - } - DBUG(("--- Found existing thread matching 0x%04lx\n", threadClass)); - err = kNuErrThreadAdd; - goto bail; - } - } - - /* - * Now look for "add" threadMods with a matching threadClass. - */ - pThreadMod = pRecord->pThreadMods; - while (pThreadMod != NULL) { - if (pThreadMod->entry.kind == kNuThreadModAdd && - NuThreadIDGetClass(pThreadMod->entry.add.threadID) == threadClass) - { - DBUG(("--- Found 'add' threadMod matching 0x%04lx\n", threadClass)); - err = kNuErrThreadAdd; - goto bail; - } - - pThreadMod = pThreadMod->pNext; - } - -bail: - return err; -} - - -/* - * Find an existing thread somewhere in the archive. If the "copy" set - * exists it will be searched. If not, the "orig" set is searched, and - * if an entry is found a "copy" set will be created. - * - * The record and thread returned will always be from the "copy" set. An - * error result is returned if the record and thread aren't found. - */ -static NuError Nu_FindThreadForWriteByIdx(NuArchive* pArchive, - NuThreadIdx threadIdx, NuRecord** ppFoundRecord, NuThread** ppFoundThread) -{ - NuError err; - - if (Nu_RecordSet_GetLoaded(&pArchive->copyRecordSet)) { - err = Nu_RecordSet_FindByThreadIdx(&pArchive->copyRecordSet, threadIdx, - ppFoundRecord, ppFoundThread); - } else { - Assert(Nu_RecordSet_GetLoaded(&pArchive->origRecordSet)); - err = Nu_RecordSet_FindByThreadIdx(&pArchive->origRecordSet, threadIdx, - ppFoundRecord, ppFoundThread); - *ppFoundThread = NULL; /* can't delete from here, wipe ptr */ - } - BailError(err); - - /* - * The thread exists. If we were looking in the "orig" set, we have - * to create a "copy" set, and delete it from that. - */ - if (*ppFoundThread == NULL) { - err = Nu_RecordSet_Clone(pArchive, &pArchive->copyRecordSet, - &pArchive->origRecordSet); - BailError(err); - err = Nu_RecordSet_FindByThreadIdx(&pArchive->copyRecordSet, threadIdx, - ppFoundRecord, ppFoundThread); - Assert(err == kNuErrNone && *ppFoundThread != NULL); /* must succeed */ - BailError(err); - } - -bail: - return err; -} - -/* - * Determine if it's okay to add a thread of the type specified by - * "threadID" into "pRecord". - * - * Returns with an error (kNuErrThreadAdd) if it's not okay. - */ -NuError Nu_OkayToAddThread(NuArchive* pArchive, const NuRecord* pRecord, - NuThreadID threadID) -{ - NuError err = kNuErrNone; - - /* - * Check for class conflicts (can't mix data and control threads). - */ - if (NuThreadIDGetClass(threadID) == kNuThreadClassData) { - err = Nu_FindNoFutureThreadClass(pArchive, pRecord, - kNuThreadClassControl); - BailError(err); - } else if (NuThreadIDGetClass(threadID) == kNuThreadClassControl) { - err = Nu_FindNoFutureThreadClass(pArchive, pRecord, - kNuThreadClassData); - BailError(err); - } - - /* - * Check for specific type conflicts. - */ - if (threadID == kNuThreadIDDataFork) { - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDataFork); - BailError(err); - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); - BailError(err); - } else if (threadID == kNuThreadIDRsrcFork) { - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDRsrcFork); - BailError(err); - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); - BailError(err); - } else if (threadID == kNuThreadIDDiskImage) { - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDataFork); - BailError(err); - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDRsrcFork); - BailError(err); - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDDiskImage); - BailError(err); - } else if (threadID == kNuThreadIDFilename) { - err = Nu_FindNoFutureThread(pArchive, pRecord, kNuThreadIDFilename); - BailError(err); - } - -bail: - return err; -} - - - -/* - * Add a new thread to a record. - * - * In some cases, you aren't allowed to add a thread whose type matches - * one that already exists. This applies to data threads and filenames, - * but not to comments, control threads, or IIgs icons. You also can't - * add a disk image thread when there are data-class threads, or vice-versa. - * - * This is the first and last place we do this sort of checking. If - * an illegal situation gets past this function, it will either get - * caught with a fatal assert or (if NDEBUG is defined) not at all. - * - * On success, the NuThreadIdx of the newly-created record will be placed - * in "*pThreadIdx", and "pDataSource" will be owned by NufxLib. - */ -NuError Nu_AddThread(NuArchive* pArchive, NuRecordIdx recIdx, - NuThreadID threadID, NuDataSource* pDataSource, NuThreadIdx* pThreadIdx) -{ - NuError err; - NuRecord* pRecord; - NuThreadMod* pThreadMod = NULL; - NuThreadFormat threadFormat; - - /* okay for pThreadIdx to be NULL */ - if (recIdx == 0 || pDataSource == NULL) - return kNuErrInvalidArg; - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* - * Find the record. If it doesn't exist in the copy set, check to - * see if it's in the "new" set. - */ - err = Nu_FindRecordForWriteByIdx(pArchive, recIdx, &pRecord); - if (err == kNuErrRecIdxNotFound && - Nu_RecordSet_GetLoaded(&pArchive->newRecordSet)) - { - err = Nu_RecordSet_FindByIdx(&pArchive->newRecordSet, recIdx, &pRecord); - } - BailError(err); - Assert(pRecord != NULL); - - /* - * Do some tests, looking for specific types of threads that conflict - * with what we're trying to add. - */ - err = Nu_OkayToAddThread(pArchive, pRecord, threadID); - BailError(err); - - /* - * Decide if we want to compress the data from this source. If the - * data is already compressed (as indicated by the data source) or - * this type of thread isn't compressible (e.g. it's a filename), then - * we don't compress it. Otherwise, we use whatever compression mode - * is currently configured. - */ - if (Nu_DataSourceGetThreadFormat(pDataSource) == kNuThreadFormatUncompressed && - Nu_IsCompressibleThreadID(threadID)) - { - threadFormat = Nu_ConvertCompressValToFormat(pArchive, - pArchive->valDataCompression); - } else { - threadFormat = kNuThreadFormatUncompressed; - } - DBUG(("--- using threadFormat = %d\n", threadFormat)); - - /* create a new ThreadMod (which makes a copy of the data source) */ - err = Nu_ThreadModAdd_New(pArchive, threadID, threadFormat, pDataSource, - &pThreadMod); - BailError(err); - Assert(pThreadMod != NULL); - - /* add the thread mod to the record */ - Nu_RecordAddThreadMod(pRecord, pThreadMod); - if (pThreadIdx != NULL) - *pThreadIdx = pThreadMod->entry.add.threadIdx; - pThreadMod = NULL; /* successful, don't free */ - - /* - * If we've got a header filename and we're adding a filename thread, - * we don't want to write the record header name when we reconstruct - * the record. - */ - if (threadID == kNuThreadIDFilename && pRecord->recFilenameLength) { - DBUG(("+++ gonna drop the filename\n")); - pRecord->dropRecFilename = true; - } - -bail: - if (pThreadMod != NULL) - Nu_ThreadModFree(pArchive, pThreadMod); - if (err == kNuErrNone && pDataSource != NULL) { - /* on success, we have ownership of the data source. ThreadMod - made its own copy, so get rid of this one */ - Nu_DataSourceFree(pDataSource); - } - return err; -} - - -/* - * Update the contents of a pre-sized thread, such as a filename or - * comment thread. - * - * The data from the source must fit within the limits of the existing - * thread. The source data is never compressed, and must not come from - * a compressed source. - * - * You aren't allowed to update threads that have been deleted. Updating - * newly-added threads isn't possible, since they aren't really threads yet. - */ -NuError Nu_UpdatePresizedThread(NuArchive* pArchive, NuThreadIdx threadIdx, - NuDataSource* pDataSource, int32_t* pMaxLen) -{ - NuError err; - NuThreadMod* pThreadMod = NULL; - NuRecord* pFoundRecord; - NuThread* pFoundThread; - - if (pDataSource == NULL) { - err = kNuErrInvalidArg; - goto bail; - } - - /* presized threads always contain uncompressed data */ - if (Nu_DataSourceGetThreadFormat(pDataSource) != - kNuThreadFormatUncompressed) - { - err = kNuErrBadFormat; - Nu_ReportError(NU_BLOB, err, - "presized threads can't hold compressed data"); - goto bail; - } - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* - * Find the thread in the "copy" set. (If there isn't a copy set, - * make one.) - */ - err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord, - &pFoundThread); - BailError(err); - - if (!Nu_IsPresizedThreadID(NuGetThreadID(pFoundThread)) || - !(pFoundThread->thCompThreadEOF >= pFoundThread->thThreadEOF)) - { - err = kNuErrNotPreSized; - Nu_ReportError(NU_BLOB, err, "invalid thread for update"); - goto bail; - } - - if (pMaxLen != NULL) - *pMaxLen = pFoundThread->thCompThreadEOF; - - /* - * Check to see if somebody is trying to delete this, or has already - * updated it. - */ - if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != NULL) { - DBUG(("--- Tried to modify a deleted or modified thread\n")); - err = kNuErrModThreadChange; - goto bail; - } - - /* - * Verify that "otherLen" in the data source is less than or equal - * to our len, if we can. If the data source is a file on disk, - * we're not really supposed to look at it until we flush. We - * could sneak a peek right now, which would prevent us from aborting - * the entire operation when it turns out the file won't fit, but - * that violates our semantics (and besides, the application really - * should've done that already). - * - * If the data source is from a file, we just assume it'll fit and - * let the chips fall where they may later on. - */ - if (Nu_DataSourceGetType(pDataSource) != kNuDataSourceFromFile) { - if (pFoundThread->thCompThreadEOF < - Nu_DataSourceGetOtherLen(pDataSource)) - { - err = kNuErrPreSizeOverflow; - Nu_ReportError(NU_BLOB, err, "can't put %u bytes into %u", - Nu_DataSourceGetOtherLen(pDataSource), - pFoundThread->thCompThreadEOF); - goto bail; - } - - /* check for zero-length and excessively long filenames */ - if (NuGetThreadID(pFoundThread) == kNuThreadIDFilename && - (Nu_DataSourceGetOtherLen(pDataSource) == 0 || - Nu_DataSourceGetOtherLen(pDataSource) > kNuReasonableFilenameLen)) - { - err = kNuErrInvalidFilename; - Nu_ReportError(NU_BLOB, err, "invalid filename (%u bytes)", - Nu_DataSourceGetOtherLen(pDataSource)); - goto bail; - } - } - - /* - * Looks like it'll fit, and it's the right kind of data. Create - * an "update" threadMod. Note this copies the data source. - */ - Assert(pFoundThread->thThreadFormat == kNuThreadFormatUncompressed); - err = Nu_ThreadModUpdate_New(pArchive, threadIdx, pDataSource, &pThreadMod); - BailError(err); - Assert(pThreadMod != NULL); - - /* add the thread mod to the record */ - Nu_RecordAddThreadMod(pFoundRecord, pThreadMod); - - /* - * NOTE: changes to filename threads will be picked up later and - * incorporated into the record's threadFilename. We don't worry - * about the record header filename, because we might be doing an - * update-in-place and that prevents us from removing the filename - * (doing so would change the size of the archive). No need to - * do any filename-specific changes here. - */ - -bail: - return err; -} - -/* - * Delete an individual thread. - * - * You aren't allowed to delete threads that have been updated. Deleting - * newly-added threads isn't possible, since they aren't really threads yet. - * - * Don't worry about deleting filename threads here; we take care of that - * later on. Besides, it's sort of handy to hang on to the filename for - * as long as possible. - */ -NuError Nu_DeleteThread(NuArchive* pArchive, NuThreadIdx threadIdx) -{ - NuError err; - NuThreadMod* pThreadMod = NULL; - NuRecord* pFoundRecord; - NuThread* pFoundThread; - - if (Nu_IsReadOnly(pArchive)) - return kNuErrArchiveRO; - err = Nu_GetTOCIfNeeded(pArchive); - BailError(err); - - /* - * Find the thread in the "copy" set. (If there isn't a copy set, - * make one.) - */ - err = Nu_FindThreadForWriteByIdx(pArchive, threadIdx, &pFoundRecord, - &pFoundThread); - BailError(err); - - /* - * Deletion of modified threads (updates or previous deletes) isn't - * allowed. Deletion of threads from deleted records can't happen, - * because deleted records are completely removed from the "copy" set. - */ - if (Nu_ThreadMod_FindByThreadIdx(pFoundRecord, threadIdx) != NULL) { - DBUG(("--- Tried to delete a deleted or modified thread\n")); - err = kNuErrModThreadChange; - goto bail; - } - - /* - * Looks good. Add a new "delete" ThreadMod to the list. - */ - err = Nu_ThreadModDelete_New(pArchive, threadIdx, - NuGetThreadID(pFoundThread), &pThreadMod); - BailError(err); - Nu_RecordAddThreadMod(pFoundRecord, pThreadMod); - pThreadMod = NULL; /* successful, don't free */ - -bail: - Nu_ThreadModFree(pArchive, pThreadMod); - return err; -} - diff --git a/ciderpress/nufxlib/Value.c b/ciderpress/nufxlib/Value.c deleted file mode 100644 index c344b01..0000000 --- a/ciderpress/nufxlib/Value.c +++ /dev/null @@ -1,326 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - * - * Get/set certain values and attributes. - */ -#include "NufxLibPriv.h" - -#define kMaxJunkSkipMax 8192 - - -/* - * Get a configurable parameter. - */ -NuError Nu_GetValue(NuArchive* pArchive, NuValueID ident, NuValue* pValue) -{ - NuError err = kNuErrNone; - - if (pValue == NULL) - return kNuErrInvalidArg; - - switch (ident) { - case kNuValueAllowDuplicates: - *pValue = pArchive->valAllowDuplicates; - break; - case kNuValueConvertExtractedEOL: - *pValue = pArchive->valConvertExtractedEOL; - break; - case kNuValueDataCompression: - *pValue = pArchive->valDataCompression; - break; - case kNuValueDiscardWrapper: - *pValue = pArchive->valDiscardWrapper; - break; - case kNuValueEOL: - *pValue = pArchive->valEOL; - break; - case kNuValueHandleExisting: - *pValue = pArchive->valHandleExisting; - break; - case kNuValueIgnoreCRC: - *pValue = pArchive->valIgnoreCRC; - break; - case kNuValueMaskDataless: - *pValue = pArchive->valMaskDataless; - break; - case kNuValueMimicSHK: - *pValue = pArchive->valMimicSHK; - break; - case kNuValueModifyOrig: - *pValue = pArchive->valModifyOrig; - break; - case kNuValueOnlyUpdateOlder: - *pValue = pArchive->valOnlyUpdateOlder; - break; - case kNuValueStripHighASCII: - *pValue = pArchive->valStripHighASCII; - break; - case kNuValueJunkSkipMax: - *pValue = pArchive->valJunkSkipMax; - break; - case kNuValueIgnoreLZW2Len: - *pValue = pArchive->valIgnoreLZW2Len; - break; - case kNuValueHandleBadMac: - *pValue = pArchive->valHandleBadMac; - break; - default: - err = kNuErrInvalidArg; - Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident); - goto bail; - } - -bail: - return err; -} - - -/* - * Set a configurable parameter. - */ -NuError Nu_SetValue(NuArchive* pArchive, NuValueID ident, NuValue value) -{ - NuError err = kNuErrInvalidArg; - - switch (ident) { - case kNuValueAllowDuplicates: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueAllowDuplicates value %u", value); - goto bail; - } - pArchive->valAllowDuplicates = value; - break; - case kNuValueConvertExtractedEOL: - if (value < kNuConvertOff || value > kNuConvertAuto) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueConvertExtractedEOL value %u", value); - goto bail; - } - pArchive->valConvertExtractedEOL = value; - break; - case kNuValueDataCompression: - if (value < kNuCompressNone || value > kNuCompressBzip2) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueDataCompression value %u", value); - goto bail; - } - pArchive->valDataCompression = value; - break; - case kNuValueDiscardWrapper: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueDiscardWrapper value %u", value); - goto bail; - } - pArchive->valDiscardWrapper = value; - break; - case kNuValueEOL: - if (value < kNuEOLUnknown || value > kNuEOLCRLF) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueEOL value %u", value); - goto bail; - } - pArchive->valEOL = value; - break; - case kNuValueHandleExisting: - if (value < kNuMaybeOverwrite || value > kNuMustOverwrite) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueHandleExisting value %u", value); - goto bail; - } - pArchive->valHandleExisting = value; - break; - case kNuValueIgnoreCRC: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueIgnoreCRC value %u", value); - goto bail; - } - pArchive->valIgnoreCRC = value; - break; - case kNuValueMaskDataless: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueMaskDataless value %u", value); - goto bail; - } - pArchive->valMaskDataless = value; - break; - case kNuValueMimicSHK: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueMimicSHK value %u", value); - goto bail; - } - pArchive->valMimicSHK = value; - break; - case kNuValueModifyOrig: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueModifyOrig value %u", value); - goto bail; - } - pArchive->valModifyOrig = value; - break; - case kNuValueOnlyUpdateOlder: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueOnlyUpdateOlder value %u", value); - goto bail; - } - pArchive->valOnlyUpdateOlder = value; - break; - case kNuValueStripHighASCII: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueStripHighASCII value %u", value); - goto bail; - } - pArchive->valStripHighASCII = value; - break; - case kNuValueJunkSkipMax: - if (value > kMaxJunkSkipMax) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueJunkSkipMax value %u", value); - goto bail; - } - pArchive->valJunkSkipMax = value; - break; - case kNuValueIgnoreLZW2Len: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueIgnoreLZW2Len value %u", value); - goto bail; - } - pArchive->valIgnoreLZW2Len = value; - break; - case kNuValueHandleBadMac: - if (value != true && value != false) { - Nu_ReportError(NU_BLOB, err, - "Invalid kNuValueHandleBadMac value %u", value); - goto bail; - } - pArchive->valHandleBadMac = value; - break; - default: - Nu_ReportError(NU_BLOB, err, "Unknown ValueID %d requested", ident); - goto bail; - } - - err = kNuErrNone; - -bail: - return err; -} - - -/* - * Get an archive attribute. These are things that you would have to - * pry into pArchive to get at (like the archive type) or get the master - * header (like the number of records). - */ -NuError Nu_GetAttr(NuArchive* pArchive, NuAttrID ident, NuAttr* pAttr) -{ - NuError err = kNuErrNone; - if (pAttr == NULL) - return kNuErrInvalidArg; - - switch (ident) { - case kNuAttrArchiveType: - *pAttr = pArchive->archiveType; - break; - case kNuAttrNumRecords: - *pAttr = pArchive->masterHeader.mhTotalRecords; - break; - case kNuAttrHeaderOffset: - *pAttr = pArchive->headerOffset; - break; - case kNuAttrJunkOffset: - *pAttr = pArchive->junkOffset; - break; - default: - err = kNuErrInvalidArg; - Nu_ReportError(NU_BLOB, err, "Unknown AttrID %d requested", ident); - goto bail; - } - -bail: - return err; -} - -/* - * Convert a NuValue compression type to a "phyiscal" ThreadFormat. - * - * Unsupported compression types cause a warning to be flagged. - */ -NuThreadFormat Nu_ConvertCompressValToFormat(NuArchive* pArchive, - NuValue compValue) -{ - NuThreadFormat threadFormat; - Boolean unsup = false; - - switch (compValue) { - case kNuCompressNone: threadFormat = kNuThreadFormatUncompressed; break; - - #ifdef ENABLE_SQ - case kNuCompressSQ: threadFormat = kNuThreadFormatHuffmanSQ; break; - #else - case kNuCompressSQ: threadFormat = kNuThreadFormatHuffmanSQ; - unsup = true; break; - #endif - - #ifdef ENABLE_LZW - case kNuCompressLZW1: threadFormat = kNuThreadFormatLZW1; break; - case kNuCompressLZW2: threadFormat = kNuThreadFormatLZW2; break; - #else - case kNuCompressLZW1: threadFormat = kNuThreadFormatLZW1; - unsup = true; break; - case kNuCompressLZW2: threadFormat = kNuThreadFormatLZW2; - unsup = true; break; - #endif - - #ifdef ENABLE_LZC - case kNuCompressLZC12: threadFormat = kNuThreadFormatLZC12; break; - case kNuCompressLZC16: threadFormat = kNuThreadFormatLZC16; break; - #else - case kNuCompressLZC12: threadFormat = kNuThreadFormatLZC12; - unsup = true; break; - case kNuCompressLZC16: threadFormat = kNuThreadFormatLZC16; - unsup = true; break; - #endif - - #ifdef ENABLE_DEFLATE - case kNuCompressDeflate: threadFormat = kNuThreadFormatDeflate; break; - #else - case kNuCompressDeflate: threadFormat = kNuThreadFormatDeflate; - unsup = true; break; - #endif - - #ifdef ENABLE_BZIP2 - case kNuCompressBzip2: threadFormat = kNuThreadFormatBzip2; break; - #else - case kNuCompressBzip2: threadFormat = kNuThreadFormatBzip2; - unsup = true; break; - #endif - - default: - Nu_ReportError(NU_BLOB, kNuErrInvalidArg, - "Unknown compress value %u", compValue); - Assert(false); - return kNuThreadFormatUncompressed; - } - - if (unsup) { - Nu_ReportError(NU_BLOB, kNuErrNone, - "Unsupported compression 0x%04x requested (%u), storing", - threadFormat, compValue); - return kNuThreadFormatUncompressed; - } - - return threadFormat; -} - diff --git a/ciderpress/nufxlib/Version.c b/ciderpress/nufxlib/Version.c deleted file mode 100644 index 9c3396c..0000000 --- a/ciderpress/nufxlib/Version.c +++ /dev/null @@ -1,41 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - */ -#include "NufxLibPriv.h" - -/* executable was build on or after this date */ -#ifdef __DATE__ -static const char gNuBuildDate[] = __DATE__; -#else -static const char gNuBuildDate[] = "??? ?? ????"; -#endif - -#ifdef OPTFLAGSTR -static const char gNuBuildFlags[] = OPTFLAGSTR; -#else -static const char gNuBuildFlags[] = "-"; -#endif - - -/* - * Return the version number, date built, and build flags. - */ -NuError Nu_GetVersion(int32_t* pMajorVersion, int32_t* pMinorVersion, - int32_t* pBugVersion, const char** ppBuildDate, const char** ppBuildFlags) -{ - if (pMajorVersion != NULL) - *pMajorVersion = kNuVersionMajor; - if (pMinorVersion != NULL) - *pMinorVersion = kNuVersionMinor; - if (pBugVersion != NULL) - *pBugVersion = kNuVersionBug; - if (ppBuildDate != NULL) - *ppBuildDate = gNuBuildDate; - if (ppBuildFlags != NULL) - *ppBuildFlags = gNuBuildFlags; - return kNuErrNone; -} - diff --git a/ciderpress/nufxlib/config.guess b/ciderpress/nufxlib/config.guess deleted file mode 100644 index ad5281e..0000000 --- a/ciderpress/nufxlib/config.guess +++ /dev/null @@ -1,1466 +0,0 @@ -#! /bin/sh -# Attempt to guess a canonical system name. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. - -timestamp='2005-08-03' - -# This file is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Originally written by Per Bothner . -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# This script attempts to guess a canonical system name similar to -# config.sub. If it succeeds, it prints the system name on stdout, and -# exits with 0. Otherwise, it exits with 1. -# -# The plan is that this can be called by configure scripts if you -# don't specify an explicit build system type. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] - -Output the configuration name of the system \`$me' is run on. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.guess ($timestamp) - -Originally written by Per Bothner. -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" >&2 - exit 1 ;; - * ) - break ;; - esac -done - -if test $# != 0; then - echo "$me: too many arguments$help" >&2 - exit 1 -fi - -trap 'exit 1' 1 2 15 - -# CC_FOR_BUILD -- compiler used by this script. Note that the use of a -# compiler to aid in system detection is discouraged as it requires -# temporary files to be created and, as you can see below, it is a -# headache to deal with in a portable fashion. - -# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still -# use `HOST_CC' if defined, but it is deprecated. - -# Portable tmp directory creation inspired by the Autoconf team. - -set_cc_for_build=' -trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; -trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; -: ${TMPDIR=/tmp} ; - { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || - { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || - { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || - { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; -dummy=$tmp/dummy ; -tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; -case $CC_FOR_BUILD,$HOST_CC,$CC in - ,,) echo "int x;" > $dummy.c ; - for c in cc gcc c89 c99 ; do - if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then - CC_FOR_BUILD="$c"; break ; - fi ; - done ; - if test x"$CC_FOR_BUILD" = x ; then - CC_FOR_BUILD=no_compiler_found ; - fi - ;; - ,,*) CC_FOR_BUILD=$CC ;; - ,*,*) CC_FOR_BUILD=$HOST_CC ;; -esac ; set_cc_for_build= ;' - -# This is needed to find uname on a Pyramid OSx when run in the BSD universe. -# (ghazi@noc.rutgers.edu 1994-08-24) -if (test -f /.attbin/uname) >/dev/null 2>&1 ; then - PATH=$PATH:/.attbin ; export PATH -fi - -UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown -UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown -UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown -UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown - -# Note: order is significant - the case branches are not exclusive. - -case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in - *:NetBSD:*:*) - # NetBSD (nbsd) targets should (where applicable) match one or - # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, - # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently - # switched to ELF, *-*-netbsd* would select the old - # object file format. This provides both forward - # compatibility and a consistent mechanism for selecting the - # object file format. - # - # Note: NetBSD doesn't particularly care about the vendor - # portion of the name. We always set it to "unknown". - sysctl="sysctl -n hw.machine_arch" - UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ - /usr/sbin/$sysctl 2>/dev/null || echo unknown)` - case "${UNAME_MACHINE_ARCH}" in - armeb) machine=armeb-unknown ;; - arm*) machine=arm-unknown ;; - sh3el) machine=shl-unknown ;; - sh3eb) machine=sh-unknown ;; - *) machine=${UNAME_MACHINE_ARCH}-unknown ;; - esac - # The Operating System including object format, if it has switched - # to ELF recently, or will in the future. - case "${UNAME_MACHINE_ARCH}" in - arm*|i386|m68k|ns32k|sh3*|sparc|vax) - eval $set_cc_for_build - if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ - | grep __ELF__ >/dev/null - then - # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). - # Return netbsd for either. FIX? - os=netbsd - else - os=netbsdelf - fi - ;; - *) - os=netbsd - ;; - esac - # The OS release - # Debian GNU/NetBSD machines have a different userland, and - # thus, need a distinct triplet. However, they do not need - # kernel version information, so it can be replaced with a - # suitable tag, in the style of linux-gnu. - case "${UNAME_VERSION}" in - Debian*) - release='-gnu' - ;; - *) - release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` - ;; - esac - # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: - # contains redundant information, the shorter form: - # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. - echo "${machine}-${os}${release}" - exit ;; - *:OpenBSD:*:*) - UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` - echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} - exit ;; - *:ekkoBSD:*:*) - echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} - exit ;; - macppc:MirBSD:*:*) - echo powerppc-unknown-mirbsd${UNAME_RELEASE} - exit ;; - *:MirBSD:*:*) - echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} - exit ;; - alpha:OSF1:*:*) - case $UNAME_RELEASE in - *4.0) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` - ;; - *5.*) - UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` - ;; - esac - # According to Compaq, /usr/sbin/psrinfo has been available on - # OSF/1 and Tru64 systems produced since 1995. I hope that - # covers most systems running today. This code pipes the CPU - # types through head -n 1, so we only detect the type of CPU 0. - ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` - case "$ALPHA_CPU_TYPE" in - "EV4 (21064)") - UNAME_MACHINE="alpha" ;; - "EV4.5 (21064)") - UNAME_MACHINE="alpha" ;; - "LCA4 (21066/21068)") - UNAME_MACHINE="alpha" ;; - "EV5 (21164)") - UNAME_MACHINE="alphaev5" ;; - "EV5.6 (21164A)") - UNAME_MACHINE="alphaev56" ;; - "EV5.6 (21164PC)") - UNAME_MACHINE="alphapca56" ;; - "EV5.7 (21164PC)") - UNAME_MACHINE="alphapca57" ;; - "EV6 (21264)") - UNAME_MACHINE="alphaev6" ;; - "EV6.7 (21264A)") - UNAME_MACHINE="alphaev67" ;; - "EV6.8CB (21264C)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8AL (21264B)") - UNAME_MACHINE="alphaev68" ;; - "EV6.8CX (21264D)") - UNAME_MACHINE="alphaev68" ;; - "EV6.9A (21264/EV69A)") - UNAME_MACHINE="alphaev69" ;; - "EV7 (21364)") - UNAME_MACHINE="alphaev7" ;; - "EV7.9 (21364A)") - UNAME_MACHINE="alphaev79" ;; - esac - # A Pn.n version is a patched version. - # A Vn.n version is a released version. - # A Tn.n version is a released field test version. - # A Xn.n version is an unreleased experimental baselevel. - # 1.2 uses "1.2" for uname -r. - echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - exit ;; - Alpha\ *:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # Should we change UNAME_MACHINE based on the output of uname instead - # of the specific Alpha model? - echo alpha-pc-interix - exit ;; - 21064:Windows_NT:50:3) - echo alpha-dec-winnt3.5 - exit ;; - Amiga*:UNIX_System_V:4.0:*) - echo m68k-unknown-sysv4 - exit ;; - *:[Aa]miga[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-amigaos - exit ;; - *:[Mm]orph[Oo][Ss]:*:*) - echo ${UNAME_MACHINE}-unknown-morphos - exit ;; - *:OS/390:*:*) - echo i370-ibm-openedition - exit ;; - *:z/VM:*:*) - echo s390-ibm-zvmoe - exit ;; - *:OS400:*:*) - echo powerpc-ibm-os400 - exit ;; - arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) - echo arm-acorn-riscix${UNAME_RELEASE} - exit ;; - arm:riscos:*:*|arm:RISCOS:*:*) - echo arm-unknown-riscos - exit ;; - SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) - echo hppa1.1-hitachi-hiuxmpp - exit ;; - Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) - # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. - if test "`(/bin/universe) 2>/dev/null`" = att ; then - echo pyramid-pyramid-sysv3 - else - echo pyramid-pyramid-bsd - fi - exit ;; - NILE*:*:*:dcosx) - echo pyramid-pyramid-svr4 - exit ;; - DRS?6000:unix:4.0:6*) - echo sparc-icl-nx6 - exit ;; - DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) - case `/usr/bin/uname -p` in - sparc) echo sparc-icl-nx7; exit ;; - esac ;; - sun4H:SunOS:5.*:*) - echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) - echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - i86pc:SunOS:5.*:*) - echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:6*:*) - # According to config.sub, this is the proper way to canonicalize - # SunOS6. Hard to guess exactly what SunOS6 will be like, but - # it's likely to be more like Solaris than SunOS4. - echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - sun4*:SunOS:*:*) - case "`/usr/bin/arch -k`" in - Series*|S4*) - UNAME_RELEASE=`uname -v` - ;; - esac - # Japanese Language versions have a version number like `4.1.3-JL'. - echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` - exit ;; - sun3*:SunOS:*:*) - echo m68k-sun-sunos${UNAME_RELEASE} - exit ;; - sun*:*:4.2BSD:*) - UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` - test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 - case "`/bin/arch`" in - sun3) - echo m68k-sun-sunos${UNAME_RELEASE} - ;; - sun4) - echo sparc-sun-sunos${UNAME_RELEASE} - ;; - esac - exit ;; - aushp:SunOS:*:*) - echo sparc-auspex-sunos${UNAME_RELEASE} - exit ;; - # The situation for MiNT is a little confusing. The machine name - # can be virtually everything (everything which is not - # "atarist" or "atariste" at least should have a processor - # > m68000). The system name ranges from "MiNT" over "FreeMiNT" - # to the lowercase version "mint" (or "freemint"). Finally - # the system name "TOS" denotes a system which is actually not - # MiNT. But MiNT is downward compatible to TOS, so this should - # be no problem. - atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) - echo m68k-atari-mint${UNAME_RELEASE} - exit ;; - milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) - echo m68k-milan-mint${UNAME_RELEASE} - exit ;; - hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) - echo m68k-hades-mint${UNAME_RELEASE} - exit ;; - *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) - echo m68k-unknown-mint${UNAME_RELEASE} - exit ;; - m68k:machten:*:*) - echo m68k-apple-machten${UNAME_RELEASE} - exit ;; - powerpc:machten:*:*) - echo powerpc-apple-machten${UNAME_RELEASE} - exit ;; - RISC*:Mach:*:*) - echo mips-dec-mach_bsd4.3 - exit ;; - RISC*:ULTRIX:*:*) - echo mips-dec-ultrix${UNAME_RELEASE} - exit ;; - VAX*:ULTRIX*:*:*) - echo vax-dec-ultrix${UNAME_RELEASE} - exit ;; - 2020:CLIX:*:* | 2430:CLIX:*:*) - echo clipper-intergraph-clix${UNAME_RELEASE} - exit ;; - mips:*:*:UMIPS | mips:*:*:RISCos) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c -#ifdef __cplusplus -#include /* for printf() prototype */ - int main (int argc, char *argv[]) { -#else - int main (argc, argv) int argc; char *argv[]; { -#endif - #if defined (host_mips) && defined (MIPSEB) - #if defined (SYSTYPE_SYSV) - printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_SVR4) - printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); - #endif - #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) - printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); - #endif - #endif - exit (-1); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && - dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && - SYSTEM_NAME=`$dummy $dummyarg` && - { echo "$SYSTEM_NAME"; exit; } - echo mips-mips-riscos${UNAME_RELEASE} - exit ;; - Motorola:PowerMAX_OS:*:*) - echo powerpc-motorola-powermax - exit ;; - Motorola:*:4.3:PL8-*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) - echo powerpc-harris-powermax - exit ;; - Night_Hawk:Power_UNIX:*:*) - echo powerpc-harris-powerunix - exit ;; - m88k:CX/UX:7*:*) - echo m88k-harris-cxux7 - exit ;; - m88k:*:4*:R4*) - echo m88k-motorola-sysv4 - exit ;; - m88k:*:3*:R3*) - echo m88k-motorola-sysv3 - exit ;; - AViiON:dgux:*:*) - # DG/UX returns AViiON for all architectures - UNAME_PROCESSOR=`/usr/bin/uname -p` - if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] - then - if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ - [ ${TARGET_BINARY_INTERFACE}x = x ] - then - echo m88k-dg-dgux${UNAME_RELEASE} - else - echo m88k-dg-dguxbcs${UNAME_RELEASE} - fi - else - echo i586-dg-dgux${UNAME_RELEASE} - fi - exit ;; - M88*:DolphinOS:*:*) # DolphinOS (SVR3) - echo m88k-dolphin-sysv3 - exit ;; - M88*:*:R3*:*) - # Delta 88k system running SVR3 - echo m88k-motorola-sysv3 - exit ;; - XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) - echo m88k-tektronix-sysv3 - exit ;; - Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) - echo m68k-tektronix-bsd - exit ;; - *:IRIX*:*:*) - echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` - exit ;; - ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. - echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id - exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' - i*86:AIX:*:*) - echo i386-ibm-aix - exit ;; - ia64:AIX:*:*) - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} - exit ;; - *:AIX:2:3) - if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - - main() - { - if (!__power_pc()) - exit(1); - puts("powerpc-ibm-aix3.2.5"); - exit(0); - } -EOF - if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` - then - echo "$SYSTEM_NAME" - else - echo rs6000-ibm-aix3.2.5 - fi - elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then - echo rs6000-ibm-aix3.2.4 - else - echo rs6000-ibm-aix3.2 - fi - exit ;; - *:AIX:*:[45]) - IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` - if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then - IBM_ARCH=rs6000 - else - IBM_ARCH=powerpc - fi - if [ -x /usr/bin/oslevel ] ; then - IBM_REV=`/usr/bin/oslevel` - else - IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} - fi - echo ${IBM_ARCH}-ibm-aix${IBM_REV} - exit ;; - *:AIX:*:*) - echo rs6000-ibm-aix - exit ;; - ibmrt:4.4BSD:*|romp-ibm:BSD:*) - echo romp-ibm-bsd4.4 - exit ;; - ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and - echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to - exit ;; # report: romp-ibm BSD 4.3 - *:BOSX:*:*) - echo rs6000-bull-bosx - exit ;; - DPX/2?00:B.O.S.:*:*) - echo m68k-bull-sysv3 - exit ;; - 9000/[34]??:4.3bsd:1.*:*) - echo m68k-hp-bsd - exit ;; - hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) - echo m68k-hp-bsd4.4 - exit ;; - 9000/[34678]??:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - case "${UNAME_MACHINE}" in - 9000/31? ) HP_ARCH=m68000 ;; - 9000/[34]?? ) HP_ARCH=m68k ;; - 9000/[678][0-9][0-9]) - if [ -x /usr/bin/getconf ]; then - sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` - sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` - case "${sc_cpu_version}" in - 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 - 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 - 532) # CPU_PA_RISC2_0 - case "${sc_kernel_bits}" in - 32) HP_ARCH="hppa2.0n" ;; - 64) HP_ARCH="hppa2.0w" ;; - '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 - esac ;; - esac - fi - if [ "${HP_ARCH}" = "" ]; then - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - - #define _HPUX_SOURCE - #include - #include - - int main () - { - #if defined(_SC_KERNEL_BITS) - long bits = sysconf(_SC_KERNEL_BITS); - #endif - long cpu = sysconf (_SC_CPU_VERSION); - - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1"); break; - case CPU_PA_RISC2_0: - #if defined(_SC_KERNEL_BITS) - switch (bits) - { - case 64: puts ("hppa2.0w"); break; - case 32: puts ("hppa2.0n"); break; - default: puts ("hppa2.0"); break; - } break; - #else /* !defined(_SC_KERNEL_BITS) */ - puts ("hppa2.0"); break; - #endif - default: puts ("hppa1.0"); break; - } - exit (0); - } -EOF - (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` - test -z "$HP_ARCH" && HP_ARCH=hppa - fi ;; - esac - if [ ${HP_ARCH} = "hppa2.0w" ] - then - eval $set_cc_for_build - - # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating - # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler - # generating 64-bit code. GNU and HP use different nomenclature: - # - # $ CC_FOR_BUILD=cc ./config.guess - # => hppa2.0w-hp-hpux11.23 - # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess - # => hppa64-hp-hpux11.23 - - if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | - grep __LP64__ >/dev/null - then - HP_ARCH="hppa2.0w" - else - HP_ARCH="hppa64" - fi - fi - echo ${HP_ARCH}-hp-hpux${HPUX_REV} - exit ;; - ia64:HP-UX:*:*) - HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` - echo ia64-hp-hpux${HPUX_REV} - exit ;; - 3050*:HI-UX:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - int - main () - { - long cpu = sysconf (_SC_CPU_VERSION); - /* The order matters, because CPU_IS_HP_MC68K erroneously returns - true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct - results, however. */ - if (CPU_IS_PA_RISC (cpu)) - { - switch (cpu) - { - case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; - case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; - case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; - default: puts ("hppa-hitachi-hiuxwe2"); break; - } - } - else if (CPU_IS_HP_MC68K (cpu)) - puts ("m68k-hitachi-hiuxwe2"); - else puts ("unknown-hitachi-hiuxwe2"); - exit (0); - } -EOF - $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - echo unknown-hitachi-hiuxwe2 - exit ;; - 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) - echo hppa1.1-hp-bsd - exit ;; - 9000/8??:4.3bsd:*:*) - echo hppa1.0-hp-bsd - exit ;; - *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) - echo hppa1.0-hp-mpeix - exit ;; - hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) - echo hppa1.1-hp-osf - exit ;; - hp8??:OSF1:*:*) - echo hppa1.0-hp-osf - exit ;; - i*86:OSF1:*:*) - if [ -x /usr/sbin/sysversion ] ; then - echo ${UNAME_MACHINE}-unknown-osf1mk - else - echo ${UNAME_MACHINE}-unknown-osf1 - fi - exit ;; - parisc*:Lites*:*:*) - echo hppa1.1-hp-lites - exit ;; - C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) - echo c1-convex-bsd - exit ;; - C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) - echo c34-convex-bsd - exit ;; - C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) - echo c38-convex-bsd - exit ;; - C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) - echo c4-convex-bsd - exit ;; - CRAY*Y-MP:*:*:*) - echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*[A-Z]90:*:*:*) - echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ - | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ - -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ - -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*TS:*:*:*) - echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*T3E:*:*:*) - echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - CRAY*SV1:*:*:*) - echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - *:UNICOS/mp:*:*) - echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' - exit ;; - F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) - FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` - echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - 5000:UNIX_System_V:4.*:*) - FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` - FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` - echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" - exit ;; - i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) - echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} - exit ;; - sparc*:BSD/OS:*:*) - echo sparc-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:BSD/OS:*:*) - echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} - exit ;; - *:FreeBSD:*:*) - echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - i*:CYGWIN*:*) - echo ${UNAME_MACHINE}-pc-cygwin - exit ;; - i*:MINGW*:*) - echo ${UNAME_MACHINE}-pc-mingw32 - exit ;; - i*:windows32*:*) - # uname -m includes "-pc" on this system. - echo ${UNAME_MACHINE}-mingw32 - exit ;; - i*:PW*:*) - echo ${UNAME_MACHINE}-pc-pw32 - exit ;; - x86:Interix*:[34]*) - echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' - exit ;; - [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) - echo i${UNAME_MACHINE}-pc-mks - exit ;; - i*:Windows_NT*:* | Pentium*:Windows_NT*:*) - # How do we know it's Interix rather than the generic POSIX subsystem? - # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we - # UNAME_MACHINE based on the output of uname instead of i386? - echo i586-pc-interix - exit ;; - i*:UWIN*:*) - echo ${UNAME_MACHINE}-pc-uwin - exit ;; - amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) - echo x86_64-unknown-cygwin - exit ;; - p*:CYGWIN*:*) - echo powerpcle-unknown-cygwin - exit ;; - prep*:SunOS:5.*:*) - echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` - exit ;; - *:GNU:*:*) - # the GNU system - echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` - exit ;; - *:GNU/*:*:*) - # other systems with GNU libc and userland - echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu - exit ;; - i*86:Minix:*:*) - echo ${UNAME_MACHINE}-pc-minix - exit ;; - arm*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - cris:Linux:*:*) - echo cris-axis-linux-gnu - exit ;; - crisv32:Linux:*:*) - echo crisv32-axis-linux-gnu - exit ;; - frv:Linux:*:*) - echo frv-unknown-linux-gnu - exit ;; - ia64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - m32r*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - m68*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - mips:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips - #undef mipsel - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mipsel - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - mips64:Linux:*:*) - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #undef CPU - #undef mips64 - #undef mips64el - #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) - CPU=mips64el - #else - #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) - CPU=mips64 - #else - CPU= - #endif - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` - test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } - ;; - or32:Linux:*:*) - echo or32-unknown-linux-gnu - exit ;; - ppc:Linux:*:*) - echo powerpc-unknown-linux-gnu - exit ;; - ppc64:Linux:*:*) - echo powerpc64-unknown-linux-gnu - exit ;; - alpha:Linux:*:*) - case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in - EV5) UNAME_MACHINE=alphaev5 ;; - EV56) UNAME_MACHINE=alphaev56 ;; - PCA56) UNAME_MACHINE=alphapca56 ;; - PCA57) UNAME_MACHINE=alphapca56 ;; - EV6) UNAME_MACHINE=alphaev6 ;; - EV67) UNAME_MACHINE=alphaev67 ;; - EV68*) UNAME_MACHINE=alphaev68 ;; - esac - objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null - if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi - echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} - exit ;; - parisc:Linux:*:* | hppa:Linux:*:*) - # Look for CPU level - case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in - PA7*) echo hppa1.1-unknown-linux-gnu ;; - PA8*) echo hppa2.0-unknown-linux-gnu ;; - *) echo hppa-unknown-linux-gnu ;; - esac - exit ;; - parisc64:Linux:*:* | hppa64:Linux:*:*) - echo hppa64-unknown-linux-gnu - exit ;; - s390:Linux:*:* | s390x:Linux:*:*) - echo ${UNAME_MACHINE}-ibm-linux - exit ;; - sh64*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - sh*:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - sparc:Linux:*:* | sparc64:Linux:*:*) - echo ${UNAME_MACHINE}-unknown-linux-gnu - exit ;; - x86_64:Linux:*:*) - echo x86_64-unknown-linux-gnu - exit ;; - i*86:Linux:*:*) - # The BFD linker knows what the default object file format is, so - # first see if it will tell us. cd to the root directory to prevent - # problems with other programs or directories called `ld' in the path. - # Set LC_ALL=C to ensure ld outputs messages in English. - ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ - | sed -ne '/supported targets:/!d - s/[ ][ ]*/ /g - s/.*supported targets: *// - s/ .*// - p'` - case "$ld_supported_targets" in - elf32-i386) - TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" - ;; - a.out-i386-linux) - echo "${UNAME_MACHINE}-pc-linux-gnuaout" - exit ;; - coff-i386) - echo "${UNAME_MACHINE}-pc-linux-gnucoff" - exit ;; - "") - # Either a pre-BFD a.out linker (linux-gnuoldld) or - # one that does not give us useful --help. - echo "${UNAME_MACHINE}-pc-linux-gnuoldld" - exit ;; - esac - # Determine whether the default compiler is a.out or elf - eval $set_cc_for_build - sed 's/^ //' << EOF >$dummy.c - #include - #ifdef __ELF__ - # ifdef __GLIBC__ - # if __GLIBC__ >= 2 - LIBC=gnu - # else - LIBC=gnulibc1 - # endif - # else - LIBC=gnulibc1 - # endif - #else - #ifdef __INTEL_COMPILER - LIBC=gnu - #else - LIBC=gnuaout - #endif - #endif - #ifdef __dietlibc__ - LIBC=dietlibc - #endif -EOF - eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` - test x"${LIBC}" != x && { - echo "${UNAME_MACHINE}-pc-linux-${LIBC}" - exit - } - test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } - ;; - i*86:DYNIX/ptx:4*:*) - # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. - # earlier versions are messed up and put the nodename in both - # sysname and nodename. - echo i386-sequent-sysv4 - exit ;; - i*86:UNIX_SV:4.2MP:2.*) - # Unixware is an offshoot of SVR4, but it has its own version - # number series starting with 2... - # I am not positive that other SVR4 systems won't match this, - # I just have to hope. -- rms. - # Use sysv4.2uw... so that sysv4* matches it. - echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} - exit ;; - i*86:OS/2:*:*) - # If we were able to find `uname', then EMX Unix compatibility - # is probably installed. - echo ${UNAME_MACHINE}-pc-os2-emx - exit ;; - i*86:XTS-300:*:STOP) - echo ${UNAME_MACHINE}-unknown-stop - exit ;; - i*86:atheos:*:*) - echo ${UNAME_MACHINE}-unknown-atheos - exit ;; - i*86:syllable:*:*) - echo ${UNAME_MACHINE}-pc-syllable - exit ;; - i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) - echo i386-unknown-lynxos${UNAME_RELEASE} - exit ;; - i*86:*DOS:*:*) - echo ${UNAME_MACHINE}-pc-msdosdjgpp - exit ;; - i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) - UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` - if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then - echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} - else - echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} - fi - exit ;; - i*86:*:5:[678]*) - # UnixWare 7.x, OpenUNIX and OpenServer 6. - case `/bin/uname -X | grep "^Machine"` in - *486*) UNAME_MACHINE=i486 ;; - *Pentium) UNAME_MACHINE=i586 ;; - *Pent*|*Celeron) UNAME_MACHINE=i686 ;; - esac - echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} - exit ;; - i*86:*:3.2:*) - if test -f /usr/options/cb.name; then - UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then - UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` - (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 - (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ - && UNAME_MACHINE=i586 - (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ - && UNAME_MACHINE=i686 - (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ - && UNAME_MACHINE=i686 - echo ${UNAME_MACHINE}-pc-sco$UNAME_REL - else - echo ${UNAME_MACHINE}-pc-sysv32 - fi - exit ;; - pc:*:*:*) - # Left here for compatibility: - # uname -m prints for DJGPP always 'pc', but it prints nothing about - # the processor, so we play safe by assuming i386. - echo i386-pc-msdosdjgpp - exit ;; - Intel:Mach:3*:*) - echo i386-pc-mach3 - exit ;; - paragon:*:*:*) - echo i860-intel-osf1 - exit ;; - i860:*:4.*:*) # i860-SVR4 - if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then - echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 - else # Add other i860-SVR4 vendors below as they are discovered. - echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 - fi - exit ;; - mini*:CTIX:SYS*5:*) - # "miniframe" - echo m68010-convergent-sysv - exit ;; - mc68k:UNIX:SYSTEM5:3.51m) - echo m68k-convergent-sysv - exit ;; - M680?0:D-NIX:5.3:*) - echo m68k-diab-dnix - exit ;; - M68*:*:R3V[5678]*:*) - test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; - 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) - OS_REL='' - test -r /etc/.relid \ - && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4.3${OS_REL}; exit; } - /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ - && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; - 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) - /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ - && { echo i486-ncr-sysv4; exit; } ;; - m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) - echo m68k-unknown-lynxos${UNAME_RELEASE} - exit ;; - mc68030:UNIX_System_V:4.*:*) - echo m68k-atari-sysv4 - exit ;; - TSUNAMI:LynxOS:2.*:*) - echo sparc-unknown-lynxos${UNAME_RELEASE} - exit ;; - rs6000:LynxOS:2.*:*) - echo rs6000-unknown-lynxos${UNAME_RELEASE} - exit ;; - PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) - echo powerpc-unknown-lynxos${UNAME_RELEASE} - exit ;; - SM[BE]S:UNIX_SV:*:*) - echo mips-dde-sysv${UNAME_RELEASE} - exit ;; - RM*:ReliantUNIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - RM*:SINIX-*:*:*) - echo mips-sni-sysv4 - exit ;; - *:SINIX-*:*:*) - if uname -p 2>/dev/null >/dev/null ; then - UNAME_MACHINE=`(uname -p) 2>/dev/null` - echo ${UNAME_MACHINE}-sni-sysv4 - else - echo ns32k-sni-sysv - fi - exit ;; - PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort - # says - echo i586-unisys-sysv4 - exit ;; - *:UNIX_System_V:4*:FTX*) - # From Gerald Hewes . - # How about differentiating between stratus architectures? -djm - echo hppa1.1-stratus-sysv4 - exit ;; - *:*:*:FTX*) - # From seanf@swdc.stratus.com. - echo i860-stratus-sysv4 - exit ;; - i*86:VOS:*:*) - # From Paul.Green@stratus.com. - echo ${UNAME_MACHINE}-stratus-vos - exit ;; - *:VOS:*:*) - # From Paul.Green@stratus.com. - echo hppa1.1-stratus-vos - exit ;; - mc68*:A/UX:*:*) - echo m68k-apple-aux${UNAME_RELEASE} - exit ;; - news*:NEWS-OS:6*:*) - echo mips-sony-newsos6 - exit ;; - R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) - if [ -d /usr/nec ]; then - echo mips-nec-sysv${UNAME_RELEASE} - else - echo mips-unknown-sysv${UNAME_RELEASE} - fi - exit ;; - BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. - echo powerpc-be-beos - exit ;; - BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. - echo powerpc-apple-beos - exit ;; - BePC:BeOS:*:*) # BeOS running on Intel PC compatible. - echo i586-pc-beos - exit ;; - SX-4:SUPER-UX:*:*) - echo sx4-nec-superux${UNAME_RELEASE} - exit ;; - SX-5:SUPER-UX:*:*) - echo sx5-nec-superux${UNAME_RELEASE} - exit ;; - SX-6:SUPER-UX:*:*) - echo sx6-nec-superux${UNAME_RELEASE} - exit ;; - Power*:Rhapsody:*:*) - echo powerpc-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Rhapsody:*:*) - echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} - exit ;; - *:Darwin:*:*) - UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown - case $UNAME_PROCESSOR in - *86) UNAME_PROCESSOR=i686 ;; - unknown) UNAME_PROCESSOR=powerpc ;; - esac - echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} - exit ;; - *:procnto*:*:* | *:QNX:[0123456789]*:*) - UNAME_PROCESSOR=`uname -p` - if test "$UNAME_PROCESSOR" = "x86"; then - UNAME_PROCESSOR=i386 - UNAME_MACHINE=pc - fi - echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} - exit ;; - *:QNX:*:4*) - echo i386-pc-qnx - exit ;; - NSE-?:NONSTOP_KERNEL:*:*) - echo nse-tandem-nsk${UNAME_RELEASE} - exit ;; - NSR-?:NONSTOP_KERNEL:*:*) - echo nsr-tandem-nsk${UNAME_RELEASE} - exit ;; - *:NonStop-UX:*:*) - echo mips-compaq-nonstopux - exit ;; - BS2000:POSIX*:*:*) - echo bs2000-siemens-sysv - exit ;; - DS/*:UNIX_System_V:*:*) - echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} - exit ;; - *:Plan9:*:*) - # "uname -m" is not consistent, so use $cputype instead. 386 - # is converted to i386 for consistency with other x86 - # operating systems. - if test "$cputype" = "386"; then - UNAME_MACHINE=i386 - else - UNAME_MACHINE="$cputype" - fi - echo ${UNAME_MACHINE}-unknown-plan9 - exit ;; - *:TOPS-10:*:*) - echo pdp10-unknown-tops10 - exit ;; - *:TENEX:*:*) - echo pdp10-unknown-tenex - exit ;; - KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) - echo pdp10-dec-tops20 - exit ;; - XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) - echo pdp10-xkl-tops20 - exit ;; - *:TOPS-20:*:*) - echo pdp10-unknown-tops20 - exit ;; - *:ITS:*:*) - echo pdp10-unknown-its - exit ;; - SEI:*:*:SEIUX) - echo mips-sei-seiux${UNAME_RELEASE} - exit ;; - *:DragonFly:*:*) - echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` - exit ;; - *:*VMS:*:*) - UNAME_MACHINE=`(uname -p) 2>/dev/null` - case "${UNAME_MACHINE}" in - A*) echo alpha-dec-vms ; exit ;; - I*) echo ia64-dec-vms ; exit ;; - V*) echo vax-dec-vms ; exit ;; - esac ;; - *:XENIX:*:SysV) - echo i386-pc-xenix - exit ;; - i*86:skyos:*:*) - echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' - exit ;; -esac - -#echo '(No uname command or uname output not recognized.)' 1>&2 -#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 - -eval $set_cc_for_build -cat >$dummy.c < -# include -#endif -main () -{ -#if defined (sony) -#if defined (MIPSEB) - /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, - I don't know.... */ - printf ("mips-sony-bsd\n"); exit (0); -#else -#include - printf ("m68k-sony-newsos%s\n", -#ifdef NEWSOS4 - "4" -#else - "" -#endif - ); exit (0); -#endif -#endif - -#if defined (__arm) && defined (__acorn) && defined (__unix) - printf ("arm-acorn-riscix\n"); exit (0); -#endif - -#if defined (hp300) && !defined (hpux) - printf ("m68k-hp-bsd\n"); exit (0); -#endif - -#if defined (NeXT) -#if !defined (__ARCHITECTURE__) -#define __ARCHITECTURE__ "m68k" -#endif - int version; - version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; - if (version < 4) - printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); - else - printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); - exit (0); -#endif - -#if defined (MULTIMAX) || defined (n16) -#if defined (UMAXV) - printf ("ns32k-encore-sysv\n"); exit (0); -#else -#if defined (CMU) - printf ("ns32k-encore-mach\n"); exit (0); -#else - printf ("ns32k-encore-bsd\n"); exit (0); -#endif -#endif -#endif - -#if defined (__386BSD__) - printf ("i386-pc-bsd\n"); exit (0); -#endif - -#if defined (sequent) -#if defined (i386) - printf ("i386-sequent-dynix\n"); exit (0); -#endif -#if defined (ns32000) - printf ("ns32k-sequent-dynix\n"); exit (0); -#endif -#endif - -#if defined (_SEQUENT_) - struct utsname un; - - uname(&un); - - if (strncmp(un.version, "V2", 2) == 0) { - printf ("i386-sequent-ptx2\n"); exit (0); - } - if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ - printf ("i386-sequent-ptx1\n"); exit (0); - } - printf ("i386-sequent-ptx\n"); exit (0); - -#endif - -#if defined (vax) -# if !defined (ultrix) -# include -# if defined (BSD) -# if BSD == 43 - printf ("vax-dec-bsd4.3\n"); exit (0); -# else -# if BSD == 199006 - printf ("vax-dec-bsd4.3reno\n"); exit (0); -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# endif -# else - printf ("vax-dec-bsd\n"); exit (0); -# endif -# else - printf ("vax-dec-ultrix\n"); exit (0); -# endif -#endif - -#if defined (alliant) && defined (i860) - printf ("i860-alliant-bsd\n"); exit (0); -#endif - - exit (1); -} -EOF - -$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && - { echo "$SYSTEM_NAME"; exit; } - -# Apollos put the system type in the environment. - -test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } - -# Convex versions that predate uname can use getsysinfo(1) - -if [ -x /usr/convex/getsysinfo ] -then - case `getsysinfo -f cpu_type` in - c1*) - echo c1-convex-bsd - exit ;; - c2*) - if getsysinfo -f scalar_acc - then echo c32-convex-bsd - else echo c2-convex-bsd - fi - exit ;; - c34*) - echo c34-convex-bsd - exit ;; - c38*) - echo c38-convex-bsd - exit ;; - c4*) - echo c4-convex-bsd - exit ;; - esac -fi - -cat >&2 < in order to provide the needed -information to handle your system. - -config.guess timestamp = $timestamp - -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null` - -hostinfo = `(hostinfo) 2>/dev/null` -/bin/universe = `(/bin/universe) 2>/dev/null` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` -/bin/arch = `(/bin/arch) 2>/dev/null` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` - -UNAME_MACHINE = ${UNAME_MACHINE} -UNAME_RELEASE = ${UNAME_RELEASE} -UNAME_SYSTEM = ${UNAME_SYSTEM} -UNAME_VERSION = ${UNAME_VERSION} -EOF - -exit 1 - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/ciderpress/nufxlib/config.h.in b/ciderpress/nufxlib/config.h.in deleted file mode 100644 index dd294e2..0000000 --- a/ciderpress/nufxlib/config.h.in +++ /dev/null @@ -1,134 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING-LIB. - */ -/* config.h.in. */ - -/* Define to empty if the keyword does not work. */ -#undef const - -/* Define to empty if the keyword does not work. */ -#undef inline - -/* Define to `int' if doesn't define. */ -#undef mode_t - -/* Define to `long' if doesn't define. */ -#undef off_t - -/* Define to `unsigned' if doesn't define. */ -#undef size_t - -/* Define if you have the ANSI C header files. */ -#undef STDC_HEADERS - -/* Define if your declares struct tm. */ -#undef TM_IN_SYS_TIME - -/* Define to `int' if doesn't define. */ -#undef mode_t - -/* Define to `long' if doesn't define. */ -#undef off_t - -/* Define to `unsigned' if doesn't define. */ -#undef size_t - -/* Define if you have the fdopen function. */ -#undef HAVE_FDOPEN - -/* Define if you have the ftruncate function. */ -#undef HAVE_FTRUNCATE - -/* Define if you have the localtime_r function. */ -#undef HAVE_LOCALTIME_R - -/* Define if you have the memmove function. */ -#undef HAVE_MEMMOVE - -/* Define if you have the mkdir function. */ -#undef HAVE_MKDIR - -/* Define if you have the mkstemp function. */ -#undef HAVE_MKSTEMP - -/* Define if you have the mktime function. */ -#undef HAVE_MKTIME - -/* Define if you have the snprintf function. */ -#undef HAVE_SNPRINTF - -/* Define if you have the strcasecmp function. */ -#undef HAVE_STRCASECMP - -/* Define if you have the strncasecmp function. */ -#undef HAVE_STRNCASECMP - -/* Define if you have the strerror function. */ -#undef HAVE_STRERROR - -/* Define if you have the strtoul function. */ -#undef HAVE_STRTOUL - -/* Define if you have the timelocal function. */ -#undef HAVE_TIMELOCAL - -/* Define if you have the vsnprintf function. */ -#undef HAVE_VSNPRINTF - -/* Define if you have the header file. */ -#undef HAVE_FCNTL_H - -/* Define if you have the header file. */ -#undef HAVE_MALLOC_H - -/* Define if you have the header file. */ -#undef HAVE_STDLIB_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_STAT_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_TIME_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_TYPES_H - -/* Define if you have the header file. */ -#undef HAVE_SYS_UTIME_H - -/* Define if you have the header file. */ -#undef HAVE_UNISTD_H - -/* Define if you have the header file. */ -#undef HAVE_UTIME_H - -/* Define if sprintf returns an int. */ -#undef SPRINTF_RETURNS_INT - -/* Define if SNPRINTF is declared in stdio.h. */ -#undef SNPRINTF_DECLARED - -/* Define if VSNPRINTF is declared in stdio.h. */ -#undef VSNPRINTF_DECLARED - -/* Define to include SQ (Huffman+RLE) compression. */ -#undef ENABLE_SQ - -/* Define to include LZW (ShrinkIt LZW/1 and LZW/2) compression. */ -#undef ENABLE_LZW - -/* Define to include LZC (12-bit and 16-bit UNIX "compress") compression. */ -#undef ENABLE_LZC - -/* Define to include deflate (zlib) compression (also need -l in Makefile). */ -#undef ENABLE_DEFLATE - -/* Define to include bzip2 (libbz2) compression (also need -l in Makefile). */ -#undef ENABLE_BZIP2 - -/* Define if we want to use the dmalloc library (also need -l in Makefile). */ -#undef USE_DMALLOC - diff --git a/ciderpress/nufxlib/config.sub b/ciderpress/nufxlib/config.sub deleted file mode 100644 index 1c366df..0000000 --- a/ciderpress/nufxlib/config.sub +++ /dev/null @@ -1,1579 +0,0 @@ -#! /bin/sh -# Configuration validation subroutine script. -# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, -# 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc. - -timestamp='2005-07-08' - -# This file is (in principle) common to ALL GNU software. -# The presence of a machine in this file suggests that SOME GNU software -# can handle that machine. It does not imply ALL GNU software can. -# -# This file is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program 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 General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA -# 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. - - -# Please send patches to . Submit a context -# diff and a properly formatted ChangeLog entry. -# -# Configuration subroutine to validate and canonicalize a configuration type. -# Supply the specified configuration type as an argument. -# If it is invalid, we print an error message on stderr and exit with code 1. -# Otherwise, we print the canonical config type on stdout and succeed. - -# This file is supposed to be the same for all GNU packages -# and recognize all the CPU types, system types and aliases -# that are meaningful with *any* GNU software. -# Each package is responsible for reporting which valid configurations -# it does not support. The user should be able to distinguish -# a failure to support a valid configuration from a meaningless -# configuration. - -# The goal of this file is to map all the various variations of a given -# machine specification into a single specification in the form: -# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM -# or in some cases, the newer four-part form: -# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM -# It is wrong to echo any other type of specification. - -me=`echo "$0" | sed -e 's,.*/,,'` - -usage="\ -Usage: $0 [OPTION] CPU-MFR-OPSYS - $0 [OPTION] ALIAS - -Canonicalize a configuration name. - -Operation modes: - -h, --help print this help, then exit - -t, --time-stamp print date of last modification, then exit - -v, --version print version number, then exit - -Report bugs and patches to ." - -version="\ -GNU config.sub ($timestamp) - -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 -Free Software Foundation, Inc. - -This is free software; see the source for copying conditions. There is NO -warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - -help=" -Try \`$me --help' for more information." - -# Parse command line -while test $# -gt 0 ; do - case $1 in - --time-stamp | --time* | -t ) - echo "$timestamp" ; exit ;; - --version | -v ) - echo "$version" ; exit ;; - --help | --h* | -h ) - echo "$usage"; exit ;; - -- ) # Stop option processing - shift; break ;; - - ) # Use stdin as input. - break ;; - -* ) - echo "$me: invalid option $1$help" - exit 1 ;; - - *local*) - # First pass through any local machine types. - echo $1 - exit ;; - - * ) - break ;; - esac -done - -case $# in - 0) echo "$me: missing argument$help" >&2 - exit 1;; - 1) ;; - *) echo "$me: too many arguments$help" >&2 - exit 1;; -esac - -# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). -# Here we must recognize all the valid KERNEL-OS combinations. -maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` -case $maybe_os in - nto-qnx* | linux-gnu* | linux-dietlibc | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | \ - kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) - os=-$maybe_os - basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` - ;; - *) - basic_machine=`echo $1 | sed 's/-[^-]*$//'` - if [ $basic_machine != $1 ] - then os=`echo $1 | sed 's/.*-/-/'` - else os=; fi - ;; -esac - -### Let's recognize common machines as not being operating systems so -### that things like config.sub decstation-3100 work. We also -### recognize some manufacturers as not being operating systems, so we -### can provide default operating systems below. -case $os in - -sun*os*) - # Prevent following clause from handling this invalid input. - ;; - -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ - -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ - -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ - -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ - -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ - -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ - -apple | -axis | -knuth | -cray) - os= - basic_machine=$1 - ;; - -sim | -cisco | -oki | -wec | -winbond) - os= - basic_machine=$1 - ;; - -scout) - ;; - -wrs) - os=-vxworks - basic_machine=$1 - ;; - -chorusos*) - os=-chorusos - basic_machine=$1 - ;; - -chorusrdb) - os=-chorusrdb - basic_machine=$1 - ;; - -hiux*) - os=-hiuxwe2 - ;; - -sco5) - os=-sco3.2v5 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco4) - os=-sco3.2v4 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2.[4-9]*) - os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco3.2v[4-9]*) - # Don't forget version if it is 3.2v4 or newer. - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -sco*) - os=-sco3.2v2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -udk*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -isc) - os=-isc2.2 - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -clix*) - basic_machine=clipper-intergraph - ;; - -isc*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` - ;; - -lynx*) - os=-lynxos - ;; - -ptx*) - basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` - ;; - -windowsnt*) - os=`echo $os | sed -e 's/windowsnt/winnt/'` - ;; - -psos*) - os=-psos - ;; - -mint | -mint[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; -esac - -# Decode aliases for certain CPU-COMPANY combinations. -case $basic_machine in - # Recognize the basic CPU types without company name. - # Some are omitted here because they have special meanings below. - 1750a | 580 \ - | a29k \ - | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ - | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ - | am33_2.0 \ - | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ - | bfin \ - | c4x | clipper \ - | d10v | d30v | dlx | dsp16xx \ - | fr30 | frv \ - | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ - | i370 | i860 | i960 | ia64 \ - | ip2k | iq2000 \ - | m32r | m32rle | m68000 | m68k | m88k | maxq | mcore \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64el \ - | mips64vr | mips64vrel \ - | mips64orion | mips64orionel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipstx39 | mipstx39el \ - | mn10200 | mn10300 \ - | ms1 \ - | msp430 \ - | ns16k | ns32k \ - | or32 \ - | pdp10 | pdp11 | pj | pjl \ - | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ - | pyramid \ - | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ - | sh64 | sh64le \ - | sparc | sparc64 | sparc64b | sparc86x | sparclet | sparclite \ - | sparcv8 | sparcv9 | sparcv9b \ - | strongarm \ - | tahoe | thumb | tic4x | tic80 | tron \ - | v850 | v850e \ - | we32k \ - | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ - | z8k) - basic_machine=$basic_machine-unknown - ;; - m32c) - basic_machine=$basic_machine-unknown - ;; - m6811 | m68hc11 | m6812 | m68hc12) - # Motorola 68HC11/12. - basic_machine=$basic_machine-unknown - os=-none - ;; - m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) - ;; - - # We use `pc' rather than `unknown' - # because (1) that's what they normally are, and - # (2) the word "unknown" tends to confuse beginning users. - i*86 | x86_64) - basic_machine=$basic_machine-pc - ;; - # Object if more than one company name word. - *-*-*) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; - # Recognize the basic CPU types with company name. - 580-* \ - | a29k-* \ - | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ - | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ - | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ - | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ - | avr-* \ - | bfin-* | bs2000-* \ - | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ - | clipper-* | craynv-* | cydra-* \ - | d10v-* | d30v-* | dlx-* \ - | elxsi-* \ - | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ - | h8300-* | h8500-* \ - | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ - | i*86-* | i860-* | i960-* | ia64-* \ - | ip2k-* | iq2000-* \ - | m32r-* | m32rle-* \ - | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ - | m88110-* | m88k-* | maxq-* | mcore-* \ - | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ - | mips16-* \ - | mips64-* | mips64el-* \ - | mips64vr-* | mips64vrel-* \ - | mips64orion-* | mips64orionel-* \ - | mips64vr4100-* | mips64vr4100el-* \ - | mips64vr4300-* | mips64vr4300el-* \ - | mips64vr5000-* | mips64vr5000el-* \ - | mips64vr5900-* | mips64vr5900el-* \ - | mipsisa32-* | mipsisa32el-* \ - | mipsisa32r2-* | mipsisa32r2el-* \ - | mipsisa64-* | mipsisa64el-* \ - | mipsisa64r2-* | mipsisa64r2el-* \ - | mipsisa64sb1-* | mipsisa64sb1el-* \ - | mipsisa64sr71k-* | mipsisa64sr71kel-* \ - | mipstx39-* | mipstx39el-* \ - | mmix-* \ - | ms1-* \ - | msp430-* \ - | none-* | np1-* | ns16k-* | ns32k-* \ - | orion-* \ - | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ - | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ - | pyramid-* \ - | romp-* | rs6000-* \ - | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | shbe-* \ - | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ - | sparc-* | sparc64-* | sparc64b-* | sparc86x-* | sparclet-* \ - | sparclite-* \ - | sparcv8-* | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ - | tahoe-* | thumb-* \ - | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ - | tron-* \ - | v850-* | v850e-* | vax-* \ - | we32k-* \ - | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ - | xstormy16-* | xtensa-* \ - | ymp-* \ - | z8k-*) - ;; - m32c-*) - ;; - # Recognize the various machine names and aliases which stand - # for a CPU type and a company and sometimes even an OS. - 386bsd) - basic_machine=i386-unknown - os=-bsd - ;; - 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) - basic_machine=m68000-att - ;; - 3b*) - basic_machine=we32k-att - ;; - a29khif) - basic_machine=a29k-amd - os=-udi - ;; - abacus) - basic_machine=abacus-unknown - ;; - adobe68k) - basic_machine=m68010-adobe - os=-scout - ;; - alliant | fx80) - basic_machine=fx80-alliant - ;; - altos | altos3068) - basic_machine=m68k-altos - ;; - am29k) - basic_machine=a29k-none - os=-bsd - ;; - amd64) - basic_machine=x86_64-pc - ;; - amd64-*) - basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - amdahl) - basic_machine=580-amdahl - os=-sysv - ;; - amiga | amiga-*) - basic_machine=m68k-unknown - ;; - amigaos | amigados) - basic_machine=m68k-unknown - os=-amigaos - ;; - amigaunix | amix) - basic_machine=m68k-unknown - os=-sysv4 - ;; - apollo68) - basic_machine=m68k-apollo - os=-sysv - ;; - apollo68bsd) - basic_machine=m68k-apollo - os=-bsd - ;; - aux) - basic_machine=m68k-apple - os=-aux - ;; - balance) - basic_machine=ns32k-sequent - os=-dynix - ;; - c90) - basic_machine=c90-cray - os=-unicos - ;; - convex-c1) - basic_machine=c1-convex - os=-bsd - ;; - convex-c2) - basic_machine=c2-convex - os=-bsd - ;; - convex-c32) - basic_machine=c32-convex - os=-bsd - ;; - convex-c34) - basic_machine=c34-convex - os=-bsd - ;; - convex-c38) - basic_machine=c38-convex - os=-bsd - ;; - cray | j90) - basic_machine=j90-cray - os=-unicos - ;; - craynv) - basic_machine=craynv-cray - os=-unicosmp - ;; - cr16c) - basic_machine=cr16c-unknown - os=-elf - ;; - crds | unos) - basic_machine=m68k-crds - ;; - crisv32 | crisv32-* | etraxfs*) - basic_machine=crisv32-axis - ;; - cris | cris-* | etrax*) - basic_machine=cris-axis - ;; - crx) - basic_machine=crx-unknown - os=-elf - ;; - da30 | da30-*) - basic_machine=m68k-da30 - ;; - decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) - basic_machine=mips-dec - ;; - decsystem10* | dec10*) - basic_machine=pdp10-dec - os=-tops10 - ;; - decsystem20* | dec20*) - basic_machine=pdp10-dec - os=-tops20 - ;; - delta | 3300 | motorola-3300 | motorola-delta \ - | 3300-motorola | delta-motorola) - basic_machine=m68k-motorola - ;; - delta88) - basic_machine=m88k-motorola - os=-sysv3 - ;; - djgpp) - basic_machine=i586-pc - os=-msdosdjgpp - ;; - dpx20 | dpx20-*) - basic_machine=rs6000-bull - os=-bosx - ;; - dpx2* | dpx2*-bull) - basic_machine=m68k-bull - os=-sysv3 - ;; - ebmon29k) - basic_machine=a29k-amd - os=-ebmon - ;; - elxsi) - basic_machine=elxsi-elxsi - os=-bsd - ;; - encore | umax | mmax) - basic_machine=ns32k-encore - ;; - es1800 | OSE68k | ose68k | ose | OSE) - basic_machine=m68k-ericsson - os=-ose - ;; - fx2800) - basic_machine=i860-alliant - ;; - genix) - basic_machine=ns32k-ns - ;; - gmicro) - basic_machine=tron-gmicro - os=-sysv - ;; - go32) - basic_machine=i386-pc - os=-go32 - ;; - h3050r* | hiux*) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - h8300hms) - basic_machine=h8300-hitachi - os=-hms - ;; - h8300xray) - basic_machine=h8300-hitachi - os=-xray - ;; - h8500hms) - basic_machine=h8500-hitachi - os=-hms - ;; - harris) - basic_machine=m88k-harris - os=-sysv3 - ;; - hp300-*) - basic_machine=m68k-hp - ;; - hp300bsd) - basic_machine=m68k-hp - os=-bsd - ;; - hp300hpux) - basic_machine=m68k-hp - os=-hpux - ;; - hp3k9[0-9][0-9] | hp9[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k2[0-9][0-9] | hp9k31[0-9]) - basic_machine=m68000-hp - ;; - hp9k3[2-9][0-9]) - basic_machine=m68k-hp - ;; - hp9k6[0-9][0-9] | hp6[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hp9k7[0-79][0-9] | hp7[0-79][0-9]) - basic_machine=hppa1.1-hp - ;; - hp9k78[0-9] | hp78[0-9]) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) - # FIXME: really hppa2.0-hp - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][13679] | hp8[0-9][13679]) - basic_machine=hppa1.1-hp - ;; - hp9k8[0-9][0-9] | hp8[0-9][0-9]) - basic_machine=hppa1.0-hp - ;; - hppa-next) - os=-nextstep3 - ;; - hppaosf) - basic_machine=hppa1.1-hp - os=-osf - ;; - hppro) - basic_machine=hppa1.1-hp - os=-proelf - ;; - i370-ibm* | ibm*) - basic_machine=i370-ibm - ;; -# I'm not sure what "Sysv32" means. Should this be sysv3.2? - i*86v32) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv32 - ;; - i*86v4*) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv4 - ;; - i*86v) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-sysv - ;; - i*86sol2) - basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` - os=-solaris2 - ;; - i386mach) - basic_machine=i386-mach - os=-mach - ;; - i386-vsta | vsta) - basic_machine=i386-unknown - os=-vsta - ;; - iris | iris4d) - basic_machine=mips-sgi - case $os in - -irix*) - ;; - *) - os=-irix4 - ;; - esac - ;; - isi68 | isi) - basic_machine=m68k-isi - os=-sysv - ;; - m88k-omron*) - basic_machine=m88k-omron - ;; - magnum | m3230) - basic_machine=mips-mips - os=-sysv - ;; - merlin) - basic_machine=ns32k-utek - os=-sysv - ;; - mingw32) - basic_machine=i386-pc - os=-mingw32 - ;; - miniframe) - basic_machine=m68000-convergent - ;; - *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) - basic_machine=m68k-atari - os=-mint - ;; - mips3*-*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` - ;; - mips3*) - basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown - ;; - monitor) - basic_machine=m68k-rom68k - os=-coff - ;; - morphos) - basic_machine=powerpc-unknown - os=-morphos - ;; - msdos) - basic_machine=i386-pc - os=-msdos - ;; - mvs) - basic_machine=i370-ibm - os=-mvs - ;; - ncr3000) - basic_machine=i486-ncr - os=-sysv4 - ;; - netbsd386) - basic_machine=i386-unknown - os=-netbsd - ;; - netwinder) - basic_machine=armv4l-rebel - os=-linux - ;; - news | news700 | news800 | news900) - basic_machine=m68k-sony - os=-newsos - ;; - news1000) - basic_machine=m68030-sony - os=-newsos - ;; - news-3600 | risc-news) - basic_machine=mips-sony - os=-newsos - ;; - necv70) - basic_machine=v70-nec - os=-sysv - ;; - next | m*-next ) - basic_machine=m68k-next - case $os in - -nextstep* ) - ;; - -ns2*) - os=-nextstep2 - ;; - *) - os=-nextstep3 - ;; - esac - ;; - nh3000) - basic_machine=m68k-harris - os=-cxux - ;; - nh[45]000) - basic_machine=m88k-harris - os=-cxux - ;; - nindy960) - basic_machine=i960-intel - os=-nindy - ;; - mon960) - basic_machine=i960-intel - os=-mon960 - ;; - nonstopux) - basic_machine=mips-compaq - os=-nonstopux - ;; - np1) - basic_machine=np1-gould - ;; - nsr-tandem) - basic_machine=nsr-tandem - ;; - op50n-* | op60c-*) - basic_machine=hppa1.1-oki - os=-proelf - ;; - openrisc | openrisc-*) - basic_machine=or32-unknown - ;; - os400) - basic_machine=powerpc-ibm - os=-os400 - ;; - OSE68000 | ose68000) - basic_machine=m68000-ericsson - os=-ose - ;; - os68k) - basic_machine=m68k-none - os=-os68k - ;; - pa-hitachi) - basic_machine=hppa1.1-hitachi - os=-hiuxwe2 - ;; - paragon) - basic_machine=i860-intel - os=-osf - ;; - pbd) - basic_machine=sparc-tti - ;; - pbb) - basic_machine=m68k-tti - ;; - pc532 | pc532-*) - basic_machine=ns32k-pc532 - ;; - pentium | p5 | k5 | k6 | nexgen | viac3) - basic_machine=i586-pc - ;; - pentiumpro | p6 | 6x86 | athlon | athlon_*) - basic_machine=i686-pc - ;; - pentiumii | pentium2 | pentiumiii | pentium3) - basic_machine=i686-pc - ;; - pentium4) - basic_machine=i786-pc - ;; - pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) - basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumpro-* | p6-* | 6x86-* | athlon-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) - basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pentium4-*) - basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - pn) - basic_machine=pn-gould - ;; - power) basic_machine=power-ibm - ;; - ppc) basic_machine=powerpc-unknown - ;; - ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppcle | powerpclittle | ppc-le | powerpc-little) - basic_machine=powerpcle-unknown - ;; - ppcle-* | powerpclittle-*) - basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64) basic_machine=powerpc64-unknown - ;; - ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ppc64le | powerpc64little | ppc64-le | powerpc64-little) - basic_machine=powerpc64le-unknown - ;; - ppc64le-* | powerpc64little-*) - basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` - ;; - ps2) - basic_machine=i386-ibm - ;; - pw32) - basic_machine=i586-unknown - os=-pw32 - ;; - rom68k) - basic_machine=m68k-rom68k - os=-coff - ;; - rm[46]00) - basic_machine=mips-siemens - ;; - rtpc | rtpc-*) - basic_machine=romp-ibm - ;; - s390 | s390-*) - basic_machine=s390-ibm - ;; - s390x | s390x-*) - basic_machine=s390x-ibm - ;; - sa29200) - basic_machine=a29k-amd - os=-udi - ;; - sb1) - basic_machine=mipsisa64sb1-unknown - ;; - sb1el) - basic_machine=mipsisa64sb1el-unknown - ;; - sei) - basic_machine=mips-sei - os=-seiux - ;; - sequent) - basic_machine=i386-sequent - ;; - sh) - basic_machine=sh-hitachi - os=-hms - ;; - sh64) - basic_machine=sh64-unknown - ;; - sparclite-wrs | simso-wrs) - basic_machine=sparclite-wrs - os=-vxworks - ;; - sps7) - basic_machine=m68k-bull - os=-sysv2 - ;; - spur) - basic_machine=spur-unknown - ;; - st2000) - basic_machine=m68k-tandem - ;; - stratus) - basic_machine=i860-stratus - os=-sysv4 - ;; - sun2) - basic_machine=m68000-sun - ;; - sun2os3) - basic_machine=m68000-sun - os=-sunos3 - ;; - sun2os4) - basic_machine=m68000-sun - os=-sunos4 - ;; - sun3os3) - basic_machine=m68k-sun - os=-sunos3 - ;; - sun3os4) - basic_machine=m68k-sun - os=-sunos4 - ;; - sun4os3) - basic_machine=sparc-sun - os=-sunos3 - ;; - sun4os4) - basic_machine=sparc-sun - os=-sunos4 - ;; - sun4sol2) - basic_machine=sparc-sun - os=-solaris2 - ;; - sun3 | sun3-*) - basic_machine=m68k-sun - ;; - sun4) - basic_machine=sparc-sun - ;; - sun386 | sun386i | roadrunner) - basic_machine=i386-sun - ;; - sv1) - basic_machine=sv1-cray - os=-unicos - ;; - symmetry) - basic_machine=i386-sequent - os=-dynix - ;; - t3e) - basic_machine=alphaev5-cray - os=-unicos - ;; - t90) - basic_machine=t90-cray - os=-unicos - ;; - tic54x | c54x*) - basic_machine=tic54x-unknown - os=-coff - ;; - tic55x | c55x*) - basic_machine=tic55x-unknown - os=-coff - ;; - tic6x | c6x*) - basic_machine=tic6x-unknown - os=-coff - ;; - tx39) - basic_machine=mipstx39-unknown - ;; - tx39el) - basic_machine=mipstx39el-unknown - ;; - toad1) - basic_machine=pdp10-xkl - os=-tops20 - ;; - tower | tower-32) - basic_machine=m68k-ncr - ;; - tpf) - basic_machine=s390x-ibm - os=-tpf - ;; - udi29k) - basic_machine=a29k-amd - os=-udi - ;; - ultra3) - basic_machine=a29k-nyu - os=-sym1 - ;; - v810 | necv810) - basic_machine=v810-nec - os=-none - ;; - vaxv) - basic_machine=vax-dec - os=-sysv - ;; - vms) - basic_machine=vax-dec - os=-vms - ;; - vpp*|vx|vx-*) - basic_machine=f301-fujitsu - ;; - vxworks960) - basic_machine=i960-wrs - os=-vxworks - ;; - vxworks68) - basic_machine=m68k-wrs - os=-vxworks - ;; - vxworks29k) - basic_machine=a29k-wrs - os=-vxworks - ;; - w65*) - basic_machine=w65-wdc - os=-none - ;; - w89k-*) - basic_machine=hppa1.1-winbond - os=-proelf - ;; - xbox) - basic_machine=i686-pc - os=-mingw32 - ;; - xps | xps100) - basic_machine=xps100-honeywell - ;; - ymp) - basic_machine=ymp-cray - os=-unicos - ;; - z8k-*-coff) - basic_machine=z8k-unknown - os=-sim - ;; - none) - basic_machine=none-none - os=-none - ;; - -# Here we handle the default manufacturer of certain CPU types. It is in -# some cases the only manufacturer, in others, it is the most popular. - w89k) - basic_machine=hppa1.1-winbond - ;; - op50n) - basic_machine=hppa1.1-oki - ;; - op60c) - basic_machine=hppa1.1-oki - ;; - romp) - basic_machine=romp-ibm - ;; - mmix) - basic_machine=mmix-knuth - ;; - rs6000) - basic_machine=rs6000-ibm - ;; - vax) - basic_machine=vax-dec - ;; - pdp10) - # there are many clones, so DEC is not a safe bet - basic_machine=pdp10-unknown - ;; - pdp11) - basic_machine=pdp11-dec - ;; - we32k) - basic_machine=we32k-att - ;; - sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) - basic_machine=sh-unknown - ;; - sparc | sparcv8 | sparcv9 | sparcv9b) - basic_machine=sparc-sun - ;; - cydra) - basic_machine=cydra-cydrome - ;; - orion) - basic_machine=orion-highlevel - ;; - orion105) - basic_machine=clipper-highlevel - ;; - mac | mpw | mac-mpw) - basic_machine=m68k-apple - ;; - pmac | pmac-mpw) - basic_machine=powerpc-apple - ;; - *-unknown) - # Make sure to match an already-canonicalized machine name. - ;; - *) - echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 - exit 1 - ;; -esac - -# Here we canonicalize certain aliases for manufacturers. -case $basic_machine in - *-digital*) - basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` - ;; - *-commodore*) - basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` - ;; - *) - ;; -esac - -# Decode manufacturer-specific aliases for certain operating systems. - -if [ x"$os" != x"" ] -then -case $os in - # First match some system type aliases - # that might get confused with valid system types. - # -solaris* is a basic system type, with this one exception. - -solaris1 | -solaris1.*) - os=`echo $os | sed -e 's|solaris1|sunos4|'` - ;; - -solaris) - os=-solaris2 - ;; - -svr4*) - os=-sysv4 - ;; - -unixware*) - os=-sysv4.2uw - ;; - -gnu/linux*) - os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` - ;; - # First accept the basic system types. - # The portable systems comes first. - # Each alternative MUST END IN A *, to match a version number. - # -sysv* is not here because it comes later, after sysvr4. - -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ - | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ - | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ - | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ - | -aos* \ - | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ - | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ - | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* | -openbsd* \ - | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ - | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ - | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ - | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ - | -chorusos* | -chorusrdb* \ - | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ - | -mingw32* | -linux-gnu* | -linux-uclibc* | -uxpv* | -beos* | -mpeix* | -udk* \ - | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ - | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ - | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ - | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ - | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ - | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ - | -skyos* | -haiku*) - # Remember, each alternative MUST END IN *, to match a version number. - ;; - -qnx*) - case $basic_machine in - x86-* | i*86-*) - ;; - *) - os=-nto$os - ;; - esac - ;; - -nto-qnx*) - ;; - -nto*) - os=`echo $os | sed -e 's|nto|nto-qnx|'` - ;; - -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ - | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ - | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) - ;; - -mac*) - os=`echo $os | sed -e 's|mac|macos|'` - ;; - -linux-dietlibc) - os=-linux-dietlibc - ;; - -linux*) - os=`echo $os | sed -e 's|linux|linux-gnu|'` - ;; - -sunos5*) - os=`echo $os | sed -e 's|sunos5|solaris2|'` - ;; - -sunos6*) - os=`echo $os | sed -e 's|sunos6|solaris3|'` - ;; - -opened*) - os=-openedition - ;; - -os400*) - os=-os400 - ;; - -wince*) - os=-wince - ;; - -osfrose*) - os=-osfrose - ;; - -osf*) - os=-osf - ;; - -utek*) - os=-bsd - ;; - -dynix*) - os=-bsd - ;; - -acis*) - os=-aos - ;; - -atheos*) - os=-atheos - ;; - -syllable*) - os=-syllable - ;; - -386bsd) - os=-bsd - ;; - -ctix* | -uts*) - os=-sysv - ;; - -nova*) - os=-rtmk-nova - ;; - -ns2 ) - os=-nextstep2 - ;; - -nsk*) - os=-nsk - ;; - # Preserve the version number of sinix5. - -sinix5.*) - os=`echo $os | sed -e 's|sinix|sysv|'` - ;; - -sinix*) - os=-sysv4 - ;; - -tpf*) - os=-tpf - ;; - -triton*) - os=-sysv3 - ;; - -oss*) - os=-sysv3 - ;; - -svr4) - os=-sysv4 - ;; - -svr3) - os=-sysv3 - ;; - -sysvr4) - os=-sysv4 - ;; - # This must come after -sysvr4. - -sysv*) - ;; - -ose*) - os=-ose - ;; - -es1800*) - os=-ose - ;; - -xenix) - os=-xenix - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - os=-mint - ;; - -aros*) - os=-aros - ;; - -kaos*) - os=-kaos - ;; - -zvmoe) - os=-zvmoe - ;; - -none) - ;; - *) - # Get rid of the `-' at the beginning of $os. - os=`echo $os | sed 's/[^-]*-//'` - echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 - exit 1 - ;; -esac -else - -# Here we handle the default operating systems that come with various machines. -# The value should be what the vendor currently ships out the door with their -# machine or put another way, the most popular os provided with the machine. - -# Note that if you're going to try to match "-MANUFACTURER" here (say, -# "-sun"), then you have to tell the case statement up towards the top -# that MANUFACTURER isn't an operating system. Otherwise, code above -# will signal an error saying that MANUFACTURER isn't an operating -# system, and we'll never get to this point. - -case $basic_machine in - *-acorn) - os=-riscix1.2 - ;; - arm*-rebel) - os=-linux - ;; - arm*-semi) - os=-aout - ;; - c4x-* | tic4x-*) - os=-coff - ;; - # This must come before the *-dec entry. - pdp10-*) - os=-tops20 - ;; - pdp11-*) - os=-none - ;; - *-dec | vax-*) - os=-ultrix4.2 - ;; - m68*-apollo) - os=-domain - ;; - i386-sun) - os=-sunos4.0.2 - ;; - m68000-sun) - os=-sunos3 - # This also exists in the configure program, but was not the - # default. - # os=-sunos4 - ;; - m68*-cisco) - os=-aout - ;; - mips*-cisco) - os=-elf - ;; - mips*-*) - os=-elf - ;; - or32-*) - os=-coff - ;; - *-tti) # must be before sparc entry or we get the wrong os. - os=-sysv3 - ;; - sparc-* | *-sun) - os=-sunos4.1.1 - ;; - *-be) - os=-beos - ;; - *-haiku) - os=-haiku - ;; - *-ibm) - os=-aix - ;; - *-knuth) - os=-mmixware - ;; - *-wec) - os=-proelf - ;; - *-winbond) - os=-proelf - ;; - *-oki) - os=-proelf - ;; - *-hp) - os=-hpux - ;; - *-hitachi) - os=-hiux - ;; - i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) - os=-sysv - ;; - *-cbm) - os=-amigaos - ;; - *-dg) - os=-dgux - ;; - *-dolphin) - os=-sysv3 - ;; - m68k-ccur) - os=-rtu - ;; - m88k-omron*) - os=-luna - ;; - *-next ) - os=-nextstep - ;; - *-sequent) - os=-ptx - ;; - *-crds) - os=-unos - ;; - *-ns) - os=-genix - ;; - i370-*) - os=-mvs - ;; - *-next) - os=-nextstep3 - ;; - *-gould) - os=-sysv - ;; - *-highlevel) - os=-bsd - ;; - *-encore) - os=-bsd - ;; - *-sgi) - os=-irix - ;; - *-siemens) - os=-sysv4 - ;; - *-masscomp) - os=-rtu - ;; - f30[01]-fujitsu | f700-fujitsu) - os=-uxpv - ;; - *-rom68k) - os=-coff - ;; - *-*bug) - os=-coff - ;; - *-apple) - os=-macos - ;; - *-atari*) - os=-mint - ;; - *) - os=-none - ;; -esac -fi - -# Here we handle the case where we know the os, and the CPU type, but not the -# manufacturer. We pick the logical manufacturer. -vendor=unknown -case $basic_machine in - *-unknown) - case $os in - -riscix*) - vendor=acorn - ;; - -sunos*) - vendor=sun - ;; - -aix*) - vendor=ibm - ;; - -beos*) - vendor=be - ;; - -hpux*) - vendor=hp - ;; - -mpeix*) - vendor=hp - ;; - -hiux*) - vendor=hitachi - ;; - -unos*) - vendor=crds - ;; - -dgux*) - vendor=dg - ;; - -luna*) - vendor=omron - ;; - -genix*) - vendor=ns - ;; - -mvs* | -opened*) - vendor=ibm - ;; - -os400*) - vendor=ibm - ;; - -ptx*) - vendor=sequent - ;; - -tpf*) - vendor=ibm - ;; - -vxsim* | -vxworks* | -windiss*) - vendor=wrs - ;; - -aux*) - vendor=apple - ;; - -hms*) - vendor=hitachi - ;; - -mpw* | -macos*) - vendor=apple - ;; - -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) - vendor=atari - ;; - -vos*) - vendor=stratus - ;; - esac - basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` - ;; -esac - -echo $basic_machine$os -exit - -# Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) -# time-stamp-start: "timestamp='" -# time-stamp-format: "%:y-%02m-%02d" -# time-stamp-end: "'" -# End: diff --git a/ciderpress/nufxlib/configure b/ciderpress/nufxlib/configure deleted file mode 100755 index f459119..0000000 --- a/ciderpress/nufxlib/configure +++ /dev/null @@ -1,5503 +0,0 @@ -#! /bin/sh -# Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69. -# -# -# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. -# -# -# This configure script is free software; the Free Software Foundation -# gives unlimited permission to copy, distribute and modify it. -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - -# Use a proper internal environment variable to ensure we don't fall - # into an infinite loop, continuously re-executing ourselves. - if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then - _as_can_reexec=no; export _as_can_reexec; - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -as_fn_exit 255 - fi - # We don't want this to propagate to other subprocesses. - { _as_can_reexec=; unset _as_can_reexec;} -if test "x$CONFIG_SHELL" = x; then - as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which - # is contrary to our usage. Disable this feature. - alias -g '\${1+\"\$@\"}'='\"\$@\"' - setopt NO_GLOB_SUBST -else - case \`(set -o) 2>/dev/null\` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi -" - as_required="as_fn_return () { (exit \$1); } -as_fn_success () { as_fn_return 0; } -as_fn_failure () { as_fn_return 1; } -as_fn_ret_success () { return 0; } -as_fn_ret_failure () { return 1; } - -exitcode=0 -as_fn_success || { exitcode=1; echo as_fn_success failed.; } -as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } -as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } -as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } -if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : - -else - exitcode=1; echo positional parameters were not saved. -fi -test x\$exitcode = x0 || exit 1 -test -x / || exit 1" - as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO - as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO - eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 -test \$(( 1 + 1 )) = 2 || exit 1" - if (eval "$as_required") 2>/dev/null; then : - as_have_required=yes -else - as_have_required=no -fi - if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : - -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -as_found=false -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - as_found=: - case $as_dir in #( - /*) - for as_base in sh bash ksh sh5; do - # Try only shells that exist, to save several forks. - as_shell=$as_dir/$as_base - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : - CONFIG_SHELL=$as_shell as_have_required=yes - if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : - break 2 -fi -fi - done;; - esac - as_found=false -done -$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && - { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : - CONFIG_SHELL=$SHELL as_have_required=yes -fi; } -IFS=$as_save_IFS - - - if test "x$CONFIG_SHELL" != x; then : - export CONFIG_SHELL - # We cannot yet assume a decent shell, so we have to provide a -# neutralization value for shells without unset; and this also -# works around shells that cannot unset nonexistent variables. -# Preserve -v and -x to the replacement shell. -BASH_ENV=/dev/null -ENV=/dev/null -(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV -case $- in # (((( - *v*x* | *x*v* ) as_opts=-vx ;; - *v* ) as_opts=-v ;; - *x* ) as_opts=-x ;; - * ) as_opts= ;; -esac -exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} -# Admittedly, this is quite paranoid, since all the known shells bail -# out after a failed `exec'. -$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 -exit 255 -fi - - if test x$as_have_required = xno; then : - $as_echo "$0: This script requires a shell more modern than all" - $as_echo "$0: the shells that I found on your system." - if test x${ZSH_VERSION+set} = xset ; then - $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" - $as_echo "$0: be upgraded to zsh 4.3.4 or later." - else - $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, -$0: including any error possibly output before this -$0: message. Then install a modern shell, or manually run -$0: the script under such a shell if you do have one." - fi - exit 1 -fi -fi -fi -SHELL=${CONFIG_SHELL-/bin/sh} -export SHELL -# Unset more variables known to interfere with behavior of common tools. -CLICOLOR_FORCE= GREP_OPTIONS= -unset CLICOLOR_FORCE GREP_OPTIONS - -## --------------------- ## -## M4sh Shell Functions. ## -## --------------------- ## -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - - - as_lineno_1=$LINENO as_lineno_1a=$LINENO - as_lineno_2=$LINENO as_lineno_2a=$LINENO - eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && - test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { - # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - - # If we had to re-execute with $CONFIG_SHELL, we're ensured to have - # already done that, so ensure we don't try to do so again and fall - # in an infinite loop. This has already happened in practice. - _as_can_reexec=no; export _as_can_reexec - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -test -n "$DJDIR" || exec 7<&0 &1 - -# Name of the host. -# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, -# so uname gets run too. -ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` - -# -# Initializations. -# -ac_default_prefix=/usr/local -ac_clean_files= -ac_config_libobj_dir=. -LIBOBJS= -cross_compiling=no -subdirs= -MFLAGS= -MAKEFLAGS= - -# Identity of this package. -PACKAGE_NAME= -PACKAGE_TARNAME= -PACKAGE_VERSION= -PACKAGE_STRING= -PACKAGE_BUGREPORT= -PACKAGE_URL= - -ac_unique_file="NufxLibPriv.h" -# Factoring default headers for most tests. -ac_includes_default="\ -#include -#ifdef HAVE_SYS_TYPES_H -# include -#endif -#ifdef HAVE_SYS_STAT_H -# include -#endif -#ifdef STDC_HEADERS -# include -# include -#else -# ifdef HAVE_STDLIB_H -# include -# endif -#endif -#ifdef HAVE_STRING_H -# if !defined STDC_HEADERS && defined HAVE_MEMORY_H -# include -# endif -# include -#endif -#ifdef HAVE_STRINGS_H -# include -#endif -#ifdef HAVE_INTTYPES_H -# include -#endif -#ifdef HAVE_STDINT_H -# include -#endif -#ifdef HAVE_UNISTD_H -# include -#endif" - -ac_subst_vars='LTLIBOBJS -LIBOBJS -SHARE_FLAGS -BUILD_FLAGS -EGREP -GREP -CPP -RANLIB -SET_MAKE -INSTALL_DATA -INSTALL_SCRIPT -INSTALL_PROGRAM -OBJEXT -EXEEXT -ac_ct_CC -CPPFLAGS -LDFLAGS -CFLAGS -CC -host_os -host_vendor -host_cpu -host -build_os -build_vendor -build_cpu -build -target_alias -host_alias -build_alias -LIBS -ECHO_T -ECHO_N -ECHO_C -DEFS -mandir -localedir -libdir -psdir -pdfdir -dvidir -htmldir -infodir -docdir -oldincludedir -includedir -localstatedir -sharedstatedir -sysconfdir -datadir -datarootdir -libexecdir -sbindir -bindir -program_transform_name -prefix -exec_prefix -PACKAGE_URL -PACKAGE_BUGREPORT -PACKAGE_STRING -PACKAGE_VERSION -PACKAGE_TARNAME -PACKAGE_NAME -PATH_SEPARATOR -SHELL' -ac_subst_files='' -ac_user_opts=' -enable_option_checking -enable_sq -enable_lzw -enable_lzc -enable_deflate -enable_bzip2 -enable_dmalloc -' - ac_precious_vars='build_alias -host_alias -target_alias -CC -CFLAGS -LDFLAGS -LIBS -CPPFLAGS -CPP' - - -# Initialize some variables set by options. -ac_init_help= -ac_init_version=false -ac_unrecognized_opts= -ac_unrecognized_sep= -# The variables have the same names as the options, with -# dashes changed to underlines. -cache_file=/dev/null -exec_prefix=NONE -no_create= -no_recursion= -prefix=NONE -program_prefix=NONE -program_suffix=NONE -program_transform_name=s,x,x, -silent= -site= -srcdir= -verbose= -x_includes=NONE -x_libraries=NONE - -# Installation directory options. -# These are left unexpanded so users can "make install exec_prefix=/foo" -# and all the variables that are supposed to be based on exec_prefix -# by default will actually change. -# Use braces instead of parens because sh, perl, etc. also accept them. -# (The list follows the same order as the GNU Coding Standards.) -bindir='${exec_prefix}/bin' -sbindir='${exec_prefix}/sbin' -libexecdir='${exec_prefix}/libexec' -datarootdir='${prefix}/share' -datadir='${datarootdir}' -sysconfdir='${prefix}/etc' -sharedstatedir='${prefix}/com' -localstatedir='${prefix}/var' -includedir='${prefix}/include' -oldincludedir='/usr/include' -docdir='${datarootdir}/doc/${PACKAGE}' -infodir='${datarootdir}/info' -htmldir='${docdir}' -dvidir='${docdir}' -pdfdir='${docdir}' -psdir='${docdir}' -libdir='${exec_prefix}/lib' -localedir='${datarootdir}/locale' -mandir='${datarootdir}/man' - -ac_prev= -ac_dashdash= -for ac_option -do - # If the previous option needs an argument, assign it. - if test -n "$ac_prev"; then - eval $ac_prev=\$ac_option - ac_prev= - continue - fi - - case $ac_option in - *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; - *=) ac_optarg= ;; - *) ac_optarg=yes ;; - esac - - # Accept the important Cygnus configure options, so we can diagnose typos. - - case $ac_dashdash$ac_option in - --) - ac_dashdash=yes ;; - - -bindir | --bindir | --bindi | --bind | --bin | --bi) - ac_prev=bindir ;; - -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) - bindir=$ac_optarg ;; - - -build | --build | --buil | --bui | --bu) - ac_prev=build_alias ;; - -build=* | --build=* | --buil=* | --bui=* | --bu=*) - build_alias=$ac_optarg ;; - - -cache-file | --cache-file | --cache-fil | --cache-fi \ - | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) - ac_prev=cache_file ;; - -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ - | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) - cache_file=$ac_optarg ;; - - --config-cache | -C) - cache_file=config.cache ;; - - -datadir | --datadir | --datadi | --datad) - ac_prev=datadir ;; - -datadir=* | --datadir=* | --datadi=* | --datad=*) - datadir=$ac_optarg ;; - - -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ - | --dataroo | --dataro | --datar) - ac_prev=datarootdir ;; - -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ - | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) - datarootdir=$ac_optarg ;; - - -disable-* | --disable-*) - ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=no ;; - - -docdir | --docdir | --docdi | --doc | --do) - ac_prev=docdir ;; - -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) - docdir=$ac_optarg ;; - - -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) - ac_prev=dvidir ;; - -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) - dvidir=$ac_optarg ;; - - -enable-* | --enable-*) - ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid feature name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"enable_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval enable_$ac_useropt=\$ac_optarg ;; - - -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ - | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ - | --exec | --exe | --ex) - ac_prev=exec_prefix ;; - -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ - | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ - | --exec=* | --exe=* | --ex=*) - exec_prefix=$ac_optarg ;; - - -gas | --gas | --ga | --g) - # Obsolete; use --with-gas. - with_gas=yes ;; - - -help | --help | --hel | --he | -h) - ac_init_help=long ;; - -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) - ac_init_help=recursive ;; - -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) - ac_init_help=short ;; - - -host | --host | --hos | --ho) - ac_prev=host_alias ;; - -host=* | --host=* | --hos=* | --ho=*) - host_alias=$ac_optarg ;; - - -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) - ac_prev=htmldir ;; - -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ - | --ht=*) - htmldir=$ac_optarg ;; - - -includedir | --includedir | --includedi | --included | --include \ - | --includ | --inclu | --incl | --inc) - ac_prev=includedir ;; - -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ - | --includ=* | --inclu=* | --incl=* | --inc=*) - includedir=$ac_optarg ;; - - -infodir | --infodir | --infodi | --infod | --info | --inf) - ac_prev=infodir ;; - -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) - infodir=$ac_optarg ;; - - -libdir | --libdir | --libdi | --libd) - ac_prev=libdir ;; - -libdir=* | --libdir=* | --libdi=* | --libd=*) - libdir=$ac_optarg ;; - - -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ - | --libexe | --libex | --libe) - ac_prev=libexecdir ;; - -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ - | --libexe=* | --libex=* | --libe=*) - libexecdir=$ac_optarg ;; - - -localedir | --localedir | --localedi | --localed | --locale) - ac_prev=localedir ;; - -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) - localedir=$ac_optarg ;; - - -localstatedir | --localstatedir | --localstatedi | --localstated \ - | --localstate | --localstat | --localsta | --localst | --locals) - ac_prev=localstatedir ;; - -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ - | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) - localstatedir=$ac_optarg ;; - - -mandir | --mandir | --mandi | --mand | --man | --ma | --m) - ac_prev=mandir ;; - -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) - mandir=$ac_optarg ;; - - -nfp | --nfp | --nf) - # Obsolete; use --without-fp. - with_fp=no ;; - - -no-create | --no-create | --no-creat | --no-crea | --no-cre \ - | --no-cr | --no-c | -n) - no_create=yes ;; - - -no-recursion | --no-recursion | --no-recursio | --no-recursi \ - | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) - no_recursion=yes ;; - - -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ - | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ - | --oldin | --oldi | --old | --ol | --o) - ac_prev=oldincludedir ;; - -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ - | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ - | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) - oldincludedir=$ac_optarg ;; - - -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) - ac_prev=prefix ;; - -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) - prefix=$ac_optarg ;; - - -program-prefix | --program-prefix | --program-prefi | --program-pref \ - | --program-pre | --program-pr | --program-p) - ac_prev=program_prefix ;; - -program-prefix=* | --program-prefix=* | --program-prefi=* \ - | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) - program_prefix=$ac_optarg ;; - - -program-suffix | --program-suffix | --program-suffi | --program-suff \ - | --program-suf | --program-su | --program-s) - ac_prev=program_suffix ;; - -program-suffix=* | --program-suffix=* | --program-suffi=* \ - | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) - program_suffix=$ac_optarg ;; - - -program-transform-name | --program-transform-name \ - | --program-transform-nam | --program-transform-na \ - | --program-transform-n | --program-transform- \ - | --program-transform | --program-transfor \ - | --program-transfo | --program-transf \ - | --program-trans | --program-tran \ - | --progr-tra | --program-tr | --program-t) - ac_prev=program_transform_name ;; - -program-transform-name=* | --program-transform-name=* \ - | --program-transform-nam=* | --program-transform-na=* \ - | --program-transform-n=* | --program-transform-=* \ - | --program-transform=* | --program-transfor=* \ - | --program-transfo=* | --program-transf=* \ - | --program-trans=* | --program-tran=* \ - | --progr-tra=* | --program-tr=* | --program-t=*) - program_transform_name=$ac_optarg ;; - - -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) - ac_prev=pdfdir ;; - -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) - pdfdir=$ac_optarg ;; - - -psdir | --psdir | --psdi | --psd | --ps) - ac_prev=psdir ;; - -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) - psdir=$ac_optarg ;; - - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - silent=yes ;; - - -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) - ac_prev=sbindir ;; - -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ - | --sbi=* | --sb=*) - sbindir=$ac_optarg ;; - - -sharedstatedir | --sharedstatedir | --sharedstatedi \ - | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ - | --sharedst | --shareds | --shared | --share | --shar \ - | --sha | --sh) - ac_prev=sharedstatedir ;; - -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ - | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ - | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ - | --sha=* | --sh=*) - sharedstatedir=$ac_optarg ;; - - -site | --site | --sit) - ac_prev=site ;; - -site=* | --site=* | --sit=*) - site=$ac_optarg ;; - - -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) - ac_prev=srcdir ;; - -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) - srcdir=$ac_optarg ;; - - -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ - | --syscon | --sysco | --sysc | --sys | --sy) - ac_prev=sysconfdir ;; - -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ - | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) - sysconfdir=$ac_optarg ;; - - -target | --target | --targe | --targ | --tar | --ta | --t) - ac_prev=target_alias ;; - -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) - target_alias=$ac_optarg ;; - - -v | -verbose | --verbose | --verbos | --verbo | --verb) - verbose=yes ;; - - -version | --version | --versio | --versi | --vers | -V) - ac_init_version=: ;; - - -with-* | --with-*) - ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=\$ac_optarg ;; - - -without-* | --without-*) - ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` - # Reject names that are not valid shell variable names. - expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && - as_fn_error $? "invalid package name: $ac_useropt" - ac_useropt_orig=$ac_useropt - ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` - case $ac_user_opts in - *" -"with_$ac_useropt" -"*) ;; - *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" - ac_unrecognized_sep=', ';; - esac - eval with_$ac_useropt=no ;; - - --x) - # Obsolete; use --with-x. - with_x=yes ;; - - -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ - | --x-incl | --x-inc | --x-in | --x-i) - ac_prev=x_includes ;; - -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ - | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) - x_includes=$ac_optarg ;; - - -x-libraries | --x-libraries | --x-librarie | --x-librari \ - | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) - ac_prev=x_libraries ;; - -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ - | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) - x_libraries=$ac_optarg ;; - - -*) as_fn_error $? "unrecognized option: \`$ac_option' -Try \`$0 --help' for more information" - ;; - - *=*) - ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` - # Reject names that are not valid shell variable names. - case $ac_envvar in #( - '' | [0-9]* | *[!_$as_cr_alnum]* ) - as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; - esac - eval $ac_envvar=\$ac_optarg - export $ac_envvar ;; - - *) - # FIXME: should be removed in autoconf 3.0. - $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 - expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 - : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" - ;; - - esac -done - -if test -n "$ac_prev"; then - ac_option=--`echo $ac_prev | sed 's/_/-/g'` - as_fn_error $? "missing argument to $ac_option" -fi - -if test -n "$ac_unrecognized_opts"; then - case $enable_option_checking in - no) ;; - fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; - *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; - esac -fi - -# Check all directory arguments for consistency. -for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ - datadir sysconfdir sharedstatedir localstatedir includedir \ - oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir -do - eval ac_val=\$$ac_var - # Remove trailing slashes. - case $ac_val in - */ ) - ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` - eval $ac_var=\$ac_val;; - esac - # Be sure to have absolute directory names. - case $ac_val in - [\\/$]* | ?:[\\/]* ) continue;; - NONE | '' ) case $ac_var in *prefix ) continue;; esac;; - esac - as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" -done - -# There might be people who depend on the old broken behavior: `$host' -# used to hold the argument of --host etc. -# FIXME: To remove some day. -build=$build_alias -host=$host_alias -target=$target_alias - -# FIXME: To remove some day. -if test "x$host_alias" != x; then - if test "x$build_alias" = x; then - cross_compiling=maybe - elif test "x$build_alias" != "x$host_alias"; then - cross_compiling=yes - fi -fi - -ac_tool_prefix= -test -n "$host_alias" && ac_tool_prefix=$host_alias- - -test "$silent" = yes && exec 6>/dev/null - - -ac_pwd=`pwd` && test -n "$ac_pwd" && -ac_ls_di=`ls -di .` && -ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - as_fn_error $? "working directory cannot be determined" -test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - as_fn_error $? "pwd does not report name of working directory" - - -# Find the source files, if location was not specified. -if test -z "$srcdir"; then - ac_srcdir_defaulted=yes - # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$as_myself" || -$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_myself" : 'X\(//\)[^/]' \| \ - X"$as_myself" : 'X\(//\)$' \| \ - X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_myself" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - srcdir=$ac_confdir - if test ! -r "$srcdir/$ac_unique_file"; then - srcdir=.. - fi -else - ac_srcdir_defaulted=no -fi -if test ! -r "$srcdir/$ac_unique_file"; then - test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" -fi -ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" -ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" - pwd)` -# When building in place, set srcdir=. -if test "$ac_abs_confdir" = "$ac_pwd"; then - srcdir=. -fi -# Remove unnecessary trailing slashes from srcdir. -# Double slashes in file names in object file debugging info -# mess up M-x gdb in Emacs. -case $srcdir in -*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; -esac -for ac_var in $ac_precious_vars; do - eval ac_env_${ac_var}_set=\${${ac_var}+set} - eval ac_env_${ac_var}_value=\$${ac_var} - eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} - eval ac_cv_env_${ac_var}_value=\$${ac_var} -done - -# -# Report the --help message. -# -if test "$ac_init_help" = "long"; then - # Omit some internal or obsolete options to make the list less imposing. - # This message is too long to be a string in the A/UX 3.1 sh. - cat <<_ACEOF -\`configure' configures this package to adapt to many kinds of systems. - -Usage: $0 [OPTION]... [VAR=VALUE]... - -To assign environment variables (e.g., CC, CFLAGS...), specify them as -VAR=VALUE. See below for descriptions of some of the useful variables. - -Defaults for the options are specified in brackets. - -Configuration: - -h, --help display this help and exit - --help=short display options specific to this package - --help=recursive display the short help of all the included packages - -V, --version display version information and exit - -q, --quiet, --silent do not print \`checking ...' messages - --cache-file=FILE cache test results in FILE [disabled] - -C, --config-cache alias for \`--cache-file=config.cache' - -n, --no-create do not create output files - --srcdir=DIR find the sources in DIR [configure dir or \`..'] - -Installation directories: - --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] - --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] - -By default, \`make install' will install all the files in -\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify -an installation prefix other than \`$ac_default_prefix' using \`--prefix', -for instance \`--prefix=\$HOME'. - -For better control, use the options below. - -Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] -_ACEOF - - cat <<\_ACEOF - -System types: - --build=BUILD configure for building on BUILD [guessed] - --host=HOST cross-compile to build programs to run on HOST [BUILD] -_ACEOF -fi - -if test -n "$ac_init_help"; then - - cat <<\_ACEOF - -Optional Features: - --disable-option-checking ignore unrecognized --enable/--with options - --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) - --enable-FEATURE[=ARG] include FEATURE [ARG=yes] - --disable-sq disable SQ compression - --disable-lzw disable LZW/1 and LZW/2 compression - --disable-lzc disable 12- and 16-bit LZC compression - --disable-deflate disable zlib deflate compression - --enable-bzip2 enable libbz2 bzip2 compression - --enable-dmalloc do dmalloc stuff - -Some influential environment variables: - CC C compiler command - CFLAGS C compiler flags - LDFLAGS linker flags, e.g. -L if you have libraries in a - nonstandard directory - LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if - you have headers in a nonstandard directory - CPP C preprocessor - -Use these variables to override the choices made by `configure' or to help -it to find libraries and programs with nonstandard names/locations. - -Report bugs to the package provider. -_ACEOF -ac_status=$? -fi - -if test "$ac_init_help" = "recursive"; then - # If there are subdirs, report their specific --help. - for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || - { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || - continue - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - cd "$ac_dir" || { ac_status=$?; continue; } - # Check for guested configure. - if test -f "$ac_srcdir/configure.gnu"; then - echo && - $SHELL "$ac_srcdir/configure.gnu" --help=recursive - elif test -f "$ac_srcdir/configure"; then - echo && - $SHELL "$ac_srcdir/configure" --help=recursive - else - $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 - fi || ac_status=$? - cd "$ac_pwd" || { ac_status=$?; break; } - done -fi - -test -n "$ac_init_help" && exit $ac_status -if $ac_init_version; then - cat <<\_ACEOF -configure -generated by GNU Autoconf 2.69 - -Copyright (C) 2012 Free Software Foundation, Inc. -This configure script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it. -_ACEOF - exit -fi - -## ------------------------ ## -## Autoconf initialization. ## -## ------------------------ ## - -# ac_fn_c_try_compile LINENO -# -------------------------- -# Try to compile conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext - if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_compile - -# ac_fn_c_try_cpp LINENO -# ---------------------- -# Try to preprocess conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_cpp () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } > conftest.i && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_cpp - -# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists, giving a warning if it cannot be compiled using -# the include files in INCLUDES and setting the cache variable VAR -# accordingly. -ac_fn_c_check_header_mongrel () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if eval \${$3+:} false; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -else - # Is the header compilable? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 -$as_echo_n "checking $2 usability... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_header_compiler=yes -else - ac_header_compiler=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 -$as_echo "$ac_header_compiler" >&6; } - -# Is the header present? -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 -$as_echo_n "checking $2 presence... " >&6; } -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include <$2> -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - ac_header_preproc=yes -else - ac_header_preproc=no -fi -rm -f conftest.err conftest.i conftest.$ac_ext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 -$as_echo "$ac_header_preproc" >&6; } - -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( - yes:no: ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 -$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; - no:yes:* ) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 -$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 -$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 -$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 -$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 -$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} - ;; -esac - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=\$ac_header_compiler" -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } -fi - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_mongrel - -# ac_fn_c_try_run LINENO -# ---------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes -# that executables *can* be run. -ac_fn_c_try_run () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then : - ac_retval=0 -else - $as_echo "$as_me: program exited with status $ac_status" >&5 - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=$ac_status -fi - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_run - -# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES -# ------------------------------------------------------- -# Tests whether HEADER exists and can be compiled using the include files in -# INCLUDES, setting the cache variable VAR accordingly. -ac_fn_c_check_header_compile () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -#include <$2> -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_header_compile - -# ac_fn_c_check_type LINENO TYPE VAR INCLUDES -# ------------------------------------------- -# Tests whether TYPE exists after having included INCLUDES, setting cache -# variable VAR accordingly. -ac_fn_c_check_type () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - eval "$3=no" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof ($2)) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -$4 -int -main () -{ -if (sizeof (($2))) - return 0; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - eval "$3=yes" -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_type - -# ac_fn_c_try_link LINENO -# ----------------------- -# Try to link conftest.$ac_ext, and return whether this succeeded. -ac_fn_c_try_link () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - rm -f conftest.$ac_objext conftest$ac_exeext - if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - grep -v '^ *+' conftest.err >conftest.er1 - cat conftest.er1 >&5 - mv -f conftest.er1 conftest.err - fi - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && { - test "$cross_compiling" = yes || - test -x conftest$ac_exeext - }; then : - ac_retval=0 -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_retval=1 -fi - # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information - # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would - # interfere with the next link command; also delete a directory that is - # left behind by Apple's compiler. We do this before executing the actions. - rm -rf conftest.dSYM conftest_ipa8_conftest.oo - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - as_fn_set_status $ac_retval - -} # ac_fn_c_try_link - -# ac_fn_c_check_func LINENO FUNC VAR -# ---------------------------------- -# Tests whether FUNC exists, setting the cache variable VAR accordingly -ac_fn_c_check_func () -{ - as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 -$as_echo_n "checking for $2... " >&6; } -if eval \${$3+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -/* Define $2 to an innocuous variant, in case declares $2. - For example, HP-UX 11i declares gettimeofday. */ -#define $2 innocuous_$2 - -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char $2 (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ - -#ifdef __STDC__ -# include -#else -# include -#endif - -#undef $2 - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char $2 (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_$2 || defined __stub___$2 -choke me -#endif - -int -main () -{ -return $2 (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - eval "$3=yes" -else - eval "$3=no" -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -fi -eval ac_res=\$$3 - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 -$as_echo "$ac_res" >&6; } - eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno - -} # ac_fn_c_check_func -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. - -It was created by $as_me, which was -generated by GNU Autoconf 2.69. Invocation command line was - - $ $0 $@ - -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## - -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` - -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` - -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` - -_ASUNAME - -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - $as_echo "PATH: $as_dir" - done -IFS=$as_save_IFS - -} >&5 - -cat >&5 <<_ACEOF - - -## ----------- ## -## Core tests. ## -## ----------- ## - -_ACEOF - - -# Keep a trace of the command line. -# Strip out --no-create and --no-recursion so they do not pile up. -# Strip out --silent because we don't want to record it for future runs. -# Also quote any args containing shell meta-characters. -# Make two passes to allow for proper duplicate-argument suppression. -ac_configure_args= -ac_configure_args0= -ac_configure_args1= -ac_must_keep_next=false -for ac_pass in 1 2 -do - for ac_arg - do - case $ac_arg in - -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil) - continue ;; - *\'*) - ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - case $ac_pass in - 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; - 2) - as_fn_append ac_configure_args1 " '$ac_arg'" - if test $ac_must_keep_next = true; then - ac_must_keep_next=false # Got value, back to normal. - else - case $ac_arg in - *=* | --config-cache | -C | -disable-* | --disable-* \ - | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ - | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ - | -with-* | --with-* | -without-* | --without-* | --x) - case "$ac_configure_args0 " in - "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; - esac - ;; - -* ) ac_must_keep_next=true ;; - esac - fi - as_fn_append ac_configure_args " '$ac_arg'" - ;; - esac - done -done -{ ac_configure_args0=; unset ac_configure_args0;} -{ ac_configure_args1=; unset ac_configure_args1;} - -# When interrupted or exit'd, cleanup temporary files, and complete -# config.log. We remove comments because anyway the quotes in there -# would cause problems or look ugly. -# WARNING: Use '\'' to represent an apostrophe within the trap. -# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. -trap 'exit_status=$? - # Save into config.log some information that might help in debugging. - { - echo - - $as_echo "## ---------------- ## -## Cache variables. ## -## ---------------- ##" - echo - # The following way of writing the cache mishandles newlines in values, -( - for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - (set) 2>&1 | - case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - sed -n \ - "s/'\''/'\''\\\\'\'''\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" - ;; #( - *) - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) - echo - - $as_echo "## ----------------- ## -## Output variables. ## -## ----------------- ##" - echo - for ac_var in $ac_subst_vars - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - - if test -n "$ac_subst_files"; then - $as_echo "## ------------------- ## -## File substitutions. ## -## ------------------- ##" - echo - for ac_var in $ac_subst_files - do - eval ac_val=\$$ac_var - case $ac_val in - *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; - esac - $as_echo "$ac_var='\''$ac_val'\''" - done | sort - echo - fi - - if test -s confdefs.h; then - $as_echo "## ----------- ## -## confdefs.h. ## -## ----------- ##" - echo - cat confdefs.h - echo - fi - test "$ac_signal" != 0 && - $as_echo "$as_me: caught signal $ac_signal" - $as_echo "$as_me: exit $exit_status" - } >&5 - rm -f core *.core core.conftest.* && - rm -f -r conftest* confdefs* conf$$* $ac_clean_files && - exit $exit_status -' 0 -for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal -done -ac_signal=0 - -# confdefs.h avoids OS command line length limits that DEFS can exceed. -rm -f -r conftest* confdefs.h - -$as_echo "/* confdefs.h */" > confdefs.h - -# Predefined preprocessor variables. - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_NAME "$PACKAGE_NAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_TARNAME "$PACKAGE_TARNAME" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_VERSION "$PACKAGE_VERSION" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_STRING "$PACKAGE_STRING" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" -_ACEOF - -cat >>confdefs.h <<_ACEOF -#define PACKAGE_URL "$PACKAGE_URL" -_ACEOF - - -# Let the site file select an alternate cache file if it wants to. -# Prefer an explicitly selected file to automatically selected ones. -ac_site_file1=NONE -ac_site_file2=NONE -if test -n "$CONFIG_SITE"; then - # We do not want a PATH search for config.site. - case $CONFIG_SITE in #(( - -*) ac_site_file1=./$CONFIG_SITE;; - */*) ac_site_file1=$CONFIG_SITE;; - *) ac_site_file1=./$CONFIG_SITE;; - esac -elif test "x$prefix" != xNONE; then - ac_site_file1=$prefix/share/config.site - ac_site_file2=$prefix/etc/config.site -else - ac_site_file1=$ac_default_prefix/share/config.site - ac_site_file2=$ac_default_prefix/etc/config.site -fi -for ac_site_file in "$ac_site_file1" "$ac_site_file2" -do - test "x$ac_site_file" = xNONE && continue - if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 -$as_echo "$as_me: loading site script $ac_site_file" >&6;} - sed 's/^/| /' "$ac_site_file" >&5 - . "$ac_site_file" \ - || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "failed to load site script $ac_site_file -See \`config.log' for more details" "$LINENO" 5; } - fi -done - -if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special files - # actually), so we avoid doing that. DJGPP emulates it as a regular file. - if test /dev/null != "$cache_file" && test -f "$cache_file"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 -$as_echo "$as_me: loading cache $cache_file" >&6;} - case $cache_file in - [\\/]* | ?:[\\/]* ) . "$cache_file";; - *) . "./$cache_file";; - esac - fi -else - { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 -$as_echo "$as_me: creating cache $cache_file" >&6;} - >$cache_file -fi - -# Check that the precious variables saved in the cache have kept the same -# value. -ac_cache_corrupted=false -for ac_var in $ac_precious_vars; do - eval ac_old_set=\$ac_cv_env_${ac_var}_set - eval ac_new_set=\$ac_env_${ac_var}_set - eval ac_old_val=\$ac_cv_env_${ac_var}_value - eval ac_new_val=\$ac_env_${ac_var}_value - case $ac_old_set,$ac_new_set in - set,) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,set) - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 -$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} - ac_cache_corrupted=: ;; - ,);; - *) - if test "x$ac_old_val" != "x$ac_new_val"; then - # differences in whitespace do not lead to failure. - ac_old_val_w=`echo x $ac_old_val` - ac_new_val_w=`echo x $ac_new_val` - if test "$ac_old_val_w" != "$ac_new_val_w"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 -$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - ac_cache_corrupted=: - else - { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 -$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} - eval $ac_var=\$ac_old_val - fi - { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 -$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 -$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} - fi;; - esac - # Pass precious variables to config.status. - if test "$ac_new_set" = set; then - case $ac_new_val in - *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; - *) ac_arg=$ac_var=$ac_new_val ;; - esac - case " $ac_configure_args " in - *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) as_fn_append ac_configure_args " '$ac_arg'" ;; - esac - fi -done -if $ac_cache_corrupted; then - { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} - { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 -$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} - as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 -fi -## -------------------- ## -## Main body of script. ## -## -------------------- ## - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -ac_config_headers="$ac_config_headers config.h" - - -ac_aux_dir= -for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi -done -if test -z "$ac_aux_dir"; then - as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 -fi - -# These three variables are undocumented and unsupported, -# and are intended to be withdrawn in a future Autoconf release. -# They can cause serious problems if a builder's source tree is in a directory -# whose full name contains unusual characters. -ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. -ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. -ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. - - -# Make sure we can run config.sub. -$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 -$as_echo_n "checking build system type... " >&6; } -if ${ac_cv_build+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_build_alias=$build_alias -test "x$ac_build_alias" = x && - ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` -test "x$ac_build_alias" = x && - as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 -ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 -$as_echo "$ac_cv_build" >&6; } -case $ac_cv_build in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; -esac -build=$ac_cv_build -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_build -shift -build_cpu=$1 -build_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -build_os=$* -IFS=$ac_save_IFS -case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 -$as_echo_n "checking host system type... " >&6; } -if ${ac_cv_host+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test "x$host_alias" = x; then - ac_cv_host=$ac_cv_build -else - ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 -$as_echo "$ac_cv_host" >&6; } -case $ac_cv_host in -*-*-*) ;; -*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; -esac -host=$ac_cv_host -ac_save_IFS=$IFS; IFS='-' -set x $ac_cv_host -shift -host_cpu=$1 -host_vendor=$2 -shift; shift -# Remember, the first character of IFS is used to create $*, -# except with old shells: -host_os=$* -IFS=$ac_save_IFS -case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. -set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_CC"; then - ac_ct_CC=$CC - # Extract the first word of "gcc", so it can be a program name with args. -set dummy gcc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="gcc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -else - CC="$ac_cv_prog_CC" -fi - -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. -set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="${ac_tool_prefix}cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - fi -fi -if test -z "$CC"; then - # Extract the first word of "cc", so it can be a program name with args. -set dummy cc; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else - ac_prog_rejected=no -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then - ac_prog_rejected=yes - continue - fi - ac_cv_prog_CC="cc" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -if test $ac_prog_rejected = yes; then - # We found a bogon in the path, so make sure we never use it. - set dummy $ac_cv_prog_CC - shift - if test $# != 0; then - # We chose a different compiler from the bogus one. - # However, it has the same basename, so the bogon will be chosen - # first if we set CC to just the basename; use the full file name. - shift - ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" - fi -fi -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$CC"; then - if test -n "$ac_tool_prefix"; then - for ac_prog in cl.exe - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$CC"; then - ac_cv_prog_CC="$CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -CC=$ac_cv_prog_CC -if test -n "$CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 -$as_echo "$CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$CC" && break - done -fi -if test -z "$CC"; then - ac_ct_CC=$CC - for ac_prog in cl.exe -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_CC+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_CC"; then - ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_CC="$ac_prog" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_CC=$ac_cv_prog_ac_ct_CC -if test -n "$ac_ct_CC"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 -$as_echo "$ac_ct_CC" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - - test -n "$ac_ct_CC" && break -done - - if test "x$ac_ct_CC" = x; then - CC="" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - CC=$ac_ct_CC - fi -fi - -fi - - -test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "no acceptable C compiler found in \$PATH -See \`config.log' for more details" "$LINENO" 5; } - -# Provide some information about the compiler. -$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 -set X $ac_compile -ac_compiler=$2 -for ac_option in --version -v -V -qversion; do - { { ac_try="$ac_compiler $ac_option >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compiler $ac_option >&5") 2>conftest.err - ac_status=$? - if test -s conftest.err; then - sed '10a\ -... rest of stderr output deleted ... - 10q' conftest.err >conftest.er1 - cat conftest.er1 >&5 - fi - rm -f conftest.er1 conftest.err - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } -done - -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" -# Try to create an executable without -o first, disregard a.out. -# It will help us diagnose broken compilers, and finding out an intuition -# of exeext. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 -$as_echo_n "checking whether the C compiler works... " >&6; } -ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` - -# The possible output files: -ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" - -ac_rmfiles= -for ac_file in $ac_files -do - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - * ) ac_rmfiles="$ac_rmfiles $ac_file";; - esac -done -rm -f $ac_rmfiles - -if { { ac_try="$ac_link_default" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link_default") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. -# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' -# in a Makefile. We should not override ac_cv_exeext if it was cached, -# so that the user can short-circuit this test for compilers unknown to -# Autoconf. -for ac_file in $ac_files '' -do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) - ;; - [ab].out ) - # We found the default executable, but exeext='' is most - # certainly right. - break;; - *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; - then :; else - ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - fi - # We set ac_cv_exeext here because the later test for it is not - # safe: cross compilers may not add the suffix if given an `-o' - # argument, so we may need to know it at that point already. - # Even if this section looks crufty: it has the advantage of - # actually working. - break;; - * ) - break;; - esac -done -test "$ac_cv_exeext" = no && ac_cv_exeext= - -else - ac_file='' -fi -if test -z "$ac_file"; then : - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -$as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error 77 "C compiler cannot create executables -See \`config.log' for more details" "$LINENO" 5; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 -$as_echo_n "checking for C compiler default output file name... " >&6; } -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 -$as_echo "$ac_file" >&6; } -ac_exeext=$ac_cv_exeext - -rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 -$as_echo_n "checking for suffix of executables... " >&6; } -if { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - # If both `conftest.exe' and `conftest' are `present' (well, observable) -# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will -# work properly (i.e., refer to `conftest.exe'), while it won't with -# `rm'. -for ac_file in conftest.exe conftest conftest.*; do - test -f "$ac_file" || continue - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; - *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` - break;; - * ) break;; - esac -done -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest conftest$ac_cv_exeext -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 -$as_echo "$ac_cv_exeext" >&6; } - -rm -f conftest.$ac_ext -EXEEXT=$ac_cv_exeext -ac_exeext=$EXEEXT -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -int -main () -{ -FILE *f = fopen ("conftest.out", "w"); - return ferror (f) || fclose (f) != 0; - - ; - return 0; -} -_ACEOF -ac_clean_files="$ac_clean_files conftest.out" -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 -$as_echo_n "checking whether we are cross compiling... " >&6; } -if test "$cross_compiling" != yes; then - { { ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; } - if { ac_try='./conftest$ac_cv_exeext' - { { case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details" "$LINENO" 5; } - fi - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 -$as_echo "$cross_compiling" >&6; } - -rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out -ac_clean_files=$ac_clean_files_save -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 -$as_echo_n "checking for suffix of object files... " >&6; } -if ${ac_cv_objext+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.o conftest.obj -if { { ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" -$as_echo "$ac_try_echo"; } >&5 - (eval "$ac_compile") 2>&5 - ac_status=$? - $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 - test $ac_status = 0; }; then : - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - $as_echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "cannot compute suffix of object files: cannot compile -See \`config.log' for more details" "$LINENO" 5; } -fi -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 -$as_echo "$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 -$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } -if ${ac_cv_c_compiler_gnu+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ -#ifndef __GNUC__ - choke me -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_compiler_gnu=yes -else - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_c_compiler_gnu=$ac_compiler_gnu - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 -$as_echo "$ac_cv_c_compiler_gnu" >&6; } -if test $ac_compiler_gnu = yes; then - GCC=yes -else - GCC= -fi -ac_test_CFLAGS=${CFLAGS+set} -ac_save_CFLAGS=$CFLAGS -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 -$as_echo_n "checking whether $CC accepts -g... " >&6; } -if ${ac_cv_prog_cc_g+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_save_c_werror_flag=$ac_c_werror_flag - ac_c_werror_flag=yes - ac_cv_prog_cc_g=no - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -else - CFLAGS="" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - -else - ac_c_werror_flag=$ac_save_c_werror_flag - CFLAGS="-g" - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_g=yes -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - ac_c_werror_flag=$ac_save_c_werror_flag -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 -$as_echo "$ac_cv_prog_cc_g" >&6; } -if test "$ac_test_CFLAGS" = set; then - CFLAGS=$ac_save_CFLAGS -elif test $ac_cv_prog_cc_g = yes; then - if test "$GCC" = yes; then - CFLAGS="-g -O2" - else - CFLAGS="-g" - fi -else - if test "$GCC" = yes; then - CFLAGS="-O2" - else - CFLAGS= - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 -$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } -if ${ac_cv_prog_cc_c89+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_prog_cc_c89=no -ac_save_CC=$CC -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -struct stat; -/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ -struct buf { int x; }; -FILE * (*rcsopen) (struct buf *, struct stat *, int); -static char *e (p, i) - char **p; - int i; -{ - return p[i]; -} -static char *f (char * (*g) (char **, int), char **p, ...) -{ - char *s; - va_list v; - va_start (v,p); - s = g (p, va_arg (v,int)); - va_end (v); - return s; -} - -/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has - function prototypes and stuff, but not '\xHH' hex character constants. - These don't provoke an error unfortunately, instead are silently treated - as 'x'. The following induces an error, until -std is added to get - proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an - array size at least. It's necessary to write '\x00'==0 to get something - that's true only with -std. */ -int osf4_cc_array ['\x00' == 0 ? 1 : -1]; - -/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters - inside strings and character constants. */ -#define FOO(x) 'x' -int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; - -int test (int i, double x); -struct s1 {int (*f) (int a);}; -struct s2 {int (*f) (double a);}; -int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); -int argc; -char **argv; -int -main () -{ -return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; - ; - return 0; -} -_ACEOF -for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ - -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" -do - CC="$ac_save_CC $ac_arg" - if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_prog_cc_c89=$ac_arg -fi -rm -f core conftest.err conftest.$ac_objext - test "x$ac_cv_prog_cc_c89" != "xno" && break -done -rm -f conftest.$ac_ext -CC=$ac_save_CC - -fi -# AC_CACHE_VAL -case "x$ac_cv_prog_cc_c89" in - x) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 -$as_echo "none needed" >&6; } ;; - xno) - { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 -$as_echo "unsupported" >&6; } ;; - *) - CC="$CC $ac_cv_prog_cc_c89" - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 -$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; -esac -if test "x$ac_cv_prog_cc_c89" != xno; then : - -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -# Find a good install program. We prefer a C program (faster), -# so one script is as good as another. But avoid the broken or -# incompatible versions: -# SysV /etc/install, /usr/sbin/install -# SunOS /usr/etc/install -# IRIX /sbin/install -# AIX /bin/install -# AmigaOS /C/install, which installs bootblocks on floppy discs -# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag -# AFS /usr/afsws/bin/install, which mishandles nonexistent args -# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" -# OS/2's system install, which has a completely different semantic -# ./install, which can be erroneously created by make from ./install.sh. -# Reject install programs that cannot install multiple files. -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 -$as_echo_n "checking for a BSD-compatible install... " >&6; } -if test -z "$INSTALL"; then -if ${ac_cv_path_install+:} false; then : - $as_echo_n "(cached) " >&6 -else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in #(( - ./ | .// | /[cC]/* | \ - /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ - /usr/ucb/* ) ;; - *) - # OSF1 and SCO ODT 3.0 have their own names for install. - # Don't use installbsd from OSF since it installs stuff as root - # by default. - for ac_prog in ginstall scoinst install; do - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then - if test $ac_prog = install && - grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # AIX install. It has an incompatible calling convention. - : - elif test $ac_prog = install && - grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then - # program-specific install script used by HP pwplus--don't use. - : - else - rm -rf conftest.one conftest.two conftest.dir - echo one > conftest.one - echo two > conftest.two - mkdir conftest.dir - if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && - test -s conftest.one && test -s conftest.two && - test -s conftest.dir/conftest.one && - test -s conftest.dir/conftest.two - then - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 - fi - fi - fi - done - done - ;; -esac - - done -IFS=$as_save_IFS - -rm -rf conftest.one conftest.two conftest.dir - -fi - if test "${ac_cv_path_install+set}" = set; then - INSTALL=$ac_cv_path_install - else - # As a last resort, use the slow shell script. Don't cache a - # value for INSTALL within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - INSTALL=$ac_install_sh - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 -$as_echo "$INSTALL" >&6; } - -# Use test -z because SunOS4 sh mishandles braces in ${var-val}. -# It thinks the first close brace ends the variable substitution. -test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' - -test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' - -test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } -set x ${MAKE-make} -ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat >conftest.make <<\_ACEOF -SHELL = /bin/sh -all: - @echo '@@@%%%=$(MAKE)=@@@%%%' -_ACEOF -# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. -case `${MAKE-make} -f conftest.make 2>/dev/null` in - *@@@%%%=?*=@@@%%%*) - eval ac_cv_prog_make_${ac_make}_set=yes;; - *) - eval ac_cv_prog_make_${ac_make}_set=no;; -esac -rm -f conftest.make -fi -if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 -$as_echo "yes" >&6; } - SET_MAKE= -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } - SET_MAKE="MAKE=${MAKE-make}" -fi - -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 -$as_echo "$RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done - done -IFS=$as_save_IFS - -fi -fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 -$as_echo "$ac_ct_RANLIB" >&6; } -else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 -$as_echo "no" >&6; } -fi - - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 -$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} -ac_tool_warned=yes ;; -esac - RANLIB=$ac_ct_RANLIB - fi -else - RANLIB="$ac_cv_prog_RANLIB" -fi - - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 -$as_echo_n "checking how to run the C preprocessor... " >&6; } -# On Suns, sometimes $CPP names a directory. -if test -n "$CPP" && test -d "$CPP"; then - CPP= -fi -if test -z "$CPP"; then - if ${ac_cv_prog_CPP+:} false; then : - $as_echo_n "(cached) " >&6 -else - # Double quotes because CPP needs to be expanded - for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" - do - ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - break -fi - - done - ac_cv_prog_CPP=$CPP - -fi - CPP=$ac_cv_prog_CPP -else - ac_cv_prog_CPP=$CPP -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 -$as_echo "$CPP" >&6; } -ac_preproc_ok=false -for ac_c_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - -else - # Broken: fails on valid input. -continue -fi -rm -f conftest.err conftest.i conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -_ACEOF -if ac_fn_c_try_cpp "$LINENO"; then : - # Broken: success on invalid input. -continue -else - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.i conftest.$ac_ext - -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.i conftest.err conftest.$ac_ext -if $ac_preproc_ok; then : - -else - { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 -$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} -as_fn_error $? "C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details" "$LINENO" 5; } -fi - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 -$as_echo_n "checking for grep that handles long lines and -e... " >&6; } -if ${ac_cv_path_GREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if test -z "$GREP"; then - ac_path_GREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_GREP" || continue -# Check for GNU ac_path_GREP and select it if it is found. - # Check for GNU $ac_path_GREP -case `"$ac_path_GREP" --version 2>&1` in -*GNU*) - ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'GREP' >> "conftest.nl" - "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_GREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_GREP="$ac_path_GREP" - ac_path_GREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_GREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_GREP"; then - as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_GREP=$GREP -fi - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 -$as_echo "$ac_cv_path_GREP" >&6; } - GREP="$ac_cv_path_GREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 -$as_echo_n "checking for egrep... " >&6; } -if ${ac_cv_path_EGREP+:} false; then : - $as_echo_n "(cached) " >&6 -else - if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 - then ac_cv_path_EGREP="$GREP -E" - else - if test -z "$EGREP"; then - ac_path_EGREP_found=false - # Loop through the user's path and test for each of PROGNAME-LIST - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - as_fn_executable_p "$ac_path_EGREP" || continue -# Check for GNU ac_path_EGREP and select it if it is found. - # Check for GNU $ac_path_EGREP -case `"$ac_path_EGREP" --version 2>&1` in -*GNU*) - ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; -*) - ac_count=0 - $as_echo_n 0123456789 >"conftest.in" - while : - do - cat "conftest.in" "conftest.in" >"conftest.tmp" - mv "conftest.tmp" "conftest.in" - cp "conftest.in" "conftest.nl" - $as_echo 'EGREP' >> "conftest.nl" - "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break - diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - as_fn_arith $ac_count + 1 && ac_count=$as_val - if test $ac_count -gt ${ac_path_EGREP_max-0}; then - # Best one so far, save it but keep looking for a better one - ac_cv_path_EGREP="$ac_path_EGREP" - ac_path_EGREP_max=$ac_count - fi - # 10*(2^10) chars as input seems more than enough - test $ac_count -gt 10 && break - done - rm -f conftest.in conftest.tmp conftest.nl conftest.out;; -esac - - $ac_path_EGREP_found && break 3 - done - done - done -IFS=$as_save_IFS - if test -z "$ac_cv_path_EGREP"; then - as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 - fi -else - ac_cv_path_EGREP=$EGREP -fi - - fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 -$as_echo "$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 -$as_echo_n "checking for ANSI C header files... " >&6; } -if ${ac_cv_header_stdc+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#include -#include - -int -main () -{ - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_header_stdc=yes -else - ac_cv_header_stdc=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then : - -else - ac_cv_header_stdc=no -fi -rm -f conftest* - -fi - -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then : - : -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif - -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - -else - ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - -fi -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 -$as_echo "$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then - -$as_echo "#define STDC_HEADERS 1" >>confdefs.h - -fi - -# On IRIX 5.3, sys/types and inttypes.h are conflicting. -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default -" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -for ac_header in fcntl.h malloc.h stdlib.h sys/stat.h sys/time.h sys/types.h \ - sys/utime.h unistd.h utime.h -do : - as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` -ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" -if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF - -fi - -done - - -LIBS="" - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for an ANSI C-conforming const" >&5 -$as_echo_n "checking for an ANSI C-conforming const... " >&6; } -if ${ac_cv_c_const+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -int -main () -{ - -#ifndef __cplusplus - /* Ultrix mips cc rejects this sort of thing. */ - typedef int charset[2]; - const charset cs = { 0, 0 }; - /* SunOS 4.1.1 cc rejects this. */ - char const *const *pcpcc; - char **ppc; - /* NEC SVR4.0.2 mips cc rejects this. */ - struct point {int x, y;}; - static struct point const zero = {0,0}; - /* AIX XL C 1.02.0.0 rejects this. - It does not let you subtract one const X* pointer from another in - an arm of an if-expression whose if-part is not a constant - expression */ - const char *g = "string"; - pcpcc = &g + (g ? g-g : 0); - /* HPUX 7.0 cc rejects these. */ - ++pcpcc; - ppc = (char**) pcpcc; - pcpcc = (char const *const *) ppc; - { /* SCO 3.2v4 cc rejects this sort of thing. */ - char tx; - char *t = &tx; - char const *s = 0 ? (char *) 0 : (char const *) 0; - - *t++ = 0; - if (s) return 0; - } - { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ - int x[] = {25, 17}; - const int *foo = &x[0]; - ++foo; - } - { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ - typedef const int *iptr; - iptr p = 0; - ++p; - } - { /* AIX XL C 1.02.0.0 rejects this sort of thing, saying - "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ - struct s { int j; const int *ap[3]; } bx; - struct s *b = &bx; b->j = 5; - } - { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ - const int foo = 10; - if (!foo) return 0; - } - return !cs[0] && !zero.x; -#endif - - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_const=yes -else - ac_cv_c_const=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5 -$as_echo "$ac_cv_c_const" >&6; } -if test $ac_cv_c_const = no; then - -$as_echo "#define const /**/" >>confdefs.h - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 -$as_echo_n "checking for inline... " >&6; } -if ${ac_cv_c_inline+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_cv_c_inline=no -for ac_kw in inline __inline__ __inline; do - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#ifndef __cplusplus -typedef int foo_t; -static $ac_kw foo_t static_foo () {return 0; } -$ac_kw foo_t foo () {return 0; } -#endif - -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_c_inline=$ac_kw -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - test "$ac_cv_c_inline" != no && break -done - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 -$as_echo "$ac_cv_c_inline" >&6; } - -case $ac_cv_c_inline in - inline | yes) ;; - *) - case $ac_cv_c_inline in - no) ac_val=;; - *) ac_val=$ac_cv_c_inline;; - esac - cat >>confdefs.h <<_ACEOF -#ifndef __cplusplus -#define inline $ac_val -#endif -_ACEOF - ;; -esac - -ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default" -if test "x$ac_cv_type_mode_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define mode_t int -_ACEOF - -fi - -ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" -if test "x$ac_cv_type_off_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define off_t long int -_ACEOF - -fi - -ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" -if test "x$ac_cv_type_size_t" = xyes; then : - -else - -cat >>confdefs.h <<_ACEOF -#define size_t unsigned int -_ACEOF - -fi - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether struct tm is in sys/time.h or time.h" >&5 -$as_echo_n "checking whether struct tm is in sys/time.h or time.h... " >&6; } -if ${ac_cv_struct_tm+:} false; then : - $as_echo_n "(cached) " >&6 -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include -#include - -int -main () -{ -struct tm tm; - int *p = &tm.tm_sec; - return !p; - ; - return 0; -} -_ACEOF -if ac_fn_c_try_compile "$LINENO"; then : - ac_cv_struct_tm=time.h -else - ac_cv_struct_tm=sys/time.h -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_tm" >&5 -$as_echo "$ac_cv_struct_tm" >&6; } -if test $ac_cv_struct_tm = sys/time.h; then - -$as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h - -fi - - -for ac_func in fdopen ftruncate memmove mkdir mkstemp mktime timelocal \ - localtime_r snprintf strcasecmp strncasecmp strtoul strerror vsnprintf -do : - as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` -ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" -if eval test \"x\$"$as_ac_var"\" = x"yes"; then : - cat >>confdefs.h <<_ACEOF -#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 -_ACEOF - -fi -done - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if snprintf is declared" >&5 -$as_echo_n "checking if snprintf is declared... " >&6; } -if ${nufxlib_cv_snprintf_in_header+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "snprintf" >/dev/null 2>&1; then : - nufxlib_cv_snprintf_in_header=yes -else - nufxlib_cv_snprintf_in_header=no -fi -rm -f conftest* - - -fi - -if test $nufxlib_cv_snprintf_in_header = "yes"; then - $as_echo "#define SNPRINTF_DECLARED 1" >>confdefs.h - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $nufxlib_cv_snprintf_in_header" >&5 -$as_echo "$nufxlib_cv_snprintf_in_header" >&6; } - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if vsnprintf is declared" >&5 -$as_echo_n "checking if vsnprintf is declared... " >&6; } -if ${nufxlib_cv_vsnprintf_in_header+:} false; then : - $as_echo_n "(cached) " >&6 -else - - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ -#include - -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "vsnprintf" >/dev/null 2>&1; then : - nufxlib_cv_vsnprintf_in_header=yes -else - nufxlib_cv_vsnprintf_in_header=no -fi -rm -f conftest* - - -fi - -if test $nufxlib_cv_vsnprintf_in_header = "yes"; then - $as_echo "#define VSNPRINTF_DECLARED 1" >>confdefs.h - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $nufxlib_cv_vsnprintf_in_header" >&5 -$as_echo "$nufxlib_cv_vsnprintf_in_header" >&6; } - -if test -z "$GCC"; then - BUILD_FLAGS='$(OPT)' -else - BUILD_FLAGS='$(OPT) $(GCC_FLAGS)' -fi - - -SHARE_FLAGS='-shared' -if test "$host_cpu" = "powerpc" -a "$host_os" = "beos"; then - CC=cc - GCC= - CFLAGS='-proc 603 -opt full' - SHARE_FLAGS='-shared -nostdlib' - echo "forcing CC to \"$CC\" and CFLAGS to \"$CFLAGS\"" -elif test "$host_os" = "beos"; then - SHARE_FLAGS='-nostartfiles -Xlinker -soname="$@"' -fi - - - - -if test "$host_os" = "beos"; then - if test "$prefix" = "NONE" -a \ - "$includedir" = '${prefix}/include' -a \ - "$libdir" = '${exec_prefix}/lib' -a \ - "$bindir" = '${exec_prefix}/bin' -a \ - "$mandir" = '${prefix}/man' - then - echo replacing install locations with BeOS values - prefix=/boot - includedir='${prefix}/develop/headers' - libdir='${exec_prefix}/home/config/lib' - bindir='${exec_prefix}/home/config/bin' - mandir='/tmp' - - - - - - fi -fi - - - -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if sprintf returns int" >&5 -$as_echo_n "checking if sprintf returns int... " >&6; } -if ${nufxlib_cv_sprintf_returns_int+:} false; then : - $as_echo_n "(cached) " >&6 -else - - if test "$cross_compiling" = yes; then : - nufxlib_cv_sprintf_returns_int=no -else - cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - - #include - int main(void) - { - int count; - char buf[8]; - count = sprintf(buf, "123"); /* should return three */ - exit(count != 3); - } - -_ACEOF -if ac_fn_c_try_run "$LINENO"; then : - nufxlib_cv_sprintf_returns_int=yes -else - nufxlib_cv_sprintf_returns_int=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ - conftest.$ac_objext conftest.beam conftest.$ac_ext -fi - - -fi - - -if test $nufxlib_cv_sprintf_returns_int = "yes"; then - $as_echo "#define SPRINTF_RETURNS_INT 1" >>confdefs.h - -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $nufxlib_cv_sprintf_returns_int" >&5 -$as_echo "$nufxlib_cv_sprintf_returns_int" >&6; } - - -# Check whether --enable-sq was given. -if test "${enable_sq+set}" = set; then : - enableval=$enable_sq; -else - enable_sq=yes -fi - -if test $enable_sq = "yes"; then - $as_echo "#define ENABLE_SQ 1" >>confdefs.h - -fi - -# Check whether --enable-lzw was given. -if test "${enable_lzw+set}" = set; then : - enableval=$enable_lzw; -else - enable_lzw=yes -fi - -if test $enable_lzw = "yes"; then - $as_echo "#define ENABLE_LZW 1" >>confdefs.h - -fi - -# Check whether --enable-lzc was given. -if test "${enable_lzc+set}" = set; then : - enableval=$enable_lzc; -else - enable_lzc=yes -fi - -if test $enable_lzc = "yes"; then - $as_echo "#define ENABLE_LZC 1" >>confdefs.h - -fi - -# Check whether --enable-deflate was given. -if test "${enable_deflate+set}" = set; then : - enableval=$enable_deflate; -else - enable_deflate=yes -fi - -if test $enable_deflate = "yes"; then - got_zlibh=false - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for deflate in -lz" >&5 -$as_echo_n "checking for deflate in -lz... " >&6; } -if ${ac_cv_lib_z_deflate+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lz $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char deflate (); -int -main () -{ -return deflate (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_z_deflate=yes -else - ac_cv_lib_z_deflate=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_deflate" >&5 -$as_echo "$ac_cv_lib_z_deflate" >&6; } -if test "x$ac_cv_lib_z_deflate" = xyes; then : - got_libz=true -else - got_libz=false -fi - - if $got_libz; then - ac_fn_c_check_header_mongrel "$LINENO" "zlib.h" "ac_cv_header_zlib_h" "$ac_includes_default" -if test "x$ac_cv_header_zlib_h" = xyes; then : - got_zlibh=true LIBS="$LIBS -lz" -fi - - - fi - if $got_zlibh; then - echo " (found libz and zlib.h, enabling deflate)" - $as_echo "#define ENABLE_DEFLATE 1" >>confdefs.h - - else - echo " (couldn't find libz and zlib.h, not enabling deflate)" - fi -fi - -# Check whether --enable-bzip2 was given. -if test "${enable_bzip2+set}" = set; then : - enableval=$enable_bzip2; -else - enable_bzip2=no -fi - -if test $enable_bzip2 = "yes"; then - got_bzlibh=false - { $as_echo "$as_me:${as_lineno-$LINENO}: checking for BZ2_bzCompress in -lbz2" >&5 -$as_echo_n "checking for BZ2_bzCompress in -lbz2... " >&6; } -if ${ac_cv_lib_bz2_BZ2_bzCompress+:} false; then : - $as_echo_n "(cached) " >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lbz2 $LIBS" -cat confdefs.h - <<_ACEOF >conftest.$ac_ext -/* end confdefs.h. */ - -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char BZ2_bzCompress (); -int -main () -{ -return BZ2_bzCompress (); - ; - return 0; -} -_ACEOF -if ac_fn_c_try_link "$LINENO"; then : - ac_cv_lib_bz2_BZ2_bzCompress=yes -else - ac_cv_lib_bz2_BZ2_bzCompress=no -fi -rm -f core conftest.err conftest.$ac_objext \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_bz2_BZ2_bzCompress" >&5 -$as_echo "$ac_cv_lib_bz2_BZ2_bzCompress" >&6; } -if test "x$ac_cv_lib_bz2_BZ2_bzCompress" = xyes; then : - got_libbz=true -else - got_libbz=false -fi - - if $got_libbz; then - ac_fn_c_check_header_mongrel "$LINENO" "bzlib.h" "ac_cv_header_bzlib_h" "$ac_includes_default" -if test "x$ac_cv_header_bzlib_h" = xyes; then : - got_bzlibh=true LIBS="$LIBS -lbz2" -fi - - - fi - if $got_bzlibh; then - echo " (found libbz2 and bzlib.h, enabling bzip2)" - $as_echo "#define ENABLE_BZIP2 1" >>confdefs.h - - else - echo " (couldn't find libbz2 and bzlib.h, not enabling bzip2)" - fi -fi - - -# Check whether --enable-dmalloc was given. -if test "${enable_dmalloc+set}" = set; then : - enableval=$enable_dmalloc; echo "--- enabling dmalloc"; - LIBS="$LIBS -L/usr/local/lib -ldmalloc"; $as_echo "#define USE_DMALLOC 1" >>confdefs.h - -fi - - -ac_config_files="$ac_config_files Makefile samples/Makefile" - -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. - -_ACEOF - -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 -$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( - *) { eval $ac_var=; unset $ac_var;} ;; - esac ;; - esac - done - - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes: double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \. - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - if test "x$cache_file" != "x/dev/null"; then - { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 -$as_echo "$as_me: updating cache $cache_file" >&6;} - if test ! -f "$cache_file" || test -h "$cache_file"; then - cat confcache >"$cache_file" - else - case $cache_file in #( - */* | ?:*) - mv -f confcache "$cache_file"$$ && - mv -f "$cache_file"$$ "$cache_file" ;; #( - *) - mv -f confcache "$cache_file" ;; - esac - fi - fi - else - { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 -$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache - -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - -DEFS=-DHAVE_CONFIG_H - -ac_libobjs= -ac_ltlibobjs= -U= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`$as_echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" - as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs - -LTLIBOBJS=$ac_ltlibobjs - - - -: "${CONFIG_STATUS=./config.status}" -ac_write_fail=0 -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 -$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} -as_write_fail=0 -cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. - -debug=false -ac_cs_recheck=false -ac_cs_silent=false - -SHELL=\${CONFIG_SHELL-$SHELL} -export SHELL -_ASEOF -cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 -## -------------------- ## -## M4sh Initialization. ## -## -------------------- ## - -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : - emulate sh - NULLCMD=: - # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in #( - *posix*) : - set -o posix ;; #( - *) : - ;; -esac -fi - - -as_nl=' -' -export as_nl -# Printing a long string crashes Solaris 7 /usr/bin/printf. -as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo -as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo -# Prefer a ksh shell builtin over an external printf program on Solaris, -# but without wasting forks for bash or zsh. -if test -z "$BASH_VERSION$ZSH_VERSION" \ - && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='print -r --' - as_echo_n='print -rn --' -elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then - as_echo='printf %s\n' - as_echo_n='printf %s' -else - if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then - as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' - as_echo_n='/usr/ucb/echo -n' - else - as_echo_body='eval expr "X$1" : "X\\(.*\\)"' - as_echo_n_body='eval - arg=$1; - case $arg in #( - *"$as_nl"*) - expr "X$arg" : "X\\(.*\\)$as_nl"; - arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; - esac; - expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" - ' - export as_echo_n_body - as_echo_n='sh -c $as_echo_n_body as_echo' - fi - export as_echo_body - as_echo='sh -c $as_echo_body as_echo' -fi - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - PATH_SEPARATOR=: - (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { - (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || - PATH_SEPARATOR=';' - } -fi - - -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -IFS=" "" $as_nl" - -# Find who we are. Look in the path if we contain no directory separator. -as_myself= -case $0 in #(( - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break - done -IFS=$as_save_IFS - - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - exit 1 -fi - -# Unset variables that we do not need and which cause bugs (e.g. in -# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" -# suppresses any "Segmentation fault" message there. '((' could -# trigger a bug in pdksh 5.2.14. -for as_var in BASH_ENV ENV MAIL MAILPATH -do eval test x\${$as_var+set} = xset \ - && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : -done -PS1='$ ' -PS2='> ' -PS4='+ ' - -# NLS nuisances. -LC_ALL=C -export LC_ALL -LANGUAGE=C -export LANGUAGE - -# CDPATH. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - - -# as_fn_error STATUS ERROR [LINENO LOG_FD] -# ---------------------------------------- -# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are -# provided, also output the error to LOG_FD, referencing LINENO. Then exit the -# script with STATUS, using 1 if that was 0. -as_fn_error () -{ - as_status=$1; test $as_status -eq 0 && as_status=1 - if test "$4"; then - as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack - $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 - fi - $as_echo "$as_me: error: $2" >&2 - as_fn_exit $as_status -} # as_fn_error - - -# as_fn_set_status STATUS -# ----------------------- -# Set $? to STATUS, without forking. -as_fn_set_status () -{ - return $1 -} # as_fn_set_status - -# as_fn_exit STATUS -# ----------------- -# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. -as_fn_exit () -{ - set +e - as_fn_set_status $1 - exit $1 -} # as_fn_exit - -# as_fn_unset VAR -# --------------- -# Portably unset VAR. -as_fn_unset () -{ - { eval $1=; unset $1;} -} -as_unset=as_fn_unset -# as_fn_append VAR VALUE -# ---------------------- -# Append the text in VALUE to the end of the definition contained in VAR. Take -# advantage of any shell optimizations that allow amortized linear growth over -# repeated appends, instead of the typical quadratic growth present in naive -# implementations. -if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : - eval 'as_fn_append () - { - eval $1+=\$2 - }' -else - as_fn_append () - { - eval $1=\$$1\$2 - } -fi # as_fn_append - -# as_fn_arith ARG... -# ------------------ -# Perform arithmetic evaluation on the ARGs, and store the result in the -# global $as_val. Take advantage of shells that can avoid forks. The arguments -# must be portable across $(()) and expr. -if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : - eval 'as_fn_arith () - { - as_val=$(( $* )) - }' -else - as_fn_arith () - { - as_val=`expr "$@" || test $? -eq 1` - } -fi # as_fn_arith - - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in #((((( --n*) - case `echo 'xy\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - xy) ECHO_C='\c';; - *) echo `echo ksh88 bug on AIX 6.1` > /dev/null - ECHO_T=' ';; - esac;; -*) - ECHO_N='-n';; -esac - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir 2>/dev/null -fi -if (echo >conf$$.file) 2>/dev/null; then - if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -pR'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -pR' - elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln - else - as_ln_s='cp -pR' - fi -else - as_ln_s='cp -pR' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - - -# as_fn_mkdir_p -# ------------- -# Create "$as_dir" as a directory, including parents if necessary. -as_fn_mkdir_p () -{ - - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || eval $as_mkdir_p || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" - - -} # as_fn_mkdir_p -if mkdir -p . 2>/dev/null; then - as_mkdir_p='mkdir -p "$as_dir"' -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - - -# as_fn_executable_p FILE -# ----------------------- -# Test if FILE is an executable regular file. -as_fn_executable_p () -{ - test -f "$1" && test -x "$1" -} # as_fn_executable_p -as_test_x='test -x' -as_executable_p=as_fn_executable_p - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - -exec 6>&1 -## ----------------------------------- ## -## Main body of $CONFIG_STATUS script. ## -## ----------------------------------- ## -_ASEOF -test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Save the log message, to keep $0 and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by $as_me, which was -generated by GNU Autoconf 2.69. Invocation command line was - - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ - -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" - -_ACEOF - -case $ac_config_files in *" -"*) set x $ac_config_files; shift; ac_config_files=$*;; -esac - -case $ac_config_headers in *" -"*) set x $ac_config_headers; shift; ac_config_headers=$*;; -esac - - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" - -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -ac_cs_usage="\ -\`$as_me' instantiates files and other configuration actions -from templates according to the current configuration. Unless the files -and actions are specified as TAGs, all are instantiated by default. - -Usage: $0 [OPTION]... [TAG]... - - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - --config print configuration, then exit - -q, --quiet, --silent - do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE - -Configuration files: -$config_files - -Configuration headers: -$config_headers - -Report bugs to the package provider." - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" -ac_cs_version="\\ -config.status -configured by $0, generated by GNU Autoconf 2.69, - with options \\"\$ac_cs_config\\" - -Copyright (C) 2012 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." - -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -test -n "\$AWK" || AWK=awk -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# The default lists apply if the user does not specify any file. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=?*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - --*=) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg= - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift - ;; - esac - - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - $as_echo "$ac_cs_version"; exit ;; - --config | --confi | --conf | --con | --co | --c ) - $as_echo "$ac_cs_config"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - '') as_fn_error $? "missing file argument" ;; - esac - as_fn_append CONFIG_FILES " '$ac_optarg'" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - case $ac_optarg in - *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; - esac - as_fn_append CONFIG_HEADERS " '$ac_optarg'" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - as_fn_error $? "ambiguous option: \`$1' -Try \`$0 --help' for more information.";; - --help | --hel | -h ) - $as_echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - - # This is an error. - -*) as_fn_error $? "unrecognized option: \`$1' -Try \`$0 --help' for more information." ;; - - *) as_fn_append ac_config_targets " $1" - ac_need_defaults=false ;; - - esac - shift -done - -ac_configure_extra_args= - -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -if \$ac_cs_recheck; then - set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion - shift - \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 - CONFIG_SHELL='$SHELL' - export CONFIG_SHELL - exec "\$@" -fi - -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -exec 5>>config.log -{ - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - $as_echo "$ac_log" -} >&5 - -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - "samples/Makefile") CONFIG_FILES="$CONFIG_FILES samples/Makefile" ;; - - *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; - esac -done - - -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers -fi - -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || -{ - tmp= ac_tmp= - trap 'exit_status=$? - : "${ac_tmp:=$tmp}" - { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status -' 0 - trap 'as_fn_exit 1' 1 2 13 15 -} -# Create a (secure) tmp directory for tmp files. - -{ - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -d "$tmp" -} || -{ - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 -ac_tmp=$tmp - -# Set up the scripts for CONFIG_FILES section. -# No need to generate them if there are no CONFIG_FILES. -# This happens for instance with `./config.status config.h'. -if test -n "$CONFIG_FILES"; then - - -ac_cr=`echo X | tr X '\015'` -# On cygwin, bash can eat \r inside `` if the user requested igncr. -# But we know of no other shell where ac_cr would be empty at this -# point, so we can use a bashism as a fallback. -if test "x$ac_cr" = x; then - eval ac_cr=\$\'\\r\' -fi -ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` -if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then - ac_cs_awk_cr='\\r' -else - ac_cs_awk_cr=$ac_cr -fi - -echo 'BEGIN {' >"$ac_tmp/subs1.awk" && -_ACEOF - - -{ - echo "cat >conf$$subs.awk <<_ACEOF" && - echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && - echo "_ACEOF" -} >conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 -ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - . ./conf$$subs.sh || - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - - ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` - if test $ac_delim_n = $ac_delim_num; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done -rm -f conf$$subs.sh - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && -_ACEOF -sed -n ' -h -s/^/S["/; s/!.*/"]=/ -p -g -s/^[^!]*!// -:repl -t repl -s/'"$ac_delim"'$// -t delim -:nl -h -s/\(.\{148\}\)..*/\1/ -t more1 -s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ -p -n -b repl -:more1 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t nl -:delim -h -s/\(.\{148\}\)..*/\1/ -t more2 -s/["\\]/\\&/g; s/^/"/; s/$/"/ -p -b -:more2 -s/["\\]/\\&/g; s/^/"/; s/$/"\\/ -p -g -s/.\{148\}// -t delim -' >$CONFIG_STATUS || ac_write_fail=1 -rm -f conf$$subs.awk -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -_ACAWK -cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && - for (key in S) S_is_set[key] = 1 - FS = "" - -} -{ - line = $ 0 - nfields = split(line, field, "@") - substed = 0 - len = length(field[1]) - for (i = 2; i < nfields; i++) { - key = field[i] - keylen = length(key) - if (S_is_set[key]) { - value = S[key] - line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) - len += length(value) + length(field[++i]) - substed = 1 - } else - len += 1 + keylen - } - - print line -} - -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then - sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" -else - cat -fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ - || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 -_ACEOF - -# VPATH may cause trouble with some makes, so we remove sole $(srcdir), -# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ -h -s/// -s/^/:/ -s/[ ]*$/:/ -s/:\$(srcdir):/:/g -s/:\${srcdir}:/:/g -s/:@srcdir@:/:/g -s/^:*// -s/:*$// -x -s/\(=[ ]*\).*/\1/ -G -s/\n// -s/^[^=]*=[ ]*$// -}' -fi - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -fi # test -n "$CONFIG_FILES" - -# Set up the scripts for CONFIG_HEADERS section. -# No need to generate them if there are no CONFIG_HEADERS. -# This happens for instance with `./config.status Makefile'. -if test -n "$CONFIG_HEADERS"; then -cat >"$ac_tmp/defines.awk" <<\_ACAWK || -BEGIN { -_ACEOF - -# Transform confdefs.h into an awk script `defines.awk', embedded as -# here-document in config.status, that substitutes the proper values into -# config.h.in to produce config.h. - -# Create a delimiter string that does not exist in confdefs.h, to ease -# handling of long lines. -ac_delim='%!_!# ' -for ac_last_try in false false :; do - ac_tt=`sed -n "/$ac_delim/p" confdefs.h` - if test -z "$ac_tt"; then - break - elif $ac_last_try; then - as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done - -# For the awk script, D is an array of macro values keyed by name, -# likewise P contains macro parameters if any. Preserve backslash -# newline sequences. - -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -sed -n ' -s/.\{148\}/&'"$ac_delim"'/g -t rset -:rset -s/^[ ]*#[ ]*define[ ][ ]*/ / -t def -d -:def -s/\\$// -t bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3"/p -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p -d -:bsnl -s/["\\]/\\&/g -s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ -D["\1"]=" \3\\\\\\n"\\/p -t cont -s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p -t cont -d -:cont -n -s/.\{148\}/&'"$ac_delim"'/g -t clear -:clear -s/\\$// -t bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/"/p -d -:bsnlc -s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p -b cont -' >$CONFIG_STATUS || ac_write_fail=1 - -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - for (key in D) D_is_set[key] = 1 - FS = "" -} -/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { - line = \$ 0 - split(line, arg, " ") - if (arg[1] == "#") { - defundef = arg[2] - mac1 = arg[3] - } else { - defundef = substr(arg[1], 2) - mac1 = arg[2] - } - split(mac1, mac2, "(") #) - macro = mac2[1] - prefix = substr(line, 1, index(line, defundef) - 1) - if (D_is_set[macro]) { - # Preserve the white space surrounding the "#". - print prefix "define", macro P[macro] D[macro] - next - } else { - # Replace #undef with comments. This is necessary, for example, - # in the case of _POSIX_SOURCE, which is predefined and required - # on some systems where configure will not decide to define it. - if (defundef == "undef") { - print "/*", prefix defundef, macro, "*/" - next - } - } -} -{ print } -_ACAWK -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 - as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 -fi # test -n "$CONFIG_HEADERS" - - -eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " -shift -for ac_tag -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift - - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$ac_tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; - esac - case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac - as_fn_append ac_file_inputs " '$ac_f'" - done - - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input='Generated from '` - $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' - `' by configure.' - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 -$as_echo "$as_me: creating $ac_file" >&6;} - fi - # Neutralize special characters interpreted by sed in replacement strings. - case $configure_input in #( - *\&* | *\|* | *\\* ) - ac_sed_conf_input=`$as_echo "$configure_input" | - sed 's/[\\\\&|]/\\\\&/g'`;; #( - *) ac_sed_conf_input=$configure_input;; - esac - - case $ac_tag in - *:-:* | *:-) cat >"$ac_tmp/stdin" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; - esac - ;; - esac - - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -$as_echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - as_dir="$ac_dir"; as_fn_mkdir_p - ac_builddir=. - -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix - -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix - - - case $ac_mode in - :F) - # - # CONFIG_FILE - # - - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac -_ACEOF - -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= -ac_sed_dataroot=' -/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p' -case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF - -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 -ac_sed_extra="$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s|@configure_input@|$ac_sed_conf_input|;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@top_build_prefix@&$ac_top_build_prefix&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -$ac_datarootdir_hack -" -eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ - >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ - "$ac_tmp/out"`; test -z "$ac_out"; } && - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&5 -$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined" >&2;} - - rm -f "$ac_tmp/stdin" - case $ac_file in - -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; - *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; - esac \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - ;; - :H) - # - # CONFIG_HEADER - # - if test x"$ac_file" != x-; then - { - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" - } >"$ac_tmp/config.h" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then - { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 -$as_echo "$as_me: $ac_file is unchanged" >&6;} - else - rm -f "$ac_file" - mv "$ac_tmp/config.h" "$ac_file" \ - || as_fn_error $? "could not create $ac_file" "$LINENO" 5 - fi - else - $as_echo "/* $configure_input */" \ - && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ - || as_fn_error $? "could not create -" "$LINENO" 5 - fi - ;; - - - esac - -done # for ac_tag - - -as_fn_exit 0 -_ACEOF -ac_clean_files=$ac_clean_files_save - -test $ac_write_fail = 0 || - as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 - - -# configure is writing to config.log, and then calls config.status. -# config.status does its own redirection, appending to config.log. -# Unfortunately, on DOS this fails, as config.log is still kept open -# by configure, so config.status won't be able to write to it; its -# output is simply discarded. So we exec the FD to /dev/null, -# effectively closing config.log, so it can be properly (re)opened and -# appended to by config.status. When coming back to configure, we -# need to make the FD available again. -if test "$no_create" != yes; then - ac_cs_success=: - ac_config_status_args= - test "$silent" = yes && - ac_config_status_args="$ac_config_status_args --quiet" - exec 5>/dev/null - $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false - exec 5>>config.log - # Use ||, not &&, to avoid exiting from the if with $? = 1, which - # would make configure fail if this is the last instruction. - $ac_cs_success || as_fn_exit 1 -fi -if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then - { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 -$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} -fi - diff --git a/ciderpress/nufxlib/configure.in b/ciderpress/nufxlib/configure.in deleted file mode 100644 index 46df1a4..0000000 --- a/ciderpress/nufxlib/configure.in +++ /dev/null @@ -1,218 +0,0 @@ -dnl NuFX archive manipulation library -dnl Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. -dnl This is free software; you can redistribute it and/or modify it under the -dnl terms of the BSD License, see the file COPYING-LIB. -dnl -dnl Process this file with autoconf to produce a configure script. - -AC_INIT(NufxLibPriv.h) -AC_CONFIG_HEADER(config.h) - -dnl Checks for programs. -AC_CANONICAL_HOST -dnl AC_PROG_AWK -AC_PROG_CC -AC_PROG_INSTALL -AC_PROG_MAKE_SET -AC_PROG_RANLIB - -dnl Checks for header files. -AC_CHECK_HEADERS(fcntl.h malloc.h stdlib.h sys/stat.h sys/time.h sys/types.h \ - sys/utime.h unistd.h utime.h) - -LIBS="" - -dnl Checks for typedefs, structures, and compiler characteristics. -AC_C_CONST -AC_C_INLINE -AC_TYPE_MODE_T -AC_TYPE_OFF_T -AC_TYPE_SIZE_T -AC_STRUCT_TM - -dnl Checks for library functions. -AC_CHECK_FUNCS(fdopen ftruncate memmove mkdir mkstemp mktime timelocal \ - localtime_r snprintf strcasecmp strncasecmp strtoul strerror vsnprintf) - -dnl Kent says: snprintf doesn't always have a declaration -AC_MSG_CHECKING(if snprintf is declared) -AC_CACHE_VAL(nufxlib_cv_snprintf_in_header, [ - AC_EGREP_HEADER(snprintf, stdio.h, - [nufxlib_cv_snprintf_in_header=yes], - [nufxlib_cv_snprintf_in_header=no]) -]) -if test $nufxlib_cv_snprintf_in_header = "yes"; then - AC_DEFINE(SNPRINTF_DECLARED) -fi -AC_MSG_RESULT($nufxlib_cv_snprintf_in_header) - -dnl Kent says: vsnprintf doesn't always have a declaration -AC_MSG_CHECKING(if vsnprintf is declared) -AC_CACHE_VAL(nufxlib_cv_vsnprintf_in_header, [ - AC_EGREP_HEADER(vsnprintf, stdio.h, - [nufxlib_cv_vsnprintf_in_header=yes], - [nufxlib_cv_vsnprintf_in_header=no]) -]) -if test $nufxlib_cv_vsnprintf_in_header = "yes"; then - AC_DEFINE(VSNPRINTF_DECLARED) -fi -AC_MSG_RESULT($nufxlib_cv_vsnprintf_in_header) - -dnl if we're using gcc, include gcc-specific warning flags -if test -z "$GCC"; then - BUILD_FLAGS='$(OPT)' -else - BUILD_FLAGS='$(OPT) $(GCC_FLAGS)' -fi - -dnl --------------------------------------------------------------------------- -dnl Some host-specific stuff. Variables you can test (set by the -dnl AC_CANONICAL_HOST call earlier) look like this: -dnl -dnl $host = i686-pc-linux-gnu -dnl $host_cpu = i686 -dnl $host_vendor = pc -dnl $host_os = linux-gnu - -dnl Figure out what the build and link flags should be; different for BeOS. -dnl (Should really use the auto-shared-lib stuff, but that adds a whole -dnl bunch of stuff.) -SHARE_FLAGS='-shared' -if test "$host_cpu" = "powerpc" -a "$host_os" = "beos"; then - dnl BeOS/PPC, with Metrowerks compiler - CC=cc - GCC= - CFLAGS='-proc 603 -opt full' - SHARE_FLAGS='-shared -nostdlib' - echo "forcing CC to \"$CC\" and CFLAGS to \"$CFLAGS\"" -elif test "$host_os" = "beos"; then - dnl BeOS/x86 - SHARE_FLAGS='-nostartfiles -Xlinker -soname="$@"' -fi - -AC_SUBST(BUILD_FLAGS) -AC_SUBST(SHARE_FLAGS) - -dnl BeOS doesn't like /usr/local/include, and gets feisty about it. If libdir -dnl and includedir are set to defaults, replace them with BeOS values. This -dnl might be going a little too far... -if test "$host_os" = "beos"; then - if test "$prefix" = "NONE" -a \ - "$includedir" = '${prefix}/include' -a \ - "$libdir" = '${exec_prefix}/lib' -a \ - "$bindir" = '${exec_prefix}/bin' -a \ - "$mandir" = '${prefix}/man' - then - echo replacing install locations with BeOS values - prefix=/boot - includedir='${prefix}/develop/headers' - libdir='${exec_prefix}/home/config/lib' - bindir='${exec_prefix}/home/config/bin' - mandir='/tmp' - AC_SUBST(prefix) - AC_SUBST(includedir) - AC_SUBST(libdir) - AC_SUBST(bindir) - AC_SUBST(mandir) - fi -fi - - - -dnl Test to see if sprintf does something reasonable. I'm going to assume -dnl that vsprintf and (if available) vsnprintf return the same thing. -AC_MSG_CHECKING(if sprintf returns int) -AC_CACHE_VAL(nufxlib_cv_sprintf_returns_int, [ - AC_TRY_RUN([ - #include - int main(void) - { - int count; - char buf[8]; - count = sprintf(buf, "123"); /* should return three */ - exit(count != 3); - } - ], - [nufxlib_cv_sprintf_returns_int=yes], [nufxlib_cv_sprintf_returns_int=no], - [nufxlib_cv_sprintf_returns_int=no]) -]) - -if test $nufxlib_cv_sprintf_returns_int = "yes"; then - AC_DEFINE(SPRINTF_RETURNS_INT) -fi -AC_MSG_RESULT($nufxlib_cv_sprintf_returns_int) - -dnl -dnl Allow selective disabling of compression algorithms. By default, -dnl all are enabled. We do a little extra work for libz and libbz2 -dnl because they're not built in. -dnl -dnl If we're creating a shared library, we need to explicitly link -dnl against libz and/or libbz2 when those features are enabled. -dnl - -AC_ARG_ENABLE(sq, - [ --disable-sq disable SQ compression], - [ ], [ enable_sq=yes ]) -if test $enable_sq = "yes"; then - AC_DEFINE(ENABLE_SQ) -fi - -AC_ARG_ENABLE(lzw, - [ --disable-lzw disable LZW/1 and LZW/2 compression], - [ ], [ enable_lzw=yes ]) -if test $enable_lzw = "yes"; then - AC_DEFINE(ENABLE_LZW) -fi - -AC_ARG_ENABLE(lzc, - [ --disable-lzc disable 12- and 16-bit LZC compression], - [ ], [ enable_lzc=yes ]) -if test $enable_lzc = "yes"; then - AC_DEFINE(ENABLE_LZC) -fi - -AC_ARG_ENABLE(deflate, - [ --disable-deflate disable zlib deflate compression], - [ ], [ enable_deflate=yes ]) -if test $enable_deflate = "yes"; then - dnl Check for zlib. Make sure it comes with zlib.h. - got_zlibh=false - AC_CHECK_LIB(z, deflate, got_libz=true, got_libz=false) - if $got_libz; then - AC_CHECK_HEADER(zlib.h, got_zlibh=true LIBS="$LIBS -lz") - fi - if $got_zlibh; then - echo " (found libz and zlib.h, enabling deflate)" - AC_DEFINE(ENABLE_DEFLATE) - else - echo " (couldn't find libz and zlib.h, not enabling deflate)" - fi -fi - -AC_ARG_ENABLE(bzip2, - [ --enable-bzip2 enable libbz2 bzip2 compression], - [ ], [ enable_bzip2=no ]) -if test $enable_bzip2 = "yes"; then - dnl Check for libbz2. Make sure it comes with bzlib.h. - dnl AC_CHECK_LIB(bz2, BZ2_bzCompress, - dnl AC_CHECK_HEADER(bzlib.h, AC_DEFINE(ENABLE_BZIP2) LIBS="$LIBS -lbz2")) - got_bzlibh=false - AC_CHECK_LIB(bz2, BZ2_bzCompress, got_libbz=true, got_libbz=false) - if $got_libbz; then - AC_CHECK_HEADER(bzlib.h, got_bzlibh=true LIBS="$LIBS -lbz2") - fi - if $got_bzlibh; then - echo " (found libbz2 and bzlib.h, enabling bzip2)" - AC_DEFINE(ENABLE_BZIP2) - else - echo " (couldn't find libbz2 and bzlib.h, not enabling bzip2)" - fi -fi - - -AC_ARG_ENABLE(dmalloc, [ --enable-dmalloc do dmalloc stuff], - [ echo "--- enabling dmalloc"; - LIBS="$LIBS -L/usr/local/lib -ldmalloc"; AC_DEFINE(USE_DMALLOC) ]) - -AC_OUTPUT(Makefile samples/Makefile) diff --git a/ciderpress/nufxlib/install-sh b/ciderpress/nufxlib/install-sh deleted file mode 100644 index e9de238..0000000 --- a/ciderpress/nufxlib/install-sh +++ /dev/null @@ -1,251 +0,0 @@ -#!/bin/sh -# -# install - install a program, script, or datafile -# This comes from X11R5 (mit/util/scripts/install.sh). -# -# Copyright 1991 by the Massachusetts Institute of Technology -# -# Permission to use, copy, modify, distribute, and sell this software and its -# documentation for any purpose is hereby granted without fee, provided that -# the above copyright notice appear in all copies and that both that -# copyright notice and this permission notice appear in supporting -# documentation, and that the name of M.I.T. not be used in advertising or -# publicity pertaining to distribution of the software without specific, -# written prior permission. M.I.T. makes no representations about the -# suitability of this software for any purpose. It is provided "as is" -# without express or implied warranty. -# -# Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it -# when there is no Makefile. -# -# This script is compatible with the BSD install script, but was written -# from scratch. It can only install one file at a time, a restriction -# shared with many OS's install programs. - - -# set DOITPROG to echo to test this script - -# Don't use :- since 4.3BSD and earlier shells don't like it. -doit="${DOITPROG-}" - - -# put in absolute paths if you don't have them in your path; or use env. vars. - -mvprog="${MVPROG-mv}" -cpprog="${CPPROG-cp}" -chmodprog="${CHMODPROG-chmod}" -chownprog="${CHOWNPROG-chown}" -chgrpprog="${CHGRPPROG-chgrp}" -stripprog="${STRIPPROG-strip}" -rmprog="${RMPROG-rm}" -mkdirprog="${MKDIRPROG-mkdir}" - -transformbasename="" -transform_arg="" -instcmd="$mvprog" -chmodcmd="$chmodprog 0755" -chowncmd="" -chgrpcmd="" -stripcmd="" -rmcmd="$rmprog -f" -mvcmd="$mvprog" -src="" -dst="" -dir_arg="" - -while [ x"$1" != x ]; do - case $1 in - -c) instcmd="$cpprog" - shift - continue;; - - -d) dir_arg=true - shift - continue;; - - -m) chmodcmd="$chmodprog $2" - shift - shift - continue;; - - -o) chowncmd="$chownprog $2" - shift - shift - continue;; - - -g) chgrpcmd="$chgrpprog $2" - shift - shift - continue;; - - -s) stripcmd="$stripprog" - shift - continue;; - - -t=*) transformarg=`echo $1 | sed 's/-t=//'` - shift - continue;; - - -b=*) transformbasename=`echo $1 | sed 's/-b=//'` - shift - continue;; - - *) if [ x"$src" = x ] - then - src=$1 - else - # this colon is to work around a 386BSD /bin/sh bug - : - dst=$1 - fi - shift - continue;; - esac -done - -if [ x"$src" = x ] -then - echo "install: no input file specified" - exit 1 -else - true -fi - -if [ x"$dir_arg" != x ]; then - dst=$src - src="" - - if [ -d $dst ]; then - instcmd=: - chmodcmd="" - else - instcmd=mkdir - fi -else - -# Waiting for this to be detected by the "$instcmd $src $dsttmp" command -# might cause directories to be created, which would be especially bad -# if $src (and thus $dsttmp) contains '*'. - - if [ -f $src -o -d $src ] - then - true - else - echo "install: $src does not exist" - exit 1 - fi - - if [ x"$dst" = x ] - then - echo "install: no destination specified" - exit 1 - else - true - fi - -# If destination is a directory, append the input filename; if your system -# does not like double slashes in filenames, you may need to add some logic - - if [ -d $dst ] - then - dst="$dst"/`basename $src` - else - true - fi -fi - -## this sed command emulates the dirname command -dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` - -# Make sure that the destination directory exists. -# this part is taken from Noah Friedman's mkinstalldirs script - -# Skip lots of stat calls in the usual case. -if [ ! -d "$dstdir" ]; then -defaultIFS=' -' -IFS="${IFS-${defaultIFS}}" - -oIFS="${IFS}" -# Some sh's can't handle IFS=/ for some reason. -IFS='%' -set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'` -IFS="${oIFS}" - -pathcomp='' - -while [ $# -ne 0 ] ; do - pathcomp="${pathcomp}${1}" - shift - - if [ ! -d "${pathcomp}" ] ; - then - $mkdirprog "${pathcomp}" - else - true - fi - - pathcomp="${pathcomp}/" -done -fi - -if [ x"$dir_arg" != x ] -then - $doit $instcmd $dst && - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi -else - -# If we're going to rename the final executable, determine the name now. - - if [ x"$transformarg" = x ] - then - dstfile=`basename $dst` - else - dstfile=`basename $dst $transformbasename | - sed $transformarg`$transformbasename - fi - -# don't allow the sed command to completely eliminate the filename - - if [ x"$dstfile" = x ] - then - dstfile=`basename $dst` - else - true - fi - -# Make a temp file name in the proper directory. - - dsttmp=$dstdir/#inst.$$# - -# Move or copy the file name to the temp name - - $doit $instcmd $src $dsttmp && - - trap "rm -f ${dsttmp}" 0 && - -# and set any options; do chmod last to preserve setuid bits - -# If any of these fail, we abort the whole thing. If we want to -# ignore errors from any of these, just make sure not to ignore -# errors from the above "$doit $instcmd $src $dsttmp" command. - - if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi && - if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi && - if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi && - if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi && - -# Now rename the file to the real destination. - - $doit $rmcmd -f $dstdir/$dstfile && - $doit $mvcmd $dsttmp $dstdir/$dstfile - -fi && - - -exit 0 diff --git a/ciderpress/nufxlib/mkinstalldirs b/ciderpress/nufxlib/mkinstalldirs deleted file mode 100644 index 936cf34..0000000 --- a/ciderpress/nufxlib/mkinstalldirs +++ /dev/null @@ -1,34 +0,0 @@ -#! /bin/sh -# mkinstalldirs --- make directory hierarchy -# Author: Noah Friedman -# Created: 1993-05-16 -# Last modified: 1995-03-05 -# Public domain - -errstatus=0 - -for file in ${1+"$@"} ; do - set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` - shift - - pathcomp= - for d in ${1+"$@"} ; do - pathcomp="$pathcomp$d" - case "$pathcomp" in - -* ) pathcomp=./$pathcomp ;; - esac - - if test ! -d "$pathcomp"; then - echo "mkdir $pathcomp" 1>&2 - mkdir "$pathcomp" > /dev/null 2>&1 || lasterr=$? - fi - - if test ! -d "$pathcomp"; then - errstatus=$lasterr - fi - - pathcomp="$pathcomp/" - done -done - -exit $errstatus diff --git a/ciderpress/nufxlib/nufxlib.def b/ciderpress/nufxlib/nufxlib.def deleted file mode 100644 index ad33e3e..0000000 --- a/ciderpress/nufxlib/nufxlib.def +++ /dev/null @@ -1,69 +0,0 @@ -; NufxLib library exported symbols -; -; This is redundant with the __declspec(dllexport) declarations enabled -; with the NUFXLIB_EXPORTS symbol. If we're just building a DLL, there's -; no need for this file. However, the objects we're building are used for -; both the static library and the dynamic library, and we don't want to -; have exports in the static library. If we do, the linker will create -; .lib and .exp files for every executable we link against it. This is -; mostly harmless, but a tad messy. I don't expect the interface to change, -; so there's not much of a maintenance burden here. -; -EXPORTS - NuAbort - NuAddFile - NuAddRecord - NuAddThread - NuClose - NuContents - NuConvertMORToUNI - NuConvertUNIToMOR - NuCreateDataSinkForBuffer - NuCreateDataSinkForFP - NuCreateDataSinkForFile - NuCreateDataSourceForBuffer - NuCreateDataSourceForFP - NuCreateDataSourceForFile - NuDataSinkGetOutCount - NuDataSourceSetRawCrc - NuDebugDumpArchive - NuDelete - NuDeleteRecord - NuDeleteThread - NuExtract - NuExtractRecord - NuExtractThread - NuFlush - NuFreeDataSink - NuFreeDataSource - NuGetAttr - NuGetExtraData - NuGetMasterHeader - NuGetRecord - NuGetRecordIdxByName - NuGetRecordIdxByPosition - NuGetValue - NuGetVersion - NuIsPresizedThreadID - NuOpenRO - NuOpenRW - NuRecordCopyAttr - NuRecordCopyThreads - NuRecordGetNumThreads - NuRename - NuSetErrorHandler - NuSetErrorMessageHandler - NuSetExtraData - NuSetGlobalErrorMessageHandler - NuSetOutputPathnameFilter - NuSetProgressUpdater - NuSetRecordAttr - NuSetSelectionFilter - NuSetValue - NuStrError - NuStreamOpenRO - NuTest - NuTestFeature - NuTestRecord - NuThreadGetByIdx - NuUpdatePresizedThread diff --git a/ciderpress/nufxlib/nufxlib.vcxproj b/ciderpress/nufxlib/nufxlib.vcxproj deleted file mode 100644 index 4f6f815..0000000 --- a/ciderpress/nufxlib/nufxlib.vcxproj +++ /dev/null @@ -1,118 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {C48AE53B-3DCB-43B1-9207-B7C5B6BB78AF} - Win32Proj - nufxlib - - - - DynamicLibrary - true - v143 - Unicode - Dynamic - - - DynamicLibrary - false - v143 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_WINDOWS;_USRDLL;NUFXLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - - - Windows - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_WINDOWS;_USRDLL;NUFXLIB_EXPORTS;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {b66109f4-217b-43c0-86aa-eb55657e5ac0} - - - - - - \ No newline at end of file diff --git a/ciderpress/nufxlib/nufxlib.vcxproj.filters b/ciderpress/nufxlib/nufxlib.vcxproj.filters deleted file mode 100644 index 816c720..0000000 --- a/ciderpress/nufxlib/nufxlib.vcxproj.filters +++ /dev/null @@ -1,102 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/ciderpress/nufxlib/samples/Common.h b/ciderpress/nufxlib/samples/Common.h deleted file mode 100644 index 2391747..0000000 --- a/ciderpress/nufxlib/samples/Common.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Common functions for NuLib tests. - */ -#ifndef NUFXLIB_SAMPLES_COMMON_H -#define NUFXLIB_SAMPLES_COMMON_H - -#include "SysDefs.h" /* might as well draft off the autoconf */ -#include "NufxLib.h" - -#ifdef USE_DMALLOC -# include "dmalloc.h" -#endif - -#define NELEM(x) (sizeof(x) / sizeof((x)[0])) - -#ifndef __cplusplus - #define false 0 - #define true (!false) -#endif - - -#ifdef FOPEN_WANTS_B -# define kNuFileOpenReadOnly "rb" -# define kNuFileOpenReadWrite "r+b" -# define kNuFileOpenWriteTrunc "wb" -# define kNuFileOpenReadWriteCreat "w+b" -#else -# define kNuFileOpenReadOnly "r" -# define kNuFileOpenReadWrite "r+" -# define kNuFileOpenWriteTrunc "w" -# define kNuFileOpenReadWriteCreat "w+" -#endif - - -/* - * Figure out what path separator to use. - * - * NOTE: recent versions of Win32 will also accept '/'. - */ - -#ifdef MSDOS -# define PATH_SEP '\\' -#endif - -#ifdef WIN32 -# define PATH_SEP '\\' -#endif - -#ifdef MACOS -# define PATH_SEP ':' -#endif - -#if defined(APW) || defined(__ORCAC__) -# define PATH_SEP ':' -#endif - -#ifndef PATH_SEP -# define PATH_SEP '/' -#endif - -#endif /*NUFXLIB_SAMPLES_COMMON_H*/ diff --git a/ciderpress/nufxlib/samples/Exerciser.c b/ciderpress/nufxlib/samples/Exerciser.c deleted file mode 100644 index 3ede796..0000000 --- a/ciderpress/nufxlib/samples/Exerciser.c +++ /dev/null @@ -1,1365 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * NufxLib exerciser. Most library functions can be invoked directly from - * the exerciser command line. - * - * This was written in C++ to evaluate the interaction between NufxLib and - * the C++ language, i.e. to make sure that all type definitions and - * function calls can be used without giving the compiler fits. This - * file will compile as either "Exerciser.c" or "Exerciser.cpp". - */ -#include "NufxLib.h" -#include "Common.h" -#include - -/* not portable to other OSs, but not all that important anyway */ -const char kFssep = PATH_SEP; - -/* ProDOS access permissions */ -#define kUnlocked 0xe3 - -#define kTempFile "exer-temp" - -#ifndef HAVE_STRCASECMP -static int strcasecmp(const char *str1, const char *str2) -{ - while (*str1 && *str2 && toupper(*str1) == toupper(*str2)) - str1++, str2++; - return (toupper(*str1) - toupper(*str2)); -} -#endif - - -/* - * =========================================================================== - * ExerciserState object - * =========================================================================== - */ - -/* - * Exerciser state. - * - * In case it isn't immediately apparent, this was written in C++ and - * then converted back to C. - */ -typedef struct ExerciserState { - NuArchive* pArchive; - char* archivePath; - const char* archiveFile; -} ExerciserState; - - -ExerciserState* ExerciserState_New(void) -{ - ExerciserState* pExerState; - - pExerState = (ExerciserState*) malloc(sizeof(*pExerState)); - if (pExerState == NULL) - return NULL; - - pExerState->pArchive = NULL; - pExerState->archivePath = NULL; - pExerState->archiveFile = NULL; - - return pExerState; -} - -void ExerciserState_Free(ExerciserState* pExerState) -{ - if (pExerState == NULL) - return; - - if (pExerState->pArchive != NULL) { - printf("Exerciser: aborting open archive\n"); - (void) NuAbort(pExerState->pArchive); - (void) NuClose(pExerState->pArchive); - } - if (pExerState->archivePath != NULL) - free(pExerState->archivePath); - - free(pExerState); -} - -inline NuArchive* ExerciserState_GetNuArchive(const ExerciserState* pExerState) -{ - return pExerState->pArchive; -} - -inline void ExerciserState_SetNuArchive(ExerciserState* pExerState, - NuArchive* newArchive) -{ - pExerState->pArchive = newArchive; -} - -inline char* ExerciserState_GetArchivePath(const ExerciserState* pExerState) -{ - return pExerState->archivePath; -} - -inline void ExerciserState_SetArchivePath(ExerciserState* pExerState, - char* newPath) -{ - if (pExerState->archivePath != NULL) - free(pExerState->archivePath); - - if (newPath == NULL) { - pExerState->archivePath = NULL; - pExerState->archiveFile = NULL; - } else { - pExerState->archivePath = strdup(newPath); - pExerState->archiveFile = strrchr(newPath, kFssep); - if (pExerState->archiveFile != NULL) - pExerState->archiveFile++; - - if (pExerState->archiveFile == NULL || *pExerState->archiveFile == '\0') - pExerState->archiveFile = pExerState->archivePath; - } -} - -inline const char* ExerciserState_GetArchiveFile(const ExerciserState* pExerState) -{ - if (pExerState->archiveFile == NULL) - return "[no archive open]"; - else - return pExerState->archiveFile; -} - - -/* - * =========================================================================== - * Utility functions - * =========================================================================== - */ - -/* - * NuContents callback function. Print the contents of an individual record. - */ -NuResult PrintEntry(NuArchive* pArchive, void* vpRecord) -{ - const NuRecord* pRecord = (const NuRecord*) vpRecord; - int idx; - - (void)pArchive; /* shut up, gcc */ - - printf("RecordIdx %u: '%s'\n", - pRecord->recordIdx, pRecord->filenameMOR); - - for (idx = 0; idx < (int) pRecord->recTotalThreads; idx++) { - const NuThread* pThread; - NuThreadID threadID; - const char* threadLabel; - - pThread = NuGetThread(pRecord, idx); - assert(pThread != NULL); - - threadID = NuGetThreadID(pThread); - switch (NuThreadIDGetClass(threadID)) { - case kNuThreadClassMessage: - threadLabel = "message class"; - break; - case kNuThreadClassControl: - threadLabel = "control class"; - break; - case kNuThreadClassData: - threadLabel = "data class"; - break; - case kNuThreadClassFilename: - threadLabel = "filename class"; - break; - default: - threadLabel = "(unknown class)"; - break; - } - - switch (threadID) { - case kNuThreadIDComment: - threadLabel = "comment"; - break; - case kNuThreadIDIcon: - threadLabel = "icon"; - break; - case kNuThreadIDMkdir: - threadLabel = "mkdir"; - break; - case kNuThreadIDDataFork: - threadLabel = "data fork"; - break; - case kNuThreadIDDiskImage: - threadLabel = "disk image"; - break; - case kNuThreadIDRsrcFork: - threadLabel = "rsrc fork"; - break; - case kNuThreadIDFilename: - threadLabel = "filename"; - break; - default: - break; - } - - printf(" ThreadIdx %u - 0x%08x (%s)\n", pThread->threadIdx, - threadID, threadLabel); - } - - return kNuOK; -} - - -#define kNiceLineLen 256 - -/* - * Get a line of input, stripping the '\n' off the end. - */ -static NuError GetLine(const char* prompt, char* buffer, int bufferSize) -{ - printf("%s> ", prompt); - fflush(stdout); - - if (fgets(buffer, bufferSize, stdin) == NULL) - return kNuErrGeneric; - - if (buffer[strlen(buffer)-1] == '\n') - buffer[strlen(buffer)-1] = '\0'; - - return kNuErrNone; -} - - -/* - * Selection filter for mass "extract" and "delete" operations. - */ -NuResult SelectionFilter(NuArchive* pArchive, void* vselFilt) -{ - const NuSelectionProposal* selProposal = (NuSelectionProposal*) vselFilt; - char buffer[8]; - - printf("%s (N/y)? ", selProposal->pRecord->filenameMOR); - fflush(stdout); - - if (fgets(buffer, sizeof(buffer), stdin) == NULL) - return kNuAbort; - - if (tolower(buffer[0]) == 'y') - return kNuOK; - else - return kNuSkip; -} - - -/* - * General-purpose error handler. - */ -NuResult ErrorHandler(NuArchive* pArchive, void* vErrorStatus) -{ - const NuErrorStatus* pErrorStatus = (const NuErrorStatus*) vErrorStatus; - char buffer[8]; - NuResult result = kNuSkip; - - printf("Exerciser: error handler op=%d err=%d sysErr=%d message='%s'\n" - "\tfilename='%s' '%c'(0x%02x)\n", - pErrorStatus->operation, pErrorStatus->err, pErrorStatus->sysErr, - pErrorStatus->message == NULL ? "(NULL)" : pErrorStatus->message, - pErrorStatus->pathnameUNI, pErrorStatus->filenameSeparator, - pErrorStatus->filenameSeparator); - printf("\tValid options are:"); - if (pErrorStatus->canAbort) - printf(" a)bort"); - if (pErrorStatus->canRetry) - printf(" r)etry"); - if (pErrorStatus->canIgnore) - printf(" i)gnore"); - if (pErrorStatus->canSkip) - printf(" s)kip"); - if (pErrorStatus->canRename) - printf(" re)name"); - if (pErrorStatus->canOverwrite) - printf(" o)verwrite"); - putc('\n', stdout); - - printf("Return what (a/r/i/s/e/o)? "); - fflush(stdout); - - if (fgets(buffer, sizeof(buffer), stdin) == NULL) { - printf("Returning kNuSkip\n"); - } else switch (buffer[0]) { - case 'a': result = kNuAbort; break; - case 'r': result = kNuRetry; break; - case 'i': result = kNuIgnore; break; - case 's': result = kNuSkip; break; - case 'e': result = kNuRename; break; - case 'o': result = kNuOverwrite; break; - default: - printf("Unknown value '%c', returning kNuSkip\n", buffer[0]); - break; - } - - return result; -} - -/* - * This gets called when a buffer DataSource is no longer needed. - */ -NuResult FreeCallback(NuArchive* pArchive, void* args) -{ - free(args); - return kNuOK; -} - - -/* - * =========================================================================== - * Command handlers - * =========================================================================== - */ - -typedef NuError (*CommandFunc)(ExerciserState* pState, int argc, - char** argv); - -static NuError HelpFunc(ExerciserState* pState, int argc, char** argv); - -#if 0 -static NuError -GenericFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - printf("Generic! argc=%d\n", argc); - return kNuErrNone; -} -#endif - -/* - * Do nothing. Useful when the user just hits on a blank line. - */ -static NuError NothingFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - return kNuErrNone; -} - -/* - * q - quit - * - * Do nothing. This is used as a trigger for quitting the program. In - * practice, we catch this earlier, and won't actually call here. - */ -static NuError QuitFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(0); - return kNuErrNone; -} - - - -/* - * ab - abort current changes - */ -static NuError AbortFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - return NuAbort(ExerciserState_GetNuArchive(pState)); -} - -/* - * af - add file to archive - */ -static NuError AddFileFunc(ExerciserState* pState, int argc, char** argv) -{ - NuFileDetails nuFileDetails; - int fromRsrc = false; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 3); - - if (strcasecmp(argv[2], "true") == 0) { - fromRsrc = true; - } else if (strcasecmp(argv[2], "false") != 0) { - fprintf(stderr, "WARNING: fromRsrc should be 'true' or 'false'\n"); - /* ignore */ - } - - memset(&nuFileDetails, 0, sizeof(nuFileDetails)); - if (fromRsrc) { - nuFileDetails.threadID = kNuThreadIDRsrcFork; - } else { - nuFileDetails.threadID = kNuThreadIDDataFork; - } - nuFileDetails.storageNameMOR = argv[1]; - nuFileDetails.fileSysID = kNuFileSysUnknown; - nuFileDetails.fileSysInfo = (short) kFssep; - nuFileDetails.access = kUnlocked; - /* fileType, extraType, storageType, dates */ - - return NuAddFile(ExerciserState_GetNuArchive(pState), argv[1], - &nuFileDetails, fromRsrc, NULL); -} - -/* - * ar - add an empty record - */ -static NuError AddRecordFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - NuRecordIdx recordIdx; - NuFileDetails nuFileDetails; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - memset(&nuFileDetails, 0, sizeof(nuFileDetails)); - nuFileDetails.threadID = 0; /* irrelevant */ - nuFileDetails.storageNameMOR = argv[1]; - nuFileDetails.fileSysID = kNuFileSysUnknown; - nuFileDetails.fileSysInfo = (short) kFssep; - nuFileDetails.access = kUnlocked; - /* fileType, extraType, storageType, dates */ - - err = NuAddRecord(ExerciserState_GetNuArchive(pState), - &nuFileDetails, &recordIdx); - if (err == kNuErrNone) - printf("Exerciser: success, new recordIdx=%u\n", recordIdx); - return err; -} - -/* - * at - add thread to record - */ -static NuError AddThreadFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - NuDataSource* pDataSource = NULL; - char* lineBuf = NULL; - long ourLen, maxLen; - NuThreadID threadID; - NuThreadIdx threadIdx; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 3); - - lineBuf = (char*)malloc(kNiceLineLen); - assert(lineBuf != NULL); - - threadID = strtol(argv[2], NULL, 0); - if (NuThreadIDGetClass(threadID) == kNuThreadClassData) { - /* load data from a file on disk */ - maxLen = 0; - err = GetLine("Enter filename", lineBuf, kNiceLineLen); - if (err != kNuErrNone) - goto bail; - if (!lineBuf[0]) { - fprintf(stderr, "Invalid filename\n"); - err = kNuErrInvalidArg; - goto bail; - } - - err = NuCreateDataSourceForFile(kNuThreadFormatUncompressed, - 0, lineBuf, false, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "Exerciser: file data source create failed (err=%d)\n", err); - goto bail; - } - } else { - if (threadID == kNuThreadIDFilename || threadID == kNuThreadIDComment) { - /* select the buffer pre-size */ - err = GetLine("Enter max buffer size", lineBuf, kNiceLineLen); - if (err != kNuErrNone) - goto bail; - maxLen = strtol(lineBuf, NULL, 0); - if (maxLen <= 0) { - fprintf(stderr, "Bad length\n"); - err = kNuErrInvalidArg; - goto bail; - } - } else { - maxLen = 0; - } - - err = GetLine("Enter the thread contents", lineBuf, kNiceLineLen); - if (err != kNuErrNone) - goto bail; - ourLen = strlen(lineBuf); - - /* create a data source from the buffer */ - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - maxLen, (uint8_t*)lineBuf, 0, ourLen, FreeCallback, - &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "Exerciser: buffer data source create failed (err=%d)\n", err); - goto bail; - } - lineBuf = NULL; /* now owned by the library */ - } - - - err = NuAddThread(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), threadID, pDataSource, &threadIdx); - if (err == kNuErrNone) { - pDataSource = NULL; /* library owns it now */ - printf("Exerciser: success; function returned threadIdx=%u\n", - threadIdx); - } - -bail: - NuFreeDataSource(pDataSource); - if (lineBuf != NULL) - free(lineBuf); - return err; -} - -/* - * cl - close archive - */ -static NuError CloseFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - err = NuClose(ExerciserState_GetNuArchive(pState)); - if (err == kNuErrNone) { - ExerciserState_SetNuArchive(pState, NULL); - ExerciserState_SetArchivePath(pState, NULL); - } - - return err; -} - -/* - * d - delete all records (selection-filtered) - */ -static NuError DeleteFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - NuSetSelectionFilter(ExerciserState_GetNuArchive(pState), SelectionFilter); - - return NuDelete(ExerciserState_GetNuArchive(pState)); -} - -/* - * dr - delete record - */ -static NuError DeleteRecordFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - return NuDeleteRecord(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0)); -} - -/* - * dt - delete thread - */ -static NuError DeleteThreadFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - return NuDeleteThread(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0)); -} - -/* - * e - extract all files (selection-filtered) - */ -static NuError ExtractFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - NuSetSelectionFilter(ExerciserState_GetNuArchive(pState), SelectionFilter); - - return NuExtract(ExerciserState_GetNuArchive(pState)); -} - -/* - * er - extract record - */ -static NuError ExtractRecordFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - return NuExtractRecord(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0)); -} - -/* - * et - extract thread - */ -static NuError ExtractThreadFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - NuDataSink* pDataSink = NULL; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 3); - - err = NuCreateDataSinkForFile(true, kNuConvertOff, argv[2], kFssep, - &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "Exerciser: data sink create failed\n"); - goto bail; - } - - err = NuExtractThread(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), pDataSink); - /* fall through with err */ - -bail: - NuFreeDataSink(pDataSink); - return err; -} - -/* - * fl - flush changes to archive - */ -static NuError FlushFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - uint32_t flushStatus; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - err = NuFlush(ExerciserState_GetNuArchive(pState), &flushStatus); - if (err != kNuErrNone) - printf("Exerciser: flush failed, status flags=0x%04x\n", flushStatus); - return err; -} - -/* - * gev - get value - * - * Currently takes numeric arguments. We could be nice and accept the - * things like "IgnoreCRC" for kNuValueIgnoreCRC, but not yet. - */ -static NuError GetValueFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - NuValue value; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - err = NuGetValue(ExerciserState_GetNuArchive(pState), - (NuValueID) strtol(argv[1], NULL, 0), &value); - if (err == kNuErrNone) - printf(" --> %u\n", value); - return err; -} - -/* - * gmh - get master header - */ -static NuError GetMasterHeaderFunc(ExerciserState* pState, int argc, - char** argv) -{ - NuError err; - const NuMasterHeader* pMasterHeader; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - err = NuGetMasterHeader(ExerciserState_GetNuArchive(pState), - &pMasterHeader); - if (err == kNuErrNone) { - printf("Exerciser: success (version=%u, totalRecords=%u, EOF=%u)\n", - pMasterHeader->mhMasterVersion, pMasterHeader->mhTotalRecords, - pMasterHeader->mhMasterEOF); - } - return err; -} - -/* - * gr - get record attributes - */ -static NuError GetRecordFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - const NuRecord* pRecord; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - err = NuGetRecord(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), &pRecord); - if (err == kNuErrNone) { - printf("Exerciser: success, call returned:\n"); - printf("\tfileSysID : %d\n", pRecord->recFileSysID); - printf("\tfileSysInfo : 0x%04x ('%c')\n", pRecord->recFileSysInfo, - NuGetSepFromSysInfo(pRecord->recFileSysInfo)); - printf("\taccess : 0x%02x\n", pRecord->recAccess); - printf("\tfileType : 0x%04x\n", pRecord->recFileType); - printf("\textraType : 0x%04x\n", pRecord->recExtraType); - printf("\tcreateWhen : ...\n"); - printf("\tmodWhen : ...\n"); /* too lazy */ - printf("\tarchiveWhen : ...\n"); - } - return err; -} - -/* - * grin - get record idx by name - */ -static NuError GetRecordIdxByNameFunc(ExerciserState* pState, int argc, - char** argv) -{ - NuError err; - NuRecordIdx recIdx; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - err = NuGetRecordIdxByName(ExerciserState_GetNuArchive(pState), - argv[1], &recIdx); - if (err == kNuErrNone) - printf("Exerciser: success, returned recordIdx=%u\n", recIdx); - return err; -} - -/* - * grip - get record idx by position - */ -static NuError GetRecordIdxByPositionFunc(ExerciserState* pState, int argc, - char** argv) -{ - NuError err; - NuRecordIdx recIdx; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - err = NuGetRecordIdxByPosition(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), &recIdx); - if (err == kNuErrNone) - printf("Exerciser: success, returned recordIdx=%u\n", recIdx); - return err; -} - -/* - * ocrw - open/create read-write - */ -static NuError OpenCreateReadWriteFunc(ExerciserState* pState, int argc, - char** argv) -{ - NuError err; - NuArchive* pArchive; - - assert(ExerciserState_GetNuArchive(pState) == NULL); - assert(argc == 2); - - err = NuOpenRW(argv[1], kTempFile, kNuOpenCreat|kNuOpenExcl, &pArchive); - if (err == kNuErrNone) { - ExerciserState_SetNuArchive(pState, pArchive); - ExerciserState_SetArchivePath(pState, argv[1]); - } - - return err; -} - -/* - * oro - open read-only - */ -static NuError OpenReadOnlyFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - NuArchive* pArchive; - - assert(ExerciserState_GetNuArchive(pState) == NULL); - assert(argc == 2); - - err = NuOpenRO(argv[1], &pArchive); - if (err == kNuErrNone) { - ExerciserState_SetNuArchive(pState, pArchive); - ExerciserState_SetArchivePath(pState, argv[1]); - } - - return err; -} - -/* - * ors - open streaming read-only - */ -static NuError OpenStreamingReadOnlyFunc(ExerciserState* pState, int argc, - char** argv) -{ - NuError err; - NuArchive* pArchive; - FILE* fp = NULL; - - assert(ExerciserState_GetNuArchive(pState) == NULL); - assert(argc == 2); - - if ((fp = fopen(argv[1], kNuFileOpenReadOnly)) == NULL) { - err = errno ? (NuError)errno : kNuErrGeneric; - fprintf(stderr, "Exerciser: unable to open '%s'\n", argv[1]); - } else { - err = NuStreamOpenRO(fp, &pArchive); - if (err == kNuErrNone) { - ExerciserState_SetNuArchive(pState, pArchive); - ExerciserState_SetArchivePath(pState, argv[1]); - fp = NULL; - } - } - - if (fp != NULL) - fclose(fp); - - return err; -} - -/* - * orw - open read-write - */ -static NuError OpenReadWriteFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - NuArchive* pArchive; - - assert(ExerciserState_GetNuArchive(pState) == NULL); - assert(argc == 2); - - err = NuOpenRW(argv[1], kTempFile, 0, &pArchive); - if (err == kNuErrNone) { - ExerciserState_SetNuArchive(pState, pArchive); - ExerciserState_SetArchivePath(pState, argv[1]); - } - - return err; -} - -/* - * p - print - */ -static NuError PrintFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - return NuContents(ExerciserState_GetNuArchive(pState), PrintEntry); -} - -/* - * pd - print debug - */ -static NuError PrintDebugFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - return NuDebugDumpArchive(ExerciserState_GetNuArchive(pState)); -} - -/* - * re - rename record - */ -static NuError RenameFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 4); - - return NuRename(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), argv[2], argv[3][0]); -} - -/* - * sec - set error callback - * - * Use an error handler callback. - */ -static NuError SetErrorCallbackFunc(ExerciserState* pState, int argc, - char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - NuSetErrorHandler(ExerciserState_GetNuArchive(pState), ErrorHandler); - return kNuErrNone; -} - -/* - * sev - set value - * - * Currently takes numeric arguments. - */ -static NuError SetValueFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 3); - - return NuSetValue(ExerciserState_GetNuArchive(pState), - (NuValueID) strtol(argv[1], NULL, 0), strtol(argv[2], NULL, 0)); -} - -/* - * sra - set record attributes - * - * Right now I'm only allowing changes to file type and aux type. This - * could be adapted to do more easily, but the command handler has a - * rigid notion of how many arguments each function should have, so - * you'd need to list all of them every time. - */ -static NuError SetRecordAttrFunc(ExerciserState* pState, int argc, char** argv) -{ - NuError err; - const NuRecord* pRecord; - NuRecordAttr recordAttr; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 4); - - err = NuGetRecord(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), &pRecord); - if (err != kNuErrNone) - return err; - printf("Exerciser: NuGetRecord succeeded, calling NuSetRecordAttr\n"); - NuRecordCopyAttr(&recordAttr, pRecord); - recordAttr.fileType = strtol(argv[2], NULL, 0); - recordAttr.extraType = strtol(argv[3], NULL, 0); - /*recordAttr.fileSysInfo = ':';*/ - return NuSetRecordAttr(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), &recordAttr); -} - -/* - * t - test archive - */ -static NuError TestFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 1); - - return NuTest(ExerciserState_GetNuArchive(pState)); -} - -/* - * tr - test record - */ -static NuError TestRecordFunc(ExerciserState* pState, int argc, char** argv) -{ - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - return NuTestRecord(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0)); -} - -/* - * upt - update pre-sized thread - */ -static NuError UpdatePresizedThreadFunc(ExerciserState* pState, int argc, - char** argv) -{ - NuError err; - NuDataSource* pDataSource = NULL; - char* lineBuf = NULL; - long ourLen; - int32_t maxLen; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - assert(ExerciserState_GetNuArchive(pState) != NULL); - assert(argc == 2); - - lineBuf = (char*)malloc(kNiceLineLen); - assert(lineBuf != NULL); - err = GetLine("Enter data for thread", lineBuf, kNiceLineLen); - if (err != kNuErrNone) - goto bail; - - ourLen = strlen(lineBuf); - - /* use "ourLen" for both buffer len and data len */ - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - ourLen, (uint8_t*)lineBuf, 0, ourLen, FreeCallback, - &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, "Exerciser: data source create failed (err=%d)\n", - err); - goto bail; - } - lineBuf = NULL; /* now owned by the library */ - - err = NuUpdatePresizedThread(ExerciserState_GetNuArchive(pState), - strtol(argv[1], NULL, 0), pDataSource, &maxLen); - if (err == kNuErrNone) - printf("Exerciser: success; function returned maxLen=%d\n", maxLen); - -bail: - NuFreeDataSource(pDataSource); - if (lineBuf != NULL) - free(lineBuf); - return err; -} - - -/* - * Command table. This drives the user interface. - */ - -/* flags for the CommandTable */ -#define kFlagArchiveReq (1L) /* must have archive open */ -#define kFlagNoArchiveReq (1L<<1) /* must NOT have archive open */ - -/* command set */ -static const struct { - const char* commandStr; - CommandFunc func; - int expectedArgCount; - const char* argumentList; - uint32_t flags; - const char* description; -} gCommandTable[] = { - { "--- exerciser commands ---", HelpFunc, 0, "", 0, - "" }, - { "?", HelpFunc, 0, "", 0, - "Show help" }, - { "h", HelpFunc, 0, "", 0, - "Show help" }, - { "q", QuitFunc, 0, "", 0, - "Quit program (will abort un-flushed changes)" }, - - { "--- archive commands ---", HelpFunc, 0, "", 0, - "" }, - - { "ab", AbortFunc, 0, "", kFlagArchiveReq, - "Abort current changes" }, - { "af", AddFileFunc, 2, "filename fromRsrc", kFlagArchiveReq, - "Add file" }, - { "ar", AddRecordFunc, 1, "storageName", kFlagArchiveReq, - "Add record" }, - { "at", AddThreadFunc, 2, "recordIdx threadID", kFlagArchiveReq, - "Add thread to record" }, - { "cl", CloseFunc, 0, "", kFlagArchiveReq, - "Close archive after flushing any changes" }, - { "d", DeleteFunc, 0, "", kFlagArchiveReq, - "Delete all records" }, - { "dr", DeleteRecordFunc, 1, "recordIdx", kFlagArchiveReq, - "Delete record" }, - { "dt", DeleteThreadFunc, 1, "threadIdx", kFlagArchiveReq, - "Delete thread" }, - { "e", ExtractFunc, 0, "", kFlagArchiveReq, - "Extract all files" }, - { "er", ExtractRecordFunc, 1, "recordIdx", kFlagArchiveReq, - "Extract record" }, - { "et", ExtractThreadFunc, 2, "threadIdx filename", kFlagArchiveReq, - "Extract thread" }, - { "fl", FlushFunc, 0, "", kFlagArchiveReq, - "Flush changes" }, - { "gev", GetValueFunc, 1, "ident", kFlagArchiveReq, - "Get value" }, - { "gmh", GetMasterHeaderFunc, 0, "", kFlagArchiveReq, - "Get master header" }, - { "gr", GetRecordFunc, 1, "recordIdx", kFlagArchiveReq, - "Get record" }, - { "grin", GetRecordIdxByNameFunc, 1, "name", kFlagArchiveReq, - "Get recordIdx by name" }, - { "grip", GetRecordIdxByPositionFunc, 1, "position", kFlagArchiveReq, - "Get recordIdx by position" }, - { "ocrw", OpenCreateReadWriteFunc, 1, "filename", kFlagNoArchiveReq, - "Open/create archive read-write" }, - { "oro", OpenReadOnlyFunc, 1, "filename", kFlagNoArchiveReq, - "Open archive read-only" }, - { "ors", OpenStreamingReadOnlyFunc, 1, "filename", kFlagNoArchiveReq, - "Open archive streaming read-only" }, - { "orw", OpenReadWriteFunc, 1, "filename", kFlagNoArchiveReq, - "Open archive read-write" }, - { "p", PrintFunc, 0, "", kFlagArchiveReq, - "Print archive contents" }, - { "pd", PrintDebugFunc, 0, "", kFlagArchiveReq, - "Print debugging output (if available)" }, - { "re", RenameFunc, 3, "recordIdx name sep", kFlagArchiveReq, - "Rename record" }, - { "sec", SetErrorCallbackFunc, 0, "", kFlagArchiveReq, - "Set error callback" }, - { "sev", SetValueFunc, 2, "ident value", kFlagArchiveReq, - "Set value" }, - { "sra", SetRecordAttrFunc, 3, "recordIdx type aux", kFlagArchiveReq, - "Set record attributes" }, - { "t", TestFunc, 0, "", kFlagArchiveReq, - "Test archive" }, - { "tr", TestRecordFunc, 1, "recordIdx", kFlagArchiveReq, - "Test record" }, - { "upt", UpdatePresizedThreadFunc, 1, "threadIdx", kFlagArchiveReq, - "Update pre-sized thread" }, -}; - -#define kMaxArgs 4 - -/* - * Display a summary of available commands. - */ -static NuError HelpFunc(ExerciserState* pState, int argc, char** argv) -{ - int i; - - (void) pState, (void) argc, (void) argv; /* shut up, gcc */ - - printf("\nAvailable commands:\n"); - for (i = 0; i < (int)NELEM(gCommandTable); i++) { - printf(" %-4s %-21s %s\n", - gCommandTable[i].commandStr, - gCommandTable[i].argumentList, - gCommandTable[i].description); - } - - return kNuErrNone; -} - - -/* - * =========================================================================== - * Control - * =========================================================================== - */ - -static const char* kWhitespace = " \t\n"; - -/* - * Parse a command from the user. - * - * "lineBuf" will be mangled. On success, "pFunc", "pArgc", and "pArgv" - * will receive the results. - */ -static NuError ParseLine(char* lineBuf, ExerciserState* pState, - CommandFunc* pFunc, int* pArgc, char*** pArgv) -{ - NuError err = kNuErrSyntax; - char* command; - char* cp; - int i; - - /* - * Parse the strings. - */ - - command = strtok(lineBuf, kWhitespace); - if (command == NULL) { - /* no command; the user probably just hit "enter" on a blank line */ - *pFunc = NothingFunc; - *pArgc = 0; - *pArgv = NULL; - err = kNuErrNone; - goto bail; - } - - /* no real need to be flexible; add 1 for command and one for NULL */ - *pArgv = (char**) malloc(sizeof(char*) * (kMaxArgs+2)); - (*pArgv)[0] = command; - *pArgc = 1; - - cp = strtok(NULL, kWhitespace); - while (cp != NULL) { - if (*pArgc >= kMaxArgs+1) { - printf("ERROR: too many arguments\n"); - goto bail; - } - (*pArgv)[*pArgc] = cp; - (*pArgc)++; - - cp = strtok(NULL, kWhitespace); - } - assert(*pArgc < kMaxArgs+2); - (*pArgv)[*pArgc] = NULL; - - /* - * Look up the command. - */ - for (i = 0; i < (int)NELEM(gCommandTable); i++) { - if (strcmp(command, gCommandTable[i].commandStr) == 0) - break; - } - if (i == NELEM(gCommandTable)) { - printf("ERROR: unrecognized command\n"); - goto bail; - } - - *pFunc = gCommandTable[i].func; - - /* - * Check arguments and flags. - */ - if (*pArgc -1 != gCommandTable[i].expectedArgCount) { - printf("ERROR: expected %d args, found %d\n", - gCommandTable[i].expectedArgCount, *pArgc -1); - goto bail; - } - - if (gCommandTable[i].flags & kFlagArchiveReq) { - if (ExerciserState_GetNuArchive(pState) == NULL) { - printf("ERROR: must have an archive open\n"); - goto bail; - } - } - if (gCommandTable[i].flags & kFlagNoArchiveReq) { - if (ExerciserState_GetNuArchive(pState) != NULL) { - printf("ERROR: an archive is already open\n"); - goto bail; - } - } - - /* - * Looks good! - */ - err = kNuErrNone; - -bail: - return err; -} - - -/* - * Interpret commands, do clever things. - */ -static NuError CommandLoop(void) -{ - NuError err = kNuErrNone; - ExerciserState* pState = ExerciserState_New(); - CommandFunc func; - char lineBuf[128]; - int argc; - char** argv = NULL; - - while (1) { - printf("\nEnter command (%s)> ", ExerciserState_GetArchiveFile(pState)); - fflush(stdout); - - if (fgets(lineBuf, sizeof(lineBuf), stdin) == NULL) { - printf("\n"); - break; - } - - if (argv != NULL) { - free(argv); - argv = NULL; - } - - func = NULL; /* sanity check */ - - err = ParseLine(lineBuf, pState, &func, &argc, &argv); - if (err != kNuErrNone) - continue; - - assert(func != NULL); - if (func == QuitFunc) - break; - - err = (*func)(pState, argc, argv); - - if (err < 0) - printf("Exerciser: received error %d (%s)\n", err, NuStrError(err)); - else if (err > 0) - printf("Exerciser: received error %d\n", err); - - if (argv != NULL) { - free(argv); - argv = NULL; - } - } - - if (ExerciserState_GetNuArchive(pState) != NULL) { - /* ought to query the archive before saying something like this... */ - printf("Exerciser: aborting any un-flushed changes in archive %s\n", - ExerciserState_GetArchivePath(pState)); - (void) NuAbort(ExerciserState_GetNuArchive(pState)); - err = NuClose(ExerciserState_GetNuArchive(pState)); - if (err != kNuErrNone) - printf("Exerciser: got error %d closing archive\n", err); - ExerciserState_SetNuArchive(pState, NULL); - } - - if (pState != NULL) - ExerciserState_Free(pState); - if (argv != NULL) - free(argv); - return kNuErrNone; -} - - -/* - * Main entry point. - * - * We don't currently take any arguments, so this is pretty straightforward. - */ -int main(void) -{ - NuError result; - int32_t majorVersion, minorVersion, bugVersion; - const char* nufxLibDate; - const char* nufxLibFlags; - - (void) NuGetVersion(&majorVersion, &minorVersion, &bugVersion, - &nufxLibDate, &nufxLibFlags); - printf("NufxLib exerciser, linked with NufxLib v%d.%d.%d [%s]\n\n", - majorVersion, minorVersion, bugVersion, nufxLibFlags); - printf("Use 'h' or '?' for help, 'q' to quit.\n"); - - /* stuff useful when debugging lots */ - if (unlink(kTempFile) == 0) - fprintf(stderr, "NOTE: whacked exer-temp\n"); - if (unlink("new.shk") == 0) - fprintf(stderr, "NOTE: whacked new.shk\n"); - -#if defined(HAS_MALLOC_CHECK_) && !defined(USE_DMALLOC) - /* - * This is really nice to have on Linux and any other system that - * uses the GNU libc/malloc stuff. It slows things down, but it - * tells you when you do something dumb with malloc/realloc/free. - * (Solaris 2.7 has a similar feature that is enabled by setting the - * environment variable LD_PRELOAD to include watchmalloc.so. Other - * OSs and 3rd-party malloc packages may have similar features.) - * - * This environment variable must be set when the program is launched. - * Tweaking the environment within the program has no effect. - * - * Now that the Linux world has "valgrind", this is probably - * unnecessary. - */ - { - char* debugSet = getenv("MALLOC_CHECK_"); - if (debugSet == NULL) - printf("WARNING: MALLOC_CHECK_ not enabled\n\n"); - } -#endif - - result = CommandLoop(); - - exit(result != kNuErrNone); -} - diff --git a/ciderpress/nufxlib/samples/ImgConv.c b/ciderpress/nufxlib/samples/ImgConv.c deleted file mode 100644 index d9624b2..0000000 --- a/ciderpress/nufxlib/samples/ImgConv.c +++ /dev/null @@ -1,642 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * 2IMG <-> SHK converter. This is a practical example of using the - * NufxLib Thread functions to add and extract data in the middle of a file. - * - * Conversions from 2IMG files do not work for raw nibble images. - * Conversions from SHK archives only work if the disk image is in the - * first record in the archive. (This is easy to fix, but I'm trying to - * keep it simple.) - */ -#include "NufxLib.h" -#include "Common.h" - -#ifndef HAVE_STRCASECMP -static int strcasecmp(const char *str1, const char *str2) -{ - while (*str1 && *str2 && toupper(*str1) == toupper(*str2)) - str1++, str2++; - return (toupper(*str1) - toupper(*str2)); -} -#endif - - -#define kTempFile "imgconv.tmp" -#define kLocalFssep PATH_SEP -#define false 0 -#define true (!false) - - -/* - * =========================================================================== - * 2IMG stuff - * =========================================================================== - */ - -#define kImgMagic "2IMG" -#define kMyCreator "NFXL" - -#define kImageFormatDOS 0 -#define kImageFormatProDOS 1 -#define kImageFormatNibble 2 - -/* - * 2IMG header definition (http://www.magnet.ch/emutech/Tech/). - */ -typedef struct ImgHeader { - char magic[4]; - char creator[4]; - uint16_t headerLen; - uint16_t version; - uint32_t imageFormat; - uint32_t flags; - uint32_t numBlocks; - uint32_t dataOffset; - uint32_t dataLen; - uint32_t cmntOffset; - uint32_t cmntLen; - uint32_t creatorOffset; - uint32_t creatorLen; - uint32_t spare[4]; -} ImgHeader; - -/* - * Read a two-byte little-endian value. - */ -void ReadShortLE(FILE* fp, uint16_t* pBuf) -{ - *pBuf = getc(fp); - *pBuf += (uint16_t) getc(fp) << 8; -} - -/* - * Write a two-byte little-endian value. - */ -void WriteShortLE(FILE* fp, uint16_t val) -{ - putc(val, fp); - putc(val >> 8, fp); -} - -/* - * Read a four-byte little-endian value. - */ -void ReadLongLE(FILE* fp, uint32_t* pBuf) -{ - *pBuf = getc(fp); - *pBuf += (uint32_t) getc(fp) << 8; - *pBuf += (uint32_t) getc(fp) << 16; - *pBuf += (uint32_t) getc(fp) << 24; -} - -/* - * Write a four-byte little-endian value. - */ -void WriteLongLE(FILE* fp, uint32_t val) -{ - putc(val, fp); - putc(val >> 8, fp); - putc(val >> 16, fp); - putc(val >> 24, fp); -} - -/* - * Read the header from a 2IMG file. - */ -int ReadImgHeader(FILE* fp, ImgHeader* pHeader) -{ - size_t ignored; - ignored = fread(pHeader->magic, 4, 1, fp); - ignored = fread(pHeader->creator, 4, 1, fp); - ReadShortLE(fp, &pHeader->headerLen); - ReadShortLE(fp, &pHeader->version); - ReadLongLE(fp, &pHeader->imageFormat); - ReadLongLE(fp, &pHeader->flags); - ReadLongLE(fp, &pHeader->numBlocks); - ReadLongLE(fp, &pHeader->dataOffset); - ReadLongLE(fp, &pHeader->dataLen); - ReadLongLE(fp, &pHeader->cmntOffset); - ReadLongLE(fp, &pHeader->cmntLen); - ReadLongLE(fp, &pHeader->creatorOffset); - ReadLongLE(fp, &pHeader->creatorLen); - ReadLongLE(fp, &pHeader->spare[0]); - ReadLongLE(fp, &pHeader->spare[1]); - ReadLongLE(fp, &pHeader->spare[2]); - ReadLongLE(fp, &pHeader->spare[3]); - - (void) ignored; - if (feof(fp) || ferror(fp)) - return -1; - - if (strncmp(pHeader->magic, kImgMagic, 4) != 0) { - fprintf(stderr, "ERROR: bad magic number on 2IMG file\n"); - return -1; - } - - if (pHeader->version > 1) { - fprintf(stderr, "WARNING: might not be able to handle version=%d\n", - pHeader->version); - } - - return 0; -} - -/* - * Write the header to a 2IMG file. - */ -int WriteImgHeader(FILE* fp, ImgHeader* pHeader) -{ - fwrite(pHeader->magic, 4, 1, fp); - fwrite(pHeader->creator, 4, 1, fp); - WriteShortLE(fp, pHeader->headerLen); - WriteShortLE(fp, pHeader->version); - WriteLongLE(fp, pHeader->imageFormat); - WriteLongLE(fp, pHeader->flags); - WriteLongLE(fp, pHeader->numBlocks); - WriteLongLE(fp, pHeader->dataOffset); - WriteLongLE(fp, pHeader->dataLen); - WriteLongLE(fp, pHeader->cmntOffset); - WriteLongLE(fp, pHeader->cmntLen); - WriteLongLE(fp, pHeader->creatorOffset); - WriteLongLE(fp, pHeader->creatorLen); - WriteLongLE(fp, pHeader->spare[0]); - WriteLongLE(fp, pHeader->spare[1]); - WriteLongLE(fp, pHeader->spare[2]); - WriteLongLE(fp, pHeader->spare[3]); - - if (ferror(fp)) - return -1; - - return 0; -} - - -/* - * Dump the contents of an ImgHeader. - */ -void DumpImgHeader(ImgHeader* pHeader) -{ - printf("--- header contents:\n"); - printf("\tmagic = '%.4s'\n", pHeader->magic); - printf("\tcreator = '%.4s'\n", pHeader->creator); - printf("\theaderLen = %d\n", pHeader->headerLen); - printf("\tversion = %d\n", pHeader->version); - printf("\timageFormat = %u\n", pHeader->imageFormat); - printf("\tflags = 0x%08x\n", pHeader->flags); - printf("\tnumBlocks = %u\n", pHeader->numBlocks); - printf("\tdataOffset = %u\n", pHeader->dataOffset); - printf("\tdataLen = %u\n", pHeader->dataLen); - printf("\tcmntOffset = %u\n", pHeader->cmntOffset); - printf("\tcmntLen = %u\n", pHeader->cmntLen); - printf("\tcreatorOffset = %u\n", pHeader->creatorOffset); - printf("\tcreatorLen = %u\n", pHeader->creatorLen); - printf("\n"); -} - - -/* - * =========================================================================== - * Main functions - * =========================================================================== - */ - -typedef enum ArchiveKind { kKindUnknown, kKindShk, kKindImg } ArchiveKind; - -/* - * This gets called when a buffer DataSource is no longer needed. - */ -NuResult FreeCallback(NuArchive* pArchive, void* args) -{ - free(args); - return kNuOK; -} - -/* - * This gets called when an "FP" DataSource is no longer needed. - */ -NuResult FcloseCallback(NuArchive* pArchive, void* args) -{ - fclose((FILE*) args); - return kNuOK; -} - -/* - * Create a data source for a ProDOS-ordered image. Since this is already - * in the correct format, we just point at the correct offset in the 2MG file. - * - * This supplies an FcloseCallback so that we can exercise that feature - * of NufxLib. We could just as easily not set it and call fclose() - * ourselves, because the structure of this program is pretty simple. - */ -NuError CreateProdosSource(const ImgHeader* pHeader, FILE* fp, - NuDataSource** ppDataSource) -{ - return NuCreateDataSourceForFP(kNuThreadFormatUncompressed, 0, fp, - pHeader->dataOffset, pHeader->dataLen, FcloseCallback,ppDataSource); -} - -/* - * Create a data source for a DOS-ordered image. This is a little harder, - * since we have to reorder the blocks into ProDOS ordering for ShrinkIt. - */ -NuError CreateDosSource(const ImgHeader* pHeader, FILE* fp, - NuDataSource** ppDataSource) -{ - NuError err; - char* diskBuffer = NULL; - long offset; - - if (pHeader->dataLen % 4096) { - fprintf(stderr, - "ERROR: image size must be multiple of 4096 (%u isn't)\n", - pHeader->dataLen); - err = kNuErrGeneric; - goto bail; - } - - if (fseek(fp, pHeader->dataOffset, SEEK_SET) < 0) { - err = errno; - perror("fseek failed"); - goto bail; - } - - diskBuffer = malloc(pHeader->dataLen); - if (diskBuffer == NULL) { - fprintf(stderr, "ERROR: malloc(%u) failed\n", pHeader->dataLen); - err = kNuErrMalloc; - goto bail; - } - - /* - * Run through the image, reordering each track. This is a - * reversible transformation, i.e. if you do this twice you're back - * to ProDOS ordering. - */ - for (offset = 0; offset < (long) pHeader->dataLen; offset += 4096) { - size_t ignored; - ignored = fread(diskBuffer + offset + 0x0000, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0e00, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0d00, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0c00, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0b00, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0a00, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0900, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0800, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0700, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0600, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0500, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0400, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0300, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0200, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0100, 256, 1, fp); - ignored = fread(diskBuffer + offset + 0x0f00, 256, 1, fp); - (void) ignored; - } - if (feof(fp) || ferror(fp)) { - err = errno ? errno : -1; - fprintf(stderr, "ERROR: failed while reading source file\n"); - goto bail; - } - - /* - * Create a data source for the buffer. We set the "doClose" flag to - * "true", so NufxLib will free the buffer for us. - */ - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, 0, - (const uint8_t*) diskBuffer, 0, pHeader->dataLen, - FreeCallback, ppDataSource); - if (err == kNuErrNone) - diskBuffer = NULL; - -bail: - if (diskBuffer != NULL) - free(diskBuffer); - return err; -} - - - -/* - * Convert a 2IMG file into a new SHK archive. - * - * This requires opening up the 2IMG file, verifying that it's okay, and - * then creating a new disk image record and thread. - */ -int ConvertFromImgToShk(const char* srcName, const char* dstName) -{ - NuError err; - NuArchive* pArchive = NULL; - NuDataSource* pDataSource = NULL; - NuRecordIdx recordIdx; - NuFileDetails fileDetails; - ImgHeader header; - FILE* fp = NULL; - uint32_t flushStatus; - char* storageName = NULL; - char* cp; - - printf("Converting 2IMG file '%s' to ShrinkIt archive '%s'\n\n", - srcName, dstName); - - err = kNuErrGeneric; - - fp = fopen(srcName, kNuFileOpenReadOnly); - if (fp == NULL) { - perror("fopen failed"); - goto bail; - } - - if (ReadImgHeader(fp, &header) < 0) { - fprintf(stderr, "ERROR: header read failed\n"); - goto bail; - } - - DumpImgHeader(&header); - - if (header.imageFormat != kImageFormatDOS && - header.imageFormat != kImageFormatProDOS) - { - fprintf(stderr, "ERROR: can only handle DOS and ProDOS images\n"); - goto bail; - } - - if (header.numBlocks > 1600) - printf("WARNING: that's a big honking image!\n"); - - /* - * Open a new archive read-write. This refuses to overwrite an - * existing file. - */ - (void) unlink(kTempFile); - err = NuOpenRW(dstName, kTempFile, kNuOpenCreat|kNuOpenExcl, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create archive (err=%d)\n", err); - goto bail; - } - - /* create the name that will be stored in the archive */ - storageName = strdup(dstName); - cp = strrchr(storageName, '.'); - if (cp != NULL) - *cp = '\0'; - cp = strrchr(storageName, kLocalFssep); - if (cp != NULL && *(cp+1) != '\0') - cp++; - else - cp = storageName; - - /* - * We can't say "add file", because NufxLib doesn't know what a 2MG - * archive is. However, we can point a DataSource at the data in - * the file, and construct the record manually. - */ - - /* set up the contents of the NuFX Record */ - memset(&fileDetails, 0, sizeof(fileDetails)); - fileDetails.storageNameMOR = cp; - fileDetails.fileSysID = kNuFileSysUnknown; /* DOS? ProDOS? */ - fileDetails.fileSysInfo = kLocalFssep; - fileDetails.access = kNuAccessUnlocked; - fileDetails.extraType = header.numBlocks; - fileDetails.storageType = 512; - /* FIX - ought to set the file dates */ - - /* add a new record */ - err = NuAddRecord(pArchive, &fileDetails, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create record (err=%d)\n", err); - goto bail; - } - - /* - * Create a data source for the 2IMG file. We do this differently - * for DOS and ProDOS, because we have to rearrange the sector - * ordering for DOS-ordered images (ShrinkIt always uses ProDOS order). - */ - switch (header.imageFormat) { - case kImageFormatDOS: - err = CreateDosSource(&header, fp, &pDataSource); - fp = NULL; - break; - case kImageFormatProDOS: - err = CreateProdosSource(&header, fp, &pDataSource); - fp = NULL; - break; - default: - fprintf(stderr, "How the heck did I get here?"); - err = kNuErrInternal; - goto bail; - } - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data source (err=%d)\n", err); - goto bail; - } - - /* add a disk image thread */ - err = NuAddThread(pArchive, recordIdx, kNuThreadIDDiskImage, pDataSource, - NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create thread (err=%d)\n", err); - goto bail; - } - pDataSource = NULL; /* library owns it now */ - - /* nothing happens until we Flush */ - err = NuFlush(pArchive, &flushStatus); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: flush failed (err=%d, status=0x%04x)\n", - err, flushStatus); - goto bail; - } - err = NuClose(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: close failed (err=%d)\n", err); - goto bail; - } - pArchive = NULL; - -bail: - if (pArchive != NULL) { - (void)NuAbort(pArchive); - (void)NuClose(pArchive); - } - NuFreeDataSource(pDataSource); - if (storageName != NULL) - free(storageName); - if (fp != NULL) - fclose(fp); - return (err == kNuErrNone) ? 0 : -1; -} - - -/* - * Convert an SHK archive into a 2IMG file. - * - * This takes a simple-minded approach and assumes that the first record - * in the archive has the disk image in it. If it doesn't, we give up. - */ -int ConvertFromShkToImg(const char* srcName, const char* dstName) -{ - NuError err; - NuArchive* pArchive = NULL; - NuDataSink* pDataSink = NULL; - NuRecordIdx recordIdx; - const NuRecord* pRecord; - const NuThread* pThread = NULL; - ImgHeader header; - FILE* fp = NULL; - int idx; - - printf("Converting ShrinkIt archive '%s' to 2IMG file '%s'\n\n", - srcName, dstName); - - /* - * Open the archive. - */ - err = NuOpenRO(srcName, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to open archive (err=%d)\n", err); - goto bail; - } - - /* get the first record */ - err = NuGetRecordIdxByPosition(pArchive, 0, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get first recordIdx (err=%d)\n", err); - goto bail; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get first record (err=%d)\n", err); - goto bail; - } - - /* find a disk image thread */ - for (idx = 0; idx < (int)NuRecordGetNumThreads(pRecord); idx++) { - pThread = NuGetThread(pRecord, idx); - - if (NuGetThreadID(pThread) == kNuThreadIDDiskImage) - break; - } - if (idx == (int)NuRecordGetNumThreads(pRecord)) { - fprintf(stderr, "ERROR: no disk image found in first record\n"); - err = -1; - goto bail; - } - - /* - * Looks good. Open the 2IMG file, and create the header. - */ - if (access(dstName, F_OK) == 0) { - fprintf(stderr, "ERROR: output file already exists\n"); - err = -1; - goto bail; - } - - fp = fopen(dstName, kNuFileOpenWriteTrunc); - if (fp == NULL) { - perror("fopen failed"); - goto bail; - } - - /* set up the 2MG header, based on the NuFX Record */ - memset(&header, 0, sizeof(header)); - memcpy(header.magic, kImgMagic, sizeof(header.magic)); - memcpy(header.creator, kMyCreator, sizeof(header.creator)); - header.headerLen = 64; - header.version = 1; - header.imageFormat = kImageFormatProDOS; /* always ProDOS-order */ - header.numBlocks = pRecord->recExtraType; - header.dataOffset = 64; - /* old versions of ShrinkIt blew the threadEOF, so use NufxLib's "actual" */ - header.dataLen = pThread->actualThreadEOF; - DumpImgHeader(&header); - if (WriteImgHeader(fp, &header) < 0) { - fprintf(stderr, "ERROR: header write failed\n"); - err = -1; - goto bail; - } - - /* - * We want to expand the disk image thread into "fp" at the current - * offset. - */ - err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", err); - goto bail; - } - - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to extract thread (err=%d)\n", err); - goto bail; - } - -bail: - if (pArchive != NULL) - NuClose(pArchive); - NuFreeDataSink(pDataSink); - if (fp != NULL) - fclose(fp); - return (err == kNuErrNone) ? 0 : -1; -} - - -/* - * Figure out what kind of archive this is by looking at the filename. - */ -ArchiveKind DetermineKind(const char* filename) -{ - const char* dot; - - dot = strrchr(filename, '.'); - if (dot == NULL) - return kKindUnknown; - - if (strcasecmp(dot, ".shk") == 0 || strcasecmp(dot, ".sdk") == 0) - return kKindShk; - else if (strcasecmp(dot, ".2mg") == 0) - return kKindImg; - - return kKindUnknown; -} - - - -/* - * Figure out what we want to do. - */ -int main(int argc, char** argv) -{ - ArchiveKind kind; - int cc; - - if (argc != 3) { - fprintf(stderr, "Usage: %s (input.2mg|input.shk) output\n", argv[0]); - exit(2); - } - - kind = DetermineKind(argv[1]); - if (kind == kKindUnknown) { - fprintf(stderr, "ERROR: input name must end in '.shk' or '.2mg'\n"); - exit(2); - } - - if (kind == kKindShk) - cc = ConvertFromShkToImg(argv[1], argv[2]); - else - cc = ConvertFromImgToShk(argv[1], argv[2]); - - if (cc) - fprintf(stderr, "Failed\n"); - else - printf("Done!\n"); - - exit(cc != 0); -} - diff --git a/ciderpress/nufxlib/samples/Launder.c b/ciderpress/nufxlib/samples/Launder.c deleted file mode 100644 index fba4913..0000000 --- a/ciderpress/nufxlib/samples/Launder.c +++ /dev/null @@ -1,701 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Run an archive through the laundry. The net result is a duplicate - * archive that matches the original in most respects. Extracting the - * files from the duplicate will yield the same results as if they were - * extracted from the original, but the duplicate archive may differ - * in subtle ways (e.g. filename threads may be added, data may be - * recompressed). - * - * This demonstrates copying threads around, both with and without - * recompressing, between two archives that are open simultaneously. This - * also tests NufxLib's thread ordering and verifies that you can abort - * frequently with no adverse effects. - * - * NOTE: depending on the options you select, you may need to have enough - * memory to hold the entire uncompressed contents of the original archive. - * The memory requirements are reduced if you use the "copy only" flag, and - * are virtually eliminated if you use "frequent flush". - */ -#include -#include -#include -#include -#include "NufxLib.h" -#include "Common.h" - - -#define kTempFile "tmp-laundry" - -#define kFlagCopyOnly (1) -#define kFlagReverseThreads (1 << 1) -#define kFlagFrequentFlush (1 << 2) -#define kFlagFrequentAbort (1 << 3) /* implies FrequentFlush */ -#define kFlagUseTmp (1 << 4) - - -/* - * Globals. - */ -char gSentRecordWarning = false; - - -/* - * This gets called when a buffer DataSource is no longer needed. - */ -NuResult FreeCallback(NuArchive* pArchive, void* args) -{ - free(args); - return kNuOK; -} - -/* - * Copy a thread, expanding and recompressing it. - * - * This assumes the library is configured for compression (it defaults - * to LZW/2, so this is a reasonable assumption). - */ -NuError CopyThreadRecompressed(NuArchive* pInArchive, NuArchive* pOutArchive, - long flags, const NuThread* pThread, long newRecordIdx) -{ - NuError err = kNuErrNone; - NuDataSource* pDataSource = NULL; - NuDataSink* pDataSink = NULL; - uint8_t* buffer = NULL; - - /* - * Allocate a buffer large enough to hold all the uncompressed data, and - * wrap a data sink around it. - * - * If the thread is zero bytes long, we can skip this part. - */ - if (pThread->actualThreadEOF) { - buffer = malloc(pThread->actualThreadEOF); - if (buffer == NULL) { - err = kNuErrMalloc; - goto bail; - } - err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", - err); - goto bail; - } - - /* - * Expand the data. For a pre-sized thread, this grabs only the - * interesting part of the buffer. - */ - err = NuExtractThread(pInArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to extract thread %u (err=%d)\n", - pThread->threadIdx, err); - goto bail; - } - } - - /* - * The expanded data is in the buffer, now create a data source that - * describes it. - * - * This is complicated by the existence of pre-sized threads, which - * require us to set "otherLen". - * - * We always use "actualThreadEOF" because "thThreadEOF" is broken - * for disk archives created by certain versions of ShrinkIt. - * - * It's okay to pass in a NULL value for "buffer", so long as the - * amount of data in the buffer is also zero. The library will do - * the right thing. - */ - if (NuIsPresizedThreadID(NuGetThreadID(pThread))) { - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - pThread->thCompThreadEOF, buffer, 0, - pThread->actualThreadEOF, FreeCallback, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: unable to create pre-sized data source (err=%d)\n",err); - goto bail; - } - } else { - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, buffer, 0, pThread->actualThreadEOF, - FreeCallback, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: unable to create data source (err=%d)\n", err); - goto bail; - } - } - buffer = NULL; /* doClose was set, so it's owned by the data source */ - - /* - * Schedule the data for addition to the record. - */ - err = NuAddThread(pOutArchive, newRecordIdx, NuGetThreadID(pThread), - pDataSource, NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to add thread (err=%d)\n", err); - goto bail; - } - pDataSource = NULL; /* library owns it now */ - -bail: - if (pDataSource != NULL) - NuFreeDataSource(pDataSource); - if (pDataSink != NULL) - NuFreeDataSink(pDataSink); - if (buffer != NULL) - free(buffer); - return err; -} - -/* - * Copy a thread from one archive to another without disturbing the - * compression. - * - * There is a much more efficient way to do this: create an FP - * data source using an offset within the archive file itself. - * Since pInArchive->archiveFp isn't exposed, we can't use that, - * but under most operating systems you aren't prevented from - * opening the same file twice in read-only mode. The file offset - * in pThread tells us where the data is. - * - * The method used below is less memory-efficient but more portable. - * - * This always extracts based on the compThreadEOF, which is - * reliable but extracts a little more than we need on pre-sized - * threads (filenames, comments). - */ -NuError CopyThreadUncompressed(NuArchive* pInArchive, NuArchive* pOutArchive, - long flags, const NuThread* pThread, long newRecordIdx) -{ - NuError err = kNuErrNone; - NuDataSource* pDataSource = NULL; - NuDataSink* pDataSink = NULL; - uint8_t* buffer = NULL; - - /* - * If we have some data files that were left uncompressed, perhaps - * because of GSHK's "don't compress anything smaller than 512 bytes" - * rule, NufxLib will try to compress them. We disable this - * behavior by disabling compression. That way, stuff that is - * already compressed will remain that way, and stuff that isn't - * compressed won't be. (We really only need to do this once, at - * the start of the program, but it's illustrative to do it here.) - * - * [ I don't understand this comment. It's necessary to disable - * compression, but I don't see why uncompressed files are - * special. ++ATM 20040821 ] - */ - err = NuSetValue(pOutArchive, kNuValueDataCompression, kNuCompressNone); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to set compression (err=%d)\n", err); - goto bail; - } - - /* - * Allocate a buffer large enough to hold all the compressed data, and - * wrap a data sink around it. - */ - buffer = malloc(pThread->thCompThreadEOF); - if (buffer == NULL) { - err = kNuErrMalloc; - goto bail; - } - err = NuCreateDataSinkForBuffer(false, kNuConvertOff, buffer, - pThread->thCompThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n", - err); - goto bail; - } - - /* - * Get the compressed data. For a pre-sized thread, this grabs the - * entire contents of the buffer, including the padding. - */ - err = NuExtractThread(pInArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to extract thread %u (err=%d)\n", - pThread->threadIdx, err); - goto bail; - } - - /* - * The (perhaps compressed) data is in the buffer, now create a data - * source that describes it. - * - * This is complicated by the existence of pre-sized threads. There - * are two possibilities: - * 1. We have a certain amount of non-pre-sized data (thCompThreadEOF) - * that will expand out to a certain length (actualThreadEOF). - * 2. We have a certain amount of pre-sized data (actualThreadEOF) - * that will fit within a buffer (thCompThreadEOF). - * As you can see, the arguments need to be reversed for pre-sized - * threads. - * - * We always use "actualThreadEOF" because "thThreadEOF" is broken - * for disk archives created by certain versions of ShrinkIt. - */ - if (NuIsPresizedThreadID(NuGetThreadID(pThread))) { - err = NuCreateDataSourceForBuffer(pThread->thThreadFormat, - pThread->thCompThreadEOF, buffer, 0, - pThread->actualThreadEOF, FreeCallback, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: unable to create pre-sized data source (err=%d)\n",err); - goto bail; - } - } else { - err = NuCreateDataSourceForBuffer(pThread->thThreadFormat, - pThread->actualThreadEOF, buffer, 0, - pThread->thCompThreadEOF, FreeCallback, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: unable to create data source (err=%d)\n", err); - goto bail; - } - } - buffer = NULL; /* doClose was set, so it's owned by the data source */ - - /* yes, this is a kluge... sigh */ - err = NuDataSourceSetRawCrc(pDataSource, pThread->thThreadCRC); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: can't set source CRC (err=%d)\n", err); - goto bail; - } - - /* - * Schedule the data for addition to the record. - * - * Note that NuAddThread makes a copy of the data source, and clears - * "doClose" on our copy, so we are free to dispose of pDataSource. - */ - err = NuAddThread(pOutArchive, newRecordIdx, NuGetThreadID(pThread), - pDataSource, NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to add thread (err=%d)\n", err); - goto bail; - } - pDataSource = NULL; /* library owns it now */ - -bail: - if (pDataSource != NULL) - NuFreeDataSource(pDataSource); - if (pDataSink != NULL) - NuFreeDataSink(pDataSink); - if (buffer != NULL) - free(buffer); - return err; -} - - -/* - * Copy a thread from one archive to another. - * - * Depending on "flags", this will either copy it raw or uncompress and - * recompress. - */ -NuError CopyThread(NuArchive* pInArchive, NuArchive* pOutArchive, long flags, - const NuThread* pThread, long newRecordIdx) -{ - if (flags & kFlagCopyOnly) { - return CopyThreadUncompressed(pInArchive, pOutArchive, flags, pThread, - newRecordIdx); - } else { - return CopyThreadRecompressed(pInArchive, pOutArchive, flags, pThread, - newRecordIdx); - } -} - - -/* - * Copy a record from the input to the output. - * - * This runs through the list of threads and copies each one individually. - * It will copy them in the original order or in reverse order (the latter - * of which will not usually have any effect since NufxLib imposes a - * specific thread ordering on most common types) depending on "flags". - */ -NuError CopyRecord(NuArchive* pInArchive, NuArchive* pOutArchive, long flags, - NuRecordIdx recordIdx) -{ - NuError err = kNuErrNone; - const NuRecord* pRecord; - const NuThread* pThread; - NuFileDetails fileDetails; - NuRecordIdx newRecordIdx; - long numThreads; - int idx; - - /* - * Grab the original record and see how many threads it has. - */ - err = NuGetRecord(pInArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get recordIdx %u\n", recordIdx); - goto bail; - } - - /* - * Pre-v3 records didn't put CRCs in the thread headers. If we just - * copy the thread over without reprocessing the data, we won't compute - * a CRC for the thread, and we will get CRC failures. - */ - if (!gSentRecordWarning && (flags & kFlagCopyOnly) && - pRecord->recVersionNumber < 3) - { - printf("WARNING: pre-v3 records that aren't recompressed may exhibit CRC failures\n"); - gSentRecordWarning = true; - } - - numThreads = NuRecordGetNumThreads(pRecord); - if (!numThreads) { - fprintf(stderr, "WARNING: recordIdx=%u was empty\n", recordIdx); - goto bail; - } - - /* - * Create a new record that looks just like the original. - */ - memset(&fileDetails, 0, sizeof(fileDetails)); - fileDetails.storageNameMOR = pRecord->filenameMOR; - fileDetails.fileSysID = pRecord->recFileSysID; - fileDetails.fileSysInfo = pRecord->recFileSysInfo; - fileDetails.access = pRecord->recAccess; - fileDetails.fileType = pRecord->recFileType; - fileDetails.extraType = pRecord->recExtraType; - fileDetails.storageType = pRecord->recStorageType; - fileDetails.createWhen = pRecord->recCreateWhen; - fileDetails.modWhen = pRecord->recModWhen; - fileDetails.archiveWhen = pRecord->recArchiveWhen; - - err = NuAddRecord(pOutArchive, &fileDetails, &newRecordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: NuAddRecord failed (err=%d)\n", err); - goto bail; - } - - /* - * Copy the threads. - */ - if (flags & kFlagReverseThreads) { - for (idx = numThreads-1; idx >= 0; idx--) { - pThread = NuGetThread(pRecord, idx); - assert(pThread != NULL); - - err = CopyThread(pInArchive, pOutArchive, flags, pThread, - newRecordIdx); - if (err != kNuErrNone) - goto bail; - } - } else { - for (idx = 0; idx < numThreads; idx++) { - pThread = NuGetThread(pRecord, idx); - assert(pThread != NULL); - - err = CopyThread(pInArchive, pOutArchive, flags, pThread, - newRecordIdx); - if (err != kNuErrNone) - goto bail; - } - } - -bail: - return err; -} - - -/* - * Launder an archive from inFile to outFile. - * - * Returns 0 on success, nonzero on failure. - */ -int LaunderArchive(const char* inFile, const char* outFile, - NuValue compressMethod, long flags) -{ - NuError err = kNuErrNone; - NuArchive* pInArchive = NULL; - NuArchive* pOutArchive = NULL; - const NuMasterHeader* pMasterHeader; - NuRecordIdx recordIdx; - uint32_t idx, flushStatus; - - err = NuOpenRO(inFile, &pInArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't open input archive '%s' (err=%d)\n", - inFile, err); - goto bail; - } - err = NuOpenRW(outFile, kTempFile, kNuOpenCreat|kNuOpenExcl, &pOutArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't open output archive '%s' (err=%d)\n", - outFile, err); - goto bail; - } - - /* turn off "mimic GSHK" */ - err = NuSetValue(pInArchive, kNuValueMimicSHK, true); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to disable GSHK quirks (err=%d)\n", - err); - goto bail; - } - - /* allow duplicates, in case the original archive has them */ - err = NuSetValue(pOutArchive, kNuValueAllowDuplicates, true); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't allow duplicates (err=%d)\n", err); - goto bail; - } - - /* set the compression method */ - err = NuSetValue(pOutArchive, kNuValueDataCompression, compressMethod); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to set compression (err=%d)\n", - err); - goto bail; - } - - if (flags & kFlagUseTmp) { - err = NuSetValue(pOutArchive, kNuValueModifyOrig, false); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: couldn't disable modify orig (err=%d)\n", err); - goto bail; - } - } - - err = NuGetMasterHeader(pInArchive, &pMasterHeader); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err); - goto bail; - } - - /* - * Iterate through the set of records. - */ - for (idx = 0; idx < pMasterHeader->mhTotalRecords; idx++) { - err = NuGetRecordIdxByPosition(pInArchive, idx, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record #%u (err=%d)\n", - idx, err); - goto bail; - } - - err = CopyRecord(pInArchive, pOutArchive, flags, recordIdx); - if (err != kNuErrNone) - goto bail; - - /* - * If "frequent abort" is set, abort what we just did and redo it. - */ - if (flags & kFlagFrequentAbort) { - /*printf("(abort)\n");*/ - err = NuAbort(pOutArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: abort failed (err=%d)\n", err); - goto bail; - } - - err = CopyRecord(pInArchive, pOutArchive, flags, recordIdx); - if (err != kNuErrNone) - goto bail; - - } - - /* - * If "frequent abort" or "frequent flush" is set, flush after - * each record is copied. - */ - if ((flags & kFlagFrequentAbort) || (flags & kFlagFrequentFlush)) { - /*printf("(flush)\n");*/ - err = NuFlush(pOutArchive, &flushStatus); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: flush failed (err=%d, status=0x%04x)\n", - err, flushStatus); - goto bail; - } - } - } - - /* first and only flush if frequent-flushing wasn't enabled */ - err = NuFlush(pOutArchive, &flushStatus); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: flush failed (err=%d, status=0x%04x)\n", - err, flushStatus); - goto bail; - } - -bail: - if (pInArchive != NULL) - NuClose(pInArchive); - if (pOutArchive != NULL) { - if (err != kNuErrNone) - NuAbort(pOutArchive); - NuClose(pOutArchive); /* flush pending changes and close */ - } - return (err != kNuErrNone); -} - - - -/* - * Can't count on having getopt on non-UNIX platforms, so just use this - * quick version instead. May not work exactly like getopt(), but it - * does everything we need here. - */ -int myoptind = 0; -char* myoptarg = NULL; -const char* curchar = NULL; -int skipnext = false; - -int mygetopt(int argc, char** argv, const char* optstr) -{ - if (!myoptind) { - myoptind = 1; - if (argc <= 1) - return EOF; - curchar = argv[myoptind]; - if (*curchar != '-') - return EOF; - } - - curchar++; - if (*curchar == '\0') { - myoptind++; - if (skipnext) - myoptind++; - if (myoptind >= argc) - return EOF; - curchar = argv[myoptind]; - if (*curchar != '-') - return EOF; - curchar++; - } - - while (*optstr != '\0') { - if (*optstr == *curchar) { - /*printf("MATCHED '%c'\n", *optstr);*/ - if (*(optstr+1) == ':') { - skipnext = true; - myoptarg = argv[myoptind+1]; - /*printf("ATTACHED '%s'\n", myoptarg);*/ - } - return *curchar; - } - - optstr++; - } - - fprintf(stderr, "Unrecognized option '%c'\n", *curchar); - return *curchar; -} - -/* - * Print usage info. - */ -void Usage(const char* argv0) -{ - fprintf(stderr, "Usage: %s [-crfat] [-m method] infile.shk outfile.shk\n", - argv0); - fprintf(stderr, "\t-c : copy only, does not recompress data\n"); - fprintf(stderr, "\t-r : copy threads in reverse order to test ordering\n"); - fprintf(stderr, "\t-f : call Flush frequently to reduce memory usage\n"); - fprintf(stderr, "\t-a : exercise nufxlib Abort code frequently\n"); - fprintf(stderr, "\t-t : write to temp file instead of directly to outfile.shk\n"); - fprintf(stderr, - "\t[method] is one of {sq,lzw1,lzw2,lzc12,lzc16,deflate,bzip2}\n"); - fprintf(stderr, "\tIf not specified, method defaults to lzw2\n"); -} - -/* - * Grab the name of an archive to read. - */ -int main(int argc, char** argv) -{ - NuValue compressMethod = kNuCompressLZW2; - int32_t major, minor, bug; - const char* pBuildDate; - long flags = 0; - int errorFlag; - int ic; - int cc; - - (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, NULL); - printf("Using NuFX lib %d.%d.%d built on or after %s\n", - major, minor, bug, pBuildDate); - - errorFlag = false; - while ((ic = mygetopt(argc, argv, "crfatm:")) != EOF) { - switch (ic) { - case 'c': flags |= kFlagCopyOnly; break; - case 'r': flags |= kFlagReverseThreads; break; - case 'f': flags |= kFlagFrequentFlush; break; - case 'a': flags |= kFlagFrequentAbort; break; - case 't': flags |= kFlagUseTmp; break; - case 'm': - { - struct { - const char* str; - NuValue val; - NuFeature feature; - } methods[] = { - { "sq", kNuCompressSQ, kNuFeatureCompressSQ }, - { "lzw1", kNuCompressLZW1, kNuFeatureCompressLZW }, - { "lzw2", kNuCompressLZW2, kNuFeatureCompressLZW }, - { "lzc12", kNuCompressLZC12, kNuFeatureCompressLZC }, - { "lzc16", kNuCompressLZC16, kNuFeatureCompressLZC }, - { "deflate", kNuCompressDeflate, kNuFeatureCompressDeflate}, - { "bzip2", kNuCompressBzip2, kNuFeatureCompressBzip2 }, - }; - char* methodStr = myoptarg; - int i; - - for (i = 0; i < NELEM(methods); i++) { - if (strcmp(methods[i].str, methodStr) == 0) { - compressMethod = methods[i].val; - break; - } - } - if (i == NELEM(methods)) { - fprintf(stderr, "ERROR: unknown method '%s'\n", methodStr); - errorFlag++; - break; - } - if (NuTestFeature(methods[i].feature) != kNuErrNone) { - fprintf(stderr, - "ERROR: compression method '%s' not supported\n", - methodStr); - errorFlag++; - break; - } - } - break; - default: - errorFlag++; - break; - } - } - - if (errorFlag || argc != myoptind+2) { - Usage(argv[0]); - exit(2); - } - - cc = LaunderArchive(argv[myoptind], argv[myoptind+1], compressMethod,flags); - - if (cc == 0) - printf("Success!\n"); - else - printf("Failed.\n"); - exit(cc != 0); -} - diff --git a/ciderpress/nufxlib/samples/Makefile.in b/ciderpress/nufxlib/samples/Makefile.in deleted file mode 100644 index 7f436bf..0000000 --- a/ciderpress/nufxlib/samples/Makefile.in +++ /dev/null @@ -1,81 +0,0 @@ -# -# Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. -# This is free software; you can redistribute it and/or modify it under the -# terms of the BSD, see the file COPYING. -# -# Makefile for nufxlib tests (should work with non-GNU make). -# -# This is normally invoked from the nufxlib makefile. -# -# If you invoke this directly, LIB_PRODUCT won't be defined, and it -# won't automatically detect changes to the library. However, any -# changes to the library should cause a re-build in here anyway if -# you're running "make" from the library directory. -# -SHELL = /bin/sh -CC = @CC@ -AR = ar rcv -#OPT = @CFLAGS@ -DNDEBUG -OPT = @CFLAGS@ -#OPT = @CFLAGS@ -DDEBUG_MSGS -#OPT = @CFLAGS@ -DDEBUG_VERBOSE -GCC_FLAGS = -Wall -Wwrite-strings -Wstrict-prototypes -Wpointer-arith -Wshadow -CFLAGS = @BUILD_FLAGS@ -I. -I.. @DEFS@ - -#ALL_SRCS = $(wildcard *.c *.cpp) -ALL_SRCS = Exerciser.c ImgConv.c Launder.c TestBasic.c \ - TestExtract.c TestSimple.c TestTwirl.c - -NUFXLIB = -L.. -lnufx - -PRODUCTS = exerciser imgconv launder test-basic test-extract test-names \ - test-simple test-twirl - -all: $(PRODUCTS) - @true - -exerciser: Exerciser.o $(LIB_PRODUCT) - $(CC) -o $@ Exerciser.o $(NUFXLIB) @LIBS@ - -imgconv: ImgConv.o $(LIB_PRODUCT) - $(CC) -o $@ ImgConv.o $(NUFXLIB) @LIBS@ - -launder: Launder.o $(LIB_PRODUCT) - $(CC) -o $@ Launder.o $(NUFXLIB) @LIBS@ - -test-basic: TestBasic.o $(LIB_PRODUCT) - $(CC) -o $@ TestBasic.o $(NUFXLIB) @LIBS@ - -test-extract: TestExtract.o $(LIB_PRODUCT) - $(CC) -o $@ TestExtract.o $(NUFXLIB) @LIBS@ - -test-names: TestNames.o $(LIB_PRODUCT) - $(CC) -o $@ TestNames.o $(NUFXLIB) @LIBS@ - -test-simple: TestSimple.o $(LIB_PRODUCT) - $(CC) -o $@ TestSimple.o $(NUFXLIB) @LIBS@ - -test-twirl: TestTwirl.o $(LIB_PRODUCT) - $(CC) -o $@ TestTwirl.o $(NUFXLIB) @LIBS@ - -tags:: - ctags --totals -R ../* - @#ctags *.cpp ../*.c *.h ../*.h - -clean: - -rm -f *.o core - -rm -f $(PRODUCTS) - -distclean: clean - -rm -f tags - -rm -f Makefile Makefile.bak - -COMMON_HDRS = ../NufxLibPriv.h ../NufxLib.h ../MiscStuff.h ../SysDefs.h -Exerciser.o: Exerciser.c $(COMMON_HDRS) -ImgConv.o: ImgConv.c $(COMMON_HDRS) -Launder.o: Launder.c $(COMMON_HDRS) -TestBasic.o: TestBasic.c $(COMMON_HDRS) -TestExtract.o: TestExtract.c $(COMMON_HDRS) -TestNames.o: TestNames.c $(COMMON_HDRS) -TestSimple.o: TestSimple.c $(COMMON_HDRS) -TestTwirl.o: TestTwirl.c $(COMMON_HDRS) diff --git a/ciderpress/nufxlib/samples/Makefile.msc b/ciderpress/nufxlib/samples/Makefile.msc deleted file mode 100644 index e8a4372..0000000 --- a/ciderpress/nufxlib/samples/Makefile.msc +++ /dev/null @@ -1,113 +0,0 @@ -# -# Makefile for Microsoft C compilers. Tested against Visual C++ 6.0. -# Not pretty but it seems to work. -# -# Run with "nmake /f Makefile.msc". Expects NufxLib to have been built -# in "..". -# -# To build without debugging info, use "nmake nodebug=1". -# To build with libz, use "nmake libz=1". -# To build with libbz2, use "nmake libbz2=1". -# If you're linking against nufxlib as a DLL, you don't need to specify -# libraries. You probably need to specify DLL=1 and the same setting -# of the NODEBUG flag as you used when building the DLL. If you don't, -# "test-extract" will fail in the fwrite() call in Nu_FWrite, because -# the non-debug /MD libc does something peculiar with FILE*. -# -# For libz/libbz2, you need to have the appropriate library either -# in this directory or in a standard location that the linker can find. -# - -# Windows magic -TARGETOS = BOTH -!include - -NUFXSRCDIR = .. -LIB_PRODUCT = $(NUFXSRCDIR)\nufxlib2.lib - -!ifdef DLL -### build using the same libc as the DLL -!ifdef NODEBUG -#OPT = /D NUFXLIB_DLL /D NDEBUG /MD /Ogityb2 -OPT = /D NUFXLIB_DLL /MD /Ogityb2 -LIB_FLAGS = /nodefaultlib:libcd.lib /nologo setargv.obj -!else -#OPT = /D NUFXLIB_DLL /MDd /Od -OPT = /D NUFXLIB_DLL /D DEBUG_MSGS /MDd /Od -LIB_FLAGS = /nodefaultlib:libc.lib /nologo setargv.obj -!endif -!else - -### build against static lib -!ifdef NODEBUG -#OPT = /D NDEBUG /ML /Ogityb2 -OPT = /ML /Ogityb2 -LIB_FLAGS = /nodefaultlib:libcd.lib /nologo libc.lib setargv.obj -!else -#OPT = /MLd /Od -OPT = /D DEBUG_MSGS /MLd /Od -LIB_FLAGS = /nodefaultlib:libc.lib /nologo libcd.lib setargv.obj -!endif -!endif - -BUILD_FLAGS = /W3 /GX /D "WIN32" /D "_CONSOLE" /I "$(NUFXSRCDIR)" -!MESSAGE Using OPT = $(OPT) - -!ifdef LIBZ -LIB_FLAGS = zlib.lib $(LIB_FLAGS) -!endif -!ifdef LIBBZ2 -LIB_FLAGS = libbz2.lib $(LIB_FLAGS) -!endif - -# how to compile sources -.c.obj: - @$(cc) $(cdebug) $(OPT) $(BUILD_FLAGS) $(cflags) $(cvars) -o $@ $< - - -PRODUCTS = exerciser.exe imgconv.exe launder.exe test-basic.exe test-extract.exe test-simple.exe test-twirl.exe - -all: $(PRODUCTS) - -exerciser.exe: Exerciser.obj $(LIB_PRODUCT) - $(link) $(ldebug) Exerciser.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -imgconv.exe: ImgConv.obj $(LIB_PRODUCT) - $(link) $(ldebug) ImgConv.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -launder.exe: Launder.obj $(LIB_PRODUCT) - $(link) $(ldebug) Launder.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -test-basic.exe: TestBasic.obj $(LIB_PRODUCT) - $(link) $(ldebug) TestBasic.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -test-simple.exe: TestSimple.obj $(LIB_PRODUCT) - $(link) $(ldebug) TestSimple.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -test-extract.exe: TestExtract.obj $(LIB_PRODUCT) - $(link) $(ldebug) TestExtract.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -test-twirl.exe: TestTwirl.obj $(LIB_PRODUCT) - $(link) $(ldebug) TestTwirl.obj -out:$@ $(NUFXSRCDIR)\nufxlib2.lib $(LIB_FLAGS) - -clean: - -del *.obj - -del *.pdb - -del *.ilk - -del *.exp - -del exerciser.exe - -del imgconv.exe - -del launder.exe - -del test-basic.exe - -del test-simple.exe - -del test-extract.exe - -del test-twirl.exe - -Exerciser.obj: Exerciser.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h -ImgConv.obj: ImgConv.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h -Launder.obj: Launder.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h -TestBasic.obj: TestBasic.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h -TestSimple.obj: TestSimple.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h -TestExtract.obj: TestExtract.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h -TestTwirl.obj: TestTwirl.c Common.h $(NUFXSRCDIR)\NufxLib.h $(NUFXSRCDIR)\SysDefs.h - diff --git a/ciderpress/nufxlib/samples/README-S.txt b/ciderpress/nufxlib/samples/README-S.txt deleted file mode 100644 index e325638..0000000 --- a/ciderpress/nufxlib/samples/README-S.txt +++ /dev/null @@ -1,131 +0,0 @@ -NufxLib "samples" README - -This directory contains some test programs and useful sample code. - - -test-basic -========== - -Basic tests. Run this to verify that things are working. - -On Win32 there will be a second executable, test-basic-d, that links against -the DLL rather than the static library. - - -exerciser -========= - -This program allows you to exercise all of NufxLib's basic functions. -Run it without arguments and hit "?" for a list of commands. - -If you think you have found a bug in NufxLib, you can use this to narrow -it down to a repeatable case. - - -imgconv -======= - -A 2IMG disk image converter. You can convert ".2MG" files to ShrinkIt -disk archives, and ShrinkIt disk archives to 2IMG format. imgconv uses -a creator type of "NFXL". - -You can use it like this: - - % imgconv file.shk file.2mg -or - % imgconv file.2mg file.shk - -It figures out what to do based on the filename. It will recognize ".sdk" -as a ShrinkIt archive. - -Limitations: works for DOS-ordered and ProDOS-ordered 2MG images, but -not for raw nibble images. Converting from .shk only works if the first -record in the archive is a disk image; you don't get to pick the one you -want from an archive with several in it. - - -launder -======= - -Run an archive through the laundry. This copies the entire contents of -an archive thread-by-thread, reconstructing it such that the data -matches the original even if the archive contents don't (e.g. records -are updated to version 3, files may be recompressed with LZW/2, option -lists are stripped out, etc). - -The basic usage is: - - % launder [-crfa] [-m method] infile.shk outfile.shk - -The flags are: - - -c Just copy data threads rather than recompressing them - -r Add threads in reverse order - -f Call NuFlush after every record - -a Call NuAbort after every record, then re-do the record and call NuFlush - -t Write to temp file, instead of writing directly into outfile.shk - -The "-m method" flag allows you to specify the compression method. Valid -values are sq (SQueeze), lzw1 (ShrinkIt LZW/1), lzw2 (ShrinkIt LZW/2), -lzc12 (12-bit UNIX "compress"), lzc16 (16-bit UNIX "compress"), deflate -(zlib deflate), and bzip2 (libbz2 compression). The default is lzw2. - -If you use the "-c" flag with an archive created by P8 ShrinkIt or NuLib, -the laundered archive may have CRC failures when you try to extract -from it. This is because "launder" creates version 3 records, which -are expected to have a valid CRC in the thread header. The only way -to compute the CRC is to uncompress the data, which "launder" doesn't -do when "-c" is set. The data itself is fine, it's just the thread CRC -that's wrong (if the data were hosed, the LZW/1 CRC would be bad too). -"launder" will issue a warning when it detects this situation. - -By default, launder will try to keep the entire archive in memory and flush -all of the operations at the end. If you find that you're running out -of memory on very large archives, you can reduce the memory requirements -by specifying the "-f" flag. - - -test-names -========== - -Tests Unicode filename handling. Run without arguments. - -(This currently fails on Win32 because the Unicode filename support is -incomplete there.) - - -test-simple -=========== - -Simple test program. Give it the name of an archive, and it will display -the contents. - - -test-extract -============ - -Simple test program. Give it the name of an archive, and it will write -all filename threads into "out.buf", "out.fp", and "out.file" using three -different kinds of NuDataSinks. - - -test-twirl -========== - -Like "launder", but not meant to be useful. This recompresses the file "in -place", deleting and adding threads within existing records several times. -The changes are periodically flushed, but the archive is never closed. -The goal is to test repeated updates on an open archive. - -The CRC verification mechanism will fail on archives created with ProDOS -8 ShrinkIt. The older "version 1" records didn't have CRCs in the thread -headers, so you will get a series of messages that look like this: - -ERROR: CRC mismatch: 0 old=0x0000 new=0x681b -ERROR: CRC mismatch: 1 old=0x0000 new=0x5570 -ERROR: CRC mismatch: 2 old=0x0000 new=0x4ec5 - -This will leave the original archive alone, making a copy of it named -"TwirlCopy678" in the current directory. It overwrites its temp file, -"TwirlTmp789", without prompting. - diff --git a/ciderpress/nufxlib/samples/TestBasic.c b/ciderpress/nufxlib/samples/TestBasic.c deleted file mode 100644 index 19fea41..0000000 --- a/ciderpress/nufxlib/samples/TestBasic.c +++ /dev/null @@ -1,1176 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Test basic features of the library. Run this without arguments. - */ -#include -#include -#include "NufxLib.h" -#include "Common.h" - -#define kTestArchive "nlbt.shk" -#define kTestTempFile "nlbt.tmp" - -#define kNumEntries 3 /* how many records are we going to add? */ - -/* stick to ASCII characters for these -- not doing conversions just yet */ -#define kTestEntryBytes "bytes" -#define kTestEntryBytesUPPER "BYTES" -#define kTestEntryEnglish "English" -#define kTestEntryLong "three|is a fairly long filename, complete with" \ - "punctuation and other nifty/bad stuff" -#define kLocalFssep '|' - -/* - * Globals. - */ -char gSuppressError = false; -#define FAIL_OK gSuppressError = true; -#define FAIL_BAD gSuppressError = false; - - -/* - * =========================================================================== - * Helper functions - * =========================================================================== - */ - -/* - * Get a single character of input from the user. - */ -static char TGetReplyChar(char defaultReply) -{ - char tmpBuf[32]; - - if (fgets(tmpBuf, sizeof(tmpBuf), stdin) == NULL) - return defaultReply; - if (tmpBuf[0] == '\n' || tmpBuf[0] == '\r') - return defaultReply; - - return tmpBuf[0]; -} - -NuError AddSimpleRecord(NuArchive* pArchive, const char* filenameMOR, - NuRecordIdx* pRecordIdx) -{ - NuFileDetails fileDetails; - - memset(&fileDetails, 0, sizeof(fileDetails)); - fileDetails.storageNameMOR = filenameMOR; - fileDetails.fileSysInfo = kLocalFssep; - fileDetails.access = kNuAccessUnlocked; - - return NuAddRecord(pArchive, &fileDetails, pRecordIdx); -} - - -/* - * Display error messages... or not. - */ -NuResult ErrorMessageHandler(NuArchive* pArchive, void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - if (gSuppressError) - return kNuOK; - - if (pErrorMessage->isDebug) { - fprintf(stderr, "%sNufxLib says: [%s:%d %s] %s\n", - pArchive == NULL ? "GLOBAL>" : "", - pErrorMessage->file, pErrorMessage->line, pErrorMessage->function, - pErrorMessage->message); - } else { - fprintf(stderr, "%sNufxLib says: %s\n", - pArchive == NULL ? "GLOBAL>" : "", - pErrorMessage->message); - } - - return kNuOK; -} - -/* - * This gets called when a buffer DataSource is no longer needed. - */ -NuResult FreeCallback(NuArchive* pArchive, void* args) -{ - free(args); - return kNuOK; -} - -/* - * If the test file currently exists, ask the user if it's okay to remove - * it. - * - * Returns 0 if the file was successfully removed, -1 if the file could not - * be removed (because the unlink failed, or the user refused). - */ -int RemoveTestFile(const char* title, const char* fileName) -{ - char answer; - - if (access(fileName, F_OK) == 0) { - printf("%s '%s' exists, remove (y/n)? ", title, fileName); - fflush(stdout); - answer = TGetReplyChar('n'); - if (tolower(answer) != 'y') - return -1; - if (unlink(fileName) < 0) { - perror("unlink"); - return -1; - } - } - return 0; -} - - -/* - * =========================================================================== - * Tests - * =========================================================================== - */ - -/* - * Make sure the flags that control how we open the file work right, - * and verify that we handle existing zero-byte archive files correctly. - */ -int Test_OpenFlags(void) -{ - NuError err; - FILE* fp = NULL; - NuArchive* pArchive = NULL; - - printf("... open zero-byte existing\n"); - fp = fopen(kTestArchive, kNuFileOpenWriteTrunc); - if (fp == NULL) { - perror("fopen kTestArchive"); - goto failed; - } - fclose(fp); - fp = NULL; - - FAIL_OK; - err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat|kNuOpenExcl, - &pArchive); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: file opened when it shouldn't have\n"); - goto failed; - } - - err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: file didn't open when it should have\n"); - goto failed; - } - - err = NuClose(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: close failed\n"); - goto failed; - } - pArchive = NULL; - - if (access(kTestArchive, F_OK) == 0) { - fprintf(stderr, "ERROR: archive should have been removed but wasn't\n"); - goto failed; - } - - return 0; - -failed: - if (pArchive != NULL) { - NuAbort(pArchive); - NuClose(pArchive); - } - return -1; -} - - -/* - * Add some files to the archive. These will be used by later tests. - */ -int Test_AddStuff(NuArchive* pArchive) -{ - NuError err; - uint8_t* buf = NULL; - NuDataSource* pDataSource = NULL; - NuRecordIdx recordIdx; - uint32_t status; - int i; - static const char* testMsg = - "This is a nice test message that has linefeeds in it so we can\n" - "see if the line conversion stuff is actually doing anything at\n" - "all. It's certainly nice to know that everything works the way\n" - "it's supposed to, which I suppose is why we have this nifty test\n" - "program available. It sure would be nice if everybody tested\n" - "their code, but where would Microsoft be without endless upgrades\n" - "and service packs? Bugs are what America was built on, and\n" - "anybody who says otherwise is a pinko commie lowlife. Verily.\n"; - - printf("... add 'bytes' record\n"); - buf = malloc(131072); - if (buf == NULL) - goto failed; - for (i = 0; i < 131072; i++) - *(buf+i) = i & 0xff; - - FAIL_OK; - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, NULL, 0, 131072, FreeCallback, &pDataSource); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: that should've failed!\n"); - goto failed; - } - - /* - * Create a data source for the big batch of bytes. - */ - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, buf, 0, 131072, FreeCallback, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: 'bytes' data source create failed (err=%d)\n", err); - goto failed; - } - buf = NULL; /* now owned by library */ - - err = AddSimpleRecord(pArchive, kTestEntryBytes, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: 'bytes' record failed (err=%d)\n", err); - goto failed; - } - - err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, - NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: 'bytes' thread add failed (err=%d)\n", err); - goto failed; - } - pDataSource = NULL; /* now owned by library */ - - - /* - * Create a data source for our lovely text message. - */ - printf("... add 'English' record\n"); - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, (const uint8_t*)testMsg, 0, strlen(testMsg), NULL, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: 'English' source create failed (err=%d)\n", err); - goto failed; - } - - FAIL_OK; - err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, - NULL); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: 'English' add should've conflicted!\n"); - goto failed; - } - - FAIL_OK; - err = AddSimpleRecord(pArchive, kTestEntryBytes, &recordIdx); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: duplicates not allowed, should've failed\n"); - goto failed; - } - - err = AddSimpleRecord(pArchive, kTestEntryEnglish, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: 'English' record failed (err=%d)\n", err); - goto failed; - } - - err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, - NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: 'English' thread add failed (err=%d)\n", err); - goto failed; - } - pDataSource = NULL; /* now owned by library */ - - - /* - * Create an empty file with a rather non-empty name. Add it as - * a resource fork. - */ - printf("... add 'long' record\n"); - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, NULL, 0, 0, NULL, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: 'English' source create failed (err=%d)\n", err); - goto failed; - } - - err = AddSimpleRecord(pArchive, kTestEntryLong, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: 'long' record failed (err=%d)\n", err); - goto failed; - } - - err = NuAddThread(pArchive, recordIdx, kNuThreadIDRsrcFork, pDataSource, - NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: 'long' thread add failed (err=%d)\n", err); - goto failed; - } - pDataSource = NULL; /* now owned by library */ - - - /* - * Flush changes. - */ - err = NuFlush(pArchive, &status); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't flush after add (err=%d, status=%u)\n", - err, status); - goto failed; - } - - /* - * Flush again; should succeed since it doesn't have to do anything. - */ - err = NuFlush(pArchive, &status); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: second add flush failed (err=%d, status=%u)\n", - err, status); - goto failed; - } - - return 0; -failed: - if (pDataSource != NULL) - NuFreeDataSource(pDataSource); - if (buf != NULL) - free(buf); - return -1; -} - - -/* - * Make sure that what we're seeing makes sense. - */ -NuResult TestContentsCallback(NuArchive* pArchive, void* vpRecord) -{ - const NuRecord* pRecord = (NuRecord*) vpRecord; - - if (strcmp(pRecord->filenameMOR, kTestEntryBytes) == 0 || - strcmp(pRecord->filenameMOR, kTestEntryEnglish) == 0 || - strcmp(pRecord->filenameMOR, kTestEntryLong) == 0) - { - return kNuOK; - } - - fprintf(stderr, "ERROR: found mystery entry '%s'\n", pRecord->filenameMOR); - return kNuAbort; -} - - -/* - * Verify that the contents look about right. - */ -int Test_Contents(NuArchive* pArchive) -{ - NuError err; - long posn; - NuRecordIdx recordIdx; - const NuRecord* pRecord; - int cc; - - /* - * First, do it with a callback. - */ - err = NuContents(pArchive, TestContentsCallback); - if (err != kNuErrNone) - goto failed; - - /* - * Now step through the records with get-by-position and verify that - * they're in the expected order. - */ - for (posn = 0; posn < kNumEntries; posn++) { - err = NuGetRecordIdxByPosition(pArchive, posn, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", - posn, err); - goto failed; - } - - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - goto failed; - } - assert(pRecord != NULL); - - switch (posn) { - case 0: - cc = strcmp(pRecord->filenameMOR, kTestEntryBytes); - break; - case 1: - cc = strcmp(pRecord->filenameMOR, kTestEntryEnglish); - break; - case 2: - cc = strcmp(pRecord->filenameMOR, kTestEntryLong); - if (!cc) - cc = !(pRecord->recStorageType == kNuStorageExtended); - break; - default: - fprintf(stderr, "ERROR: somebody forgot to put a case here (%ld)\n", - posn); - cc = -1; - } - - if (cc) { - fprintf(stderr, "ERROR: got '%s' for %ld (%u), not expected\n", - pRecord->filenameMOR, posn, recordIdx); - goto failed; - } - } - - /* - * Read one more past the end, should fail. - */ - FAIL_OK; - err = NuGetRecordIdxByPosition(pArchive, posn, &recordIdx); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: too many records (%ld was ok)\n", posn); - goto failed; - } - - return 0; -failed: - return -1; -} - - -/* - * Selection callback filter for "test". This gets called once per record, - * twice per record for forked files. - */ -NuResult VerifySelectionCallback(NuArchive* pArchive, void* vpProposal) -{ - NuError err; - const NuSelectionProposal* pProposal = vpProposal; - long count; - - if (pProposal->pRecord == NULL || pProposal->pThread == NULL || - pProposal->pRecord->filenameMOR == NULL) - { - fprintf(stderr, "ERROR: unexpected NULL in proposal\n"); - goto failed; - } - - err = NuGetExtraData(pArchive, (void**) &count); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get extra data (err=%d)\n", err); - goto failed; - } - - count++; - - err = NuSetExtraData(pArchive, (void*) count); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to inc extra data (err=%d)\n", err); - goto failed; - } - - return kNuOK; -failed: - return kNuAbort; -} - -/* - * Verify the archive contents. - */ -int Test_Verify(NuArchive* pArchive) -{ - NuError err; - long count; - - printf("... verifying CRCs\n"); - - if (NuSetSelectionFilter(pArchive, VerifySelectionCallback) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: unable to set selection filter\n"); - goto failed; - } - - err = NuSetExtraData(pArchive, (void*) 0); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to set extra data (err=%d)\n", err); - goto failed; - } - - err = NuTest(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: verify failed (err=%d)\n", err); - goto failed; - } - - err = NuGetExtraData(pArchive, (void**) &count); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: last extra data get failed (err=%d)\n", err); - goto failed; - } - - /* the count will be one higher than the number of records because - the last entry is forked, and each fork is tested separately */ - if (count != kNumEntries + 1) { - fprintf(stderr, "ERROR: verified %ld when expecting %d\n", count, - kNumEntries); - goto failed; - } - - return 0; -failed: - return -1; -} - -/* - * Extract stuff. - */ -int Test_Extract(NuArchive* pArchive) -{ - NuError err; - NuRecordIdx recordIdx; - const NuRecord* pRecord; - const NuThread* pThread; - NuDataSink* pDataSink = NULL; - uint8_t* buf = NULL; - - printf("... extracting files\n"); - - /* - * Tell it the current system uses CRLF, so it'll bloat up when we do - * a text conversion. - */ - err = NuSetValue(pArchive, kNuValueEOL, kNuEOLCRLF); - - /* - * Extract "bytes". - */ - err = NuGetRecordIdxByName(pArchive, kTestEntryBytesUPPER, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", kTestEntryBytes, - err); - goto failed; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - goto failed; - } - assert(pRecord != NULL); - - /* we're not using ShrinkIt compat mode, so there should not be a comment */ - pThread = NuGetThread(pRecord, 1); - assert(pThread != NULL); - if (NuGetThreadID(pThread) != kNuThreadIDDataFork) { - fprintf(stderr, "ERROR: 'bytes' had unexpected threadID 0x%08x\n", - NuGetThreadID(pThread)); - goto failed; - } - - buf = malloc(pThread->actualThreadEOF); - if (buf == NULL) { - fprintf(stderr, "ERROR: malloc(%u) failed\n",pThread->actualThreadEOF); - goto failed; - } - - /* - * Try to extract it with text conversion off. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); - goto failed; - } - - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't extract 'bytes' (off) (err=%d)\n", - err); - goto failed; - } - NuFreeDataSink(pDataSink); - pDataSink = NULL; - - /* - * Try to extract with "on" conversion, which should fail because the - * buffer is too small. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertOn, buf, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); - goto failed; - } - - FAIL_OK; - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: managed to extract bloated 'bytes'?\n"); - goto failed; - } - NuFreeDataSink(pDataSink); - pDataSink = NULL; - - /* - * Try to extract with "auto" conversion, which should conclude that - * the input is text and not try to convert. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertAuto, buf, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); - goto failed; - } - - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't extract 'bytes' (auto) (err=%d)\n", - err); - goto failed; - } - NuFreeDataSink(pDataSink); - pDataSink = NULL; - - - - free(buf); - buf = NULL; - - - - /* - * Extract "English". - */ - err = NuGetRecordIdxByName(pArchive, kTestEntryEnglish, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", - kTestEntryEnglish, err); - goto failed; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - goto failed; - } - assert(pRecord != NULL); - - /* we're not using ShrinkIt compat mode, so there should not be a comment */ - pThread = NuGetThread(pRecord, 1); - assert(pThread != NULL); - if (NuGetThreadID(pThread) != kNuThreadIDDataFork) { - fprintf(stderr, "ERROR: 'English' had unexpected threadID 0x%08x\n", - NuGetThreadID(pThread)); - goto failed; - } - - buf = malloc(pThread->actualThreadEOF); - if (buf == NULL) { - fprintf(stderr, "ERROR: malloc(%u) failed\n", pThread->actualThreadEOF); - goto failed; - } - - /* - * Try to extract it with text conversion off. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); - goto failed; - } - - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't extract 'bytes' (off) (err=%d)\n", - err); - goto failed; - } - NuFreeDataSink(pDataSink); - pDataSink = NULL; - - /* - * Try to extract with "auto" conversion, which should fail because the - * buffer is too small, and the input looks like text. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertAuto, buf, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); - goto failed; - } - - FAIL_OK; - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: managed to extract bloated 'English'?\n"); - goto failed; - } - NuFreeDataSink(pDataSink); - pDataSink = NULL; - - - - /*Free(buf);*/ - /*buf = NULL;*/ - - - - /* - * Extract "long" (which is zero bytes). - */ - err = NuGetRecordIdxByName(pArchive, kTestEntryLong, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", - kTestEntryLong, err); - goto failed; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - goto failed; - } - assert(pRecord != NULL); - - /* we're not using ShrinkIt compat mode, so there should not be a comment */ - pThread = NuGetThread(pRecord, 1); - assert(pThread != NULL); - if (NuGetThreadID(pThread) != kNuThreadIDRsrcFork) { - fprintf(stderr, "ERROR: 'Long' had unexpected threadID 0x%08x\n", - NuGetThreadID(pThread)); - goto failed; - } - - /* - * Try it with text conversion on; shouldn't matter. - */ - err = NuCreateDataSinkForBuffer(true, kNuConvertOn, buf, - 1, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't create data sink (err=%d)\n", err); - goto failed; - } - - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't extract 'Long' (off) (err=%d)\n", - err); - goto failed; - } - NuFreeDataSink(pDataSink); - pDataSink = NULL; - - - - free(buf); - buf = NULL; - - - - return 0; -failed: - if (buf != NULL) - free(buf); - if (pDataSink != NULL) - (void) NuFreeDataSink(pDataSink); - return -1; -} - -/* - * Delete the first and last records. Does *not* flush the archive. - */ -int Test_Delete(NuArchive* pArchive) -{ - NuError err; - NuRecordIdx recordIdx; - const NuRecord* pRecord; - const NuThread* pThread = NULL; - long count; - int idx; - - printf("... deleting first and last\n"); - - /* - * Delete all threads from the first record ("bytes"). - */ - err = NuGetRecordIdxByPosition(pArchive, 0, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't find #%d (err=%d)\n", 0, err); - goto failed; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - goto failed; - } - assert(pRecord != NULL); - assert(pRecord->recTotalThreads > 0); - - for (idx = 0; idx < (int)pRecord->recTotalThreads; idx++) { - pThread = NuGetThread(pRecord, idx); - assert(pThread != NULL); - - err = NuDeleteThread(pArchive, pThread->threadIdx); - if (err != kNuErrNone) { - fprintf(stderr, - "ERROR: couldn't delete thread #%d (%u) (err=%d)\n", - idx, recordIdx, err); - goto failed; - } - } - - /* try to re-delete the same thread */ - assert(pThread != NULL); - FAIL_OK; - err = NuDeleteThread(pArchive, pThread->threadIdx); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, "ERROR: allowed to re-delete thread (%u) (err=%d)\n", - recordIdx, err); - goto failed; - } - - /* try to delete the modified record */ - FAIL_OK; - err = NuDeleteRecord(pArchive, recordIdx); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, - "ERROR: able to delete modified record (%u) (err=%d)\n", - recordIdx, err); - goto failed; - } - - /* - * Make sure the attr hasn't been updated yet. - */ - count = 0; - err = NuGetAttr(pArchive, kNuAttrNumRecords, (uint32_t*) &count); - if (count != kNumEntries) { - fprintf(stderr, "ERROR: kNuAttrNumRecords %ld vs %d\n", - count, kNumEntries); - goto failed; - } - - /* - * Delete the last record ("long"). - */ - err = NuGetRecordIdxByPosition(pArchive, kNumEntries-1, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't find #%d (err=%d)\n", 0, err); - goto failed; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - goto failed; - } - assert(pRecord != NULL); - - /* grab the first thread before we whack the record */ - pThread = NuGetThread(pRecord, 0); - assert(pThread != NULL); - - err = NuDeleteRecord(pArchive, recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to delete record #%d (%u) (err=%d)\n", - kNumEntries-1, recordIdx, err); - goto failed; - } - - /* try to delete a thread from the deleted record */ - FAIL_OK; - err = NuDeleteThread(pArchive, pThread->threadIdx); - FAIL_BAD; - if (err == kNuErrNone) { - fprintf(stderr, - "ERROR: allowed to delete from deleted (%u) (err=%d)\n", - pThread->threadIdx, err); - goto failed; - } - - return 0; -failed: - return -1; -} - - -/* - * Verify that the count in the master header has been updated. - */ -int Test_MasterCount(NuArchive* pArchive, long expected) -{ - NuError err; - const NuMasterHeader* pMasterHeader; - - printf("... checking master count\n"); - - err = NuGetMasterHeader(pArchive, &pMasterHeader); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err); - goto failed; - } - - if (pMasterHeader->mhTotalRecords != (uint32_t)expected) { - fprintf(stderr, "ERROR: unexpected MH count (%u vs %ld)\n", - pMasterHeader->mhTotalRecords, expected); - goto failed; - } - - return 0; -failed: - return -1; -} - - -/* - * Run some tests. - * - * Returns 0 on success, -1 on error. - */ -int DoTests(void) -{ - NuError err; - NuArchive* pArchive = NULL; - uint32_t status; - int cc, result = 0; - - /* - * Make sure we're starting with a clean slate. - */ - if (RemoveTestFile("Test archive", kTestArchive) < 0) { - goto failed; - } - if (RemoveTestFile("Test temp file", kTestTempFile) < 0) { - goto failed; - } - - /* - * Test some of the open flags. - */ - if (Test_OpenFlags() != 0) - goto failed; - - /* - * Create a new archive to play with. - */ - err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat|kNuOpenExcl, - &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: NuOpenRW failed (err=%d)\n", err); - goto failed; - } - if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: couldn't set message handler\n"); - goto failed; - } - - /* - * Add some test entries. - */ - if (Test_AddStuff(pArchive) != 0) - goto failed; - - /* - * Check the archive contents. - */ - printf("... checking contents\n"); - if (Test_Contents(pArchive) != 0) - goto failed; - - /* - * Reopen it read-only. - */ - printf("... reopening archive read-only\n"); - err = NuClose(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: mid NuClose failed (err=%d)\n", err); - goto failed; - } - pArchive = NULL; - - err = NuOpenRO(kTestArchive, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: NuOpenRO failed (err=%d)\n", err); - goto failed; - } - if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: couldn't set message handler\n"); - goto failed; - } - - /* - * Make sure the TOC (i.e. list of files) is still what we expect. - */ - printf("... checking contents\n"); - if (Test_Contents(pArchive) != 0) - goto failed; - - /* - * Verify the archive data. - */ - if (Test_Verify(pArchive) != 0) - goto failed; - - /* - * Extract the files. - */ - if (Test_Extract(pArchive) != 0) - goto failed; - - /* - * Reopen it read-write. - */ - printf("... reopening archive read-write\n"); - err = NuClose(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: late NuClose failed (err=%d)\n", err); - goto failed; - } - pArchive = NULL; - - err = NuOpenRW(kTestArchive, kTestTempFile, 0, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: re-NuOpenRW failed (err=%d)\n", err); - goto failed; - } - if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: couldn't set message handler\n"); - goto failed; - } - - /* - * Contents shouldn't have changed. - */ - printf("... checking contents\n"); - if (Test_Contents(pArchive) != 0) - goto failed; - - /* - * Test deletion. - */ - if (Test_Delete(pArchive) != 0) - goto failed; - - /* - * Abort the changes and verify that nothing has changed. - */ - printf("... aborting changes\n"); - err = NuAbort(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: abort failed (err=%d)\n", err); - goto failed; - } - - printf("... checking contents\n"); - if (Test_Contents(pArchive) != 0) - goto failed; - - /* - * Delete them again. - */ - if (Test_Delete(pArchive) != 0) - goto failed; - - /* - * Flush the deletions. This should remove the first and last records. - */ - err = NuFlush(pArchive, &status); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: flush failed (err=%d, status=%d)\n", - err, status); - goto failed; - } - - /* - * Check count in master header. - */ - if (Test_MasterCount(pArchive, kNumEntries-2) != 0) - goto failed; - - /* - * That's all, folks... - */ - NuClose(pArchive); - pArchive = NULL; - - printf("... removing '%s'\n", kTestArchive); - cc = unlink(kTestArchive); - if (cc < 0) { - perror("unlink kTestArchive"); - goto failed; - } - - -leave: - if (pArchive != NULL) { - NuAbort(pArchive); - NuClose(pArchive); - } - return result; - -failed: - result = -1; - goto leave; -} - - -/* - * Crank away. - */ -int main(void) -{ - int32_t major, minor, bug; - const char* pBuildDate; - const char* pBuildFlags; - int cc; - - (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, &pBuildFlags); - printf("Using NuFX library v%d.%d.%d, built on or after\n" - " %s with [%s]\n\n", - major, minor, bug, pBuildDate, pBuildFlags); - - if (NuSetGlobalErrorMessageHandler(ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: can't set the global message handler"); - exit(1); - } - - printf("... starting tests\n"); - - cc = DoTests(); - - printf("... tests ended, %s\n", cc == 0 ? "SUCCESS" : "FAILURE"); - exit(cc != 0); -} - diff --git a/ciderpress/nufxlib/samples/TestExtract.c b/ciderpress/nufxlib/samples/TestExtract.c deleted file mode 100644 index 4e8bfb2..0000000 --- a/ciderpress/nufxlib/samples/TestExtract.c +++ /dev/null @@ -1,473 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Test extraction of individual threads in various ways. The net result - * of this is three files (out.file, out.fp, out.buf) that contain the - * result of writing all filenames in an archive to the same data sink. - * - * This gathers up information on the contents of the archive via a - * callback, and then emits all of the data at once. - * - * (This was originally written in C++, and converted to C after I repented.) - */ -#include -#include -#include -#include -#include "NufxLib.h" -#include "Common.h" - - -/*#define false 0*/ -/*#define true (!false)*/ - -#define kHappySize 2408 - - -/* - * =========================================================================== - * ArchiveRecord - * =========================================================================== - */ - -/* - * Track an archive record. - */ -typedef struct ArchiveRecord { - char* filenameMOR; - NuRecordIdx recordIdx; - - long numThreads; - NuThread* pThreads; - - struct ArchiveRecord* pNext; -} ArchiveRecord; - - -/* - * Alloc a new ArchiveRecord. - */ -ArchiveRecord* ArchiveRecord_New(const NuRecord* pRecord) -{ - ArchiveRecord* pArcRec = NULL; - - pArcRec = malloc(sizeof(*pArcRec)); - if (pArcRec == NULL) - return NULL; - - if (pRecord->filenameMOR == NULL) - pArcRec->filenameMOR = strdup(""); - else - pArcRec->filenameMOR = strdup(pRecord->filenameMOR); - - pArcRec->recordIdx = pRecord->recordIdx; - pArcRec->numThreads = NuRecordGetNumThreads(pRecord); - (void) NuRecordCopyThreads(pRecord, &pArcRec->pThreads); - - pArcRec->pNext = NULL; - - return pArcRec; -} - -/* - * Free up an ArchiveRecord. - */ -void ArchiveRecord_Free(ArchiveRecord* pArcRec) -{ - if (pArcRec == NULL) - return; - - if (pArcRec->filenameMOR != NULL) - free(pArcRec->filenameMOR); - if (pArcRec->pThreads != NULL) - free(pArcRec->pThreads); - free(pArcRec); -} - -/* - * Find a thread with a matching NuThreadID. - */ -const NuThread* ArchiveRecord_FindThreadByID(const ArchiveRecord* pArcRec, - NuThreadID threadID) -{ - const NuThread* pThread; - int i; - - for (i = 0; i < pArcRec->numThreads; i++) { - pThread = NuThreadGetByIdx(pArcRec->pThreads, i); - if (NuGetThreadID(pThread) == threadID) - return pThread; - } - - return NULL; -} - - -const char* ArchiveRecord_GetFilename(const ArchiveRecord* pArcRec) -{ - return pArcRec->filenameMOR; -} - -NuRecordIdx ArchiveRecord_GetRecordIdx(const ArchiveRecord* pArcRec) -{ - return pArcRec->recordIdx; -} - -long ArchiveRecord_GetNumThreads(const ArchiveRecord* pArcRec) -{ - return pArcRec->numThreads; -} - -const NuThread* ArchiveRecord_GetThread(const ArchiveRecord* pArcRec, int idx) -{ - if (idx < 0 || idx >= pArcRec->numThreads) - return NULL; - return NuThreadGetByIdx(pArcRec->pThreads, idx); -} - -void ArchiveRecord_SetNext(ArchiveRecord* pArcRec, ArchiveRecord* pNextRec) -{ - pArcRec->pNext = pNextRec; -} - -ArchiveRecord* ArchiveRecord_GetNext(const ArchiveRecord* pArcRec) -{ - return pArcRec->pNext; -} - - -/* - * =========================================================================== - * ArchiveData - * =========================================================================== - */ - -/* - * A collection of records. - */ -typedef struct ArchiveData { - long numRecords; - ArchiveRecord* pRecordHead; - ArchiveRecord* pRecordTail; -} ArchiveData; - - -ArchiveData* ArchiveData_New(void) -{ - ArchiveData* pArcData; - - pArcData = malloc(sizeof(*pArcData)); - if (pArcData == NULL) - return NULL; - - pArcData->numRecords = 0; - pArcData->pRecordHead = pArcData->pRecordTail = NULL; - - return pArcData; -} - -void ArchiveData_Free(ArchiveData* pArcData) -{ - ArchiveRecord* pNext; - - if (pArcData == NULL) - return; - - printf("*** Deleting %ld records!\n", pArcData->numRecords); - while (pArcData->pRecordHead != NULL) { - pNext = ArchiveRecord_GetNext(pArcData->pRecordHead); - ArchiveRecord_Free(pArcData->pRecordHead); - pArcData->pRecordHead = pNext; - } - - free(pArcData); -} - - -ArchiveRecord* ArchiveData_GetRecordHead(const ArchiveData* pArcData) -{ - return pArcData->pRecordHead; -} - - -/* add an ArchiveRecord to the list pointed at by ArchiveData */ -void ArchiveData_AddRecord(ArchiveData* pArcData, ArchiveRecord* pRecord) -{ - assert(pRecord != NULL); - assert((pArcData->pRecordHead == NULL && pArcData->pRecordTail == NULL) || - (pArcData->pRecordHead != NULL && pArcData->pRecordTail != NULL)); - - if (pArcData->pRecordHead == NULL) { - /* first */ - pArcData->pRecordHead = pArcData->pRecordTail = pRecord; - } else { - /* not first, add to end */ - ArchiveRecord_SetNext(pArcData->pRecordTail, pRecord); - pArcData->pRecordTail = pRecord; - } - - pArcData->numRecords++; -} - -/* dump the contents of the ArchiveData to stdout */ -void ArchiveData_DumpContents(const ArchiveData* pArcData) -{ - ArchiveRecord* pArcRec; - - pArcRec = pArcData->pRecordHead; - while (pArcRec != NULL) { - const NuThread* pThread; - int i, count; - - printf("%5u '%s'\n", - ArchiveRecord_GetRecordIdx(pArcRec), - ArchiveRecord_GetFilename(pArcRec)); - - count = ArchiveRecord_GetNumThreads(pArcRec); - for (i = 0; i < count; i++) { - pThread = ArchiveRecord_GetThread(pArcRec, i); - printf(" %5u 0x%04x 0x%04x\n", pThread->threadIdx, - pThread->thThreadClass, pThread->thThreadKind); - } - - pArcRec = ArchiveRecord_GetNext(pArcRec); - } -} - - -/* - * =========================================================================== - * Main stuff - * =========================================================================== - */ - -/* - * Callback function to collect archive information. - */ -NuResult GatherContents(NuArchive* pArchive, void* vpRecord) -{ - NuRecord* pRecord = (NuRecord*) vpRecord; - ArchiveData* pArchiveData = NULL; - ArchiveRecord* pArchiveRecord = ArchiveRecord_New(pRecord); - - NuGetExtraData(pArchive, (void**)&pArchiveData); - assert(pArchiveData != NULL); - - printf("*** Filename = '%s'\n", - pRecord->filenameMOR == NULL ? - "" : pRecord->filenameMOR); - - ArchiveData_AddRecord(pArchiveData, pArchiveRecord); - - return kNuOK; -} - - -/* - * Copy the filename thread from every record to "pDataSink". - */ -NuError ReadAllFilenameThreads(NuArchive* pArchive, ArchiveData* pArchiveData, - NuDataSink* pDataSink) -{ - NuError err = kNuErrNone; - ArchiveRecord* pArchiveRecord; - const NuThread* pThread; - - pArchiveRecord = ArchiveData_GetRecordHead(pArchiveData); - while (pArchiveRecord != NULL) { - pThread = ArchiveRecord_FindThreadByID(pArchiveRecord, - kNuThreadIDFilename); - if (pThread != NULL) { - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "*** Extract failed (%d)\n", err); - goto bail; - } - } - pArchiveRecord = ArchiveRecord_GetNext(pArchiveRecord); - } - -bail: - return err; -} - - -/* extract every filename thread into a single file, overwriting each time */ -NuError ExtractToFile(NuArchive* pArchive, ArchiveData* pArchiveData) -{ - NuError err; - NuDataSink* pDataSink = NULL; - - err = NuCreateDataSinkForFile(true, kNuConvertOff, "out.file", PATH_SEP, - &pDataSink); - if (err != kNuErrNone) - goto bail; - - err = NuSetValue(pArchive, kNuValueHandleExisting, kNuAlwaysOverwrite); - if (err != kNuErrNone) - goto bail; - - err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink); - if (err != kNuErrNone) - goto bail; - -bail: - (void) NuFreeDataSink(pDataSink); - if (err == kNuErrNone) - printf("*** File write complete\n"); - return err; -} - -/* extract every filename thread into a FILE*, appending */ -NuError ExtractToFP(NuArchive* pArchive, ArchiveData* pArchiveData) -{ - NuError err; - FILE* fp = NULL; - NuDataSink* pDataSink = NULL; - - if ((fp = fopen("out.fp", kNuFileOpenWriteTrunc)) == NULL) - return kNuErrFileOpen; - - err = NuCreateDataSinkForFP(true, kNuConvertOff, fp, &pDataSink); - if (err != kNuErrNone) - goto bail; - - err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink); - if (err != kNuErrNone) - goto bail; - -bail: - (void) NuFreeDataSink(pDataSink); - if (fp != NULL) - fclose(fp); - if (err == kNuErrNone) - printf("*** FP write complete\n"); - return err; -} - -/* extract every filename thread into a buffer, advancing as we go */ -NuError ExtractToBuffer(NuArchive* pArchive, ArchiveData* pArchiveData) -{ - NuError err; - uint8_t buffer[kHappySize]; - NuDataSink* pDataSink = NULL; - uint32_t count; - - err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buffer, kHappySize, - &pDataSink); - if (err != kNuErrNone) - goto bail; - - err = ReadAllFilenameThreads(pArchive, pArchiveData, pDataSink); - if (err != kNuErrNone) { - if (err == kNuErrBufferOverrun) - fprintf(stderr, "*** Hey, buffer wasn't big enough!\n"); - goto bail; - } - - /* write the buffer to a file */ - (void) NuDataSinkGetOutCount(pDataSink, &count); - if (count > 0) { - FILE* fp; - if ((fp = fopen("out.buf", kNuFileOpenWriteTrunc)) != NULL) { - - printf("*** Writing %u bytes\n", count); - if (fwrite(buffer, count, 1, fp) != 1) - err = kNuErrFileWrite; - fclose(fp); - } - } else { - printf("*** No data found!\n"); - } - -bail: - (void) NuFreeDataSink(pDataSink); - return err; -} - - -/* - * Do file stuff. - */ -int DoFileStuff(const UNICHAR* filenameUNI) -{ - NuError err; - NuArchive* pArchive = NULL; - ArchiveData* pArchiveData = ArchiveData_New(); - - err = NuOpenRO(filenameUNI, &pArchive); - if (err != kNuErrNone) - goto bail; - - NuSetExtraData(pArchive, pArchiveData); - - printf("*** Gathering contents!\n"); - err = NuContents(pArchive, GatherContents); - if (err != kNuErrNone) - goto bail; - - printf("*** Dumping contents!\n"); - ArchiveData_DumpContents(pArchiveData); - - err = ExtractToFile(pArchive, pArchiveData); - if (err != kNuErrNone) - goto bail; - err = ExtractToFP(pArchive, pArchiveData); - if (err != kNuErrNone) - goto bail; - err = ExtractToBuffer(pArchive, pArchiveData); - if (err != kNuErrNone) - goto bail; - -bail: - if (err != kNuErrNone) - fprintf(stderr, "*** ERROR: got error %d\n", err); - - if (pArchive != NULL) { - NuError err2 = NuClose(pArchive); - if (err == kNuErrNone && err2 != kNuErrNone) - err = err2; - } - - ArchiveData_Free(pArchiveData); - - return err; -} - - -/* - * Grab the name of an archive to read. If no name was provided, use stdin. - */ -int main(int argc, char** argv) -{ - int32_t major, minor, bug; - const char* pBuildDate; - FILE* infp = NULL; - int cc; - - (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, NULL); - printf("Using NuFX lib %d.%d.%d built on or after %s\n", - major, minor, bug, pBuildDate); - - if (argc == 2) { - infp = fopen(argv[1], kNuFileOpenReadOnly); - if (infp == NULL) { - perror("fopen failed"); - exit(1); - } - } else { - fprintf(stderr, "ERROR: you have to specify a filename\n"); - exit(2); - } - - cc = DoFileStuff(argv[1]); - - if (infp != NULL) - fclose(infp); - - exit(cc != 0); -} - diff --git a/ciderpress/nufxlib/samples/TestNames.c b/ciderpress/nufxlib/samples/TestNames.c deleted file mode 100644 index 61ea479..0000000 --- a/ciderpress/nufxlib/samples/TestNames.c +++ /dev/null @@ -1,580 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Test local and storage names with Unicode and Mac OS Roman content. - * - * On Windows, opening files with fancy filenames requires UTF-16 and - * special functions. On Linux and Mac OS X we're just writing UTF-8 data, - * so they don't really need to do anything special other than be 8-bit - * clean. NufxLib functions take UTF-8 strings, so on Windows we define - * everything in UTF-16 and convert to UTF-8. (We need the UTF-16 form so - * we can use "wide" I/O functions to confirm that the file was created - * with the correct name.) - * - * To see files with the correct appearance with "ls", you may need to - * do something like: - * - * % LC_ALL=en_US.UTF-8 ls - * - * (Many users set LC_ALL=POSIX to avoid GNU grep slowdowns and altered - * sort ordering in ls.) - */ -#include -#include -#include "NufxLib.h" -#include "Common.h" - -/* - * Test filenames. - * - * The local filename (kTestArchive) contains non-MOR Unicode values - * (two Japanese characters that Google Translate claims form the verb - * "shrink"). The temp file name is similar. - * - * The entry name uses a mix of simple ASCII, CP1252 MOR, and - * non-CP1252 MOR characters: fl ligature, double dagger, copyright symbol, - * Apple logo (the latter of which doesn't have a glyph on Windows or Linux). - * All of the characters have MOR translations. - */ -#ifdef USE_UTF16 -const UNICHAR kTestArchive[] = L"nlTest\u7e2e\u3080.shk"; -const UNICHAR kTestEntryName[] = L"nl-test\u2013\ufb01_\u2021_\u00a9\uf8ff!"; -const UNICHAR kTestTempFile[] = L"nlTest\4e00\u6642\u30d5\u30a1\u30a4\u30eb.tmp"; -#else -const UNICHAR kTestArchive[] = "nlTest\xe7\xb8\xae\xe3\x82\x80.shk"; -const UNICHAR kTestEntryName[] = "nl-test\xe2\x80\x93\xef\xac\x81_\xe2\x80\xa1_" - "\xc2\xa9\xef\xa3\xbf!"; -const UNICHAR kTestTempFile[] = "nlTest\xe4\xb8\x80\xe6\x99\x82\xe3\x83\x95" - "\xe3\x82\xa1\xe3\x82\xa4\xe3\x83\xab.tmp"; -#endif - -const UNICHAR kLocalFssep = '|'; - - -/* - * =========================================================================== - * Helper functions - * =========================================================================== - */ - -/* - * Get a single character of input from the user. - */ -static char TGetReplyChar(char defaultReply) -{ - char tmpBuf[32]; - - if (fgets(tmpBuf, sizeof(tmpBuf), stdin) == NULL) - return defaultReply; - if (tmpBuf[0] == '\n' || tmpBuf[0] == '\r') - return defaultReply; - - return tmpBuf[0]; -} - -NuError AddSimpleRecord(NuArchive* pArchive, const char* fileNameMOR, - NuRecordIdx* pRecordIdx) -{ - NuFileDetails fileDetails; - - memset(&fileDetails, 0, sizeof(fileDetails)); - fileDetails.storageNameMOR = fileNameMOR; - fileDetails.fileSysInfo = kLocalFssep; - fileDetails.access = kNuAccessUnlocked; - - return NuAddRecord(pArchive, &fileDetails, pRecordIdx); -} - -/* - * Display error messages... or not. - */ -NuResult ErrorMessageHandler(NuArchive* pArchive, void* vErrorMessage) -{ - const NuErrorMessage* pErrorMessage = (const NuErrorMessage*) vErrorMessage; - - //if (gSuppressError) - // return kNuOK; - - if (pErrorMessage->isDebug) { - fprintf(stderr, "%sNufxLib says: [%s:%d %s] %s\n", - pArchive == NULL ? "GLOBAL>" : "", - pErrorMessage->file, pErrorMessage->line, pErrorMessage->function, - pErrorMessage->message); - } else { - fprintf(stderr, "%sNufxLib says: %s\n", - pArchive == NULL ? "GLOBAL>" : "", - pErrorMessage->message); - } - - return kNuOK; -} - -#ifdef USE_UTF16 -TODO - use _waccess, _wunlink, etc. -#else -int RemoveTestFile(const char* title, const char* fileName) -{ - char answer; - - if (access(fileName, F_OK) == 0) { - printf("%s '%s' exists, remove (y/n)? ", title, fileName); - fflush(stdout); - answer = TGetReplyChar('n'); - if (tolower(answer) != 'y') - return -1; - if (unlink(fileName) < 0) { - perror("unlink"); - return -1; - } - } - return 0; -} -#endif - -/* - * Utility function that wraps NuConvertUNIToMOR, allocating a new - * buffer to hold the converted string. The caller must free the result. - */ -char* CopyUNIToMOR(const UNICHAR* stringUNI) -{ - size_t morLen; - char* morBuf; - - morLen = NuConvertUNIToMOR(stringUNI, NULL, 0); - if (morLen == (size_t) -1) { - return NULL; - } - morBuf = (char*) malloc(morLen); - (void) NuConvertUNIToMOR(stringUNI, morBuf, morLen); - return morBuf; -} - -/* - * Utility function that wraps NuConvertMORToUNI, allocating a new - * buffer to hold the converted string. The caller must free the result. - */ -UNICHAR* CopyMORToUNI(const char* stringMOR) -{ - size_t uniLen; - char* uniBuf; - - uniLen = NuConvertMORToUNI(stringMOR, NULL, 0); - if (uniLen == (size_t) -1) { - return NULL; - } - uniBuf = (UNICHAR*) malloc(uniLen); - (void) NuConvertMORToUNI(stringMOR, uniBuf, uniLen); - return uniBuf; -} - - -/* - * =========================================================================== - * Tests - * =========================================================================== - */ - -void DumpMorString(const char* str) -{ - printf("(%d) ", (int) strlen(str)); - while (*str != '\0') { - if (*str >= 0x20 && *str < 0x7f) { - putchar(*str); - } else { - printf("\\x%02x", (uint8_t) *str); - } - str++; - } - putchar('\n'); -} - -void DumpUnicharString(const UNICHAR* str) -{ - printf("(%d) ", (int) strlen(str)); - while (*str != '\0') { - if (*str >= 0x20 && *str < 0x7f) { - putchar(*str); - } else { - if (sizeof(UNICHAR) == 1) { - printf("\\x%02x", (uint8_t) *str); - } else { - printf("\\u%04x", (uint16_t) *str); - } - } - str++; - } - putchar('\n'); -} - -/* - * Some basic string conversion unit tests. - * - * TODO: test with short buffer, make sure we don't get partial code - * points when converting to Unicode - */ -int TestStringConversion(void) -{ - static const char kMORTest[] = "test\xe0\xe9\xed\xf3\xfa#\xf0\xb0"; - - size_t outLen; - char morBuf[512]; - UNICHAR uniBuf[512]; - - // convert test string to Unicode - memset(uniBuf, 0xcc, sizeof(uniBuf)); - //printf("MOR: "); DumpMorString(kMORTest); - - outLen = NuConvertMORToUNI(kMORTest, NULL, 0); - //printf("outLen is %u\n", (unsigned int) outLen); - if (NuConvertMORToUNI(kMORTest, uniBuf, sizeof(uniBuf)) != outLen) { - fprintf(stderr, "Inconsistent MORToUNI len\n"); - return -1; - } - //printf("UNI: "); DumpUnicharString(uniBuf); - if (strlen(uniBuf) + 1 != outLen) { - fprintf(stderr, "Expected length != actual length\n"); - return -1; - } - - // convert Unicode back to MOR - memset(morBuf, 0xcc, sizeof(morBuf)); - - outLen = NuConvertUNIToMOR(uniBuf, NULL, 0); - //printf("outLen is %u\n", (unsigned int) outLen); - if (NuConvertUNIToMOR(uniBuf, morBuf, sizeof(morBuf)) != outLen) { - fprintf(stderr, "Inconsistent UNIToMOR len\n"); - return -1; - } - //printf("MOR: "); DumpMorString(morBuf); - if (strlen(morBuf) + 1 != outLen) { - fprintf(stderr, "Expected length != actual length\n"); - return -1; - } - - // check vs. original - if (strcmp(kMORTest, morBuf) != 0) { - fprintf(stderr, "Test string corrupted by double conversion\n"); - return -1; - } - -#ifdef USE_UTF16 - static const UNICHAR kNonMorUniStr[] = L"nlTest\u7e2e\u3080.shk"; - static const UNICHAR kBadUniStr[] = L"nlTest\u7e2e\x30"; -#else - static const UNICHAR kNonMorUniStr[] = "nlTest\xe7\xb8\xae\xe3\x82\x80.shk"; - static const UNICHAR kBadUniStr[] = "nlTest\x81\xe7"; -#endif - static const char kNonMorExpected[] = "nlTest??.shk"; - static const char kBadExpected[] = "nlTest??"; - - NuConvertUNIToMOR(kNonMorUniStr, morBuf, sizeof(morBuf)); - if (strcmp(morBuf, kNonMorExpected) != 0) { - fprintf(stderr, "Non-MOR string conversion failed\n"); - return -1; - } - - NuConvertUNIToMOR(kBadUniStr, morBuf, sizeof(morBuf)); - if (strcmp(morBuf, kBadExpected) != 0) { - fprintf(stderr, "Bad UNI string conversion failed\n"); - return -1; - } - - printf("... string conversion tests successful\n"); - - return 0; -} - -/* - * Create a new entry and give it a trivial data fork. - */ -int AddTestEntry(NuArchive* pArchive, const char* entryNameMOR) -{ - NuDataSource* pDataSource = NULL; - NuRecordIdx recordIdx; - static const char* kTestMsg = "Hello, world!\n"; - uint32_t status; - NuError err; - - /* - * Add our test entry. - */ - err = AddSimpleRecord(pArchive, entryNameMOR, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: add record failed (err=%d)\n", err); - goto failed; - } - - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, (const uint8_t*)kTestMsg, 0, strlen(kTestMsg), NULL, - &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: source create failed (err=%d)\n", err); - goto failed; - } - - err = NuAddThread(pArchive, recordIdx, kNuThreadIDDataFork, pDataSource, - NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: thread add failed (err=%d)\n", err); - goto failed; - } - pDataSource = NULL; /* now owned by library */ - - /* - * Flush changes. - */ - err = NuFlush(pArchive, &status); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't flush after add (err=%d, status=%u)\n", - err, status); - goto failed; - } - - return 0; -failed: - if (pDataSource != NULL) - NuFreeDataSource(pDataSource); - return -1; -} - -/* - * Extract the file we created. - */ -int TestExtract(NuArchive* pArchive, const char* entryNameMOR) -{ - const NuRecord* pRecord; - NuRecordIdx recordIdx; - NuError err; - - err = NuGetRecordIdxByName(pArchive, entryNameMOR, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't find '%s' (err=%d)\n", - entryNameMOR, err); - return -1; - } - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record index %u (err=%d)\n", - recordIdx, err); - return -1; - } - assert(pRecord != NULL); - - const NuThread* pThread = NULL; - uint32_t idx; - for (idx = 0; idx < NuRecordGetNumThreads(pRecord); idx++) { - pThread = NuGetThread(pRecord, idx); - - if (NuGetThreadID(pThread) == kNuThreadIDDataFork) - break; - } - if (pThread == NULL) { - fprintf(stderr, "ERROR: no data thread?\n"); - return -1; - } - - /* - * Prepare the output file. - */ - UNICHAR* entryNameUNI = CopyMORToUNI(entryNameMOR); - NuDataSink* pDataSink = NULL; - err = NuCreateDataSinkForFile(true, kNuConvertOff, entryNameUNI, - kLocalFssep, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data sink for file (err=%d)\n", - err); - free(entryNameUNI); - return -1; - } - - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: extract failed (err=%d)\n", err); - (void) NuFreeDataSink(pDataSink); - free(entryNameUNI); - return -1; - } - - (void) NuFreeDataSink(pDataSink); - - printf("... confirming extraction of '%s'\n", entryNameUNI); - if (access(entryNameUNI, F_OK) != 0) { - fprintf(stderr, "ERROR: unable to read '%s' (err=%d)\n", - entryNameUNI, errno); - free(entryNameUNI); - return -1; - } - - if (unlink(entryNameUNI) < 0) { - perror("unlink test entry"); - free(entryNameUNI); - return -1; - } - - free(entryNameUNI); - return 0; -} - -/* - * Run some tests. - * - * Returns 0 on success, -1 on error. - */ -int DoTests(void) -{ - NuError err; - NuArchive* pArchive = NULL; - char* testEntryNameMOR = NULL; - int result = 0; - - if (TestStringConversion() < 0) { - goto failed; - } - - /* - * Make sure we're starting with a clean slate. - */ - if (RemoveTestFile("Test archive", kTestArchive) < 0) { - goto failed; - } - if (RemoveTestFile("Test temp file", kTestTempFile) < 0) { - goto failed; - } - if (RemoveTestFile("Test entry", kTestEntryName) < 0) { - goto failed; - } - - testEntryNameMOR = CopyUNIToMOR(kTestEntryName); - - /* - * Create a new archive to play with. - */ - err = NuOpenRW(kTestArchive, kTestTempFile, kNuOpenCreat|kNuOpenExcl, - &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: NuOpenRW failed (err=%d)\n", err); - goto failed; - } - if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: couldn't set message handler\n"); - goto failed; - } - - /* - * Add a single entry. - */ - if (AddTestEntry(pArchive, testEntryNameMOR) != 0) { - goto failed; - } - - printf("... checking presence of '%s' and '%s'\n", - kTestArchive, kTestTempFile); - - if (access(kTestTempFile, F_OK) != 0) { - /* in theory, NufxLib doesn't need to use the temp file we provide, - so this test isn't entirely sound */ - fprintf(stderr, "ERROR: did not find %s (err=%d)\n", - kTestTempFile, err); - goto failed; - } - - /* - * Close it and confirm that the file has the expected name. - */ - err = NuClose(pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: mid NuClose failed (err=%d)\n", err); - goto failed; - } - pArchive = NULL; - - if (access(kTestArchive, F_OK) != 0) { - fprintf(stderr, "ERROR: did not find %s (err=%d)\n", kTestArchive, err); - goto failed; - } - - /* - * Reopen it read-only. - */ - printf("... reopening archive read-only\n"); - err = NuOpenRO(kTestArchive, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: NuOpenRO failed (err=%d)\n", err); - goto failed; - } - if (NuSetErrorMessageHandler(pArchive, ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: couldn't set message handler\n"); - goto failed; - } - - /* - * Extract the file. - */ - if (TestExtract(pArchive, testEntryNameMOR) < 0) { - goto failed; - } - - /* - * That's all, folks... - */ - NuClose(pArchive); - pArchive = NULL; - - printf("... removing '%s'\n", kTestArchive); - if (unlink(kTestArchive) < 0) { - perror("unlink kTestArchive"); - goto failed; - } - - -leave: - if (pArchive != NULL) { - NuAbort(pArchive); - NuClose(pArchive); - } - free(testEntryNameMOR); - return result; - -failed: - result = -1; - goto leave; -} - - -/* - * Start here. - */ -int main(void) -{ - int32_t major, minor, bug; - const char* pBuildDate; - const char* pBuildFlags; - int cc; - - (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, &pBuildFlags); - printf("Using NuFX library v%d.%d.%d, built on or after\n" - " %s with [%s]\n\n", - major, minor, bug, pBuildDate, pBuildFlags); - - if (NuSetGlobalErrorMessageHandler(ErrorMessageHandler) == - kNuInvalidCallback) - { - fprintf(stderr, "ERROR: can't set the global message handler"); - exit(1); - } - - printf("... starting tests\n"); - - cc = DoTests(); - - printf("... tests ended, %s\n", cc == 0 ? "SUCCESS" : "FAILURE"); - exit(cc != 0); -} - diff --git a/ciderpress/nufxlib/samples/TestSimple.c b/ciderpress/nufxlib/samples/TestSimple.c deleted file mode 100644 index cc77e8b..0000000 --- a/ciderpress/nufxlib/samples/TestSimple.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Simple test program. Opens an archive, dumps the contents. - * - * If the first argument is "-", this will read from stdin. Otherwise, - * the first argument is taken to be an archive filename, and opened. - */ -#include -#include "NufxLib.h" -#include "Common.h" - - -/* - * Callback function to display the contents of a single record. - * - * "pRecord->filename" is the record's filename, whether from the record - * header, a filename thread, or a default value ("UNKNOWN", stuffed in - * when a record has no filename at all). - */ -NuResult ShowContents(NuArchive* pArchive, void* vpRecord) -{ - const NuRecord* pRecord = (NuRecord*) vpRecord; - - size_t bufLen = NuConvertMORToUNI(pRecord->filenameMOR, NULL, 0); - if (bufLen == (size_t) -1) { - fprintf(stderr, "GLITCH: unable to convert '%s'\n", - pRecord->filenameMOR); - } else { - UNICHAR* buf = (UNICHAR*) malloc(bufLen); - NuConvertMORToUNI(pRecord->filenameMOR, buf, bufLen); - printf("*** Filename = '%s'\n", buf); - free(buf); - } - - return kNuOK; -} - - -/* - * Dump the contents from the streaming input. - * - * If we're not interested in handling an archive on stdin, we could just - * pass the filename in here and use NuOpenRO instead. - */ -int DoStreamStuff(FILE* fp) -{ - NuError err; - NuArchive* pArchive = NULL; - - err = NuStreamOpenRO(fp, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to open stream archive (err=%d)\n", err); - goto bail; - } - - printf("*** Streaming contents!\n"); - - err = NuContents(pArchive, ShowContents); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: NuContents failed (err=%d)\n", err); - goto bail; - } - -bail: - if (pArchive != NULL) { - NuError err2 = NuClose(pArchive); - if (err == kNuErrNone) - err = err2; - } - - return err; -} - - -/* - * Grab the name of an archive to read. If "-" was given, use stdin. - */ -int main(int argc, char** argv) -{ - int32_t major, minor, bug; - const char* pBuildDate; - FILE* infp = NULL; - int cc; - - (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, NULL); - printf("Using NuFX lib %d.%d.%d built on or after %s\n", - major, minor, bug, pBuildDate); - - if (argc != 2) { - fprintf(stderr, "Usage: %s (archive-name|-)\n", argv[0]); - exit(2); - } - - if (strcmp(argv[1], "-") == 0) - infp = stdin; - else { - infp = fopen(argv[1], kNuFileOpenReadOnly); - if (infp == NULL) { - fprintf(stderr, "ERROR: unable to open '%s'\n", argv[1]); - exit(1); - } - } - - cc = DoStreamStuff(infp); - exit(cc != 0); -} - diff --git a/ciderpress/nufxlib/samples/TestTwirl.c b/ciderpress/nufxlib/samples/TestTwirl.c deleted file mode 100644 index 1712172..0000000 --- a/ciderpress/nufxlib/samples/TestTwirl.c +++ /dev/null @@ -1,709 +0,0 @@ -/* - * NuFX archive manipulation library - * Copyright (C) 2000-2007 by Andy McFadden, All Rights Reserved. - * This is free software; you can redistribute it and/or modify it under the - * terms of the BSD License, see the file COPYING.LIB. - * - * Recompress records in place, several times, possibly deleting records - * or threads as we go. The goal is to perform a large number of operations - * that modify the archive without closing and reopening it. - * - * Depending on which #defines are enabled, this can be very destructive, - * so a copy of the archive is made before processing begins. - */ -#include -#include -#include -#include -#include "NufxLib.h" -#include "Common.h" - -/* copy the archive to this file before starting */ -static const char* kWorkFileName = "TwirlCopy678"; -static const char* kTempFileName = "TwirlTmp789"; - -/* after loading this much stuff into memory, flush changes */ -const int kMaxHeldLen = 1024 * 1024; - -/* - * A list of CRCs. - */ -typedef struct CRCList { - int numEntries; - uint16_t* entries; -} CRCList; - - -/* - * Returns true if the compression type is supported, false otherwise. - */ -int CompressionSupported(NuValue compression) -{ - int result; - - switch (compression) { - case kNuCompressNone: - result = true; - break; - case kNuCompressSQ: - result = (NuTestFeature(kNuFeatureCompressSQ) == kNuErrNone); - break; - case kNuCompressLZW1: - case kNuCompressLZW2: - result = (NuTestFeature(kNuFeatureCompressLZW) == kNuErrNone); - break; - case kNuCompressLZC12: - case kNuCompressLZC16: - result = (NuTestFeature(kNuFeatureCompressLZC) == kNuErrNone); - break; - case kNuCompressDeflate: - result = (NuTestFeature(kNuFeatureCompressDeflate) == kNuErrNone); - break; - case kNuCompressBzip2: - result = (NuTestFeature(kNuFeatureCompressBzip2) == kNuErrNone); - break; - default: - assert(false); - result = false; - } - - /*printf("Returning %d for %ld\n", result, compression);*/ - - return result; -} - -/* - * This gets called when a buffer DataSource is no longer needed. - */ -NuResult FreeCallback(NuArchive* pArchive, void* args) -{ - free(args); - return kNuOK; -} - - -/* - * Dump a CRC list. - */ -void DumpCRCs(const CRCList* pCRCList) -{ - int i; - - printf(" NumEntries: %d\n", pCRCList->numEntries); - - for (i = 0; i < pCRCList->numEntries; i++) - printf(" %5d: 0x%04x\n", i, pCRCList->entries[i]); -} - -/* - * Free a CRC list. - */ -void FreeCRCs(CRCList* pCRCList) -{ - if (pCRCList == NULL) - return; - - free(pCRCList->entries); - free(pCRCList); -} - -/* - * Gather a list of CRCs from the archive. - * - * We assume there are at most two data threads (e.g. data fork and rsrc - * fork) in a record. - * - * Returns the list on success, NULL on failure. - */ -CRCList* GatherCRCs(NuArchive* pArchive) -{ - NuError err = kNuErrNone; - const NuMasterHeader* pMasterHeader; - CRCList* pCRCList = NULL; - uint16_t* pEntries = NULL; - long recCount, maxCRCs; - long recIdx, crcIdx; - int i; - - pCRCList = malloc(sizeof(*pCRCList)); - if (pCRCList == NULL) { - fprintf(stderr, "ERROR: couldn't alloc CRC list\n"); - err = kNuErrGeneric; - goto bail; - } - memset(pCRCList, 0, sizeof(*pCRCList)); - - /* get record count out of master header, just for fun */ - err = NuGetMasterHeader(pArchive, &pMasterHeader); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get master header (err=%d)\n", err); - goto bail; - } - recCount = pMasterHeader->mhTotalRecords; - maxCRCs = recCount * 2; - - pEntries = malloc(sizeof(*pEntries) * maxCRCs); - if (pEntries == NULL) { - fprintf(stderr, "ERROR: unable to alloc CRC list (%ld entries)\n", - maxCRCs); - err = kNuErrGeneric; - goto bail; - } - pCRCList->entries = pEntries; - - for (i = 0; i < maxCRCs; i++) - pEntries[i] = 0xdead; - - /* - * Enumerate our way through the records. If something was disturbed - * we should end up in a different place and the CRCs will be off. - */ - crcIdx = 0; - for (recIdx = 0; recIdx < recCount; recIdx++) { - NuRecordIdx recordIdx; - const NuRecord* pRecord; - const NuThread* pThread; - - err = NuGetRecordIdxByPosition(pArchive, recIdx, &recordIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", - recIdx, err); - goto bail; - } - - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get recordIdx %u\n", recordIdx); - goto bail; - } - - if (NuRecordGetNumThreads(pRecord) == 0) { - fprintf(stderr, "ERROR: not expecting empty record (%u)!\n", - recordIdx); - err = kNuErrGeneric; - goto bail; - } - - int rsrcCrcIdx = -1; - for (i = 0; i < (int)NuRecordGetNumThreads(pRecord); i++) { - pThread = NuGetThread(pRecord, i); - if (pThread->thThreadClass == kNuThreadClassData) { - if (crcIdx >= maxCRCs) { - fprintf(stderr, "ERROR: CRC buffer exceeded\n"); - assert(false); - err = kNuErrGeneric; - goto bail; - } - - /* - * Ensure that the data fork CRC comes first. Otherwise - * we can fail if it gets rearranged. This is only a - * problem for GSHK-created archives that don't have - * threads for every fork, so "mask dataless" is create - * fake entries. - * - * The correct way to do this is to store a tuple - * { thread-kind, crc }, but that's more work. - */ - if (pThread->thThreadKind == kNuThreadKindRsrcFork) { - rsrcCrcIdx = crcIdx; - } - - if (pThread->thThreadKind == kNuThreadKindDataFork && - rsrcCrcIdx != -1) - { - /* this is the data fork, we've already seen the - resource fork; swap entries */ - pEntries[crcIdx++] = pEntries[rsrcCrcIdx]; - pEntries[rsrcCrcIdx] = pThread->thThreadCRC; - } else { - pEntries[crcIdx++] = pThread->thThreadCRC; - } - } - } - } - - pCRCList->numEntries = crcIdx; - - DumpCRCs(pCRCList); - -bail: - if (err != kNuErrNone) { - FreeCRCs(pCRCList); - pCRCList = NULL; - } - return pCRCList; -} - - -/* - * Compare the current set of CRCs against our saved list. If any of - * the records or threads were deleted or rearranged, this will fail. - * I happen to think this is a *good* thing: if something is the least - * bit screwy, I want to know about it. - * - * Unfortunately, if we *deliberately* delete records, this can't - * help us with the survivors. - * - * Returns 0 on success, nonzero on failure. - */ -int CompareCRCs(NuArchive* pArchive, const CRCList* pOldCRCList) -{ - CRCList* pNewCRCList = NULL; - int result = -1; - int badCrc = 0; - int i; - - pNewCRCList = GatherCRCs(pArchive); - if (pNewCRCList == NULL) { - fprintf(stderr, "ERROR: unable to gather new list\n"); - goto bail; - } - - if (pOldCRCList->numEntries != pNewCRCList->numEntries) { - fprintf(stderr, "ERROR: numEntries mismatch: %d vs %d\n", - pOldCRCList->numEntries, pNewCRCList->numEntries); - goto bail; - } - - for (i = 0; i < pNewCRCList->numEntries; i++) { - if (pOldCRCList->entries[i] != pNewCRCList->entries[i]) { - fprintf(stderr, "ERROR: CRC mismatch: %5d old=0x%04x new=0x%04x\n", - i, pOldCRCList->entries[i], pNewCRCList->entries[i]); - badCrc = 1; - } - } - if (!badCrc) { - printf(" Matched %d CRCs\n", pOldCRCList->numEntries); - result = 0; - } - -bail: - FreeCRCs(pNewCRCList); - return result; -} - - -/* - * Recompress a single thread. - * - * This entails (1) extracting the existing thread, (2) deleting the - * thread, and (3) adding the extracted data. - * - * All of this good stuff gets queued up until the next NuFlush call. - */ -NuError RecompressThread(NuArchive* pArchive, const NuRecord* pRecord, - const NuThread* pThread) -{ - NuError err = kNuErrNone; - NuDataSource* pDataSource = NULL; - NuDataSink* pDataSink = NULL; - uint8_t* buf = NULL; - - if (pThread->actualThreadEOF == 0) { - buf = malloc(1); - if (buf == NULL) { - fprintf(stderr, "ERROR: failed allocating trivial buffer\n"); - err = kNuErrGeneric; - goto bail; - } - } else { - /* - * Create a buffer and data sink to hold the data. - */ - buf = malloc(pThread->actualThreadEOF); - if (buf == NULL) { - fprintf(stderr, "ERROR: failed allocating %u bytes\n", - pThread->actualThreadEOF); - err = kNuErrGeneric; - goto bail; - } - - err = NuCreateDataSinkForBuffer(true, kNuConvertOff, buf, - pThread->actualThreadEOF, &pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data sink (err=%d)\n",err); - goto bail; - } - - /* - * Extract the data. - */ - err = NuExtractThread(pArchive, pThread->threadIdx, pDataSink); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: failed extracting thread %u in '%s': %s\n", - pThread->threadIdx, pRecord->filenameMOR, NuStrError(err)); - goto bail; - } - } - - /* - * Delete the existing thread. - */ - err = NuDeleteThread(pArchive, pThread->threadIdx); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to delete thread %u\n", - pThread->threadIdx); - goto bail; - } - - /* - * Create a data source for the new thread. Specify a callback to free - * the buffer when NufxLib is done with it. - */ - err = NuCreateDataSourceForBuffer(kNuThreadFormatUncompressed, - 0, buf, 0, pThread->actualThreadEOF, FreeCallback, &pDataSource); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to create data source (err=%d)\n", err); - goto bail; - } - buf = NULL; - - /* - * Create replacement thread. - */ - err = NuAddThread(pArchive, pRecord->recordIdx, NuGetThreadID(pThread), - pDataSource, NULL); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to add new thread ID=0x%08x (err=%d)\n", - NuGetThreadID(pThread), err); - goto bail; - } - pDataSource = NULL; /* now owned by NufxLib */ - -bail: - NuFreeDataSink(pDataSink); - NuFreeDataSource(pDataSource); - free(buf); - return err; -} - -/* - * Recompress a single record. - * - * The amount of data we're holding in memory as a result of the - * recompression is placed in "*pLen". - */ -NuError RecompressRecord(NuArchive* pArchive, NuRecordIdx recordIdx, long* pLen) -{ - NuError err = kNuErrNone; - const NuRecord* pRecord; - const NuThread* pThread; - int i; - - printf(" Recompressing %u\n", recordIdx); - - *pLen = 0; - - err = NuGetRecord(pArchive, recordIdx, &pRecord); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get record %u (err=%d)\n", - recordIdx, err); - goto bail; - } - - for (i = 0; i < (int)NuRecordGetNumThreads(pRecord); i++) { - pThread = NuGetThread(pRecord, i); - if (pThread->thThreadClass == kNuThreadClassData) { - /*printf(" Recompressing %d (threadID=0x%08lx)\n", i, - NuGetThreadID(pThread));*/ - err = RecompressThread(pArchive, pRecord, pThread); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: failed recompressing thread %u " - " in record %u (err=%d)\n", - pThread->threadIdx, pRecord->recordIdx, err); - goto bail; - } - *pLen += pThread->actualThreadEOF; - } else { - /*printf(" Skipping %d (threadID=0x%08lx)\n", i, - NuGetThreadID(pThread));*/ - } - } - -bail: - return err; -} - -/* - * Recompress every data thread in the archive. - */ -NuError RecompressArchive(NuArchive* pArchive, NuValue compression) -{ - NuError err = kNuErrNone; - NuRecordIdx* pIndices = NULL; - NuAttr countAttr; - long heldLen; - long idx; - - err = NuSetValue(pArchive, kNuValueDataCompression, compression); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to set compression to %u (err=%d)\n", - compression, err); - goto bail; - } - - printf("Recompressing threads with compression type %u\n", compression); - - err = NuGetAttr(pArchive, kNuAttrNumRecords, &countAttr); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to get numRecords (err=%d)\n", err); - goto bail; - } - - if (countAttr == 0) { - printf("No records found!\n"); - goto bail; - } - - /* - * Get all of the indices up front. This way, if something causes a - * record to "disappear" during processing, we will know about it. - */ - pIndices = malloc(countAttr * sizeof(*pIndices)); - if (pIndices == NULL) { - fprintf(stderr, "ERROR: malloc on %u indices failed\n", countAttr); - err = kNuErrGeneric; - goto bail; - } - - for (idx = 0; idx < (int)countAttr; idx++) { - err = NuGetRecordIdxByPosition(pArchive, idx, &pIndices[idx]); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't get record #%ld (err=%d)\n", - idx, err); - goto bail; - } - } - - /* - * Walk through the index list, handling each record individually. - */ - heldLen = 0; - for (idx = 0; idx < (int)countAttr; idx++) { - long recHeldLen; - - err = RecompressRecord(pArchive, pIndices[idx], &recHeldLen); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: failed recompressing record %u (err=%d)\n", - pIndices[idx], err); - goto bail; - } - - heldLen += recHeldLen; - - if (heldLen > kMaxHeldLen) { - uint32_t statusFlags; - - printf(" (flush)\n"); - err = NuFlush(pArchive, &statusFlags); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: intra-recompress flush failed: %s\n", - NuStrError(err)); - goto bail; - } - - heldLen = 0; - } - } - -bail: - free(pIndices); - return err; -} - -/* - * Initiate the twirling. - */ -int TwirlArchive(const char* filename) -{ - NuError err = kNuErrNone; - NuArchive* pArchive = NULL; - CRCList* pCRCList = NULL; - int compression; - int cc; - - /* - * Open the archive after removing any temp file remnants. - */ - cc = unlink(kTempFileName); - if (cc == 0) - printf("Removed stale temp file '%s'\n", kTempFileName); - - err = NuOpenRW(filename, kTempFileName, 0, &pArchive); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: unable to open archive '%s': %s\n", - filename, NuStrError(err)); - goto bail; - } - - /* - * Mask records with no data threads, so we don't have to - * special-case them. - */ - err = NuSetValue(pArchive, kNuValueMaskDataless, true); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: couldn't mask dataless (err=%d)\n", err); - goto bail; - } - - pCRCList = GatherCRCs(pArchive); - if (pCRCList == NULL) { - fprintf(stderr, "ERROR: unable to get CRC list\n"); - goto bail; - } - - /* - * For each type of compression, recompress the entire archive. - */ - for (compression = kNuCompressNone; compression <= kNuCompressBzip2; - compression++) - { - uint32_t statusFlags; - - if (!CompressionSupported(compression)) - continue; - - err = RecompressArchive(pArchive, compression); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: recompress failed: %s\n", NuStrError(err)); - goto bail; - } - - err = NuFlush(pArchive, &statusFlags); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: post-recompress flush failed: %s\n", - NuStrError(err)); - goto bail; - } - } - - /* - * Same thing, reverse order. We want to start with the same one we - * ended on above, so we can practice skipping over things. - */ - for (compression = kNuCompressBzip2; compression >= kNuCompressNone; - compression--) - { - uint32_t statusFlags; - - if (!CompressionSupported(compression)) - continue; - - err = RecompressArchive(pArchive, compression); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: recompress2 failed: %s\n", NuStrError(err)); - goto bail; - } - - err = NuFlush(pArchive, &statusFlags); - if (err != kNuErrNone) { - fprintf(stderr, "ERROR: post-recompress flush2 failed: %s\n", - NuStrError(err)); - goto bail; - } - } - - if (CompareCRCs(pArchive, pCRCList) != 0) { - fprintf(stderr, "ERROR: CRCs didn't match\n"); - goto bail; - } - - printf("Done!\n"); - -bail: - FreeCRCs(pCRCList); - if (pArchive != NULL) { - NuAbort(pArchive); - NuClose(pArchive); - } - - return (err != kNuErrNone); -} - - -/* - * Copy from the current offset in "srcfp" to a new file called - * "outFileName". Returns a writable file descriptor for the new file - * on success, or NULL on error. - * - * (Note "CopyFile()" exists under Win32.) - */ -FILE* MyCopyFile(const char* outFileName, FILE* srcfp) -{ - char buf[24576]; - FILE* outfp; - size_t count; - - outfp = fopen(outFileName, kNuFileOpenWriteTrunc); - if (outfp == NULL) { - fprintf(stderr, "ERROR: unable to open '%s' (err=%d)\n", outFileName, - errno); - return NULL; - } - - while (!feof(srcfp)) { - count = fread(buf, 1, sizeof(buf), srcfp); - if (count == 0) - break; - if (fwrite(buf, 1, count, outfp) != count) { - fprintf(stderr, "ERROR: failed writing outfp (err=%d)\n", errno); - fclose(outfp); - return NULL; - } - } - - if (ferror(srcfp)) { - fprintf(stderr, "ERROR: failed reading srcfp (err=%d)\n", errno); - fclose(outfp); - return NULL; - } - - return outfp; -} - -/* - * Let's get started. - */ -int main(int argc, char** argv) -{ - int32_t major, minor, bug; - const char* pBuildDate; - FILE* srcfp = NULL; - FILE* infp = NULL; - int cc; - - /* don't buffer output */ - setvbuf(stdout, NULL, _IONBF, 0); - setvbuf(stderr, NULL, _IONBF, 0); - - (void) NuGetVersion(&major, &minor, &bug, &pBuildDate, NULL); - printf("Using NuFX lib %d.%d.%d built on or after %s\n\n", - major, minor, bug, pBuildDate); - - if (argc == 2) { - srcfp = fopen(argv[1], kNuFileOpenReadOnly); - if (srcfp == NULL) { - perror("fopen failed"); - exit(1); - } - } else { - fprintf(stderr, "ERROR: you have to specify a filename\n"); - exit(2); - } - - printf("Copying '%s' to '%s'\n", argv[1], kWorkFileName); - - infp = MyCopyFile(kWorkFileName, srcfp); - if (infp == NULL) { - fprintf(stderr, "Copy failed, bailing.\n"); - exit(1); - } - fclose(srcfp); - fclose(infp); - - cc = TwirlArchive(kWorkFileName); - - exit(cc != 0); -} - diff --git a/ciderpress/reformat/AWGS.cpp b/ciderpress/reformat/AWGS.cpp deleted file mode 100644 index 4b9efab..0000000 --- a/ciderpress/reformat/AWGS.cpp +++ /dev/null @@ -1,535 +0,0 @@ -/* - * CiderPress - * Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved. - * See the file LICENSE for distribution terms. - */ -/* - * Reformat AWGS files. - */ -#include "StdAfx.h" -#include "AWGS.h" - -/* - * Decide whether or not we want to handle this file. - */ -void ReformatAWGS_WP::Examine(ReformatHolder* pHolder) -{ - ReformatHolder::ReformatApplies applies = ReformatHolder::kApplicNot; - - if (pHolder->GetFileType() == kTypeGWP && pHolder->GetAuxType() == 0x8010) - applies = ReformatHolder::kApplicYes; - - pHolder->SetApplic(ReformatHolder::kReformatAWGS_WP, applies, - ReformatHolder::kApplicNot, ReformatHolder::kApplicNot); -} - -/* - * Convert AWGS into formatted text. - */ -int ReformatAWGS_WP::Process(const ReformatHolder* pHolder, - ReformatHolder::ReformatID id, ReformatHolder::ReformatPart part, - ReformatOutput* pOutput) -{ - const uint8_t* srcBuf = pHolder->GetSourceBuf(part); - long srcLen = pHolder->GetSourceLen(part); - fUseRTF = true; - Chunk doc, header, footer; - uint16_t val; - - Charset::CheckGSCharConv(); - - /* must at least have the doc header and globals */ - if (srcLen < kMinExpectedLen) { - LOGI("Too short to be AWGS"); - return -1; - } - - RTFBegin(kRTFFlagColorTable); - - /* - * Pull interesting values out of the document header. - */ - val = Get16LE(srcBuf + 0); - if (val != kExpectedVersion1 && val != kExpectedVersion2) { - LOGI("AWGS_WP: unexpected version number (got 0x%04x, wanted 0x%04x)", - val, kExpectedVersion1); - DebugBreak(); - } - val = Get16LE(srcBuf + 2); - if (val != kDocHeaderLen) { - LOGI("Unexpected doc header len (got 0x%04x, wanted 0x%04x)", - val, kDocHeaderLen); - return -1; - } - /* the color table is 32 bytes at +56, should we be interested */ - - srcBuf += kDocHeaderLen; - srcLen -= kDocHeaderLen; - - /* - * Pull interesting values out of the WP global variables section. - */ - val = Get16LE(srcBuf + 0); - if (val > kExpectedIntVersion) { - LOGI("Unexpected internal version number (got %d, expected %d)", - val, kExpectedIntVersion); - return -1; - } - - /* date/time are pascal strings */ - LOGI("File saved at '%.26hs' '%.10s'", srcBuf + 6, srcBuf + 32); - - srcBuf += kWPGlobalsLen; - srcLen -= kWPGlobalsLen; - - /* - * Now come the three chunks, in order: main document, header, footer. - */ - LOGI("AWGS_WP: scanning doc"); - if (!ReadChunk(&srcBuf, &srcLen, &doc)) - return -1; - LOGI("AWGS_WP: scanning header"); - if (!ReadChunk(&srcBuf, &srcLen, &header)) - return -1; - LOGI("AWGS_WP: scanning footer"); - if (!ReadChunk(&srcBuf, &srcLen, &footer)) - return -1; - - if (srcLen != 0) { - LOGI("AWGS NOTE: %ld bytes left in file", srcLen); - } - - /* - * Dump the chunks, starting with header and footer. - */ - RTFSetColor(kColorMediumBlue); - RTFSetFont(kFontCourierNew); - RTFSetFontSize(10); - BufPrintf("
"); - RTFSetColor(kColorNone); - RTFNewPara(); - - PrintChunk(&header); - - RTFSetColor(kColorMediumBlue); - RTFSetFont(kFontCourierNew); - RTFSetFontSize(10); - BufPrintf("
"); - RTFSetColor(kColorNone); - RTFNewPara(); - - RTFSetColor(kColorMediumBlue); - RTFSetFont(kFontCourierNew); - RTFSetFontSize(10); - BufPrintf("
"); - RTFSetColor(kColorNone); - RTFNewPara(); - - PrintChunk(&footer); - - RTFSetColor(kColorMediumBlue); - RTFSetFont(kFontCourierNew); - RTFSetFontSize(10); - BufPrintf("
"); - RTFSetColor(kColorNone); - RTFNewPara(); - - LOGI("AWGS_WP: rendering document"); - PrintChunk(&doc); - - RTFEnd(); - - SetResultBuffer(pOutput, true); - return 0; -} - -/* - * Read one of the chunks of the file. - */ -bool ReformatAWGS_WP::ReadChunk(const uint8_t** pSrcBuf, long* pSrcLen, - Chunk* pChunk) -{ - /* starts with the saveArray count */ - pChunk->saveArrayCount = Get16LE(*pSrcBuf); - if (pChunk->saveArrayCount == 0) { - /* AWGS always has at least 1 paragraph */ - LOGI("Save array is empty"); - return false; - } - - *pSrcBuf += 2; - *pSrcLen -= 2; - - /* locate and move past the SaveArray */ - pChunk->saveArray = *pSrcBuf; - - *pSrcBuf += pChunk->saveArrayCount * kSaveArrayEntryLen; - *pSrcLen -= pChunk->saveArrayCount * kSaveArrayEntryLen; - if (*pSrcLen <= 0) { - LOGI("SaveArray exceeds file length (count=%d len now %ld)", - pChunk->saveArrayCount, *pSrcLen); - return false; - } - - /* - * Scan the "save array" to find the highest-numbered ruler. This tells - * us how many rulers there are. - */ - pChunk->numRulers = GetNumRulers(pChunk->saveArray, pChunk->saveArrayCount); - if (*pSrcLen < pChunk->numRulers * kRulerEntryLen) { - LOGI("Not enough room for rulers (rem=%ld, needed=%ld)", - *pSrcLen, pChunk->numRulers * kRulerEntryLen); - return false; - } - LOGI("+++ found %d rulers", pChunk->numRulers); - - pChunk->rulers = *pSrcBuf; - *pSrcBuf += pChunk->numRulers * kRulerEntryLen; - *pSrcLen -= pChunk->numRulers * kRulerEntryLen; - - /* - * Now we're at the docTextBlocks section. - */ - pChunk->textBlocks = *pSrcBuf; - pChunk->numTextBlocks = GetNumTextBlocks(pChunk->saveArray, - pChunk->saveArrayCount); - if (!SkipTextBlocks(pSrcBuf, pSrcLen, pChunk->numTextBlocks)) - return false; - - return true; -} - -/* - * Output a single chunk. We do this by walking down the saveArray. - */ -void ReformatAWGS_WP::PrintChunk(const Chunk* pChunk) -{ - const int kDefaultStatusBits = kAWGSJustifyLeft | kAWGSSingleSpace; - SaveArrayEntry sae; - const uint8_t* saveArray; - int saCount; - const uint8_t* blockPtr; - long blockLen; - const uint8_t* pRuler; - uint16_t rulerStatusBits; - - saveArray = pChunk->saveArray; - saCount = pChunk->saveArrayCount; - for ( ; saCount > 0; saCount--, saveArray += kSaveArrayEntryLen) { - UnpackSaveArrayEntry(saveArray, &sae); - - /* - * Page-break paragraphs have no real data and an invalid value - * in the "rulerNum" field. So we just throw out a page break - * here and call it a day. - */ - if (sae.attributes == 0x0001) { - /* this is a page-break paragraph */ - RTFSetColor(kColorMediumBlue); - RTFSetFont(kFontCourierNew); - RTFSetFontSize(10); - BufPrintf(""); - RTFSetColor(kColorNone); - RTFNewPara(); - RTFPageBreak(); // only supported by Word - continue; - } - - if (sae.rulerNum < pChunk->numRulers) { - pRuler = pChunk->rulers + sae.rulerNum * kRulerEntryLen; - rulerStatusBits = Get16LE(pRuler + 2); - } else { - LOGI("AWGS_WP GLITCH: invalid ruler index %d", sae.rulerNum); - rulerStatusBits = kDefaultStatusBits; - } - - if (rulerStatusBits & kAWGSJustifyFull) - RTFParaJustify(); - else if (rulerStatusBits & kAWGSJustifyRight) - RTFParaRight(); - else if (rulerStatusBits & kAWGSJustifyCenter) - RTFParaCenter(); - else if (rulerStatusBits & kAWGSJustifyLeft) - RTFParaLeft(); - RTFSetPara(); - - /* - * Find the text block that holds this paragraph. We could speed - * this up by creating an array of entries rather than walking the - * list every time. However, the block count tends to be fairly - * small (e.g. 7 for a 16K doc). - */ - blockPtr = FindTextBlock(pChunk, sae.textBlock); - if (blockPtr == NULL) { - LOGI("AWGS_WP bad textBlock %d", sae.textBlock); - return; - } - blockLen = (long) Get32LE(blockPtr); - if (blockLen <= 0 || blockLen > 65535) { - LOGI("AWGS_WP invalid block len %d", blockLen); - return; - } - blockPtr += 4; - - if (sae.offset >= blockLen) { - LOGI("AWGS_WP bad offset: %d, blockLen=%ld", - sae.offset, blockLen); - return; - } - PrintParagraph(blockPtr + sae.offset, blockLen - sae.offset); - } -} - -/* - * Print the contents of the text blocks. - * - * We're assured that the text block format is correct because we had to - * skip through them earlier. We don't really need to worry about running - * off the end due to a bad file. - */ -const uint8_t* ReformatAWGS_WP::FindTextBlock(const Chunk* pChunk, int blockNum) -{ - const uint8_t* blockPtr = pChunk->textBlocks; - uint32_t blockSize; - - while (blockNum--) { - blockSize = Get32LE(blockPtr); - blockPtr += 4 + blockSize; - } - - return blockPtr; -} - - -/* - * Print one paragraph. - * - * Stop when we hit '\r'. We watch "maxLen" just to be safe. - * - * Returns the #of bytes consumed. - */ -int ReformatAWGS_WP::PrintParagraph(const uint8_t* ptr, long maxLen) -{ - const uint8_t* startPtr = ptr; - uint16_t firstFont; - uint8_t firstStyle, firstSize, firstColor; - uint8_t uch; - - if (maxLen < 7) { - LOGI("AWGS_WP GLITCH: not enough storage for para header (%d)", - maxLen); - return 1; // don't return zero or we might loop forever - } - /* pull out the paragraph header */ - firstFont = Get16LE(ptr); - firstStyle = *(ptr + 2); - firstSize = *(ptr + 3); - firstColor = *(ptr + 4); - - ptr += 7; - maxLen -= 7; - - /* - * Set the font first; that defines the point size multiplier. Set - * the size second, because the point size determines whether we - * show underline. Set the style last. - */ - //LOGI("+++ Para start: font=0x%04x size=%d style=0x%02x", - // firstFont, firstSize, firstStyle); - RTFSetGSFont(firstFont); - RTFSetGSFontSize(firstSize); - RTFSetGSFontStyle(firstStyle); - - while (maxLen > 0) { - uch = *ptr++; - maxLen--; - switch (uch) { - case 0x01: // font change - two bytes follow - if (maxLen >= 2) { - RTFSetGSFont(Get16LE(ptr)); - ptr += 2; - maxLen -= 2; - } - break; - case 0x02: // text style change - if (maxLen >= 1) { - RTFSetGSFontStyle(*ptr++); - maxLen--; - } - break; - case 0x03: // text size change - if (maxLen >= 1) { - RTFSetGSFontSize(*ptr++); - maxLen--; - } - break; - case 0x04: // color change (0-15) - if (maxLen >= 1) { - ptr++; - maxLen--; - } - break; - case 0x05: // page token (replace with page #) - case 0x06: // date token (replace with date) - case 0x07: // time token (replace with time) - RTFSetColor(kColorMediumBlue); - if (uch == 0x05) - BufPrintf(""); - else if (uch == 0x06) - BufPrintf(""); - else - BufPrintf("