Compare commits
2 Commits
e74f463a1e
...
1fb1aa259d
Author | SHA1 | Date |
---|---|---|
Shawn Quick | 1fb1aa259d | |
Shawn Quick | 9c66992c23 |
|
@ -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)
|
||||
|
||||
|
||||
|
||||
|
|
12
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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
config.h
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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/.
|
|
@ -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
|
|
@ -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.
|
||||
|
|
@ -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.
|
||||
|
|
@ -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.
|
|
@ -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 <errno.h>
|
||||
|
||||
/*
|
||||
+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;
|
||||
}
|
|
@ -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*/
|
|
@ -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
|
|
@ -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*/
|
|
@ -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));
|
||||
}
|
|
@ -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*/
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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*/
|
|
@ -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);
|
||||
}
|
|
@ -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*/
|
|
@ -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 <errno.h>
|
||||
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* 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);
|
||||
}
|
||||
}
|
|
@ -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 <archive.shk> <file>"
|
||||
* - rename the type-preservation header off of <file>'s data fork
|
||||
* - combine the forks with "cat <file>#nnnnr > <file>/..namedfork/rsrc"
|
||||
* - use "xattr -l <file>" to confirm that the file has a resource fork
|
||||
* and the FinderInfo with the ProDOS file type
|
||||
* - use "applesingle encode <file>" to create <file>.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*/
|
|
@ -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"<bogus>");
|
||||
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();
|
||||
}
|
|
@ -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*/
|
|
@ -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 <errno.h>
|
||||
|
||||
|
||||
/*
|
||||
* ===========================================================================
|
||||
* 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;
|
||||
}
|
|
@ -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*/
|
|
@ -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;
|
||||
}
|
|
@ -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*/
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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*/
|
|
@ -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*/
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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*/
|
|
@ -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 <afxshellmanager.h>
|
||||
|
||||
|
||||
/*
|
||||
* 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*/
|
|
@ -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 <time.h>
|
||||
|
||||
|
||||
/*
|
||||
* ==========================================================================
|
||||
* 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();
|
||||
}
|
||||
}
|
|
@ -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*/
|
|
@ -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;
|
||||
}
|
|
@ -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 <afxwin.h>
|
||||
#include <afxcmn.h>
|
||||
|
||||
|
||||
/*
|
||||
* 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*/
|
|
@ -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);
|
||||
}
|
|
@ -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*/
|
|
@ -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);
|
||||
}
|
|
@ -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*/
|
|
@ -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);
|
||||
}
|
|
@ -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*/
|
|
@ -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;
|
||||
}
|
|
@ -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*/
|
|
@ -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());
|
||||
}
|
|
@ -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*/
|
|
@ -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*/
|
|
@ -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);
|
||||
}
|
|
@ -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*/
|
|
@ -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 <return> key
|
||||
virtual BOOL PreTranslateMessage(MSG* pMsg) override;
|
||||
|
||||
/*
|
||||
* Handle the "Done" button. We don't use IDOK because we don't want
|
||||
* <return> 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*/
|
|
@ -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();
|
||||
}
|
|
@ -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 <afxwin.h>
|
||||
#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*/
|
|
@ -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;
|
||||
}
|
|
@ -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*/
|
|
@ -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*/
|
|
@ -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();
|
||||
}
|
|
@ -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*/
|
|
@ -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
|
|
@ -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*/
|
|
@ -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);
|
||||
}
|
|
@ -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*/
|
|
@ -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;
|
||||
}
|
|
@ -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*/
|
|
@ -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*/
|
|
@ -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*/
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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*/
|
|
@ -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*/
|
Before Width: | Height: | Size: 246 B |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 246 B |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 374 B |
Before Width: | Height: | Size: 758 B |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 374 B |
|
@ -1,232 +0,0 @@
|
|||
<HTML><HEAD>
|
||||
|
||||
<META NAME="AUTHOR" CONTENT="Copyright (C) 2014 by CiderPress authors">
|
||||
<META NAME="GENERATOR" CONTENT="HelpScribble 7.8.8">
|
||||
<!-- Sitemap 1.0 -->
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#800080" ALINK="#FF0000">
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Introduction">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Welcome!">
|
||||
<PARAM NAME="Local" VALUE="html/t10.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Features">
|
||||
<PARAM NAME="Local" VALUE="html/t47.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Getting Help">
|
||||
<PARAM NAME="Local" VALUE="html/t284.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="How (and Why) to Register">
|
||||
<PARAM NAME="Local" VALUE="html/t46.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Credits">
|
||||
<PARAM NAME="Local" VALUE="html/t24.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Using CiderPress">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Available Commands">
|
||||
<PARAM NAME="Local" VALUE="html/t48.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Commands">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Selecting Commands">
|
||||
<PARAM NAME="Local" VALUE="html/t52.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Opening, Closing, and Creating Files">
|
||||
<PARAM NAME="Local" VALUE="html/t51.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Opening a Volume">
|
||||
<PARAM NAME="Local" VALUE="html/t241.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Archive Info">
|
||||
<PARAM NAME="Local" VALUE="html/t258.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Working With the File List">
|
||||
<PARAM NAME="Local" VALUE="html/t50.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Printing">
|
||||
<PARAM NAME="Local" VALUE="html/t53.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Viewing Files">
|
||||
<PARAM NAME="Local" VALUE="html/t54.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Adding Files and Disks">
|
||||
<PARAM NAME="Local" VALUE="html/t55.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Creating Subdirectories">
|
||||
<PARAM NAME="Local" VALUE="html/t263.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Extracting Files">
|
||||
<PARAM NAME="Local" VALUE="html/t39.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Copying and Pasting">
|
||||
<PARAM NAME="Local" VALUE="html/t273.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Testing Archives">
|
||||
<PARAM NAME="Local" VALUE="html/t56.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Rename Entries">
|
||||
<PARAM NAME="Local" VALUE="html/t42.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Delete Entries">
|
||||
<PARAM NAME="Local" VALUE="html/t57.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Re-Compress Entries">
|
||||
<PARAM NAME="Local" VALUE="html/t58.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Edit Comment">
|
||||
<PARAM NAME="Local" VALUE="html/t43.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Edit File Attributes">
|
||||
<PARAM NAME="Local" VALUE="html/t203.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Convert Disk Image to File Archive">
|
||||
<PARAM NAME="Local" VALUE="html/t215.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Convert File Archive to Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t216.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Import from Cassette">
|
||||
<PARAM NAME="Local" VALUE="html/t111.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Import BASIC Program">
|
||||
<PARAM NAME="Local" VALUE="html/t112.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Tools">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Disk Sector Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t13.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Disk Image Converter">
|
||||
<PARAM NAME="Local" VALUE="html/t187.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Bulk Disk Image Converter">
|
||||
<PARAM NAME="Local" VALUE="html/t233.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="SST Image Merge">
|
||||
<PARAM NAME="Local" VALUE="html/t201.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Windows Volume Copier">
|
||||
<PARAM NAME="Local" VALUE="html/t245.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="EOL Scanner">
|
||||
<PARAM NAME="Local" VALUE="html/t272.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="2MG Properties Editor">
|
||||
<PARAM NAME="Local" VALUE="html/t277.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Preferences">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="General Preferences">
|
||||
<PARAM NAME="Local" VALUE="html/t19.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Disk Image Preferences">
|
||||
<PARAM NAME="Local" VALUE="html/t259.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Compression Preferences">
|
||||
<PARAM NAME="Local" VALUE="html/t29.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="File Viewer Preferences">
|
||||
<PARAM NAME="Local" VALUE="html/t23.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="File Preferences">
|
||||
<PARAM NAME="Local" VALUE="html/t28.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
</UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Appendix">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="About Disk Images">
|
||||
<PARAM NAME="Local" VALUE="html/t18.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Embedded DOS Volumes">
|
||||
<PARAM NAME="Local" VALUE="html/t21.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="File Format Converters">
|
||||
<PARAM NAME="Local" VALUE="html/t22.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Disassembly Notes">
|
||||
<PARAM NAME="Local" VALUE="html/t109.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="File Extensions">
|
||||
<PARAM NAME="Local" VALUE="html/t45.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="File Attribute Preservation">
|
||||
<PARAM NAME="Local" VALUE="html/t68.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Compression Algorithms">
|
||||
<PARAM NAME="Local" VALUE="html/t69.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="About Removable Media (CF, floppy, CD-ROM)">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Name" VALUE="Administrator Privileges">
|
||||
<PARAM NAME="Local" VALUE="html/t262.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
</UL>
|
||||
</BODY></HTML>
|
|
@ -1,564 +0,0 @@
|
|||
<HTML><HEAD>
|
||||
<META NAME="AUTHOR" CONTENT="Copyright (C) 2014 by CiderPress authors">
|
||||
<META NAME="GENERATOR" CONTENT="HelpScribble 7.8.8">
|
||||
<!-- Sitemap 1.0 -->
|
||||
</HEAD>
|
||||
<BODY BGCOLOR="#FFFFFF" TEXT="#000000" LINK="#0000FF" VLINK="#800080" ALINK="#FF0000">
|
||||
<OBJECT TYPE="text/site properties">
|
||||
</OBJECT>
|
||||
<UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="2img">
|
||||
<PARAM NAME="Local" VALUE="html/t277.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="2mg">
|
||||
<PARAM NAME="Local" VALUE="html/t277.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="access">
|
||||
<PARAM NAME="Name" VALUE="Edit File Attributes">
|
||||
<PARAM NAME="Local" VALUE="html/t203.htm">
|
||||
<PARAM NAME="Name" VALUE="List - Access">
|
||||
<PARAM NAME="Local" VALUE="html/t67.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="add">
|
||||
<PARAM NAME="Name" VALUE="Add Files Dialog">
|
||||
<PARAM NAME="Local" VALUE="html/t41.htm">
|
||||
<PARAM NAME="Name" VALUE="Adding Files and Disks">
|
||||
<PARAM NAME="Local" VALUE="html/t55.htm">
|
||||
<PARAM NAME="Name" VALUE="Select Location">
|
||||
<PARAM NAME="Local" VALUE="html/t257.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="add files">
|
||||
<PARAM NAME="Local" VALUE="html/t41.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="administrator">
|
||||
<PARAM NAME="Local" VALUE="html/t262.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="associations">
|
||||
<PARAM NAME="Local" VALUE="html/t44.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="attributes">
|
||||
<PARAM NAME="Local" VALUE="html/t203.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="auxtype">
|
||||
<PARAM NAME="Local" VALUE="html/t61.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="basic">
|
||||
<PARAM NAME="Local" VALUE="html/t112.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="blank">
|
||||
<PARAM NAME="Local" VALUE="html/t247.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="bulk">
|
||||
<PARAM NAME="Local" VALUE="html/t233.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="cassette">
|
||||
<PARAM NAME="Local" VALUE="html/t111.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="cd-rom">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="cf">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Removable Media (CF, floppy, CD-ROM)">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
<PARAM NAME="Name" VALUE="Opening a Volume">
|
||||
<PARAM NAME="Local" VALUE="html/t241.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="cffa">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Removable Media (CF, floppy, CD-ROM)">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
<PARAM NAME="Name" VALUE="Opening a Volume">
|
||||
<PARAM NAME="Local" VALUE="html/t241.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Windows Volume Copier">
|
||||
<PARAM NAME="Local" VALUE="html/t245.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="characteristics">
|
||||
<PARAM NAME="Local" VALUE="html/t20.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="choose folder">
|
||||
<PARAM NAME="Local" VALUE="html/t38.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="click">
|
||||
<PARAM NAME="Local" VALUE="html/t50.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="close">
|
||||
<PARAM NAME="Local" VALUE="html/t51.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="commands">
|
||||
<PARAM NAME="Name" VALUE="Available Commands">
|
||||
<PARAM NAME="Local" VALUE="html/t48.htm">
|
||||
<PARAM NAME="Name" VALUE="Selecting Commands">
|
||||
<PARAM NAME="Local" VALUE="html/t52.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="comment">
|
||||
<PARAM NAME="Local" VALUE="html/t43.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="company">
|
||||
<PARAM NAME="Local" VALUE="html/t49.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="compatibility">
|
||||
<PARAM NAME="Local" VALUE="html/t69.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="compress">
|
||||
<PARAM NAME="Local" VALUE="html/t58.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="compression">
|
||||
<PARAM NAME="Name" VALUE="Appendix - Compression Algorithms">
|
||||
<PARAM NAME="Local" VALUE="html/t69.htm">
|
||||
<PARAM NAME="Name" VALUE="List - Format">
|
||||
<PARAM NAME="Local" VALUE="html/t63.htm">
|
||||
<PARAM NAME="Name" VALUE="List - Packed">
|
||||
<PARAM NAME="Local" VALUE="html/t66.htm">
|
||||
<PARAM NAME="Name" VALUE="List - Ratio">
|
||||
<PARAM NAME="Local" VALUE="html/t65.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - Compression">
|
||||
<PARAM NAME="Local" VALUE="html/t29.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="convert">
|
||||
<PARAM NAME="Name" VALUE="Convert Disk Image to File Archive">
|
||||
<PARAM NAME="Local" VALUE="html/t215.htm">
|
||||
<PARAM NAME="Name" VALUE="Convert File Archive to Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t216.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Bulk Disk Image Converter">
|
||||
<PARAM NAME="Local" VALUE="html/t233.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Disk Image Converter">
|
||||
<PARAM NAME="Local" VALUE="html/t187.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="converter">
|
||||
<PARAM NAME="Name" VALUE="Appendix - File Format Converters">
|
||||
<PARAM NAME="Local" VALUE="html/t22.htm">
|
||||
<PARAM NAME="Name" VALUE="Extracting Files">
|
||||
<PARAM NAME="Local" VALUE="html/t39.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - File Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t23.htm">
|
||||
<PARAM NAME="Name" VALUE="Using the File Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t25.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="copy">
|
||||
<PARAM NAME="Local" VALUE="html/t273.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="crc">
|
||||
<PARAM NAME="Local" VALUE="html/t56.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="credits">
|
||||
<PARAM NAME="Local" VALUE="html/t24.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="date">
|
||||
<PARAM NAME="Local" VALUE="html/t62.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="delete">
|
||||
<PARAM NAME="Local" VALUE="html/t57.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="diassembly">
|
||||
<PARAM NAME="Local" VALUE="html/t109.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="disk">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Disk Images">
|
||||
<PARAM NAME="Local" VALUE="html/t18.htm">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Removable Media (CF, floppy, CD-ROM)">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t259.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="disk editor">
|
||||
<PARAM NAME="Local" VALUE="html/t13.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="disk image">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Disk Images">
|
||||
<PARAM NAME="Local" VALUE="html/t18.htm">
|
||||
<PARAM NAME="Name" VALUE="Convert Disk Image to File Archive">
|
||||
<PARAM NAME="Local" VALUE="html/t215.htm">
|
||||
<PARAM NAME="Name" VALUE="Convert File Archive to Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t216.htm">
|
||||
<PARAM NAME="Name" VALUE="Disk Image Characteristics">
|
||||
<PARAM NAME="Local" VALUE="html/t20.htm">
|
||||
<PARAM NAME="Name" VALUE="Disk Image Creator">
|
||||
<PARAM NAME="Local" VALUE="html/t247.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Bulk Disk Image Converter">
|
||||
<PARAM NAME="Local" VALUE="html/t233.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Disk Image Converter">
|
||||
<PARAM NAME="Local" VALUE="html/t187.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - SST Image Merge">
|
||||
<PARAM NAME="Local" VALUE="html/t201.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="disks">
|
||||
<PARAM NAME="Local" VALUE="html/t55.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="dos">
|
||||
<PARAM NAME="Local" VALUE="html/t21.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="embedded">
|
||||
<PARAM NAME="Local" VALUE="html/t21.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="eol">
|
||||
<PARAM NAME="Name" VALUE="Extracting Files">
|
||||
<PARAM NAME="Local" VALUE="html/t39.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - EOL Scanner">
|
||||
<PARAM NAME="Local" VALUE="html/t272.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="extension">
|
||||
<PARAM NAME="Local" VALUE="html/t45.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="extract">
|
||||
<PARAM NAME="Local" VALUE="html/t39.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="faddenSoft">
|
||||
<PARAM NAME="Local" VALUE="html/t46.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="features">
|
||||
<PARAM NAME="Local" VALUE="html/t47.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="file archive">
|
||||
<PARAM NAME="Name" VALUE="Convert Disk Image to File Archive">
|
||||
<PARAM NAME="Local" VALUE="html/t215.htm">
|
||||
<PARAM NAME="Name" VALUE="Convert File Archive to Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t216.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="file converter">
|
||||
<PARAM NAME="Local" VALUE="html/t23.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="file format">
|
||||
<PARAM NAME="Local" VALUE="html/t22.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="file list">
|
||||
<PARAM NAME="Local" VALUE="html/t53.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="file type">
|
||||
<PARAM NAME="Name" VALUE="Appendix - File Attribute Preservation">
|
||||
<PARAM NAME="Local" VALUE="html/t68.htm">
|
||||
<PARAM NAME="Name" VALUE="Edit File Attributes">
|
||||
<PARAM NAME="Local" VALUE="html/t203.htm">
|
||||
<PARAM NAME="Name" VALUE="List - File Type">
|
||||
<PARAM NAME="Local" VALUE="html/t60.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="file viewer">
|
||||
<PARAM NAME="Name" VALUE="Preferences - File Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t23.htm">
|
||||
<PARAM NAME="Name" VALUE="Using the File Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t25.htm">
|
||||
<PARAM NAME="Name" VALUE="Viewing Files">
|
||||
<PARAM NAME="Local" VALUE="html/t54.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="filename">
|
||||
<PARAM NAME="Local" VALUE="html/t45.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="files">
|
||||
<PARAM NAME="Local" VALUE="html/t28.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="filesystem separator">
|
||||
<PARAM NAME="Local" VALUE="html/t42.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="floppy">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Removable Media (CF, floppy, CD-ROM)">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Windows Volume Copier">
|
||||
<PARAM NAME="Local" VALUE="html/t245.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="folder">
|
||||
<PARAM NAME="Local" VALUE="html/t263.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="format">
|
||||
<PARAM NAME="Local" VALUE="html/t63.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="general">
|
||||
<PARAM NAME="Local" VALUE="html/t19.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="help">
|
||||
<PARAM NAME="Local" VALUE="html/t284.htm">
|
||||
</OBJECT>
|
||||
<UL> <LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="faq">
|
||||
<PARAM NAME="Local" VALUE="html/t284.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="image">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Disk Images">
|
||||
<PARAM NAME="Local" VALUE="html/t18.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t259.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Windows Volume Copier">
|
||||
<PARAM NAME="Local" VALUE="html/t245.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="import">
|
||||
<PARAM NAME="Name" VALUE="Import BASIC Program">
|
||||
<PARAM NAME="Local" VALUE="html/t112.htm">
|
||||
<PARAM NAME="Name" VALUE="Import From Cassette">
|
||||
<PARAM NAME="Local" VALUE="html/t111.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="info">
|
||||
<PARAM NAME="Local" VALUE="html/t258.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="list">
|
||||
<PARAM NAME="Local" VALUE="html/t50.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="listing">
|
||||
<PARAM NAME="Local" VALUE="html/t109.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="locked">
|
||||
<PARAM NAME="Local" VALUE="html/t67.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="menu">
|
||||
<PARAM NAME="Local" VALUE="html/t48.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="merge">
|
||||
<PARAM NAME="Local" VALUE="html/t201.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="mod date">
|
||||
<PARAM NAME="Local" VALUE="html/t62.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="modification">
|
||||
<PARAM NAME="Local" VALUE="html/t62.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="monitor">
|
||||
<PARAM NAME="Local" VALUE="html/t109.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="new archive">
|
||||
<PARAM NAME="Local" VALUE="html/t51.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="new folder">
|
||||
<PARAM NAME="Local" VALUE="html/t38.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="nufxlib">
|
||||
<PARAM NAME="Local" VALUE="html/t24.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="open">
|
||||
<PARAM NAME="Name" VALUE="Opening a Volume">
|
||||
<PARAM NAME="Local" VALUE="html/t241.htm">
|
||||
<PARAM NAME="Name" VALUE="Opening, Closing, and Creating Files">
|
||||
<PARAM NAME="Local" VALUE="html/t51.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="packed">
|
||||
<PARAM NAME="Local" VALUE="html/t66.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="paste">
|
||||
<PARAM NAME="Local" VALUE="html/t273.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="pathname">
|
||||
<PARAM NAME="Local" VALUE="html/t59.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="permissions">
|
||||
<PARAM NAME="Local" VALUE="html/t67.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="preferences">
|
||||
<PARAM NAME="Name" VALUE="Preferences - Compression">
|
||||
<PARAM NAME="Local" VALUE="html/t29.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - Disk Image">
|
||||
<PARAM NAME="Local" VALUE="html/t259.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - File Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t23.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - Files">
|
||||
<PARAM NAME="Local" VALUE="html/t28.htm">
|
||||
<PARAM NAME="Name" VALUE="Preferences - General">
|
||||
<PARAM NAME="Local" VALUE="html/t19.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="preservation">
|
||||
<PARAM NAME="Local" VALUE="html/t68.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="printing">
|
||||
<PARAM NAME="Local" VALUE="html/t53.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="properties">
|
||||
<PARAM NAME="Local" VALUE="html/t277.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="ratio">
|
||||
<PARAM NAME="Local" VALUE="html/t65.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="raw">
|
||||
<PARAM NAME="Local" VALUE="html/t241.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="re-compress">
|
||||
<PARAM NAME="Local" VALUE="html/t58.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="reformat">
|
||||
<PARAM NAME="Local" VALUE="html/t22.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="register">
|
||||
<PARAM NAME="Local" VALUE="html/t49.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="registration">
|
||||
<PARAM NAME="Name" VALUE="Entering Registration Data">
|
||||
<PARAM NAME="Local" VALUE="html/t49.htm">
|
||||
<PARAM NAME="Name" VALUE="How (and Why) to Register">
|
||||
<PARAM NAME="Local" VALUE="html/t46.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="rename">
|
||||
<PARAM NAME="Name" VALUE="Rename Entry">
|
||||
<PARAM NAME="Local" VALUE="html/t42.htm">
|
||||
<PARAM NAME="Name" VALUE="Renaming a Volume">
|
||||
<PARAM NAME="Local" VALUE="html/t268.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="scan">
|
||||
<PARAM NAME="Local" VALUE="html/t272.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="size">
|
||||
<PARAM NAME="Local" VALUE="html/t64.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="SST">
|
||||
<PARAM NAME="Local" VALUE="html/t201.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="subdirectory">
|
||||
<PARAM NAME="Local" VALUE="html/t263.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="sub-volume">
|
||||
<PARAM NAME="Local" VALUE="html/t21.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="test">
|
||||
<PARAM NAME="Local" VALUE="html/t56.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="type">
|
||||
<PARAM NAME="Name" VALUE="List - Aux Type">
|
||||
<PARAM NAME="Local" VALUE="html/t61.htm">
|
||||
<PARAM NAME="Name" VALUE="List - File Type">
|
||||
<PARAM NAME="Local" VALUE="html/t60.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="uncompressed size">
|
||||
<PARAM NAME="Local" VALUE="html/t64.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="unlocked">
|
||||
<PARAM NAME="Local" VALUE="html/t67.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="using">
|
||||
<PARAM NAME="Local" VALUE="html/t48.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="view">
|
||||
<PARAM NAME="Local" VALUE="html/t54.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="viewer">
|
||||
<PARAM NAME="Name" VALUE="Appendix - File Format Converters">
|
||||
<PARAM NAME="Local" VALUE="html/t22.htm">
|
||||
<PARAM NAME="Name" VALUE="Using the File Viewer">
|
||||
<PARAM NAME="Local" VALUE="html/t25.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="volume">
|
||||
<PARAM NAME="Name" VALUE="Appendix - About Removable Media (CF, floppy, CD-ROM)">
|
||||
<PARAM NAME="Local" VALUE="html/t244.htm">
|
||||
<PARAM NAME="Name" VALUE="Appendix - Embedded DOS Volumes">
|
||||
<PARAM NAME="Local" VALUE="html/t21.htm">
|
||||
<PARAM NAME="Name" VALUE="Opening a Volume">
|
||||
<PARAM NAME="Local" VALUE="html/t241.htm">
|
||||
<PARAM NAME="Name" VALUE="Tool - Windows Volume Copier">
|
||||
<PARAM NAME="Local" VALUE="html/t245.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="volume number">
|
||||
<PARAM NAME="Local" VALUE="html/t277.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="wav">
|
||||
<PARAM NAME="Local" VALUE="html/t111.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="Welcome">
|
||||
<PARAM NAME="Local" VALUE="html/t10.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="win2k">
|
||||
<PARAM NAME="Local" VALUE="html/t262.htm">
|
||||
</OBJECT>
|
||||
<LI><OBJECT TYPE="text/sitemap">
|
||||
<PARAM NAME="Keyword" VALUE="winxp">
|
||||
<PARAM NAME="Local" VALUE="html/t262.htm">
|
||||
</OBJECT>
|
||||
</UL>
|
||||
</BODY></HTML>
|