Compare commits

...

2 Commits

Author SHA1 Message Date
Shawn Quick 1fb1aa259d build ciderpress libraries with cmake 2023-02-04 15:37:30 -08:00
Shawn Quick 9c66992c23 building ciderpress libraries with cmake 2023-02-04 15:34:50 -08:00
511 changed files with 14402 additions and 102212 deletions

View File

@ -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)

View File

@ -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

1
app_config.h Symbolic link
View File

@ -0,0 +1 @@
config.h

4123
asm.cpp

File diff suppressed because it is too large Load Diff

28
ciderpress/.gitignore vendored
View File

@ -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

View File

@ -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

View File

@ -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.

File diff suppressed because it is too large Load Diff

View File

@ -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/.

Binary file not shown.

Binary file not shown.

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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;
}

View File

@ -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*/

View File

@ -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, &reg, &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

View File

@ -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*/

View File

@ -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));
}

View File

@ -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*/

File diff suppressed because it is too large Load Diff

View File

@ -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();
}
}

View File

@ -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*/

View File

@ -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);
}

View File

@ -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*/

View File

@ -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);
}
}

View File

@ -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*/

View File

@ -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();
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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;
}
}

View File

@ -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*/

File diff suppressed because it is too large Load Diff

View File

@ -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*/

View File

@ -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;
}
}

View File

@ -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*/

View File

@ -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*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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();
}
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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);
}

View File

@ -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*/

View File

@ -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);
}

View File

@ -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*/

View File

@ -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);
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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());
}

View File

@ -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*/

File diff suppressed because it is too large Load Diff

View File

@ -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*/

View File

@ -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);
}

View File

@ -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*/

File diff suppressed because it is too large Load Diff

View File

@ -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*/

View File

@ -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();
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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*/

View File

@ -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();
}

View File

@ -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*/

View File

@ -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

View File

@ -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*/

View File

@ -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);
}

View File

@ -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*/

View File

@ -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;
}

View File

@ -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*/

View File

@ -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, &reg, &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*/

View File

@ -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*/

View File

@ -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);
}
}

View File

@ -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*/

File diff suppressed because it is too large Load Diff

View File

@ -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*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 758 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 B

Binary file not shown.

View File

@ -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>

View File

@ -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>

Some files were not shown because too many files have changed in this diff Show More