initial import

This commit is contained in:
Andy McFadden 2007-03-27 17:47:10 +00:00
commit 0d7d1b67e0
337 changed files with 140029 additions and 0 deletions

107
CP.dsw Normal file
View File

@ -0,0 +1,107 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "app"=.\app\app.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name diskimg
End Project Dependency
Begin Project Dependency
Project_Dep_Name util
End Project Dependency
Begin Project Dependency
Project_Dep_Name reformat
End Project Dependency
}}}
###############################################################################
Project: "diskimg"=.\diskimg\diskimg.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name libhfs
End Project Dependency
}}}
###############################################################################
Project: "libhfs"=.\diskimg\libhfs\libhfs.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "mdc"=.\mdc\mdc.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
Begin Project Dependency
Project_Dep_Name diskimg
End Project Dependency
Begin Project Dependency
Project_Dep_Name util
End Project Dependency
}}}
###############################################################################
Project: "reformat"=.\reformat\reformat.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Project: "util"=.\util\util.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

67
CP.sln Normal file
View File

@ -0,0 +1,67 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "app", "app\app.vcproj", "{B023611B-7086-46E1-847B-3B21C4732384}"
ProjectSection(ProjectDependencies) = postProject
{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5} = {04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}
{18BCF397-397E-460C-A1DC-3E26798966E4} = {18BCF397-397E-460C-A1DC-3E26798966E4}
{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4} = {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "diskimg", "diskimg\diskimg.vcproj", "{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}"
ProjectSection(ProjectDependencies) = postProject
{0FA742E9-8C07-43DD-AFF8-CE31FAF70821} = {0FA742E9-8C07-43DD-AFF8-CE31FAF70821}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdc", "mdc\mdc.vcproj", "{7DF41D71-C8DC-48AA-B372-4613210310A4}"
ProjectSection(ProjectDependencies) = postProject
{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5} = {04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}
{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4} = {0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "util", "util\util.vcproj", "{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhfs", "diskimg\libhfs\libhfs.vcproj", "{0FA742E9-8C07-43DD-AFF8-CE31FAF70821}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "reformat", "reformat\reformat.vcproj", "{18BCF397-397E-460C-A1DC-3E26798966E4}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{B023611B-7086-46E1-847B-3B21C4732384}.Debug.ActiveCfg = Debug|Win32
{B023611B-7086-46E1-847B-3B21C4732384}.Debug.Build.0 = Debug|Win32
{B023611B-7086-46E1-847B-3B21C4732384}.Release.ActiveCfg = Release|Win32
{B023611B-7086-46E1-847B-3B21C4732384}.Release.Build.0 = Release|Win32
{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Debug.ActiveCfg = Debug|Win32
{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Debug.Build.0 = Debug|Win32
{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Release.ActiveCfg = Release|Win32
{0CFE6FAD-0126-4E99-8625-C807D1D2AAF4}.Release.Build.0 = Release|Win32
{7DF41D71-C8DC-48AA-B372-4613210310A4}.Debug.ActiveCfg = Debug|Win32
{7DF41D71-C8DC-48AA-B372-4613210310A4}.Debug.Build.0 = Debug|Win32
{7DF41D71-C8DC-48AA-B372-4613210310A4}.Release.ActiveCfg = Release|Win32
{7DF41D71-C8DC-48AA-B372-4613210310A4}.Release.Build.0 = Release|Win32
{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Debug.ActiveCfg = Debug|Win32
{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Debug.Build.0 = Debug|Win32
{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Release.ActiveCfg = Release|Win32
{04BFAE2A-7AB3-4B63-B4AB-42FF1D6AD3C5}.Release.Build.0 = Release|Win32
{0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Debug.ActiveCfg = Debug|Win32
{0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Debug.Build.0 = Debug|Win32
{0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Release.ActiveCfg = Release|Win32
{0FA742E9-8C07-43DD-AFF8-CE31FAF70821}.Release.Build.0 = Release|Win32
{18BCF397-397E-460C-A1DC-3E26798966E4}.Debug.ActiveCfg = Debug|Win32
{18BCF397-397E-460C-A1DC-3E26798966E4}.Debug.Build.0 = Debug|Win32
{18BCF397-397E-460C-A1DC-3E26798966E4}.Release.ActiveCfg = Release|Win32
{18BCF397-397E-460C-A1DC-3E26798966E4}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

37
DIST/License.txt Normal file
View File

@ -0,0 +1,37 @@
End-User License Agreement for CiderPress
Copyright (C) 2007 FaddenSoft, LLC. 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.

2516
DIST/NList.Data.TXT Normal file

File diff suppressed because it is too large Load Diff

11
DIST/ReadMe.txt Normal file
View File

@ -0,0 +1,11 @@
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 faddenSoft CiderPress
web site at http://www.faddensoft.com/ciderpress/.

271
DIST/with-mdc.deploy Normal file
View File

@ -0,0 +1,271 @@
DeployMaster Installation Script
9
faddenSoft
http://www.faddensoft.com/
CiderPress
http://www.faddensoft.com/ciderpress/
3.0.0
39166
C:\DATA\faddenSoft\fs.ico
Copyright © 2007 faddenSoft, LLC. All rights reserved.
C:\Src\CiderPress\DIST\ReadMe.txt
C:\Src\CiderPress\DIST\License.txt
%PROGRAMFILES%\faddenSoft\CiderPress
%COMMONFILES%\faddenSoft\
%PROGRAMSMENU%\CiderPress
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
+
%APPFOLDER%
1
+
C:\Src\CiderPress\DIST\CiderPress.cnt
3
0
FALSE
FALSE
C:\Src\CiderPress\DIST\CiderPress.exe
3
0
FALSE
FALSE
C:\Src\CiderPress\DIST\CIDERPRESS.HLP
3
0
FALSE
FALSE
C:\Src\CiderPress\DIST\NList.Data.TXT
3
1
FALSE
FALSE
-
%APPCOMMONFOLDER%
1
%APPMENU%
1
+
C:\Src\CiderPress\DIST\CiderPress.exe
4
CiderPress
The ultimate Apple II archive utility
0
C:\Src\CiderPress\DIST\CIDERPRESS.HLP
4
CiderPress Help
Help file for CiderPress
0
-
%DESKTOP%
1
%SENDTO%
1
%STARTUP%
1
%WINDOWS%
1
%SYSTEM%
1
-
Common DLLs
0
FALSE
FALSE
NuFX, zlib, and disk image access libraries.
+
%APPFOLDER%
1
+
C:\Src\CiderPress\DIST\diskimg4.dll
3
0
FALSE
FALSE
C:\Src\CiderPress\DIST\nufxlib2.dll
3
0
FALSE
FALSE
C:\Src\CiderPress\DIST\zlib1.dll
3
0
FALSE
FALSE
-
%APPCOMMONFOLDER%
1
%APPMENU%
1
%DESKTOP%
1
%SENDTO%
1
%STARTUP%
1
%WINDOWS%
1
%SYSTEM%
1
-
MDC
0
TRUE
FALSE
Multi-Disk Catalog utility.
+
%APPFOLDER%
1
+
C:\Src\CiderPress\DIST\mdc.exe
3
0
FALSE
FALSE
-
%APPCOMMONFOLDER%
1
%APPMENU%
1
+
C:\Src\CiderPress\DIST\mdc.exe
4
MDC
Multi-Disk Catalog for Apple II disk images
0
-
%DESKTOP%
1
%SENDTO%
1
%STARTUP%
1
%WINDOWS%
1
%SYSTEM%
1
-
-
1
1
0
1
1
+
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
TRUE
FALSE
TRUE
TRUE
C:\Src\CiderPress\DIST\CiderPress.exe
-install
C:\Src\CiderPress\DIST\CiderPress.exe
-uninstall
TRUE
TRUE
FALSE
36725
0
FALSE
36725
0
10
Setup300.exe
FALSE

25
LICENSE.txt Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) 2007, FaddenSoft, LLC
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.

313
ReadMe.htm Normal file
View File

@ -0,0 +1,313 @@
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<meta name="GENERATOR" content="Microsoft FrontPage 4.0">
<meta name="ProgId" content="FrontPage.Editor.Document">
<title>CiderPress Source README</title>
</head>
<body>
<h1>CiderPress Source README</h1>
Release notes for v3.0.0<br>
Last updated: 25-Mar-2007
<h2>Contents:</h2>
<ul>
<li><a href="#intro">What's Ciderpress?</a>
<li><a href="#build">Building the Sources</a>
<li><a href="#notes">Source Notes</a>
</ul>
<hr>
<h2><a name="intro">What's CiderPress?</a></h2>
<p>CiderPress is a Windows utility for manipulating Apple II disk images
and file archives. It supports all major disk and archive formats used
on the Apple II and by Apple II emulators.
<p>CiderPress was sold by faddenSoft, LLC as a shareware product for about four
years, starting in March 2003.
<h3>Why Bother?</h3>
<p>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!".
<p>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.
<p>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.
<p>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).
<p>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.
<p>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".</p>
<h3>License</h3>
<p>The source code to CiderPress is being made available under the BSD license:</p>
<blockquote><code>
Copyright (c) 2007, FaddenSoft, LLC<br>
All rights reserved.
<p>Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
<ul>
<li> Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
<li> 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.
<li> 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.
</ul>
<p>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.
</code></blockquote>
CiderPress requires three other libraries, all of which are included as either
source or binaries:
<ul>
<li>NufxLib, also available under the BSD license.
<li>Zlib, available under the Zlib license.
<li>libhfs, available under the GPL license.
</ul>
<p>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.</p>
<p>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
&quot;EXCISE_GPL_CODE&quot; in DiskImg.h).</p>
<hr>
<h2><a name="build">Building the Sources</a></h2>
<p>How to build your own copy of CiderPress.
<h3>Windows</h3>
<p>The sources are distributed with the necessary project/solution files for
Microsoft Visual Studio 6.0 and Microsoft Visual Studio 2003.&nbsp; All you
really need is the C++ compiler.&nbsp; The free or student editions of the
compilers will probably work.
<p>I usually work on CiderPress in Visual C++ 6.0,
and use that when building for distribution.&nbsp; I chose to stick with 6.0 for
CiderPress because you need an additional MSVC DLL when you build with
2003.&nbsp; Also, the newer tools cause the newer file selection dialogs to be
used, and the custom dialog code gets all wonky.</p>
<p>In 6.0, you need to select &quot;app&quot; as the build target (Build-&gt;Set
active configuration), and hit F7.&nbsp; This will build everything except MDC,
which you can then build by selecting &quot;mdc&quot; and hitting F7.&nbsp; You
can also configure a batch build.&nbsp; In 2003 just select &quot;build
solution&quot;.&nbsp; The necessary prebuilt libraries should be there for both
&quot;debug&quot; and &quot;release&quot; builds.&nbsp; (You can tell how it has
been built by looking at the version string in the About box.)</p>
<p>When the build completes, the necessary DLLs are copied around so you can
execute the binary by launching it within Visual Studio (hit F5).&nbsp; You will
get a warning about not being able to find the NiftyList data file unless you
copy that from &quot;DIST&quot; into the app source directory.</p>
<p>The distribution comes with prebuilt versions of NufxLib2 and zlib in DLL
form.&nbsp; If you like, you can download and compile your own.</p>
<p>There are two things you can't create with the standard tools:</p>
<ul>
<li>The help text was created with HelpMatic Pro. The source file is in a
proprietary format. It should probably be converted to HTML Help.</li>
<li>The installation packages are created with DeployMaster. The
".deploy" file specifies how the package is built. </li>
</ul>
<p>Compiled help files are included, so you can still generate a new version of
CiderPress even if you can't update the help.&nbsp; You don't strictly need the installer either, though it is
quite handy, and the uninstaller will clean up the registry entries CiderPress
creates.</p>
<h3>Linux</h3>
<p>The &quot;linux&quot; directory has a few command-line utilities and a simple
makefile.&nbsp; It will build the diskimg and HFS libraries, then the sample utilities:</p>
<ul>
<li><code>getfile disk-image filename</code> -- extract a file from a disk
image.&nbsp; The file is written to stdout.</li>
<li><code>makedisk {dos|prodos|pascal} size image-filename.po file1 ...</code>
-- create a new disk image, with the specified size and format, and copy the
specified files onto it.</li>
<li><code>mdc file1 ...</code> -- this is equivalent to the MDC application
for Windows.&nbsp; It recursively scans all files and directories specified,
displaying the contents of any disk images it finds.</li>
</ul>
<p>This are mostly intended for testing and illustration of the interfaces, but
they can be useful as well.&nbsp; Some other code is also provided:</p>
<ul>
<li><code>iconv infile outfile</code> -- convert an image from one format to
another.&nbsp; This was used for testing.</li>
<li><code>packddd infile outfile</code> -- the DDD code was originally
developed under Linux.&nbsp; This code is here for historical reasons.</li>
<li><code>sstasm part1 part2</code> -- the SST re-assembly code was originally
developed under Linux.&nbsp; The code is here for historical reasons.</li>
</ul>
<p>The &quot;prebuilt&quot; directory has a pre-built copy of NufxLib, so you
don't have to build your own.</p>
<p>If you're planning to hack on this stuff, use &quot;make depend&quot; before
&quot;make&quot; to fill in the dependencies.</p>
<hr>
<h2><a name="notes">Source Notes</a></h2>
<p>Some notes on what you'll find in the various directories.
<h3>Main Application</h3>
<p>This is highly Windows-centric.&nbsp; My goal was to learn how to write a
Windows application, so I made no pretense at portability.&nbsp; For better or
worse, I avoided the Visual Studio &quot;wizards&quot; for the dialogs.
<p>Much of the user interface text is in the resource file.&nbsp; Much is not,
especially when it comes to error messages.&nbsp; This will need to be addressed
if internationalization is attempted.
<p>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.
<h3>MDC Application</h3>
<p>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.&nbsp; There's not
much to it, and it hasn't changed substantially since it was first written.
<h3>DiskImg Library</h3>
<p>This library provides access to disk images.&nbsp; It automatically handles a
wide variety of formats.
<p>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).
<p>The basic classes, defined in DiskImg.h, are:
<ul>
<li>DiskImg.&nbsp; This represents a single disk image, which may have
sub-images.&nbsp; Operations on a DiskImg are roughly equivalent to a device
driver: you can read and write blocks, detect image formats, and create new
images.</li>
<li>DiskFS.&nbsp; Paired with a DiskImg, this is roughly equivalent to a GS/OS
FST (FileSystem Translator).&nbsp; You can perform file operations like
rename and delete, format disks, see how much space is available, and search
for sub-volumes.</li>
<li>A2File.&nbsp; Represents a file on a DiskFS.&nbsp; This holds the file's
name, attributes, track/sector or block lists, and provides a call to open a
file.</li>
<li>A2FileDescr.&nbsp; Represents an open file.&nbsp; You can read or write
data.</li>
</ul>
<p>Sub-classes are defined in DiskImgDetail.h.&nbsp; Most applications won't
need to access this header file.&nbsp; Each Apple II filesystem defines
sub-classes of DiskFS, A2File, and A2FileDescr.</p>
<p>In an ideal world, the code would mimic the GS/OS file operations.&nbsp; In
practice, CiderPress didn't need the full range of capabilities, so the
functions have some basic limitations:
<ul>
<li>On ProDOS and HFS, you can only open one fork at a time. This allowed me
to use simpler data structures.
<li>Files are expected to be written in one large chunk. This reduced the
complexity of the task enormously, because there's so much less that can
go wrong.
</ul>
<p>Some things that can be improved:
<ul>
<li>The overall structure of the filesystem handlers evolved over time. There
is some amount of redundancy that could be factored out.
<li>The API, especially in DiskImg, could probably be simplified.
</ul>
<p>The library depends on NufxLib and zlib for access to compressed images.</p>
<h3>Reformat Library</h3>
<p>This is probably the most "fun" component of CiderPress. It converts Apple II files
to more easily accessible Windows equivalents.
<p>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.
<p>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 &quot;examine&quot; 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 &quot;process&quot; function is called to convert the data.
<p>Bear in mind that reformatters may be disabled from the preferences menu.&nbsp;
Also, when extracting files for easy access in Windows, the &quot;best&quot;
reformatter is employed by the extraction code.<p>Most of the code should be portable, though some of it uses the MFC CString class.&nbsp;
This could probably be altered to use STL strings or plain.
<h3>Util Library</h3>
<p>Miscellaneous utility functions.
<p>For a good time, look at SelectFilesDialog.cpp.
<p>To enable debug logging for one of the applications, define _DEBUG_LOG in
MyDebug.h in this library. You will see "_DEBUG_LOG" in the version string
in the About box when this is defined. The log is written to C:\cplog.txt.
An existing log file will be appended to if the previous log was written to
less than 8 hours ago.</p>
<hr>
<p>Enjoy!</p>
<address>Andy McFadden</address>
</body>
</html>

906
app/ACUArchive.cpp Normal file
View File

@ -0,0 +1,906 @@
/*
* 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
* ===========================================================================
*/
/*
* Extract data from an entry.
*
* If "*ppText" is non-nil, the data will be read into the pointed-to buffer
* so long as it's shorter than *pLength bytes. The value in "*pLength"
* will be set to the actual length used.
*
* If "*ppText" is nil, the uncompressed data will be placed into a buffer
* allocated with "new[]".
*
* Returns IDOK on success, IDCANCEL if the operation was cancelled by the
* user, and -1 value on failure. On failure, "*pErrMsg" holds an error
* message.
*
* "which" is an anonymous GenericArchive enum (e.g. "kDataThread").
*/
int
AcuEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const
{
NuError nerr;
ExpandBuffer expBuf;
char* dataBuf = nil;
long len;
bool needAlloc = true;
int result = -1;
ASSERT(fpArchive != nil);
ASSERT(fpArchive->fFp != nil);
if (*ppText != nil)
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("Unable to seek to offset %ld: %s",
fOffset, strerror(errno));
goto bail;
}
if (GetSqueezed()) {
nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(),
&expBuf, false, 0);
if (nerr != kNuErrNone) {
pErrMsg->Format("File read failed: %s", NuStrError(nerr));
goto bail;
}
char* unsqBuf = nil;
long unsqLen = 0;
expBuf.SeizeBuffer(&unsqBuf, &unsqLen);
WMSG2("Unsqueezed %ld bytes to %d\n",
(unsigned long) GetCompressedLen(), unsqLen);
if (unsqLen == 0) {
// some bonehead squeezed a zero-length file
delete[] unsqBuf;
ASSERT(*ppText == nil);
WMSG0("Handling zero-length squeezed file!\n");
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("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 == nil) {
pErrMsg->Format("allocation of %ld bytes failed", len);
goto bail;
}
} else {
if (*pLength < (long) len) {
pErrMsg->Format("buf size %ld too short (%ld)",
*pLength, len);
goto bail;
}
dataBuf = *ppText;
}
if (fread(dataBuf, len, 1, fpArchive->fFp) != 1) {
pErrMsg->Format("File read failed: %s", 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 == nil);
}
}
return result;
}
/*
* Extract data from a thread to a file. Since we're not copying to memory,
* we can't assume that we're able to hold the entire file all at once.
*
* Returns IDOK on success, IDCANCEL if the operation was cancelled by the
* user, and -1 value on failure. On failure, "*pMsg" holds an
* error message.
*/
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 = "No such fork";
goto bail;
}
len = (long) GetUncompressedLen();
if (len == 0) {
WMSG0("Empty fork\n");
result = IDOK;
goto bail;
}
errno = 0;
if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) {
pErrMsg->Format("Unable to seek to offset %ld: %s",
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) GetCompressedLen(),
&expBuf, false, 0);
if (nerr != kNuErrNone) {
pErrMsg->Format("File read failed: %s", NuStrError(nerr));
goto bail;
}
expBuf.SeizeBuffer(&buf, &uncLen);
WMSG2("Unsqueezed %ld bytes to %d\n", len, uncLen);
// some bonehead squeezed a zero-length file
if (uncLen == 0) {
ASSERT(buf == nil);
WMSG0("Handling zero-length squeezed file!\n");
result = IDOK;
goto bail;
}
int err = GenericEntry::WriteConvert(outfp, buf, uncLen, &conv,
&convHA, &lastCR);
if (err != 0) {
pErrMsg->Format("File write failed: %s", strerror(err));
delete[] buf;
goto bail;
}
delete[] buf;
} else {
nerr = CopyData(outfp, conv, convHA, pErrMsg);
if (nerr != kNuErrNone) {
if (pErrMsg->IsEmpty()) {
pErrMsg->Format("Failed while copying data: %s\n",
NuStrError(nerr));
}
goto bail;
}
}
result = IDOK;
bail:
SET_PROGRESS_END();
return result;
}
/*
* Copy data from the seeked archive to outfp, possibly converting EOL along
* the way.
*/
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("File read failed: %s.", NuStrError(nerr));
goto bail;
}
/* write chunk to destination file */
int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv,
&convHA, &lastCR);
if (err != 0) {
pMsg->Format("File write failed: %s.", strerror(err));
nerr = kNuErrGeneric;
goto bail;
}
dataRem -= chunkLen;
SET_PROGRESS_UPDATE(ComputePercent(srcLen - dataRem, srcLen));
}
bail:
return nerr;
}
/*
* 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 "nil" buffer pointer.
*/
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("Unable to seek to offset %ld: %s\n",
fOffset, strerror(errno));
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
goto bail;
}
if (GetSqueezed()) {
nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetCompressedLen(),
nil, false, 0);
if (nerr != kNuErrNone) {
errMsg.Format("Unsqueeze failed: %s.", 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("Unable to seek to offset %ld (file truncated?): %s\n",
fOffset, strerror(errno));
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
goto bail;
}
}
if (SET_PROGRESS_UPDATE(100) == IDCANCEL)
nerr = kNuErrAborted;
bail:
return nerr;
}
/*
* ===========================================================================
* AcuArchive
* ===========================================================================
*/
/*
* Perform one-time initialization. There really isn't any for us.
*
* Returns 0 on success, nonzero on error.
*/
/*static*/ CString
AcuArchive::AppInit(void)
{
return "";
}
/*
* Open an ACU archive.
*
* Returns an error string on failure, or "" on success.
*/
GenericArchive::OpenResult
AcuArchive::Open(const char* filename, bool readOnly, CString* pErrMsg)
{
CString errMsg;
fIsReadOnly = true; // ignore "readOnly"
errno = 0;
fFp = fopen(filename, "rb");
if (fFp == nil) {
errMsg.Format("Unable to open %s: %s.", filename, strerror(errno));
goto bail;
}
{
CWaitCursor waitc;
int result;
result = LoadContents();
if (result < 0) {
errMsg.Format("The file is not an ACU archive.");
goto bail;
} else if (result > 0) {
errMsg.Format("Failed while reading data from ACU archive.");
goto bail;
}
}
SetPathName(filename);
bail:
*pErrMsg = errMsg;
if (!errMsg.IsEmpty())
return kResultFailure;
else
return kResultSuccess;
}
/*
* Finish instantiating an AcuArchive object by creating a new archive.
*
* Returns an error string on failure, or "" on success.
*/
CString
AcuArchive::New(const char* /*filename*/, const void* /*options*/)
{
CString retmsg("Sorry, AppleLink Compression Utility files can't be created.");
return retmsg;
}
/*
* Our capabilities.
*/
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;
}
}
/*
* Load the contents of the archive.
*
* Returns 0 on success, < 0 if this is not an ACU archive > 0 if this appears
* to be an ACU archive but it's damaged.
*/
int
AcuArchive::LoadContents(void)
{
NuError nerr;
int numEntries;
ASSERT(fFp != nil);
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;
}
/*
* Reload the contents of the archive.
*/
CString
AcuArchive::Reload(void)
{
fReloadFlag = true; // tell everybody that cached data is invalid
DeleteEntries();
if (LoadContents() != 0) {
return "Reload failed.";
}
return "";
}
/*
* 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
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)
{
WMSG0("Not an ACU archive\n");
return -1;
}
WMSG1("Looks like an ACU archive with %d entries\n", header.fileCount);
*pNumEntries = header.fileCount;
return 0;
}
/*
* Read and decode an AppleLink Compression Utility file entry header.
* This leaves the file seeked to the point immediately past the filename.
*/
NuError
AcuArchive::ReadFileHeader(AcuFileEntry* pEntry)
{
NuError err = kNuErrNone;
unsigned char buf[kAcuEntryHeaderLen];
ASSERT(pEntry != nil);
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) {
WMSG1("GLITCH: filename is too long (%d bytes)\n",
pEntry->fileNameLen);
err = kNuErrGeneric;
goto bail;
}
if (!pEntry->fileNameLen) {
WMSG0("GLITCH: filename missing\n");
err = kNuErrGeneric;
goto bail;
}
/* don't know if this is possible or not */
if (pEntry->storageType == 5) {
WMSG0("HEY: EXTENDED FILE\n");
}
err = AcuRead(pEntry->fileName, pEntry->fileNameLen);
if (err != kNuErrNone)
goto bail;
pEntry->fileName[pEntry->fileNameLen] = '\0';
//DumpFileHeader(pEntry);
bail:
return err;
}
/*
* Dump the contents of an AcuFileEntry struct.
*/
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);
WMSG1(" Header for file '%s':\n", pEntry->fileName);
WMSG4(" dataStorageLen=%d eof=%d blockCount=%d checksum=0x%04x\n",
pEntry->dataStorageLen, pEntry->dataEof, pEntry->blockCount,
pEntry->dataChecksum);
WMSG4(" fileType=0x%02x auxType=0x%04x storageType=0x%02x access=0x%04x\n",
pEntry->fileType, pEntry->auxType, pEntry->storageType, pEntry->access);
WMSG2(" created %s, modified %s\n", (const char*) createStr,
(const char*) modStr);
WMSG2(" fileNameLen=%d headerChecksum=0x%04x\n",
pEntry->fileNameLen, pEntry->headerChecksum);
}
/*
* Given an AcuFileEntry structure, add an appropriate entry to the list.
*/
int
AcuArchive::CreateEntry(const AcuFileEntry* pEntry)
{
const int kAcuFssep = '/';
NuError err = kNuErrNone;
AcuEntry* pNewEntry;
/*
* Create the new entry.
*/
pNewEntry = new AcuEntry(this);
pNewEntry->SetPathName(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("Uncompr");
} else if (pEntry->compressionType == kAcuCompSqueeze) {
pNewEntry->SetFormatStr("Squeeze");
pNewEntry->SetSqueezed(true);
} else {
pNewEntry->SetFormatStr("(unknown)");
pNewEntry->SetSqueezed(false);
}
pNewEntry->SetOffset(ftell(fFp));
AddEntry(pNewEntry);
return err;
}
/*
* ===========================================================================
* ACU functions
* ===========================================================================
*/
/*
* Test if this entry is a directory.
*/
bool
AcuArchive::IsDir(const AcuFileEntry* pEntry)
{
return (pEntry->storageType == 0x0d);
}
/*
* Wrapper for fread(). Note the arguments resemble read(2) rather
* than fread(3S).
*/
NuError
AcuArchive::AcuRead(void* buf, size_t nbyte)
{
size_t result;
ASSERT(buf != nil);
ASSERT(nbyte > 0);
ASSERT(fFp != nil);
errno = 0;
result = fread(buf, 1, nbyte, fFp);
if (result != nbyte)
return errno ? (NuError)errno : kNuErrFileRead;
return kNuErrNone;
}
/*
* 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
AcuArchive::AcuSeek(long offset)
{
ASSERT(fFp != nil);
ASSERT(offset > 0);
/*DBUG(("--- seeking forward %ld bytes\n", offset));*/
if (fseek(fFp, offset, SEEK_CUR) < 0)
return kNuErrFileSeek;
return kNuErrNone;
}
/*
* Convert from ProDOS compact date format to the expanded DateTime format.
*/
void
AcuArchive::AcuConvertDateTime(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;
}
/*
* ===========================================================================
* AcuArchive -- test files
* ===========================================================================
*/
/*
* Test the records represented in the selection set.
*/
bool
AcuArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{
NuError nerr;
AcuEntry* pEntry;
CString errMsg;
bool retVal = false;
ASSERT(fFp != nil);
WMSG1("Testing %d entries\n", pSelSet->GetNumEntries());
SelectionEntry* pSelEntry = pSelSet->IterNext();
while (pSelEntry != nil) {
pEntry = (AcuEntry*) pSelEntry->GetEntry();
WMSG2(" Testing '%s' (offset=%ld)\n", pEntry->GetDisplayName(),
pEntry->GetOffset());
SET_PROGRESS_UPDATE2(0, pEntry->GetDisplayName(), nil);
nerr = pEntry->TestEntry(pMsgWnd);
if (nerr != kNuErrNone) {
if (nerr == kNuErrAborted) {
CString title;
title.LoadString(IDS_MB_APP_NAME);
errMsg = "Cancelled.";
pMsgWnd->MessageBox(errMsg, title, MB_OK);
} else {
errMsg.Format("Failed while testing '%s': %s.",
pEntry->GetPathName(), NuStrError(nerr));
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
}
goto bail;
}
pSelEntry = pSelSet->IterNext();
}
/* show success message */
errMsg.Format("Tested %d file%s, no errors found.",
pSelSet->GetNumEntries(),
pSelSet->GetNumEntries() == 1 ? "" : "s");
pMsgWnd->MessageBox(errMsg);
retVal = true;
bail:
SET_PROGRESS_END();
return retVal;
}

222
app/ACUArchive.h Normal file
View File

@ -0,0 +1,222 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* AppleLink Compression Utility archive support.
*/
#ifndef __ACU_ARCHIVE__
#define __ACU_ARCHIVE__
#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) {}
// retrieve thread data
virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const;
virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
ConvertHighASCII convHA, CString* pErrMsg) const;
virtual long GetSelectionSerial(void) const { return -1; } // doesn't matter
virtual bool GetFeatureFlag(Feature feature) const {
if (feature == kFeaturePascalTypes || feature == kFeatureDOSTypes ||
feature == kFeatureHasSimpleAccess)
return false;
else
return true;
}
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:
NuError CopyData(FILE* outfp, ConvertEOL conv, ConvertHighASCII convHA,
CString* pMsg) const;
//NuError BNYUnSqueeze(ExpandBuffer* outExp) const;
AcuArchive* fpArchive; // holds FILE* for archive
bool fIsSqueezed;
long fOffset;
};
/*
* ACU archive definition.
*/
class AcuArchive : public GenericArchive {
public:
AcuArchive(void) : fIsReadOnly(false), fFp(nil)
{}
virtual ~AcuArchive(void) { (void) Close(); }
// One-time initialization; returns an error string.
static CString AppInit(void);
virtual OpenResult Open(const char* filename, bool readOnly,
CString* pErrMsg);
virtual CString New(const char* filename, const void* options);
virtual CString Flush(void) { return ""; }
virtual CString Reload(void);
virtual bool IsReadOnly(void) const { return fIsReadOnly; };
virtual bool IsModified(void) const { return false; }
virtual void GetDescription(CString* pStr) const { *pStr = "AppleLink ACU"; }
virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts)
{ ASSERT(false); return false; }
virtual bool AddDisk(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts)
{ ASSERT(false); return false; }
virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
const char* newName)
{ ASSERT(false); return false; }
virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet);
virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{ ASSERT(false); return false; }
virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{ ASSERT(false); return false; }
virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
const char* newName)
{ ASSERT(false); return false; }
virtual CString TestVolumeName(const DiskFS* pDiskFS,
const char* newName) const
{ ASSERT(false); return "!"; }
virtual CString TestPathName(const GenericEntry* pGenericEntry,
const CString& basePath, const CString& newName, char newFssep) const
{ ASSERT(false); return "!"; }
virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
const RecompressOptionsDialog* pRecompOpts)
{ ASSERT(false); return false; }
virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
ActionProgressDialog* pActionProgress, const XferFileOptions* pXferOpts)
{ ASSERT(false); return kXferFailed; }
virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
CString* pStr)
{ ASSERT(false); return false; }
virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
const CString& str)
{ ASSERT(false); return false; }
virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry)
{ ASSERT(false); return false; }
virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
const FileProps* pProps)
{ ASSERT(false); return false; }
virtual void PreferencesChanged(void) {}
virtual long GetCapability(Capability cap);
friend class AcuEntry;
private:
virtual CString Close(void) {
if (fFp != nil) {
fclose(fFp);
fFp = nil;
}
return "";
}
virtual void XferPrepare(const XferFileOptions* pXferOpts)
{ ASSERT(false); }
virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
long dataLen, unsigned char** pRsrcBuf, long rsrcLen)
{ ASSERT(false); return "!"; }
virtual void XferAbort(CWnd* pMsgWnd)
{ ASSERT(false); }
virtual void XferFinish(CWnd* pMsgWnd)
{ ASSERT(false); }
virtual ArchiveKind GetArchiveKind(void) { return kArchiveACU; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails)
{ ASSERT(false); return kNuErrGeneric; }
enum {
kAcuMaxFileName = 256, // nice big number
kAcuMasterHeaderLen = 20,
kAcuEntryHeaderLen = 54,
};
/*
* The header at the front of an ACU archive.
*/
typedef struct AcuMasterHeader {
unsigned short fileCount;
unsigned short unknown1; // 0x01 00 -- might be "version 1?"
unsigned char fZink[6]; // "fZink", low ASCII
unsigned char unknown2[11]; // 0x01 36 00 00 00 00 00 00 00 00 dd
} AcuMasterHeader;
/*
* 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;
typedef struct AcuFileEntry {
unsigned char compressionType;
unsigned short dataChecksum; // ??
unsigned short blockCount; // total blocks req'd to hold file
unsigned long dataStorageLen; // length of data within archive
unsigned short access;
unsigned short fileType;
unsigned long auxType;
unsigned char storageType;
unsigned long dataEof;
unsigned short prodosModDate;
unsigned short prodosModTime;
NuDateTime modWhen; // computed from previous two fields
unsigned short prodosCreateDate;
unsigned short prodosCreateTime;
NuDateTime createWhen; // computed from previous two fields
unsigned short fileNameLen;
unsigned short headerChecksum; // ??
char fileName[kAcuMaxFileName+1];
// possibilities for mystery fields:
// - OS type (note ProDOS is $00)
// - forked file support
} AcuFileEntry;
/* known compression types */
enum CompressionType {
kAcuCompNone = 0,
kAcuCompSqueeze = 3,
};
int LoadContents(void);
int ReadMasterHeader(int* pNumEntries);
NuError ReadFileHeader(AcuFileEntry* pEntry);
void DumpFileHeader(const AcuFileEntry* pEntry);
int CreateEntry(const AcuFileEntry* pEntry);
bool IsDir(const AcuFileEntry* pEntry);
NuError AcuRead(void* buf, size_t nbyte);
NuError AcuSeek(long offset);
void AcuConvertDateTime(unsigned short prodosDate,
unsigned short prodosTime, NuDateTime* pWhen);
FILE* fFp;
bool fIsReadOnly;
};
#endif /*__ACU_ARCHIVE__*/

208
app/AboutDialog.cpp Normal file
View File

@ -0,0 +1,208 @@
/*
* 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 "HelpTopics.h"
#include "MyApp.h"
#include "resource.h"
#include "../prebuilt/NufxLib.h"
#include "../diskimg/DiskImg.h"
#define ZLIB_DLL
#include "../prebuilt/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 char* kVersionExtra =
#ifdef _DEBUG
" _DEBUG"
#else
""
#endif
#ifdef _DEBUG_LOG
" _LOG"
#else
""
#endif
;
/*
* Update the static strings with DLL version numbers.
*/
BOOL
AboutDialog::OnInitDialog(void)
{
NuError nerr;
long major, minor, bug;
CString newVersion, tmpStr;
CStatic* pStatic;
//CString versionFmt;
/* CiderPress version string */
pStatic = (CStatic*) GetDlgItem(IDC_CIDERPRESS_VERS_TEXT);
ASSERT(pStatic != nil);
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 != nil);
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 != nil);
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 != nil);
pStatic->GetWindowText(tmpStr);
newVersion.Format(tmpStr, zlibVersion());
pStatic->SetWindowText(newVersion);
/* and, finally, the ASPI version */
pStatic = (CStatic*) GetDlgItem(IDC_ASPI_VERS_TEXT);
ASSERT(pStatic != nil);
if (DiskImgLib::Global::GetHasASPI()) {
CString versionStr;
DWORD version = DiskImgLib::Global::GetASPIVersion();
versionStr.Format("%d.%d.%d.%d",
version & 0x0ff,
(version >> 8) & 0xff,
(version >> 16) & 0xff,
(version >> 24) & 0xff);
pStatic->GetWindowText(tmpStr);
newVersion.Format(tmpStr, versionStr);
} else {
newVersion.LoadString(IDS_ASPI_NOT_LOADED);
}
pStatic->SetWindowText(newVersion);
//ShowRegistrationInfo();
{
CWnd* pWnd = GetDlgItem(IDC_ABOUT_ENTER_REG);
if (pWnd != nil) {
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 != nil);
pCompanyWnd = GetDlgItem(IDC_REG_COMPANY_NAME);
ASSERT(pCompanyWnd != nil);
//pExpireWnd = GetDlgItem(IDC_REG_EXPIRES);
//ASSERT(pExpireWnd != nil);
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(nil);
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 != nil) {
pWnd->EnableWindow(FALSE);
}
}
}
}
#endif
/*
* User hit the "Credits" button.
*/
void
AboutDialog::OnAboutCredits(void)
{
WinHelp(HELP_TOPIC_CREDITS, HELP_CONTEXT /*HELP_CONTEXTPOPUP*/);
}
#if 0
/*
* User hit "enter registration" button. Bring up the appropriate dialog.
*/
void
AboutDialog::OnEnterReg(void)
{
if (EnterRegDialog::GetRegInfo(this) == 0) {
ShowRegistrationInfo();
}
}
#endif

37
app/AboutDialog.h Normal file
View File

@ -0,0 +1,37 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Class definition for About dialog.
*/
#ifndef __ABOUT_DIALOG__
#define __ABOUT_DIALOG__
//#include <afxwin.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:
// overrides
virtual BOOL OnInitDialog(void);
afx_msg void OnAboutCredits(void);
afx_msg void OnEnterReg(void);
//void ShowRegistrationInfo(void);
DECLARE_MESSAGE_MAP()
};
#endif /*__ABOUT_DIALOG__*/

View File

@ -0,0 +1,157 @@
/*
* 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()
/*
* Initialize the static text controls to say something reasonable.
*/
BOOL
ActionProgressDialog::OnInitDialog(void)
{
CDialog::OnInitDialog();
WMSG1("Action is %d\n", fAction);
CenterWindow(AfxGetMainWnd());
CWnd* pWnd;
// clear the filename fields
pWnd = GetDlgItem(IDC_PROG_ARC_NAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(_T("-"));
pWnd = GetDlgItem(IDC_PROG_FILE_NAME);
ASSERT(pWnd != nil);
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 != nil);
tmpStr.LoadString(IDS_NOW_EXPANDING);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_PROG_TOFROM);
ASSERT(pWnd != nil);
tmpStr.LoadString(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 != nil);
tmpStr.LoadString(IDS_NOW_ADDING);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_PROG_TOFROM);
ASSERT(pWnd != nil);
tmpStr.LoadString(IDS_ADDING_AS);
pWnd->SetWindowText(tmpStr);
} else if (fAction == kActionDelete) {
CString tmpStr;
pWnd = GetDlgItem(IDC_PROG_VERB);
ASSERT(pWnd != nil);
tmpStr.LoadString(IDS_NOW_DELETING);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_PROG_TOFROM);
pWnd->DestroyWindow();
pWnd = GetDlgItem(IDC_PROG_FILE_NAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(_T(""));
} else if (fAction == kActionTest) {
CString tmpStr;
pWnd = GetDlgItem(IDC_PROG_VERB);
ASSERT(pWnd != nil);
tmpStr.LoadString(IDS_NOW_TESTING);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_PROG_TOFROM);
pWnd->DestroyWindow();
pWnd = GetDlgItem(IDC_PROG_FILE_NAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(_T(""));
} else {
ASSERT(false);
}
return FALSE;
}
/*
* Set the name of the file as it appears in the archive.
*/
void
ActionProgressDialog::SetArcName(const char* str)
{
CString oldStr;
CWnd* pWnd = GetDlgItem(IDC_PROG_ARC_NAME);
ASSERT(pWnd != nil);
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 != nil);
pWnd->GetWindowText(str);
return str;
}
/*
* Set the name of the file as it appears under Windows.
*/
void
ActionProgressDialog::SetFileName(const char* str)
{
CString oldStr;
CWnd* pWnd = GetDlgItem(IDC_PROG_FILE_NAME);
ASSERT(pWnd != nil);
pWnd->GetWindowText(oldStr);
if (oldStr != str)
pWnd->SetWindowText(str);
}
/*
* Update the progress meter.
*
* We take a percentage, but the underlying control uses 1000ths.
*/
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

@ -0,0 +1,65 @@
/*
* 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 __ACTIONPROGRESSDIALOG__
#define __ACTIONPROGRESSDIALOG__
#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 = nil;
//fpOptionsDlg = nil;
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();
}
void SetArcName(const char* str);
void SetFileName(const char* str);
const CString GetFileName(void);
int SetProgress(int perc);
private:
virtual BOOL OnInitDialog(void);
Action fAction;
bool fCancel;
DECLARE_MESSAGE_MAP()
};
#endif /*__ACTIONPROGRESSDIALOG__*/

2561
app/Actions.cpp Normal file

File diff suppressed because it is too large Load Diff

60
app/AddClashDialog.cpp Normal file
View File

@ -0,0 +1,60 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for AddClashDialog class.
*/
#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)
//ON_WM_HELPINFO()
END_MESSAGE_MAP()
/*
* Replace some static text fields.
*/
BOOL
AddClashDialog::OnInitDialog(void)
{
CWnd* pWnd;
pWnd = GetDlgItem(IDC_CLASH_WINNAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(fWindowsName);
pWnd = GetDlgItem(IDC_CLASH_STORAGENAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(fStorageName);
return CDialog::OnInitDialog();
}
/*
* One of the buttons was hit.
*/
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();
}
}

39
app/AddClashDialog.h Normal file
View File

@ -0,0 +1,39 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Resolve a filename clash when adding files.
*/
#ifndef __ADDCLASHDIALOG__
#define __ADDCLASHDIALOG__
/*
*
*/
class AddClashDialog : public CDialog {
public:
AddClashDialog(CWnd* pParentWnd = nil) :
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);
DECLARE_MESSAGE_MAP()
};
#endif /*__ADDCLASHDIALOG__*/

202
app/AddFilesDialog.cpp Normal file
View File

@ -0,0 +1,202 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for the "add files" dialog.
*/
#include "stdafx.h"
#include "AddFilesDialog.h"
#include "FileNameConv.h"
#include "HelpTopics.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;
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 != nil);
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 != nil);
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;
}
}
/*
* Make sure the storage prefix they entered is valid.
*/
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("The storage prefix may not start or end with '%c'.",
kFssep);
MessageBox(errMsg, m_ofn.lpstrTitle, MB_OK | MB_ICONWARNING);
return false;
}
return true;
}
/*
* Override base class version.
*/
UINT
AddFilesDialog::MyOnCommand(WPARAM wParam, LPARAM lParam)
{
switch (wParam) {
case IDHELP:
OnIDHelp();
return 1;
default:
return SelectFilesDialog::MyOnCommand(wParam, lParam);
}
}
/*
* Override base class version so we can move our stuff around.
*
* It's important that the base class be called last, because it calls
* Invalidate to redraw the dialog.
*/
void
AddFilesDialog::ShiftControls(int deltaX, int deltaY)
{
/*
* These only need to be here so that the initial move puts them
* where they belong. Once the dialog has been created, the
* CFileDialog will move things where they need to go.
*/
MoveControl(this, IDC_ADDFILES_STATIC1, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_NOPRESERVE, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_PRESERVE, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_PRESERVEPLUS, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_STATIC2, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_STRIP_FOLDER, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_INCLUDE_SUBFOLDERS, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_OVERWRITE, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_STATIC3, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_PREFIX, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_STATIC4, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_CONVEOLNONE, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_CONVEOLTYPE, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_CONVEOLTEXT, 0, deltaY, false);
MoveControl(this, IDC_ADDFILES_CONVEOLALL, 0, deltaY, false);
/*
* These actively move.
*/
MoveControl(this, IDHELP, deltaX, deltaY, false);
StretchControl(this, IDC_ADDFILES_PREFIX, deltaX, 0, false);
SelectFilesDialog::ShiftControls(deltaX, deltaY);
}
/*
* User pressed the "Help" button.
*/
void
AddFilesDialog::OnIDHelp(void)
{
CWnd* pWndMain = ::AfxGetMainWnd();
CWinApp* pAppMain = ::AfxGetApp();
::WinHelp(pWndMain->m_hWnd, pAppMain->m_pszHelpFilePath,
HELP_CONTEXT, HELP_TOPIC_ADD_FILES_DLG);
}

80
app/AddFilesDialog.h Normal file
View File

@ -0,0 +1,80 @@
/*
* 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 __ADDFILESDIALOG__
#define __ADDFILESDIALOG__
#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("IDD_ADD_FILES", pParentWnd)
{
SetWindowTitle(_T("Add Files..."));
fStoragePrefix = "";
fStoragePrefixEnable = true;
fIncludeSubfolders = FALSE;
fStripFolderNames = FALSE;
fStripFolderNamesEnable = true;
fOverwriteExisting = FALSE;
fTypePreservation = 0;
fConvEOL = 0;
fConvEOLEnable = true;
fAcceptButtonID = IDC_SELECT_ACCEPT;
fpTargetDiskFS = nil;
//fpTargetSubdir = nil;
fpDiskImg = nil;
}
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);
virtual void ShiftControls(int deltaX, int deltaY);
virtual UINT MyOnCommand(WPARAM wParam, LPARAM lParam);
void OnIDHelp(void);
bool ValidateStoragePrefix(void);
//DECLARE_MESSAGE_MAP()
};
#endif /*__ADDFILESDIALOG__*/

433
app/ArchiveInfoDialog.cpp Normal file
View File

@ -0,0 +1,433 @@
/*
* 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 "HelpTopics.h"
#include "ArchiveInfoDialog.h"
#include "../prebuilt/NufxLib.h"
/*
* ===========================================================================
* ArchiveInfoDialog
* ===========================================================================
*/
BEGIN_MESSAGE_MAP(ArchiveInfoDialog, CDialog)
ON_COMMAND(IDHELP, OnHelp)
END_MESSAGE_MAP()
/*
* Show general help for the archive info dialogs.
*/
void
ArchiveInfoDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_ARCHIVE_INFO, HELP_CONTEXT);
}
/*
* ===========================================================================
* NufxArchiveInfoDialog
* ===========================================================================
*/
/*
* Set up fields with NuFX archive info.
*/
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 != nil);
pNuArchive = fpArchive->GetNuArchivePointer();
ASSERT(pNuArchive != nil);
(void) NuGetMasterHeader(pNuArchive, &pMasterHeader);
ASSERT(pMasterHeader != nil);
pWnd = GetDlgItem(IDC_AI_FILENAME);
pWnd->SetWindowText(fpArchive->GetPathName());
pWnd = GetDlgItem(IDC_AINUFX_RECORDS);
nerr = NuGetAttr(pNuArchive, kNuAttrNumRecords, &attr);
if (nerr == kNuErrNone)
tmpStr.Format("%ld", attr);
else
tmpStr = notAvailable;
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AINUFX_FORMAT);
nerr = NuGetAttr(pNuArchive, kNuAttrArchiveType, &attr);
switch (attr) {
case kNuArchiveNuFX: tmpStr = "NuFX"; break;
case kNuArchiveNuFXInBNY: tmpStr = "NuFX in Binary II"; break;
case kNuArchiveNuFXSelfEx: tmpStr = "Self-extracting NuFX"; break;
case kNuArchiveNuFXSelfExInBNY: tmpStr = "Self-extracting NuFX in Binary II";
break;
case kNuArchiveBNY: tmpStr = "Binary II"; break;
default:
tmpStr = "(unknown)";
break;
};
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AINUFX_MASTERVERSION);
tmpStr.Format("%ld", pMasterHeader->mhMasterVersion);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AINUFX_CREATEWHEN);
when = NufxArchive::DateTimeToSeconds(&pMasterHeader->mhArchiveCreateWhen);
tmpStr.Format("%.24s", ctime(&when));
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AINUFX_MODIFYWHEN);
when = NufxArchive::DateTimeToSeconds(&pMasterHeader->mhArchiveModWhen);
tmpStr.Format("%.24s", ctime(&when));
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AINUFX_JUNKSKIPPED);
nerr = NuGetAttr(pNuArchive, kNuAttrJunkOffset, &attr);
if (nerr == kNuErrNone)
tmpStr.Format("%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()
/*
* Set up fields with disk archive info.
*/
BOOL
DiskArchiveInfoDialog::OnInitDialog(void)
{
CWnd* pWnd;
CString tmpStr;
const DiskImg* pDiskImg;
const DiskFS* pDiskFS;
ASSERT(fpArchive != nil);
pDiskImg = fpArchive->GetDiskImg();
ASSERT(pDiskImg != nil);
pDiskFS = fpArchive->GetDiskFS();
ASSERT(pDiskFS != nil);
/*
* Volume characteristics.
*/
pWnd = GetDlgItem(IDC_AI_FILENAME);
pWnd->SetWindowText(fpArchive->GetPathName());
pWnd = GetDlgItem(IDC_AIDISK_OUTERFORMAT);
pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetOuterFormat()));
pWnd = GetDlgItem(IDC_AIDISK_FILEFORMAT);
pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetFileFormat()));
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 != nil)
tmpStr.Format("%s, layout is \"%s\"",
DiskImg::ToString(physicalFormat), pNibbleDescr->description);
else
tmpStr = DiskImg::ToString(physicalFormat); // unexpected
pWnd->SetWindowText(tmpStr);
} else {
pWnd->SetWindowText(DiskImg::ToString(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, "", &idx);
ASSERT(idx > 0); // must have at least the top-level DiskFS
pCombo->SetCurSel(0);
if (idx == 1)
pCombo->EnableWindow(FALSE);
return ArchiveInfoDialog::OnInitDialog();
}
/*
* Recursively add sub-volumes to the list.
*/
void
DiskArchiveInfoDialog::AddSubVolumes(const DiskFS* pDiskFS, const char* prefix,
int* pIdx)
{
CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL);
CString tmpStr;
/*
* Add the current DiskFS.
*/
tmpStr = prefix;
tmpStr += pDiskFS->GetVolumeID();
pCombo->AddString(tmpStr);
pCombo->SetItemData(*pIdx, (unsigned long) pDiskFS);
(*pIdx)++;
/*
* Add everything beneath the current level.
*/
DiskFS::SubVolume* pSubVol;
pSubVol = pDiskFS->GetNextSubVolume(nil);
tmpStr = prefix;
tmpStr += " ";
while (pSubVol != nil) {
AddSubVolumes(pSubVol->GetDiskFS(), tmpStr, pIdx);
pSubVol = pDiskFS->GetNextSubVolume(pSubVol);
}
}
/*
* The user has changed their selection in the sub-volume pulldown menu.
*/
void
DiskArchiveInfoDialog::OnSubVolSelChange(void)
{
CComboBox* pCombo = (CComboBox*) GetDlgItem(IDC_AIDISK_SUBVOLSEL);
ASSERT(pCombo != nil);
//WMSG1("+++ SELECTION IS NOW %d\n", pCombo->GetCurSel());
const DiskFS* pDiskFS;
pDiskFS = (DiskFS*) pCombo->GetItemData(pCombo->GetCurSel());
ASSERT(pDiskFS != nil);
FillInVolumeInfo(pDiskFS);
}
/*
* Fill in the volume-specific info fields.
*/
void
DiskArchiveInfoDialog::FillInVolumeInfo(const DiskFS* pDiskFS)
{
const DiskImg* pDiskImg = pDiskFS->GetDiskImg();
CString unknown = "(unknown)";
CString tmpStr;
DIError dierr;
CWnd* pWnd;
pWnd = GetDlgItem(IDC_AIDISK_SECTORORDER);
pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetSectorOrder()));
pWnd = GetDlgItem(IDC_AIDISK_FSFORMAT);
pWnd->SetWindowText(DiskImg::ToString(pDiskImg->GetFSFormat()));
pWnd = GetDlgItem(IDC_AIDISK_FILECOUNT);
tmpStr.Format("%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("%ld blocks (%s)",
totalUnits, reducedSize);
if (totalUnits != pDiskImg->GetNumBlocks()) {
CString tmpStr2;
tmpStr2.Format(", image has room for %ld blocks",
pDiskImg->GetNumBlocks());
tmpStr += tmpStr2;
}
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AIDISK_FREESPACE);
GetReducedSize(freeUnits, unitSize, &reducedSize);
tmpStr.Format("%ld blocks (%s)",
freeUnits, reducedSize);
pWnd->SetWindowText(tmpStr);
} else {
ASSERT(unitSize == DiskImgLib::kSectorSize);
pWnd = GetDlgItem(IDC_AIDISK_CAPACITY);
GetReducedSize(totalUnits, unitSize, &reducedSize);
tmpStr.Format("%ld sectors (%s)",
totalUnits, reducedSize);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AIDISK_FREESPACE);
GetReducedSize(freeUnits, unitSize, &reducedSize);
tmpStr.Format("%ld sectors (%s)",
freeUnits, 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("%ld blocks (%s)",
totalUnits, reducedSize);
} else if (pDiskImg->GetHasSectors()) {
tmpStr.Format("%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() ? "Yes" : "No";
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_AIDISK_DAMAGED);
tmpStr = pDiskFS->GetFSDamaged() ? "Yes" : "No";
pWnd->SetWindowText(tmpStr);
const char* cp;
char* outp;
pWnd = GetDlgItem(IDC_AIDISK_NOTES);
cp = pDiskImg->GetNotes();
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);
}
/*
* Reduce a size to something meaningful (KB, MB, GB).
*/
void
DiskArchiveInfoDialog::GetReducedSize(long numUnits, int unitSize,
CString* pOut) const
{
LONGLONG sizeInBytes = numUnits;
sizeInBytes *= unitSize;
long reducedSize;
if (sizeInBytes < 0) {
ASSERT(false);
pOut->Format("<bogus>");
return;
}
if (sizeInBytes >= 1024*1024*1024) {
reducedSize = (long) (sizeInBytes / (1024*1024));
pOut->Format("%.2fGB", reducedSize / 1024.0);
} else if (sizeInBytes >= 1024*1024) {
reducedSize = (long) (sizeInBytes / 1024);
pOut->Format("%.2fMB", reducedSize / 1024.0);
} else {
pOut->Format("%.2fKB", ((long) sizeInBytes) / 1024.0);
}
}
/*
* ===========================================================================
* BnyArchiveInfoDialog
* ===========================================================================
*/
/*
* Set up fields with Binary II info.
*
* Binary II files are pretty dull.
*/
BOOL
BnyArchiveInfoDialog::OnInitDialog(void)
{
CWnd* pWnd;
CString tmpStr;
ASSERT(fpArchive != nil);
pWnd = GetDlgItem(IDC_AI_FILENAME);
pWnd->SetWindowText(fpArchive->GetPathName());
tmpStr.Format("%ld", fpArchive->GetNumEntries());
pWnd = GetDlgItem(IDC_AIBNY_RECORDS);
pWnd->SetWindowText(tmpStr);
return ArchiveInfoDialog::OnInitDialog();
}
/*
* ===========================================================================
* AcuArchiveInfoDialog
* ===========================================================================
*/
/*
* Set up fields with ACU info.
*/
BOOL
AcuArchiveInfoDialog::OnInitDialog(void)
{
CWnd* pWnd;
CString tmpStr;
ASSERT(fpArchive != nil);
pWnd = GetDlgItem(IDC_AI_FILENAME);
pWnd->SetWindowText(fpArchive->GetPathName());
tmpStr.Format("%ld", fpArchive->GetNumEntries());
pWnd = GetDlgItem(IDC_AIBNY_RECORDS);
pWnd->SetWindowText(tmpStr);
return ArchiveInfoDialog::OnInitDialog();
}

118
app/ArchiveInfoDialog.h Normal file
View File

@ -0,0 +1,118 @@
/*
* 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 __ARCHIVEINFODIALOG__
#define __ARCHIVEINFODIALOG__
#include "resource.h"
#include "GenericArchive.h"
#include "NufxArchive.h"
#include "DiskArchive.h"
#include "BnyArchive.h"
#include "AcuArchive.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:
afx_msg void OnHelp(void);
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:
// overrides
virtual BOOL OnInitDialog(void);
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:
// overrides
virtual BOOL OnInitDialog(void);
afx_msg void OnSubVolSelChange(void);
void FillInVolumeInfo(const DiskFS* pDiskFS);
void AddSubVolumes(const DiskFS* pDiskFS, const char* prefix,
int* pIdx);
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:
// overrides
virtual BOOL OnInitDialog(void);
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:
// overrides
virtual BOOL OnInitDialog(void);
AcuArchive* fpArchive;
};
#endif /*__ARCHIVEINFODIALOG__*/

998
app/BNYArchive.cpp Normal file
View File

@ -0,0 +1,998 @@
/*
* 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
* ===========================================================================
*/
/*
* Extract data from an entry.
*
* If "*ppText" is non-nil, the data will be read into the pointed-to buffer
* so long as it's shorter than *pLength bytes. The value in "*pLength"
* will be set to the actual length used.
*
* If "*ppText" is nil, the uncompressed data will be placed into a buffer
* allocated with "new[]".
*
* Returns IDOK on success, IDCANCEL if the operation was cancelled by the
* user, and -1 value on failure. On failure, "*pErrMsg" holds an error
* message.
*
* "which" is an anonymous GenericArchive enum (e.g. "kDataThread").
*/
int
BnyEntry::ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const
{
NuError nerr;
ExpandBuffer expBuf;
char* dataBuf = nil;
long len;
bool needAlloc = true;
int result = -1;
ASSERT(fpArchive != nil);
ASSERT(fpArchive->fFp != nil);
if (*ppText != nil)
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("Unable to seek to offset %ld: %s",
fOffset, strerror(errno));
goto bail;
}
if (GetSqueezed()) {
nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(),
&expBuf, true, kBNYBlockSize);
if (nerr != kNuErrNone) {
pErrMsg->Format("File read failed: %s", NuStrError(nerr));
goto bail;
}
char* unsqBuf = nil;
long unsqLen = 0;
expBuf.SeizeBuffer(&unsqBuf, &unsqLen);
WMSG2("Unsqueezed %ld bytes to %d\n", len, unsqLen);
if (unsqLen == 0) {
// some bonehead squeezed a zero-length file
delete[] unsqBuf;
ASSERT(*ppText == nil);
WMSG0("Handling zero-length squeezed file!\n");
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("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 == nil) {
pErrMsg->Format("allocation of %ld bytes failed", len);
goto bail;
}
} else {
if (*pLength < (long) len) {
pErrMsg->Format("buf size %ld too short (%ld)",
*pLength, len);
goto bail;
}
dataBuf = *ppText;
}
if (fread(dataBuf, len, 1, fpArchive->fFp) != 1) {
pErrMsg->Format("File read failed: %s", 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 == nil);
}
}
return result;
}
/*
* Extract data from a thread to a file. Since we're not copying to memory,
* we can't assume that we're able to hold the entire file all at once.
*
* Returns IDOK on success, IDCANCEL if the operation was cancelled by the
* user, and -1 value on failure. On failure, "*pMsg" holds an
* error message.
*/
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 = "No such fork";
goto bail;
}
len = (long) GetUncompressedLen();
if (len == 0) {
WMSG0("Empty fork\n");
result = IDOK;
goto bail;
}
errno = 0;
if (fseek(fpArchive->fFp, fOffset, SEEK_SET) < 0) {
pErrMsg->Format("Unable to seek to offset %ld: %s",
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("File read failed: %s", NuStrError(nerr));
goto bail;
}
expBuf.SeizeBuffer(&buf, &uncLen);
WMSG2("Unsqueezed %ld bytes to %d\n", len, uncLen);
// some bonehead squeezed a zero-length file
if (uncLen == 0) {
ASSERT(buf == nil);
WMSG0("Handling zero-length squeezed file!\n");
result = IDOK;
goto bail;
}
int err = GenericEntry::WriteConvert(outfp, buf, uncLen, &conv,
&convHA, &lastCR);
if (err != 0) {
pErrMsg->Format("File write failed: %s", strerror(err));
delete[] buf;
goto bail;
}
delete[] buf;
} else {
nerr = CopyData(outfp, conv, convHA, pErrMsg);
if (nerr != kNuErrNone) {
if (pErrMsg->IsEmpty()) {
pErrMsg->Format("Failed while copying data: %s\n",
NuStrError(nerr));
}
goto bail;
}
}
result = IDOK;
bail:
SET_PROGRESS_END();
return result;
}
/*
* Copy data from the seeked archive to outfp, possibly converting EOL along
* the way.
*/
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("File read failed: %s.", NuStrError(nerr));
goto bail;
}
/* write chunk to destination file */
int err = GenericEntry::WriteConvert(outfp, buf, chunkLen, &conv,
&convHA, &lastCR);
if (err != 0) {
pMsg->Format("File write failed: %s.", strerror(err));
nerr = kNuErrGeneric;
goto bail;
}
dataRem -= chunkLen;
SET_PROGRESS_UPDATE(ComputePercent(srcLen - dataRem, srcLen));
}
bail:
return nerr;
}
/*
* 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 "nil" buffer pointer.
*/
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("Unable to seek to offset %ld: %s\n",
fOffset, strerror(errno));
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
goto bail;
}
if (GetSqueezed()) {
nerr = UnSqueeze(fpArchive->fFp, (unsigned long) GetUncompressedLen(),
nil, true, kBNYBlockSize);
if (nerr != kNuErrNone) {
errMsg.Format("Unsqueeze failed: %s.", 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("Unable to seek to offset %ld (file truncated?): %s\n",
fOffset, strerror(errno));
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
goto bail;
}
}
if (SET_PROGRESS_UPDATE(100) == IDCANCEL)
nerr = kNuErrAborted;
bail:
return nerr;
}
/*
* ===========================================================================
* BnyArchive
* ===========================================================================
*/
/*
* Perform one-time initialization. There really isn't any for us. Having
* this is kind of silly, but we include it for consistency.
*
* Returns 0 on success, nonzero on error.
*/
/*static*/ CString
BnyArchive::AppInit(void)
{
return "";
}
/*
* Open a BNY archive.
*
* Returns an error string on failure, or "" on success.
*/
GenericArchive::OpenResult
BnyArchive::Open(const char* filename, bool readOnly, CString* pErrMsg)
{
CString errMsg;
fIsReadOnly = true; // ignore "readOnly"
errno = 0;
fFp = fopen(filename, "rb");
if (fFp == nil) {
errMsg.Format("Unable to open %s: %s.", filename, strerror(errno));
goto bail;
}
{
CWaitCursor waitc;
if (LoadContents() != 0) {
errMsg.Format("Failed while loading contents of Binary II file.");
goto bail;
}
}
SetPathName(filename);
bail:
*pErrMsg = errMsg;
if (!errMsg.IsEmpty())
return kResultFailure;
else
return kResultSuccess;
}
/*
* Finish instantiating a BnyArchive object by creating a new archive.
*
* Returns an error string on failure, or "" on success.
*/
CString
BnyArchive::New(const char* /*filename*/, const void* /*options*/)
{
CString retmsg("Sorry, Binary II files can't be created.");
return retmsg;
}
/*
* Our capabilities.
*/
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;
}
}
/*
* Load the contents of the archive.
*
* Returns 0 on success, nonzero on failure.
*/
int
BnyArchive::LoadContents(void)
{
NuError nerr;
ASSERT(fFp != nil);
rewind(fFp);
nerr = BNYIterate();
WMSG1("BNYIterate returned %d\n", nerr);
return (nerr != kNuErrNone);
}
/*
* Reload the contents of the archive.
*/
CString
BnyArchive::Reload(void)
{
fReloadFlag = true; // tell everybody that cached data is invalid
DeleteEntries();
if (LoadContents() != 0) {
return "Reload failed.";
}
return "";
}
/*
* Given a BnyFileEntry structure, add an appropriate entry to the list.
*
* Note this can mangle pEntry (notably the filename).
*/
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 (strcasecmp(ext, ".qq") == 0)
*ext = '\0';
}
/*
* Create the new entry.
*/
pNewEntry = new BnyEntry(this);
pNewEntry->SetPathName(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("Squeeze");
else
pNewEntry->SetFormatStr("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.
*/
/*
* Test for the magic number on a file in SQueezed format.
*/
bool
BnyArchive::IsSqueezed(uchar one, uchar two)
{
return (one == 0x76 && two == 0xff);
}
/*
* Test if this entry is a directory.
*/
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);
}
/*
* Wrapper for fread(). Note the arguments resemble read(2) rather
* than fread(3S).
*/
NuError
BnyArchive::BNYRead(void* buf, size_t nbyte)
{
size_t result;
ASSERT(buf != nil);
ASSERT(nbyte > 0);
ASSERT(fFp != nil);
errno = 0;
result = fread(buf, 1, nbyte, fFp);
if (result != nbyte)
return errno ? (NuError)errno : kNuErrFileRead;
return kNuErrNone;
}
/*
* 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
BnyArchive::BNYSeek(long offset)
{
ASSERT(fFp != nil);
ASSERT(offset > 0);
/*DBUG(("--- seeking forward %ld bytes\n", offset));*/
if (fseek(fFp, offset, SEEK_CUR) < 0)
return kNuErrFileSeek;
return kNuErrNone;
}
/*
* Convert from ProDOS compact date format to the expanded DateTime format.
*/
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;
}
/*
* Decode a Binary II header.
*
* See the File Type Note for $e0/8000 to decipher the buffer offsets
* and meanings.
*/
NuError
BnyArchive::BNYDecodeHeader(BnyFileEntry* pEntry)
{
NuError err = kNuErrNone;
uchar* raw;
int len;
ASSERT(pEntry != nil);
raw = pEntry->blockBuf;
if (raw[0] != 0x0a || raw[1] != 0x47 || raw[2] != 0x4c || raw[18] != 0x02) {
err = kNuErrBadData;
WMSG0("this doesn't look like a Binary II header\n");
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;
WMSG1("invalid filename length %d\n", 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;
WMSG1("invalid filename length %d\n", 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 = nil;
pathProposal.newFilenameSeparator = '\0';
pathProposal.newDataSink = nil;
/* 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 != nil) {
if (fwrite(pEntry->blockBuf, toWrite, 1, outfp) != 1) {
err = errno ? (NuError) errno : kNuErrFileWrite;
WMSG0("BNY write failed\n");
goto bail;
}
}
bytesLeft -= toWrite;
if (bytesLeft) {
err = BNYRead(pEntry->blockBuf, kBNYBlockSize);
if (err != kNuErrNone) {
WMSG0("BNY read failed\n");
goto bail;
}
}
}
bail:
return err;
}
#endif
/*
* Iterate through a Binary II archive, loading the data.
*/
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) {
WMSG0("failed while reading header\n");
goto bail;
}
err = BNYDecodeHeader(&entry);
if (err != kNuErrNone) {
if (first) {
WMSG0("not a Binary II archive?\n");
}
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) {
WMSG0("failed while reading\n");
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) {
WMSG0("failed while seeking forward\n");
goto bail;
}
}
}
if (!first) {
if (entry.filesToFollow != toFollow -1) {
WMSG2("WARNING: filesToFollow %d, expected %d\n",
entry.filesToFollow, toFollow -1);
}
}
toFollow = entry.filesToFollow;
first = false;
}
bail:
if (err != kNuErrNone) {
WMSG1("--- Iterator returning failure %d\n", err);
}
return err;
}
/*
* ===========================================================================
* BnyArchive -- test files
* ===========================================================================
*/
/*
* Test the records represented in the selection set.
*/
bool
BnyArchive::TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{
NuError nerr;
BnyEntry* pEntry;
CString errMsg;
bool retVal = false;
ASSERT(fFp != nil);
WMSG1("Testing %d entries\n", pSelSet->GetNumEntries());
SelectionEntry* pSelEntry = pSelSet->IterNext();
while (pSelEntry != nil) {
pEntry = (BnyEntry*) pSelEntry->GetEntry();
WMSG2(" Testing '%s' (offset=%ld)\n", pEntry->GetDisplayName(),
pEntry->GetOffset());
SET_PROGRESS_UPDATE2(0, pEntry->GetDisplayName(), nil);
nerr = pEntry->TestEntry(pMsgWnd);
if (nerr != kNuErrNone) {
if (nerr == kNuErrAborted) {
CString title;
title.LoadString(IDS_MB_APP_NAME);
errMsg = "Cancelled.";
pMsgWnd->MessageBox(errMsg, title, MB_OK);
} else {
errMsg.Format("Failed while testing '%s': %s.",
pEntry->GetPathName(), NuStrError(nerr));
ShowFailureMsg(pMsgWnd, errMsg, IDS_FAILED);
}
goto bail;
}
pSelEntry = pSelSet->IterNext();
}
/* show success message */
errMsg.Format("Tested %d file%s, no errors found.",
pSelSet->GetNumEntries(),
pSelSet->GetNumEntries() == 1 ? "" : "s");
pMsgWnd->MessageBox(errMsg);
retVal = true;
bail:
SET_PROGRESS_END();
return retVal;
}

218
app/BNYArchive.h Normal file
View File

@ -0,0 +1,218 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Binary II support.
*/
#ifndef __BNY_ARCHIVE__
#define __BNY_ARCHIVE__
#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) {}
// retrieve thread data
virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const;
virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
ConvertHighASCII convHA, CString* pErrMsg) const;
virtual long GetSelectionSerial(void) const { return -1; } // doesn't matter
virtual bool GetFeatureFlag(Feature feature) const {
if (feature == kFeaturePascalTypes || feature == kFeatureDOSTypes ||
feature == kFeatureHasSimpleAccess)
return false;
else
return true;
}
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:
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(nil)
{}
virtual ~BnyArchive(void) { (void) Close(); }
// One-time initialization; returns an error string.
static CString AppInit(void);
virtual OpenResult Open(const char* filename, bool readOnly,
CString* pErrMsg);
virtual CString New(const char* filename, const void* options);
virtual CString Flush(void) { return ""; }
virtual CString Reload(void);
virtual bool IsReadOnly(void) const { return fIsReadOnly; };
virtual bool IsModified(void) const { return false; }
virtual void GetDescription(CString* pStr) const { *pStr = "Binary II"; }
virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts)
{ ASSERT(false); return false; }
virtual bool AddDisk(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts)
{ ASSERT(false); return false; }
virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
const char* newName)
{ ASSERT(false); return false; }
virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet);
virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{ ASSERT(false); return false; }
virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{ ASSERT(false); return false; }
virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
const char* newName)
{ ASSERT(false); return false; }
virtual CString TestVolumeName(const DiskFS* pDiskFS,
const char* newName) const
{ ASSERT(false); return "!"; }
virtual CString TestPathName(const GenericEntry* pGenericEntry,
const CString& basePath, const CString& newName, char newFssep) const
{ ASSERT(false); return "!"; }
virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
const RecompressOptionsDialog* pRecompOpts)
{ ASSERT(false); return false; }
virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
ActionProgressDialog* pActionProgress, const XferFileOptions* pXferOpts)
{ ASSERT(false); return kXferFailed; }
virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
CString* pStr)
{ ASSERT(false); return false; }
virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
const CString& str)
{ ASSERT(false); return false; }
virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry)
{ ASSERT(false); return false; }
virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
const FileProps* pProps)
{ ASSERT(false); return false; }
virtual void PreferencesChanged(void) {}
virtual long GetCapability(Capability cap);
friend class BnyEntry;
private:
virtual CString Close(void) {
if (fFp != nil) {
fclose(fFp);
fFp = nil;
}
return "";
}
virtual void XferPrepare(const XferFileOptions* pXferOpts)
{ ASSERT(false); }
virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
long dataLen, unsigned char** pRsrcBuf, long rsrcLen)
{ ASSERT(false); return "!"; }
virtual void XferAbort(CWnd* pMsgWnd)
{ ASSERT(false); }
virtual void XferFinish(CWnd* pMsgWnd)
{ ASSERT(false); }
virtual ArchiveKind GetArchiveKind(void) { return kArchiveBNY; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails)
{ ASSERT(false); return kNuErrGeneric; }
enum {
kBNYBlockSize = BnyEntry::kBNYBlockSize,
kBNYMaxFileName = 64,
kBNYMaxNativeName = 48,
kBNYFlagCompressed = (1<<7),
kBNYFlagEncrypted = (1<<6),
kBNYFlagSparse = (1),
};
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned long ulong;
/*
* 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 {
ushort access;
ushort fileType;
ulong auxType;
uchar storageType;
ulong fileSize; /* in 512-byte blocks */
ushort prodosModDate;
ushort prodosModTime;
NuDateTime modWhen; /* computed from previous two fields */
ushort prodosCreateDate;
ushort prodosCreateTime;
NuDateTime createWhen; /* computed from previous two fields */
ulong eof;
ulong realEOF; /* eof is bogus for directories */
char fileName[kBNYMaxFileName+1];
char nativeName[kBNYMaxNativeName+1];
ulong diskSpace; /* in 512-byte blocks */
uchar osType; /* not exactly same as NuFileSysID */
ushort nativeFileType;
uchar phantomFlag;
uchar dataFlags; /* advisory flags */
uchar version;
uchar filesToFollow; /* #of files after this one */
uchar blockBuf[kBNYBlockSize];
} BnyFileEntry;
int LoadContents(void);
NuError LoadContentsCallback(BnyFileEntry* pEntry);
bool IsSqueezed(uchar one, uchar two);
bool IsDir(BnyFileEntry* pEntry);
NuError BNYRead(void* buf, size_t nbyte);
NuError BNYSeek(long offset);
void BNYConvertDateTime(unsigned short prodosDate,
unsigned short prodosTime, NuDateTime* pWhen);
NuError BNYDecodeHeader(BnyFileEntry* pEntry);
NuError BNYIterate(void);
FILE* fFp;
bool fIsReadOnly;
};
#endif /*__BNY_ARCHIVE__*/

714
app/BasicImport.cpp Normal file
View File

@ -0,0 +1,714 @@
/*
* 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"
#include "HelpTopics.h"
/*
* ==========================================================================
* BASTokenLookup
* ==========================================================================
*/
/*
* Constructor. Pass in the info for the token blob.
*/
void
BASTokenLookup::Init(const char* tokenList, int numTokens,
int tokenLen)
{
int i;
ASSERT(tokenList != nil);
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;
}
}
/*
* Return the index of the longest token that matches "str".
*
* Returns -1 if no match is found.
*/
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 &&
strncasecmp(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()
/*
* Set up the dialog.
*/
BOOL
ImportBASDialog::OnInitDialog(void)
{
CDialog::OnInitDialog(); // base class init
PathName path(fFileName);
CString fileNameOnly(path.GetFileName());
CString ext(fileNameOnly.Right(4));
if (ext.CompareNoCase(".txt") == 0) {
WMSG1("removing extension from '%s'\n", (const char*) 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";
/*
* Import an Applesoft BASIC program from the specified file.
*/
bool
ImportBASDialog::ImportBAS(const char* fileName)
{
FILE* fp = NULL;
ExpandBuffer msgs(1024);
long fileLen, outLen, count;
char* buf = nil;
char* outBuf = nil;
bool result = false;
msgs.Printf("Importing from '%s'...", fileName);
fp = fopen(fileName, "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 = nil;
long msgLen;
msgs.SeizeBuffer(&msgBuf, &msgLen);
pEdit->SetWindowText(msgBuf);
delete[] msgBuf;
return result;
}
/*
* Do the actual conversion.
*/
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: %s", kFailed, lineNum, (const char*) 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;
}
/*
* Process a line of Applesoft BASIC text.
*
* Writes output to "pOutput".
*
* On failure, writes an error message to "msg" and returns false.
*/
/*
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') {
//WMSG0("removed CRLF\n");
len -= 2;
} else if (buf[len-1] == '\r') {
//WMSG0("removed CR\n");
len--;
} else if (buf[len-1] == '\n') {
//WMSG0("removed LF\n");
len--;
} else {
//WMSG0("no EOL marker found\n");
}
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 = "found nothing except line number";
return false;
}
}
gotOne = true;
if (!isdigit(ch))
break;
if (tokenLen == 5) { // theoretical max is "65535"
msg = "line number has too many digits";
return false;
}
tokenBuf[tokenLen++] = ch;
}
if (!tokenLen) {
msg = "line did not start with a line number";
return false;
}
tokenBuf[tokenLen] = '\0';
lineNum = atoi(tokenBuf);
WMSG1("FOUND line %d\n", 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);
//WMSG2("TOKEN '%s' (%d)\n",
// 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;
}
/*
* Fix up the line pointers. We left dummy nonzero values in them initially.
*/
bool
ImportBASDialog::FixBASLinePointers(char* buf, long len, unsigned short addr)
{
unsigned short val;
char* start;
while (len >= 4) {
start = buf;
val = (*buf) & 0xff | (*(buf+1)) << 8;
if (val == 0)
break;
if (val != 0xcccc) {
WMSG1("unexpected value 0x%04x found\n", val);
return false;
}
buf += 4;
len -= 4;
/*
* Find the next end-of-line marker.
*/
while (*buf != '\0' && len > 0) {
buf++;
len--;
}
if (!len) {
WMSG0("ran off the end?\n");
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;
}
/*
* 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*
ImportBASDialog::FindEOL(const char* buf, long max)
{
ASSERT(max >= 0);
if (max == 0)
return nil;
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;
}
/*
* 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
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 == nil) {
*pCh = ch;
return true;
}
}
return false;
}
/*
* Save the imported data.
*/
void ImportBASDialog::OnOK(void)
{
CEdit* pEdit = (CEdit*) GetDlgItem(IDC_IMPORT_BAS_SAVEAS);
CString fileName;
pEdit->GetWindowText(fileName);
if (fileName.IsEmpty()) {
CString appName;
appName.LoadString(IDS_MB_APP_NAME);
MessageBox("You must specify a filename.",
appName, MB_OK);
}
/*
* Write the file to the currently-open archive.
*/
GenericArchive::FileDetails details;
details.entryKind = GenericArchive::FileDetails::kFileKindDataFork;
details.origName = "Imported BASIC";
details.storageName = fileName;
details.access = 0xe3; // unlocked, backup bit set
details.fileType = kFileTypeBAS;
details.extraType = 0x0801;
details.storageType = DiskFS::kStorageSeedling;
time_t now = time(nil);
GenericArchive::UNIXTimeToDateTime(&now, &details.createWhen);
GenericArchive::UNIXTimeToDateTime(&now, &details.archiveWhen);
GenericArchive::UNIXTimeToDateTime(&now, &details.modWhen);
CString errMsg;
fDirty = true;
if (!MainWindow::SaveToArchive(&details, (const unsigned char*) fOutput,
fOutputLen, nil, -1, /*ref*/errMsg, this))
{
goto bail;
}
/* success! close the dialog */
CDialog::OnOK();
bail:
if (!errMsg.IsEmpty()) {
CString msg;
msg.Format("Unable to import file: %s.", (const char *) errMsg);
ShowFailureMsg(this, msg, IDS_FAILED);
return;
}
return;
}
/*
* User pressed the "Help" button.
*/
void
ImportBASDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_IMPORT_BASIC, HELP_CONTEXT);
}

104
app/BasicImport.h Normal file
View File

@ -0,0 +1,104 @@
/*
* 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 __BASICIMPORT__
#define __BASICIMPORT__
/*
* 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(nil), fTokenLen(nil)
{}
~BASTokenLookup(void) {
delete[] fTokenPtr;
delete[] fTokenLen;
}
// Initialize the array.
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(nil), fOutputLen(-1)
{}
virtual ~ImportBASDialog(void) {
delete[] fOutput;
}
CString fFileName; // file to open
// did we add something to the archive?
bool IsDirty(void) const { return fDirty; }
private:
virtual BOOL OnInitDialog(void);
//virtual void DoDataExchange(CDataExchange* pDX);
virtual void OnOK(void);
afx_msg void OnHelp(void);
bool ImportBAS(const char* fileName);
bool ConvertTextToBAS(const char* buf, long fileLen,
char** pOutBuf, long* pOutLen, ExpandBuffer* pMsgs);
bool ProcessBASLine(const char* buf, int len,
ExpandBuffer* pOutput, CString& msg);
bool FixBASLinePointers(char* buf, long len, unsigned short addr);
const char* FindEOL(const char* buf, long max);
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;
DECLARE_MESSAGE_MAP()
};
#endif /*__BASICIMPORT__*/

184
app/CassImpTargetDialog.cpp Normal file
View File

@ -0,0 +1,184 @@
/*
* 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()
/*
* Set up the dialog.
*/
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
}
/*
* Copy values in and out of the dialog.
*/
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;
appName.LoadString(IDS_MB_APP_NAME);
if (fFileTypeIndex == kTypeBIN) {
if (GetStartAddr() < 0) {
MessageBox("The address field must be a valid 4-digit "
" hexadecimal number.",
appName, MB_OK);
pDX->Fail();
return;
}
fStartAddr = (unsigned short) GetStartAddr();
}
if (fFileName.IsEmpty()) {
MessageBox("You must enter a filename.", appName, MB_OK);
pDX->Fail();
return;
}
} else {
CWnd* pWnd;
CString tmpStr;
pWnd = GetDlgItem(IDC_CASSIMPTARG_BINADDR);
tmpStr.Format("%04X", fStartAddr);
pWnd->SetWindowText(tmpStr);
}
}
/*
* They selected a different file type. Enable or disable the address
* entry window.
*/
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);
}
/*
* If the user changes the address, update the "end of range" field.
*/
void
CassImpTargetDialog::OnAddrChange(void)
{
CWnd* pWnd;
CString tmpStr;
long val;
val = GetStartAddr();
if (val < 0)
val = 0;
tmpStr.Format(".%04X", val + fFileLength-1);
pWnd = GetDlgItem(IDC_CASSIMPTARG_RANGE);
pWnd->SetWindowText(tmpStr);
}
/*
* 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
CassImpTargetDialog::GetStartAddr(void) const
{
CWnd* pWnd = GetDlgItem(IDC_CASSIMPTARG_BINADDR);
ASSERT(pWnd != nil);
CString aux;
pWnd->GetWindowText(aux);
const char* str = aux;
char* end;
long val;
if (str[0] == '\0') {
WMSG0(" HEY: blank addr, returning -1\n");
return -1;
}
val = strtoul(aux, &end, 16);
if (end != str + strlen(str)) {
WMSG1(" HEY: found some garbage in addr '%s', returning -1\n",
(LPCTSTR) aux);
return -1;
}
return val;
}
/*
* Get the selected file type. Call this after the modal dialog exits.
*/
long
CassImpTargetDialog::GetFileType(void) const
{
switch (fFileTypeIndex) {
case kTypeBIN: return kFileTypeBIN;
case kTypeINT: return kFileTypeINT;
case kTypeBAS: return kFileTypeBAS;
default:
assert(false);
return -1;
}
}
/*
* Convert a ProDOS file type into a radio button enum.
*/
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;
}
}

52
app/CassImpTargetDialog.h Normal file
View File

@ -0,0 +1,52 @@
/*
* 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 __CASSIMPTARGETDIALOG__
#define __CASSIMPTARGETDIALOG__
#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) {}
long GetFileType(void) const;
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);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg void OnTypeChange(void);
afx_msg void OnAddrChange(void);
MyEdit fAddrEdit; // replacement edit ctrl for addr field
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 /*__CASSIMPTARGETDIALOG__*/

1192
app/CassetteDialog.cpp Normal file

File diff suppressed because it is too large Load Diff

160
app/CassetteDialog.h Normal file
View File

@ -0,0 +1,160 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Apple II cassette I/O functions.
*/
#ifndef __CASSETTEDIALOG__
#define __CASSETTEDIALOG__
/*
* 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);
//virtual void DoDataExchange(CDataExchange* pDX);
//virtual void OnOK(void);
//enum { WMU_DIALOG_READY = WM_USER+2 };
afx_msg void OnListChange(NMHDR* pNotifyStruct, LRESULT* pResult);
afx_msg void OnListDblClick(NMHDR* pNotifyStruct, LRESULT* pResult);
afx_msg void OnAlgorithmChange(void);
afx_msg void OnHelp(void);
afx_msg void OnImport(void);
/*
* 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(nil), 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;
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;
void ConvertSamplesToReal(const WAVEFORMATEX* pFormat,
const unsigned char* buf, long chunkLen, float* sampleBuf);
bool ProcessSample(float sample, long sampleIndex,
ScanState* pScanState, int* pBitVal);
bool ProcessSampleZero(float sample, long sampleIndex,
ScanState* pScanState, int* pBitVal);
bool ProcessSamplePeak(float sample, long sampleIndex,
ScanState* pScanState, int* pBitVal);
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;
};
bool AnalyzeWAV(void);
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 /*__CASSETTEDIALOG__*/

View File

@ -0,0 +1,102 @@
/*
* 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 "HelpTopics.h"
#include "DiskFSTree.h"
using namespace DiskImgLib;
BEGIN_MESSAGE_MAP(ChooseAddTargetDialog, CDialog)
ON_COMMAND(IDHELP, OnHelp)
END_MESSAGE_MAP()
/*
* Initialize the dialog box. This requires scanning the provided disk
* archive.
*/
BOOL
ChooseAddTargetDialog::OnInitDialog(void)
{
CDialog::OnInitDialog();
CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_ADD_TARGET_TREE);
ASSERT(fpDiskFS != nil);
ASSERT(pTree != nil);
fDiskFSTree.fIncludeSubdirs = true;
fDiskFSTree.fExpandDepth = -1;
if (!fDiskFSTree.BuildTree(fpDiskFS, pTree)) {
WMSG0("Tree load failed!\n");
OnCancel();
}
int count = pTree->GetCount();
WMSG1("ChooseAddTargetDialog tree has %d items\n", count);
if (count <= 1) {
WMSG0(" Skipping out of target selection\n");
// adding to root volume of the sole DiskFS
fpChosenDiskFS = fpDiskFS;
ASSERT(fpChosenSubdir == nil);
OnOK();
}
return TRUE;
}
/*
* 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.
*/
void
ChooseAddTargetDialog::DoDataExchange(CDataExchange* pDX)
{
if (pDX->m_bSaveAndValidate) {
CTreeCtrl* pTree = (CTreeCtrl*) GetDlgItem(IDC_ADD_TARGET_TREE);
CString errMsg, appName;
appName.LoadString(IDS_MB_APP_NAME);
/* shortcut for simple disk images */
if (pTree->GetCount() == 1 && fpChosenDiskFS != nil)
return;
HTREEITEM selected;
selected = pTree->GetSelectedItem();
if (selected == nil) {
errMsg = "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 = "You can't add files there.";
MessageBox(errMsg, appName, MB_OK);
pDX->Fail();
return;
}
fpChosenDiskFS = pTargetData->pDiskFS;
fpChosenSubdir = pTargetData->pFile;
}
}
/*
* User pressed the "Help" button.
*/
void
ChooseAddTargetDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_CHOOSE_TARGET, HELP_CONTEXT);
}

View File

@ -0,0 +1,47 @@
/*
* 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 __CHOOSE_ADD_TARGET_DIALOG__
#define __CHOOSE_ADD_TARGET_DIALOG__
#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 = nil;
fpChosenSubdir = nil;
}
virtual ~ChooseAddTargetDialog(void) {}
/* set this before calling DoModal */
DiskImgLib::DiskFS* fpDiskFS;
/* results; fpChosenSubdir will be nil if root vol selected */
DiskImgLib::DiskFS* fpChosenDiskFS;
DiskImgLib::A2File* fpChosenSubdir;
private:
virtual BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg void OnHelp(void);
DiskFSTree fDiskFSTree;
DECLARE_MESSAGE_MAP()
};
#endif /*__CHOOSE_ADD_TARGET_DIALOG__*/

199
app/ChooseDirDialog.cpp Normal file
View File

@ -0,0 +1,199 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for "choose a directory" dialog.
*/
#include "stdafx.h"
#include "ChooseDirDialog.h"
#include "NewFolderDialog.h"
#include "DiskFSTree.h"
#include "HelpTopics.h"
BEGIN_MESSAGE_MAP(ChooseDirDialog, CDialog)
ON_NOTIFY(TVN_SELCHANGED, IDC_CHOOSEDIR_TREE, OnSelChanged)
ON_BN_CLICKED(IDC_CHOOSEDIR_EXPAND_TREE, OnExpandTree)
ON_BN_CLICKED(IDC_CHOOSEDIR_NEW_FOLDER, OnNewFolder)
ON_WM_HELPINFO()
//ON_COMMAND(ID_HELP, OnIDHelp)
ON_BN_CLICKED(IDHELP, OnHelp)
END_MESSAGE_MAP()
/*
* Initialize dialog components.
*/
BOOL
ChooseDirDialog::OnInitDialog(void)
{
CDialog::OnInitDialog();
/* set up the "new folder" button */
fNewFolderButton.ReplaceDlgCtrl(this, IDC_CHOOSEDIR_NEW_FOLDER);
fNewFolderButton.SetBitmapID(IDB_NEW_FOLDER);
/* replace the tree control with a ShellTree */
if (fShellTree.ReplaceDlgCtrl(this, IDC_CHOOSEDIR_TREE) != TRUE) {
WMSG0("WARNING: ShellTree replacement failed\n");
ASSERT(false);
}
//enable images
fShellTree.EnableImages();
//populate for the with Shell Folders for the first time
fShellTree.PopulateTree(/*CSIDL_DRIVES*/);
if (fPathName.IsEmpty()) {
// start somewhere reasonable
fShellTree.ExpandMyComputer();
} else {
CString msg("");
fShellTree.TunnelTree(fPathName, &msg);
if (!msg.IsEmpty()) {
/* failed */
WMSG2("TunnelTree failed on '%s' (%s), using MyComputer instead\n",
fPathName, msg);
fShellTree.ExpandMyComputer();
}
}
fShellTree.SetFocus();
return FALSE; // leave focus on shell tree
}
/*
* Special keypress handling.
*/
BOOL
ChooseDirDialog::PreTranslateMessage(MSG* pMsg)
{
if (pMsg->message == WM_KEYDOWN &&
pMsg->wParam == VK_RETURN)
{
//WMSG0("RETURN!\n");
if (GetFocus() == GetDlgItem(IDC_CHOOSEDIR_PATHEDIT)) {
OnExpandTree();
return TRUE;
}
}
return CDialog::PreTranslateMessage(pMsg);
}
/*
* F1 key hit, or '?' button in title bar used to select help for an
* item in the dialog.
*/
BOOL
ChooseDirDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
DWORD context = lpHelpInfo->iCtrlId;
WinHelp(context, HELP_CONTEXTPOPUP);
return TRUE; // indicate success??
}
/*
* User pressed Ye Olde Helppe Button.
*/
void
ChooseDirDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_CHOOSE_FOLDER, HELP_CONTEXT);
}
/*
* Replace the ShellTree's default SELCHANGED handler with this so we can
* track changes to the edit control.
*/
void
ChooseDirDialog::OnSelChanged(NMHDR* pnmh, LRESULT* pResult)
{
CString path;
CWnd* pWnd = GetDlgItem(IDC_CHOOSEDIR_PATH);
ASSERT(pWnd != nil);
if (fShellTree.GetFolderPath(&path))
fPathName = path;
else
fPathName = "";
pWnd->SetWindowText(fPathName);
// disable the "Select" button when there's no path ready
pWnd = GetDlgItem(IDOK);
ASSERT(pWnd != nil);
pWnd->EnableWindow(!fPathName.IsEmpty());
// It's confusing to have two different paths showing, so wipe out the
// free entry field when the selection changes.
pWnd = GetDlgItem(IDC_CHOOSEDIR_PATHEDIT);
pWnd->SetWindowText("");
*pResult = 0;
}
/*
* User pressed "Expand Tree" button.
*/
void
ChooseDirDialog::OnExpandTree(void)
{
CWnd* pWnd;
CString str;
CString msg;
pWnd = GetDlgItem(IDC_CHOOSEDIR_PATHEDIT);
ASSERT(pWnd != nil);
pWnd->GetWindowText(str);
if (!str.IsEmpty()) {
fShellTree.TunnelTree(str, &msg);
if (!msg.IsEmpty()) {
CString failed;
failed.LoadString(IDS_FAILED);
MessageBox(msg, failed, MB_OK | MB_ICONERROR);
}
}
}
/*
* User pressed "New Folder" button.
*/
void
ChooseDirDialog::OnNewFolder(void)
{
if (fPathName.IsEmpty()) {
MessageBox("You can't create a folder in this part of the tree.",
"Bad Location", MB_OK | MB_ICONERROR);
return;
}
NewFolderDialog newFolderDlg;
newFolderDlg.fCurrentFolder = fPathName;
if (newFolderDlg.DoModal() == IDOK) {
if (newFolderDlg.GetFolderCreated()) {
/*
* They created a new folder. We want to add it to the tree
* and then select it. This is not too hard because we know
* that the folder was created under the currently-selected
* tree node.
*/
if (fShellTree.AddFolderAtSelection(newFolderDlg.fNewFolder)) {
CString msg;
WMSG1("Success, tunneling to '%s'\n",
newFolderDlg.fNewFullPath);
fShellTree.TunnelTree(newFolderDlg.fNewFullPath, &msg);
if (!msg.IsEmpty()) {
WMSG1("TunnelTree failed: %s\n", (LPCTSTR) msg);
}
} else {
WMSG0("AddFolderAtSelection FAILED\n");
ASSERT(false);
}
} else {
WMSG0("NewFolderDialog returned IDOK but no create\n");
}
}
}

53
app/ChooseDirDialog.h Normal file
View File

@ -0,0 +1,53 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Dialog for choosing a directory.
*/
#ifndef __CHOOSEDIRDIALOG__
#define __CHOOSEDIRDIALOG__
#include "../util/UtilLib.h"
#include "resource.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.
*/
class ChooseDirDialog : public CDialog {
public:
ChooseDirDialog(CWnd* pParent = NULL, int dialogID = IDD_CHOOSEDIR) :
CDialog(dialogID, pParent)
{
fPathName = "";
}
virtual ~ChooseDirDialog(void) {}
const char* GetPathName(void) const { return fPathName; }
// set the pathname; when DoModal is called this will tunnel in
void SetPathName(const char* str) { fPathName = str; }
protected:
virtual BOOL OnInitDialog(void);
virtual BOOL PreTranslateMessage(MSG* pMsg);
afx_msg void OnSelChanged(NMHDR* pnmh, LRESULT* pResult);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg void OnExpandTree(void);
afx_msg void OnNewFolder(void);
afx_msg void OnHelp(void);
private:
CString fPathName;
ShellTree fShellTree;
MyBitmapButton fNewFolderButton;
DECLARE_MESSAGE_MAP()
};
#endif /*__CHOOSEDIRDIALOG__*/

2428
app/CiderPress.rc Normal file

File diff suppressed because it is too large Load Diff

1079
app/Clipboard.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,173 @@
/*
* 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()
/*
* Init static text fields.
*/
BOOL
RenameOverwriteDialog::OnInitDialog(void)
{
CWnd* pWnd;
pWnd = GetDlgItem(IDC_RENOVWR_SOURCE_NAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(fNewFileSource);
return CDialog::OnInitDialog();
}
/*
* Convert values.
*/
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("You must specify a new name.",
"CiderPress", MB_OK);
pDX->Fail();
}
// we *could* try to validate the path here...
}
}
BOOL
RenameOverwriteDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* ==========================================================================
* 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()
/*
* Replace some static text fields.
*/
BOOL
ConfirmOverwriteDialog::OnInitDialog(void)
{
CWnd* pWnd;
CString tmpStr, dateStr;
pWnd = GetDlgItem(IDC_OVWR_EXIST_NAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(fExistingFile);
pWnd = GetDlgItem(IDC_OVWR_EXIST_INFO);
ASSERT(pWnd != nil);
FormatDate(fExistingFileModWhen, &dateStr);
tmpStr.Format("Modified %s", dateStr);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_OVWR_NEW_NAME);
ASSERT(pWnd != nil);
pWnd->SetWindowText(fNewFileSource);
pWnd = GetDlgItem(IDC_OVWR_NEW_INFO);
ASSERT(pWnd != nil);
FormatDate(fNewFileModWhen, &dateStr);
tmpStr.Format("Modified %s", dateStr);
pWnd->SetWindowText(tmpStr);
pWnd = GetDlgItem(IDC_OVWR_RENAME);
ASSERT(pWnd != nil);
pWnd->EnableWindow(fAllowRename);
return CDialog::OnInitDialog();
}
/*
* Handle a click on the question-mark button.
*/
BOOL
ConfirmOverwriteDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* One of the buttons was hit.
*/
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

@ -0,0 +1,93 @@
/*
* 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 __CONFIRMOVERWRITEDIALOG__
#define __CONFIRMOVERWRITEDIALOG__
#include "resource.h"
/*
* Accept or reject overwriting an existing file or archive record.
*/
class ConfirmOverwriteDialog : public CDialog {
public:
ConfirmOverwriteDialog(CWnd* pParentWnd = nil) :
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:
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);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
virtual BOOL OnInitDialog(void);
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 = nil) :
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);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
DECLARE_MESSAGE_MAP()
};
#endif /*__CONFIRMOVERWRITEDIALOG__*/

1150
app/ContentList.cpp Normal file

File diff suppressed because it is too large Load Diff

136
app/ContentList.h Normal file
View File

@ -0,0 +1,136 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Class declaration for a list control showing archive contents.
*/
#ifndef __CONTENT_LIST__
#define __CONTENT_LIST__
#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 != nil);
ASSERT(pLayout != nil);
fpArchive = pArchive;
fpLayout = pLayout;
// fInvalid = false;
//fRightClickItem = -1;
fpArchive->ClearReloadFlag();
}
// call this before updating underlying storage; call Reload to un-inval
// void Invalidate(void);
// reload from underlying storage
void Reload(bool saveSelection = false);
void NewSortOrder(void);
void NewColumnWidths(void);
void ExportColumnWidths(void);
void SelectAll(void);
void InvertSelection(void);
void ClearSelection(void);
void SelectSubdirContents(void);
void FindNext(const char* str, bool down, bool matchCase, bool wholeWord);
bool CompareFindString(int num, const char* str, bool matchCase,
bool wholeWord);
//int GetRightClickItem(void) const { return fRightClickItem; }
//void ClearRightClickItem(void) { fRightClickItem = -1; }
enum { kFileTypeBufLen = 5, kAuxTypeBufLen = 6 };
static void MakeFileTypeDisplayString(const GenericEntry* pEntry,
char* buf);
static void MakeAuxTypeDisplayString(const GenericEntry* pEntry,
char* buf);
protected:
// overridden functions
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
virtual void PostNcDestroy(void);
afx_msg int OnCreate(LPCREATESTRUCT);
afx_msg void OnDestroy(void);
afx_msg void OnSysColorChange(void);
//afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
afx_msg void OnColumnClick(NMHDR*, LRESULT*);
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))
WMSG0("GLITCH: header list create failed\n");
fHdrImageList.SetBkColor(::GetSysColor(COLOR_BTNFACE));
}
void LoadListImages(void) {
if (!fListImageList.Create(IDB_LIST_PICS, 16, 1, CLR_DEFAULT))
WMSG0("GLITCH: list image create failed\n");
fListImageList.SetBkColor(::GetSysColor(COLOR_WINDOW));
}
enum { // defs for IDB_LIST_PICS
kListIconNone = 0,
kListIconComment = 1,
kListIconNonEmptyComment = 2,
kListIconDamaged = 3,
kListIconSuspicious = 4,
};
int LoadData(void);
long* GetSelectionSerials(long* pSelCount);
void RestoreSelection(const long* savedSel, long selCount);
int GetDefaultWidth(int col);
static void MakeMacTypeString(unsigned long val, char* buf);
static void MakeRatioDisplayString(const GenericEntry* pEntry, char* buf,
int* pPerc);
void SetSortIcon(void);
static int CALLBACK CompareFunc(LPARAM lParam1, LPARAM lParam2,
LPARAM lParamSort);
void OnDoubleClick(NMHDR* pnmh, LRESULT* pResult);
void OnRightClick(NMHDR* pnmh, LRESULT* pResult);
void SelectSubdir(const char* displayPrefix);
CImageList fHdrImageList;
CImageList fListImageList;
GenericArchive* fpArchive; // data we're expected to display
ColumnLayout* fpLayout;
// int fRightClickItem;
// bool fInvalid;
DECLARE_MESSAGE_MAP()
};
#endif /*__CONTENT_LIST__*/

View File

@ -0,0 +1,344 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for ConvDiskOptionsDialog.
*/
#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_COMMAND(IDHELP, OnHelp)
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()
const int kProDOSVolNameMax = 15; // longest possible ProDOS volume name
/*
* Set up our modified version of the "use selection" dialog.
*/
BOOL
ConvDiskOptionsDialog::OnInitDialog(void)
{
CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_VOLNAME);
ASSERT(pEdit != nil);
pEdit->SetLimitText(kProDOSVolNameMax);
ResetSizeControls();
pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_SPECIFY_EDIT);
ASSERT(pEdit != nil);
pEdit->SetLimitText(5); // enough for "65535"
pEdit->EnableWindow(FALSE);
return UseSelectionDialog::OnInitDialog();
}
/*
* Convert values.
*/
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))
errMsg.LoadString(IDS_VALID_VOLNAME_PRODOS);
}
}
if (!errMsg.IsEmpty()) {
CString appName;
appName.LoadString(IDS_MB_APP_NAME);
MessageBox(errMsg, appName, MB_OK);
pDX->Fail();
}
UseSelectionDialog::DoDataExchange(pDX);
}
/*
* When one of the radio buttons is clicked on, update the active status
* and contents of the "specify size" edit box.
*/
void
ConvDiskOptionsDialog::OnRadioChangeRange(UINT nID)
{
WMSG1("OnChangeRange id=%d\n", 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);
}
/*
* Test a ProDOS filename for validity.
*/
bool
ConvDiskOptionsDialog::IsValidVolumeName_ProDOS(const char* name)
{
return DiskImgLib::DiskFSProDOS::IsValidVolumeName(name);
}
/*
* 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.
*/
void
ConvDiskOptionsDialog::ResetSizeControls(void)
{
CWnd* pWnd;
CString spaceReq;
WMSG0("Resetting size controls\n");
spaceReq.Format(IDS_CONVDISK_SPACEREQ, "(unknown)");
pWnd = GetDlgItem(IDC_CONVDISK_SPACEREQ);
ASSERT(pWnd != nil);
pWnd->SetWindowText(spaceReq);
#if 0
int i;
for (i = 0; i < NELEM(gDiskSizes); i++) {
pWnd = GetDlgItem(gDiskSizes[i].ctrlID);
ASSERT(pWnd != nil);
pWnd->EnableWindow(TRUE);
}
#endif
NewDiskSize::EnableButtons(this);
}
/*
* 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
ConvDiskOptionsDialog::LimitSizeControls(long totalBlocks, long blocksUsed)
{
WMSG2("LimitSizeControls %ld %ld\n", totalBlocks, blocksUsed);
WMSG1("Full volume requires %ld bitmap blocks\n",
NewDiskSize::GetNumBitmapBlocks_ProDOS(totalBlocks));
CWnd* pWnd;
long usedWithoutBitmap =
blocksUsed - NewDiskSize::GetNumBitmapBlocks_ProDOS(totalBlocks);
long sizeInK = usedWithoutBitmap / 2;
CString sizeStr, spaceReq;
sizeStr.Format("%dK", sizeInK);
spaceReq.Format(IDS_CONVDISK_SPACEREQ, sizeStr);
pWnd = GetDlgItem(IDC_CONVDISK_SPACEREQ);
ASSERT(pWnd != nil);
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 != nil);
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
}
/*
* 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.
*/
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("No files matched the selection criteria.",
"No match", MB_OK|MB_ICONEXCLAMATION);
return;
}
XferFileOptions xferOpts;
//xferOpts.fAllowLowerCase =
// pPreferences->GetPrefBool(kPrProDOSAllowLower) != 0;
//xferOpts.fUseSparseBlocks =
// pPreferences->GetPrefBool(kPrProDOSUseSparse) != 0;
WMSG1("New volume name will be '%s'\n", fVolName);
/*
* Create a new disk image file.
*/
CString errStr;
char nameBuf[MAX_PATH];
UINT unique;
unique = GetTempFileName(pMain->GetPreferences()->GetPrefString(kPrTempPath),
"CPdisk", 0, nameBuf);
if (unique == 0) {
DWORD dwerr = ::GetLastError();
errStr.Format("GetTempFileName failed on '%s' (err=0x%08lx)\n",
pMain->GetPreferences()->GetPrefString(kPrTempPath), dwerr);
ShowFailureMsg(this, errStr, IDS_FAILED);
return;
}
WMSG1(" Will xfer to file '%s'\n", nameBuf);
// annoying -- DiskArchive insists on creating it
(void) unlink(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.
*/
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(nil);
if (result == GenericArchive::kXferOK) {
DiskFS* pDiskFS;
long totalBlocks, freeBlocks;
int unitSize;
DIError dierr;
WMSG0("SUCCESS\n");
pDiskFS = ((DiskArchive*) xferOpts.fTarget)->GetDiskFS();
ASSERT(pDiskFS != nil);
dierr = pDiskFS->GetFreeSpaceCount(&totalBlocks, &freeBlocks,
&unitSize);
if (dierr != kDIErrNone) {
errStr.Format("Unable to get free space count: %s.\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) {
WMSG0("CANCEL - cancel button hit\n");
ResetSizeControls();
} else {
WMSG1("FAILURE (result=%d)\n", result);
ResetSizeControls();
}
}
// debug
((DiskArchive*) (xferOpts.fTarget))->GetDiskFS()->DumpFileList();
/* clean up */
delete xferOpts.fTarget;
(void) unlink(nameBuf);
}

View File

@ -0,0 +1,53 @@
/*
* 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 __CONVDISK_OPTIONS_DIALOG__
#define __CONVDISK_OPTIONS_DIALOG__
#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 = "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);
virtual void DoDataExchange(CDataExchange* pDX);
// BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg void ResetSizeControls(void);
afx_msg void OnCompute(void);
afx_msg void OnRadioChangeRange(UINT nID);
void LimitSizeControls(long totalBlocks, long blocksUsed);
bool IsValidVolumeName_ProDOS(const char* name);
DECLARE_MESSAGE_MAP()
};
#endif /*__CONVDISK_OPTIONS_DIALOG__*/

View File

@ -0,0 +1,35 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for ConvFileOptionsDialog.
*/
#include "stdafx.h"
#include "ConvFileOptionsDialog.h"
//#include "NufxArchive.h"
#if 0
/*
* Set up our modified version of the "use selection" dialog.
*/
BOOL
ConvFileOptionsDialog::OnInitDialog(void)
{
return UseSelectionDialog::OnInitDialog();
}
#endif
/*
* Convert values.
*/
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

@ -0,0 +1,38 @@
/*
* 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 __CONVFILE_OPTIONS_DIALOG__
#define __CONVFILE_OPTIONS_DIALOG__
#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 BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
//DECLARE_MESSAGE_MAP()
};
#endif /*__CONVFILE_OPTIONS_DIALOG__*/

347
app/CreateImageDialog.cpp Normal file
View File

@ -0,0 +1,347 @@
/*
* 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 "HelpTopics.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()
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
/*
* Set up our modified version of the "use selection" dialog.
*/
BOOL
CreateImageDialog::OnInitDialog(void)
{
// high bit set in signed short means key is down
if (::GetKeyState(VK_SHIFT) < 0) {
WMSG0("Shift key is down, enabling extended options\n");
fExtendedOpts = true;
}
CEdit* pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSPRODOS_VOLNAME);
ASSERT(pEdit != nil);
pEdit->SetLimitText(kProDOSVolNameMax);
pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSPASCAL_VOLNAME);
ASSERT(pEdit != nil);
pEdit->SetLimitText(kPascalVolNameMax);
pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSHFS_VOLNAME);
ASSERT(pEdit != nil);
pEdit->SetLimitText(kHFSVolNameMax);
pEdit = (CEdit*) GetDlgItem(IDC_CREATEFSDOS_VOLNUM);
ASSERT(pEdit != nil);
pEdit->SetLimitText(3); // 3 digit volume number
pEdit = (CEdit*) GetDlgItem(IDC_CONVDISK_SPECIFY_EDIT);
ASSERT(pEdit != nil);
pEdit->EnableWindow(FALSE);
return CDialog::OnInitDialog();
}
/*
* Convert values.
*/
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("%d", fDOSVolumeNum);
if (!IsValidVolumeName_DOS(tmpStr))
errMsg.LoadString(IDS_VALID_VOLNAME_DOS);
} else if (fDiskFormatIdx == kFmtDOS33) {
CString tmpStr;
tmpStr.Format("%d", fDOSVolumeNum);
if (!IsValidVolumeName_DOS(tmpStr))
errMsg.LoadString(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 = "Specify a size between 144 blocks (18 tracks) and"
" 800 blocks (50 tracks/32 sectors). The block count"
" must be a multiple of 8 for 16-sector disks, or a"
" multiple of 16 for 32-sector disks. 32 sector"
" formatting starts at 400 blocks. Disks larger than"
" 400 blocks but less than 800 aren't recognized by"
" 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 = "Specify a size of at least 16 blocks and no more"
" than 65536 blocks.";
} else if (fVolName_ProDOS.IsEmpty() ||
fVolName_ProDOS.GetLength() > kProDOSVolNameMax)
{
errMsg = "You must specify a volume name 1-15 characters long.";
} else {
if (!IsValidVolumeName_ProDOS(fVolName_ProDOS))
errMsg.LoadString(IDS_VALID_VOLNAME_PRODOS);
}
} else if (fDiskFormatIdx == kFmtPascal) {
if (fVolName_Pascal.IsEmpty() ||
fVolName_Pascal.GetLength() > kPascalVolNameMax)
{
errMsg = "You must specify a volume name 1-7 characters long.";
} else {
if (!IsValidVolumeName_Pascal(fVolName_Pascal))
errMsg.LoadString(IDS_VALID_VOLNAME_PASCAL);
}
} else if (fDiskFormatIdx == kFmtHFS) {
if (fNumBlocks < 1600 || fNumBlocks > 4194303) {
errMsg = "Specify a size of at least 1600 blocks and no more"
" than 4194303 blocks.";
} else if (fVolName_HFS.IsEmpty() ||
fVolName_HFS.GetLength() > kHFSVolNameMax)
{
errMsg = "You must specify a volume name 1-27 characters long.";
} else {
if (!IsValidVolumeName_HFS(fVolName_HFS))
errMsg.LoadString(IDS_VALID_VOLNAME_HFS);
}
} else if (fDiskFormatIdx == kFmtBlank) {
if (fNumBlocks < 1 || fNumBlocks > kMaxBlankBlocks)
errMsg = "Specify a size of at least 1 block and no more"
" than 16777216 blocks.";
} else {
ASSERT(false);
}
} else {
OnFormatChangeRange(IDC_CREATEFS_DOS32 + fDiskFormatIdx);
}
if (!errMsg.IsEmpty()) {
CString appName;
appName.LoadString(IDS_MB_APP_NAME);
MessageBox(errMsg, appName, MB_OK);
pDX->Fail();
}
CDialog::DoDataExchange(pDX);
}
/*
* When the user chooses a format, enable and disable controls as
* appropriate.
*/
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;
WMSG1("OnFormatChangeRange id=%d\n", 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 != nil)
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 != nil);
if (pWnd != nil)
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);
}
}
/*
* When one of the radio buttons is clicked on, update the active status
* and contents of the "specify size" edit box.
*/
void
CreateImageDialog::OnSizeChangeRange(UINT nID)
{
WMSG1("OnSizeChangeRange id=%d\n", 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);
}
/*
* Test a DOS filename for validity.
*/
bool
CreateImageDialog::IsValidVolumeName_DOS(const char* name)
{
return DiskImgLib::DiskFSDOS33::IsValidVolumeName(name);
}
/*
* Test a ProDOS filename for validity.
*/
bool
CreateImageDialog::IsValidVolumeName_ProDOS(const char* name)
{
return DiskImgLib::DiskFSProDOS::IsValidVolumeName(name);
}
/*
* Test a Pascal filename for validity.
*/
bool
CreateImageDialog::IsValidVolumeName_Pascal(const char* name)
{
return DiskImgLib::DiskFSPascal::IsValidVolumeName(name);
}
/*
* Test an HFS filename for validity.
*/
bool
CreateImageDialog::IsValidVolumeName_HFS(const char* name)
{
return DiskImgLib::DiskFSHFS::IsValidVolumeName(name);
}
/*
* Context help request (question mark button).
*/
BOOL
CreateImageDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* User pressed the "Help" button.
*/
void
CreateImageDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_IMAGE_CREATOR, HELP_CONTEXT);
}

75
app/CreateImageDialog.h Normal file
View File

@ -0,0 +1,75 @@
/*
* 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 __CREATE_IMAGE_DIALOG__
#define __CREATE_IMAGE_DIALOG__
#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 = "NEW.DISK";
fVolName_Pascal = "BLANK";
fVolName_HFS = "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);
virtual void DoDataExchange(CDataExchange* pDX);
// BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg void OnFormatChangeRange(UINT nID);
afx_msg void OnSizeChangeRange(UINT nID);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg void OnHelp(void);
bool IsValidVolumeName_DOS(const char* name);
bool IsValidVolumeName_ProDOS(const char* name);
bool IsValidVolumeName_Pascal(const char* name);
bool IsValidVolumeName_HFS(const char* name);
bool fExtendedOpts;
DECLARE_MESSAGE_MAP()
};
#endif /*__CREATE_IMAGE_DIALOG__*/

View File

@ -0,0 +1,82 @@
/*
* 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()
/*
* Set up the control.
*/
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 != nil);
pEdit->SetSel(0, -1);
pEdit->SetFocus();
return FALSE; // we set the focus
}
/*
* Convert values.
*/
void
CreateSubdirDialog::DoDataExchange(CDataExchange* pDX)
{
CString msg, failed;
msg = "";
failed.LoadString(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 = "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;
}
/*
* Context help request (question mark button).
*/
BOOL
CreateSubdirDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}

45
app/CreateSubdirDialog.h Normal file
View File

@ -0,0 +1,45 @@
/*
* 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 __CREATESUBDIRDIALOG__
#define __CREATESUBDIRDIALOG__
#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 = nil;
fpParentEntry = nil;
}
virtual ~CreateSubdirDialog(void) {}
CString fBasePath; // where subdir will be created
CString fNewName;
const GenericArchive* fpArchive;
const GenericEntry* fpParentEntry;
protected:
// overrides
virtual BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
private:
DECLARE_MESSAGE_MAP()
};
#endif /*__CREATESUBDIRDIALOG__*/

69
app/DEFileDialog.cpp Normal file
View File

@ -0,0 +1,69 @@
/*
* 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()
/*
* Turn off the "OK" button, which is only active when some text
* has been typed in the window.
*/
BOOL
DEFileDialog::OnInitDialog(void)
{
CWnd* pWnd = GetDlgItem(IDOK);
ASSERT(pWnd != nil);
pWnd->EnableWindow(FALSE);
return CDialog::OnInitDialog();
}
/*
* Get the filename and the "open resource fork" check box.
*/
void
DEFileDialog::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_DEFILE_FILENAME, fName);
DDX_Check(pDX, IDC_DEFILE_RSRC, fOpenRsrcFork);
}
/*
* The text has changed. If there's nothing in the box, dim the
* "OK" button.
*/
void
DEFileDialog::OnChange(void)
{
CEdit* pEdit = (CEdit*) GetDlgItem(IDC_DEFILE_FILENAME);
ASSERT(pEdit != nil);
CString str;
pEdit->GetWindowText(str);
//WMSG2("STR is '%s' (%d)\n", str, str.GetLength());
CWnd* pWnd = GetDlgItem(IDOK);
ASSERT(pWnd != nil);
pWnd->EnableWindow(!str.IsEmpty());
}
/*
* Context help request (question mark button).
*/
BOOL
DEFileDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}

60
app/DEFileDialog.h Normal file
View File

@ -0,0 +1,60 @@
/*
* 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 __DEFILEDIALOG__
#define __DEFILEDIALOG__
#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 = "";
}
virtual ~DEFileDialog(void) {}
void Setup(DiskFS* pDiskFS) {
fpDiskFS = pDiskFS;
}
CString fName;
int fOpenRsrcFork;
protected:
// overrides
virtual BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg virtual void OnChange(void);
afx_msg virtual BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
private:
DiskFS* fpDiskFS;
DECLARE_MESSAGE_MAP()
};
#endif /*__DEFILEDIALOG__*/

3155
app/DiskArchive.cpp Normal file

File diff suppressed because it is too large Load Diff

244
app/DiskArchive.h Normal file
View File

@ -0,0 +1,244 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Disk image "archive" support.
*/
#ifndef __DISK_ARCHIVE__
#define __DISK_ARCHIVE__
#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) {}
// retrieve thread data
virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const;
virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
ConvertHighASCII convHA, CString* pErrMsg) const;
virtual long GetSelectionSerial(void) const { return -1; } // idea: T/S block number
virtual bool GetFeatureFlag(Feature feature) const;
// return the underlying FS format for this file
virtual DiskImg::FSFormat GetFSFormat(void) const {
ASSERT(fpFile != nil);
return fpFile->GetFSFormat();
}
A2File* GetA2File(void) const { return fpFile; }
void SetA2File(A2File* pFile) { fpFile = pFile; }
private:
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(nil), fIsReadOnly(false),
fpAddDataHead(nil), fpAddDataTail(nil)
{}
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 char* volName;
long numBlocks;
} prodos;
struct {
NewOptionsBase base;
const char* volName;
long numBlocks;
} pascalfs; // "pascal" is reserved token in MSVC++
struct {
NewOptionsBase base;
const char* volName;
long numBlocks;
} hfs;
struct {
NewOptionsBase base;
int volumeNum;
long numTracks;
int numSectors;
bool allocDOSTracks;
} dos;
} NewOptions;
// One-time initialization; returns an error string.
static CString AppInit(void);
// one-time cleanup at app shutdown time
static void AppCleanup(void);
virtual OpenResult Open(const char* filename, bool readOnly, CString* pErrMsg);
virtual CString New(const char* filename, const void* options);
virtual CString Flush(void);
virtual CString Reload(void);
virtual bool IsReadOnly(void) const { return fIsReadOnly; };
virtual bool IsModified(void) const;
virtual void GetDescription(CString* pStr) const;
virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts);
virtual bool AddDisk(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts)
{ ASSERT(false); return false; }
virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
const char* newName);
virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet)
{ ASSERT(false); return false; }
virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet);
virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet);
virtual CString TestPathName(const GenericEntry* pGenericEntry,
const CString& basePath, const CString& newName, char newFssep) const;
virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
const char* newName);
virtual CString TestVolumeName(const DiskFS* pDiskFS,
const char* newName) const;
virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
const RecompressOptionsDialog* pRecompOpts)
{ ASSERT(false); return false; }
virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
CString* pStr)
{ ASSERT(false); return false; }
virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
const CString& str)
{ ASSERT(false); return false; }
virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry)
{ ASSERT(false); return false; }
virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
const FileProps* pProps);
virtual void PreferencesChanged(void);
virtual long GetCapability(Capability cap);
virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
ActionProgressDialog* pActionProgress, const XferFileOptions* pXferOpts);
const DiskImg* GetDiskImg(void) const { return &fDiskImg; }
DiskFS* GetDiskFS(void) const { return fpPrimaryDiskFS; }
/* internal function, used by DiskArchive and DiskEntry */
static bool ProgressCallback(DiskImgLib::A2FileDescr* pFile,
DiskImgLib::di_off_t max, DiskImgLib::di_off_t current, void* state);
private:
virtual CString Close(void);
virtual void XferPrepare(const XferFileOptions* pXferOpts);
virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
long dataLen, unsigned char** pRsrcBuf, long rsrcLen);
virtual void XferAbort(CWnd* pMsgWnd);
virtual void XferFinish(CWnd* pMsgWnd);
/* internal function, used during initial scan of volume */
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 FileDetails* pDetails, const char* fsNormalPath) {
fDetails = *pDetails;
fFSNormalPath = fsNormalPath;
fpOtherFork = nil;
fpNext = nil;
}
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 FileDetails* GetDetails(void) const { return &fDetails; }
const char* GetFSNormalPath(void) const { return fFSNormalPath; }
private:
// Three filenames stored here:
// fDetails.origName -- the name of the Windows file
// fDetails.storageName -- the normalized Windows name
// fFSNormalPath -- the FS-normalized version of "storageName"
FileDetails fDetails;
CString fFSNormalPath;
FileAddData* fpOtherFork;
FileAddData* fpNext;
};
virtual ArchiveKind GetArchiveKind(void) { return kArchiveDiskImage; }
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails);
int InternalReload(CWnd* pMsgWnd);
static int CompareDisplayNamesDesc(const void* ventry1, const void* ventry2);
int LoadContents(void);
int LoadDiskFSContents(DiskFS* pDiskFS, const char* volName);
void DowncaseSubstring(CString* pStr, int startPos, int endPos,
bool prevWasSpace);
static void DebugMsgHandler(const char* file, int line, const char* msg);
NuResult HandleReplaceExisting(const A2File* pExisting,
FileDetails* pDetails);
CString ProcessFileAddData(DiskFS* pDiskFS, int addOptsConvEOL);
CString LoadFile(const char* pathName, unsigned char** pBuf, long* pLen,
GenericEntry::ConvertEOL conv, GenericEntry::ConvertHighASCII convHA) const;
DIError AddForksToDisk(DiskFS* pDiskFS, const DiskFS::CreateParms* pParms,
const unsigned char* dataBuf, long dataLen,
const unsigned char* rsrcBuf, long rsrcLen) const;
void AddToAddDataList(FileAddData* pData);
void FreeAddDataList(void);
void ConvertFDToCP(const FileDetails* pDetails,
DiskFS::CreateParms* pCreateParms);
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 /*__DISK_ARCHIVE__*/

307
app/DiskConvertDialog.cpp Normal file
View File

@ -0,0 +1,307 @@
/*
* 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"
#include "HelpTopics.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()
/*
* Initialize the set of available options based on the source image.
*/
void
DiskConvertDialog::Init(const DiskImg* pDiskImg)
{
ASSERT(pDiskImg != nil);
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 */
fDiskDescription.LoadString(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 */
fDiskDescription.LoadString(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 */
fDiskDescription.LoadString(IDS_CDEC_140K_13);
fAllowUnadornedNibble = true;
fAllowD13 = true;
fConvertIdx = kConvNibbleRaw;
} else if (diskSizeInSectors == kMagicNibbles) {
/* blob of nibbles with no recognizable format; allow self-convert */
fDiskDescription.LoadString(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 */
WMSG0(" GLITCH: we don't allow self-convert of .nb2 files\n");
ASSERT(false);
} else {
/* this should be impossible */
ASSERT(false);
}
} else if (diskSizeInSectors == 3200) {
/* 800K disk image */
fDiskDescription.LoadString(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;
}
}
/*
* Initialize options for a bulk transfer.
*/
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("%d images selected", fBulkFileCount);
}
/*
* Disable unavailable options.
*/
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;
}
/*
* Convert options in and out.
*/
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 = "do"; break;
case kConvDOS2MG: fExtension = "2mg"; break;
case kConvProDOSRaw: fExtension = "po"; break;
case kConvProDOS2MG: fExtension = "2mg"; break;
case kConvNibbleRaw: fExtension = "nib"; break;
case kConvNibble2MG: fExtension = "2mg"; break;
case kConvD13: fExtension = "d13"; break;
case kConvDiskCopy42: fExtension = "dsk"; break;
case kConvNuFX: fExtension = "sdk"; break;
case kConvTrackStar: fExtension = "app"; break;
case kConvSim2eHDV: fExtension = "hdv"; break;
case kConvDDD: fExtension = "ddd"; break;
default:
fExtension = "???";
ASSERT(false);
break;
}
if (fAddGzip && fConvertIdx != kConvNuFX)
fExtension += ".gz";
WMSG1(" DCD recommending extension '%s'\n", (LPCTSTR) fExtension);
}
}
/*
* 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.
*/
void
DiskConvertDialog::OnChangeRadio(UINT nID)
{
CWnd* pGzip = GetDlgItem(IDC_DISKCONV_GZIP);
ASSERT(pGzip != nil);
CButton* pNuFX = (CButton*)GetDlgItem(IDC_DISKCONV_SDK);
ASSERT(pNuFX != nil);
if (fSizeInBlocks > DiskImgLib::kGzipMax / 512)
pGzip->EnableWindow(FALSE);
else
pGzip->EnableWindow(pNuFX->GetCheck() == BST_UNCHECKED);
}
/*
* Context help request (question mark button).
*/
BOOL
DiskConvertDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* User pressed the "Help" button.
*/
void
DiskConvertDialog::OnHelp(void)
{
if (fBulkFileCount < 0)
WinHelp(HELP_TOPIC_DISK_CONV, HELP_CONTEXT);
else
WinHelp(HELP_TOPIC_BULK_DISK_CONV, HELP_CONTEXT);
}

85
app/DiskConvertDialog.h Normal file
View File

@ -0,0 +1,85 @@
/*
* 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 __DISKCONVERTDIALOG__
#define __DISKCONVERTDIALOG__
#include "resource.h"
#include "../diskimg/DiskImg.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) {}
void Init(const DiskImgLib::DiskImg* pDiskImg); // single file init
void Init(int fileCount); // bulk init
/* 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);
void DoDataExchange(CDataExchange* pDX);
afx_msg void OnChangeRadio(UINT nID);
afx_msg void OnHelp(void);
BOOL OnHelpInfo(HELPINFO* 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 /*__DISKCONVERTDIALOG__*/

1641
app/DiskEditDialog.cpp Normal file

File diff suppressed because it is too large Load Diff

315
app/DiskEditDialog.h Normal file
View File

@ -0,0 +1,315 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Class definition for DiskEdit dialog.
*/
#ifndef __DISK_EDIT_DIALOG__
#define __DISK_EDIT_DIALOG__
#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 = nil;
fFileName = "";
fPositionShift = 0;
fFirstResize = true;
}
virtual ~DiskEditDialog() {}
void Setup(DiskFS* pDiskFS, const char* fileName) {
ASSERT(pDiskFS != nil);
ASSERT(fileName != nil);
fpDiskFS = pDiskFS;
fFileName = fileName;
}
enum { kSectorSize=256, kBlockSize=512 };
virtual int LoadData(void) = 0;
virtual void DisplayData(void) = 0;
virtual void DisplayData(const unsigned char* buf, int size);
virtual void DisplayNibbleData(const unsigned char* 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 char* 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;
}
// overrides
virtual BOOL OnInitDialog(void);
afx_msg virtual BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg virtual void OnDone(void);
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;
afx_msg virtual void OnSubVolume(void);
afx_msg virtual void OnOpenFile(void) = 0;
afx_msg virtual void OnNibbleParms(void);
afx_msg virtual void OnHelp(void);
virtual BOOL PreTranslateMessage(MSG* pMsg);
void SetSpinMode(int id, int base);
int ReadSpinner(int id, long* pVal);
void SetSpinner(int id, long val);
//void FillWithPattern(unsigned char* buf, int size, const char* pattern);
DIError OpenFile(const char* fileName, bool openRsrc, A2File** ppFile,
A2FileDescr** ppOpenFile);
DiskFS* fpDiskFS;
CString fFileName;
CString fAlertMsg;
bool fReadOnly;
int fPositionShift;
private:
void InitNibbleParmList(void);
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); // load the current track/sector
virtual void DisplayData(void) {
DiskEditDialog::DisplayData(fSectorData, kSectorSize);
}
//void SetTrack(int val) { fTrack = val; }
//void SetSector(int val) { fSector = val; }
// overrides
virtual BOOL OnInitDialog(void);
protected:
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);
long fTrack;
long fSector;
unsigned char 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 char* 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); // load the current block
virtual void DisplayData(void) {
DiskEditDialog::DisplayData(fBlockData, kBlockSize);
}
// overrides
virtual BOOL OnInitDialog(void);
protected:
//void MoveControl(int id, int deltaX, int deltaY);
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);
long fBlock;
unsigned char 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 char* 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 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); // load the current track/sector
virtual void DisplayData(void) {
DiskEditDialog::DisplayNibbleData(fNibbleData, fNibbleDataLen);
}
// overrides
virtual BOOL OnInitDialog(void);
protected:
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;
unsigned char fNibbleData[DiskImgLib::kTrackAllocSize];
long fNibbleDataLen;
};
#endif /*__DISK_EDIT_DIALOG__*/

View File

@ -0,0 +1,53 @@
/*
* 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 != nil);
pButton->EnableWindow(FALSE);
}
return CDialog::OnInitDialog();
}
/* user clicked "open file" button */
void
DiskEditOpenDialog::OnButtonFile(void)
{
fOpenWhat = kOpenFile;
OnOK();
}
/* user clicked "open volume" button */
void
DiskEditOpenDialog::OnButtonVolume(void)
{
fOpenWhat = kOpenVolume;
OnOK();
}
/* user clicked "open current" button */
void
DiskEditOpenDialog::OnButtonCurrent(void)
{
fOpenWhat = kOpenCurrent;
OnOK();
}

49
app/DiskEditOpenDialog.h Normal file
View File

@ -0,0 +1,49 @@
/*
* 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 __DISKEDITOPENDIALOG__
#define __DISKEDITOPENDIALOG__
#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);
afx_msg void OnButtonFile(void);
afx_msg void OnButtonVolume(void);
afx_msg void OnButtonCurrent(void);
DECLARE_MESSAGE_MAP()
};
#endif /*__DISKEDITOPENDIALOG__*/

252
app/DiskFSTree.cpp Normal file
View File

@ -0,0 +1,252 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* DiskFSTree implementation.
*/
#include "StdAfx.h"
#include "ChooseAddTargetDialog.h"
#include "HelpTopics.h"
using namespace DiskImgLib;
/*
* Build the tree.
*/
bool
DiskFSTree::BuildTree(DiskFS* pDiskFS, CTreeCtrl* pTree)
{
ASSERT(pDiskFS != nil);
ASSERT(pTree != nil);
pTree->SetImageList(&fTreeImageList, TVSIL_NORMAL);
return AddDiskFS(pTree, TVI_ROOT, pDiskFS, 1);
}
/*
* Load the specified DiskFS into the tree, recursively adding any
* sub-volumes.
*
* Pass in an initial depth of 1.
*
* Returns "true" on success, "false" on failure.
*/
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.
*/
pTarget = AllocTargetData();
pTarget->kind = kTargetDiskFS;
pTarget->pDiskFS = pDiskFS;
pTarget->pFile = nil; // could also use volume dir for ProDOS
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
tvi.pszText = const_cast<char*>(pDiskFS->GetVolumeID());
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 == nil) {
WMSG0("Tree root InsertItem failed\n");
return false;
}
/*
* Scan for and handle all sub-volumes.
*/
pSubVol = pDiskFS->GetNextSubVolume(nil);
while (pSubVol != nil) {
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, nil, 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;
}
/*
* 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 nil 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*
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 == nil && pParentFile == nil) {
/* this can happen on an empty DOS 3.3 disk; under ProDOS, we always
have the volume entry */
/* note pFile will be nil if this happens to be a subdirectory
positioned as the very last file on the disk */
return nil;
}
if (pParentFile == nil) {
/*
* 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).
*/
pTarget = AllocTargetData();
pTarget->kind = kTargetSubdir;
pTarget->selectable = true;
pTarget->pDiskFS = pDiskFS;
pTarget->pFile = pParentFile;
tvi.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
tvi.pszText = const_cast<char*>(pParentFile->GetFileName());
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 == nil) {
WMSG1("Tree insert '%s' failed\n", tvi.pszText);
return nil;
}
}
while (pFile != nil) {
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 == nil)
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;
}
/*
* Allocate a new TargetData struct, and add it to our list.
*/
DiskFSTree::TargetData*
DiskFSTree::AllocTargetData(void)
{
TargetData* pNew = new TargetData;
if (pNew == nil)
return nil;
memset(pNew, 0, sizeof(*pNew));
/* insert it at the head of the list, and update the head pointer */
pNew->pNext = fpTargetData;
fpTargetData = pNew;
return pNew;
}
/*
* Free up the TargetData structures we created.
*
* Rather than
*/
void
DiskFSTree::FreeAllTargetData(void)
{
TargetData* pTarget;
TargetData* pNext;
pTarget = fpTargetData;
while (pTarget != nil) {
pNext = pTarget->pNext;
delete pTarget;
pTarget = pNext;
}
fpTargetData = nil;
}

81
app/DiskFSTree.h Normal file
View File

@ -0,0 +1,81 @@
/*
* 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 __DISKFSTREE__
#define __DISKFSTREE__
#include "resource.h"
#include "../diskimg/DiskImg.h"
/*
* This class could probably be part of DiskArchive, but things are pretty
* cluttered up there already.
*/
class DiskFSTree {
public:
DiskFSTree(void) {
fIncludeSubdirs = false;
fExpandDepth = 0;
fpDiskFS = nil;
fpTargetData = nil;
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;
typedef struct TargetData {
TargetKind kind;
bool selectable;
DiskImgLib::DiskFS* pDiskFS;
DiskImgLib::A2File* pFile;
// easier to keep a list than to chase through the tree
struct TargetData* pNext;
} TargetData;
private:
bool AddDiskFS(CTreeCtrl* pTree, HTREEITEM root,
DiskImgLib::DiskFS* pDiskFS, int depth);
DiskImgLib::A2File* AddSubdir(CTreeCtrl* pTree, HTREEITEM parent,
DiskImgLib::DiskFS* pDiskFS, DiskImgLib::A2File* pFile,
int depth);
TargetData* AllocTargetData(void);
void FreeAllTargetData(void);
void LoadTreeImages(void) {
if (!fTreeImageList.Create(IDB_TREE_PICS, 16, 1, CLR_DEFAULT))
WMSG0("GLITCH: list image create failed\n");
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;
};
#endif /*__DISKFSTREE__*/

16
app/DoneOpenDialog.h Normal file
View File

@ -0,0 +1,16 @@
/*
* 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.
*/
#include "resource.h"
class DoneOpenDialog : public CDialog {
public:
DoneOpenDialog(CWnd* pParentWnd = NULL) : CDialog(IDD_DONEOPEN, pParentWnd)
{}
virtual ~DoneOpenDialog(void) {}
};

59
app/EOLScanDialog.cpp Normal file
View File

@ -0,0 +1,59 @@
/*
* 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"
#include "HelpTopics.h"
BEGIN_MESSAGE_MAP(EOLScanDialog, CDialog)
ON_COMMAND(IDHELP, OnHelp)
END_MESSAGE_MAP()
/*
* Fill in the blanks.
*/
BOOL
EOLScanDialog::OnInitDialog(void)
{
CWnd* pWnd;
CString fmt;
fmt.Format("%ld", fCountChars);
pWnd = GetDlgItem(IDC_EOLSCAN_CHARS);
pWnd->SetWindowText(fmt);
fmt.Format("%ld", fCountCR);
pWnd = GetDlgItem(IDC_EOLSCAN_CR);
pWnd->SetWindowText(fmt);
fmt.Format("%ld", fCountLF);
pWnd = GetDlgItem(IDC_EOLSCAN_LF);
pWnd->SetWindowText(fmt);
fmt.Format("%ld", fCountCRLF);
pWnd = GetDlgItem(IDC_EOLSCAN_CRLF);
pWnd->SetWindowText(fmt);
fmt.Format("%ld", fCountHighASCII);
pWnd = GetDlgItem(IDC_EOLSCAN_HIGHASCII);
pWnd->SetWindowText(fmt);
return CDialog::OnInitDialog();
}
/*
* User pressed the "Help" button.
*/
void
EOLScanDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_EOL_SCAN, HELP_CONTEXT);
}

37
app/EOLScanDialog.h Normal file
View File

@ -0,0 +1,37 @@
/*
* 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 __EOLSCANDIALOG__
#define __EOLSCANDIALOG__
#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);
afx_msg void OnHelp(void);
DECLARE_MESSAGE_MAP()
};
#endif /*__EOLSCANDIALOG__*/

159
app/EditAssocDialog.cpp Normal file
View File

@ -0,0 +1,159 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for EditAssocDialog.
*/
#include "stdafx.h"
#include "EditAssocDialog.h"
#include "MyApp.h"
#include "Registry.h"
#include "HelpTopics.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
/*
* Tweak the controls.
*/
BOOL
EditAssocDialog::OnInitDialog(void)
{
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_ASSOCIATION_LIST);
ASSERT(pListView != nil);
//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 == nil) {
fOurAssociations = new bool[gMyApp.fRegistry.GetNumFileAssocs()];
Setup(true);
} else {
Setup(false);
}
return CDialog::OnInitDialog();
}
/*
* 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
EditAssocDialog::Setup(bool loadAssoc)
{
WMSG0("Setup!\n");
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_ASSOCIATION_LIST);
ASSERT(pListView != nil);
ASSERT(fOurAssociations != nil);
/* two columns */
CRect rect;
pListView->GetClientRect(&rect);
int width;
width = pListView->GetStringWidth("XXExtensionXX");
pListView->InsertColumn(0, "Extension", LVCFMT_LEFT, width);
pListView->InsertColumn(1, "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);
pListView->InsertItem(idx, ext);
pListView->SetItemText(idx, 1, handler);
if (loadAssoc)
fOurAssociations[idx] = ours;
idx++;
}
//DeleteAllItems(); // for Reload case
}
/*
* Copy state in and out of dialog.
*/
void
EditAssocDialog::DoDataExchange(CDataExchange* pDX)
{
CListCtrl* pListView = (CListCtrl*) GetDlgItem(IDC_ASSOCIATION_LIST);
ASSERT(fOurAssociations != nil);
if (fOurAssociations == nil)
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++;
}
}
}
/*
* Context help request (question mark button).
*/
BOOL
EditAssocDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
return ShowContextHelp(this, lpHelpInfo);
}
/*
* User pressed the "Help" button.
*/
void
EditAssocDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_EDIT_ASSOC, HELP_CONTEXT);
}

45
app/EditAssocDialog.h Normal file
View File

@ -0,0 +1,45 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* File associations edit dialog.
*/
#ifndef __EDITASSOCDIALOG__
#define __EDITASSOCDIALOG__
#include "resource.h"
/*
* Edit whatever associations our registry class cares about.
*/
class EditAssocDialog : public CDialog {
public:
EditAssocDialog(CWnd* pParentWnd = nil) :
CDialog(IDD_ASSOCIATIONS, pParentWnd),
fOurAssociations(nil)
{}
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:
// overrides
virtual BOOL OnInitDialog(void);
BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
void DoDataExchange(CDataExchange* pDX);
afx_msg void OnHelp(void);
void Setup(bool loadAssoc);
DECLARE_MESSAGE_MAP()
};
#endif /*__EDITASSOCDIALOG__*/

79
app/EditCommentDialog.cpp Normal file
View File

@ -0,0 +1,79 @@
/*
* 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"
#include "HelpTopics.h"
BEGIN_MESSAGE_MAP(EditCommentDialog, CDialog)
ON_BN_CLICKED(IDC_COMMENT_DELETE, OnDelete)
ON_WM_HELPINFO()
ON_COMMAND(IDHELP, OnHelp)
END_MESSAGE_MAP()
/*
* Set up the control. If this is a new comment, don't show the delete
* button.
*/
BOOL
EditCommentDialog::OnInitDialog(void)
{
if (fNewComment) {
CWnd* pWnd = GetDlgItem(IDC_COMMENT_DELETE);
pWnd->EnableWindow(FALSE);
}
return CDialog::OnInitDialog();
}
/*
* Convert values.
*/
void
EditCommentDialog::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_COMMENT_EDIT, fComment);
}
/*
* User wants to delete the comment. Verify first.
*/
void
EditCommentDialog::OnDelete(void)
{
CString question, title;
int result;
title.LoadString(IDS_EDIT_COMMENT);
question.LoadString(IDS_DEL_COMMENT_OK);
result = MessageBox(question, title, MB_OKCANCEL | MB_ICONQUESTION);
if (result == IDCANCEL)
return;
EndDialog(kDeleteCommentID);
}
/*
* Context help request (question mark button).
*/
BOOL
EditCommentDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* User pressed the "Help" button.
*/
void
EditCommentDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_EDIT_COMMENT, HELP_CONTEXT);
}

47
app/EditCommentDialog.h Normal file
View File

@ -0,0 +1,47 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Edit a comment.
*/
#ifndef __EDITCOMMENTDIALOG__
#define __EDITCOMMENTDIALOG__
#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)
{
//fComment = "";
fNewComment = false;
}
virtual ~EditCommentDialog(void) {}
enum { kDeleteCommentID = IDC_COMMENT_DELETE };
CString fComment;
bool fNewComment; // entry doesn't already have one
protected:
// overrides
virtual BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg void OnHelp(void);
afx_msg void OnDelete(void);
private:
DECLARE_MESSAGE_MAP()
};
#endif /*__EDITCOMMENTDIALOG__*/

523
app/EditPropsDialog.cpp Normal file
View File

@ -0,0 +1,523 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for file properties edit dialog.
*/
#include "StdAfx.h"
#include "EditPropsDialog.h"
#include "FileNameConv.h"
#include "HelpTopics.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()
/*
* Initialize fProps from the stuff in pEntry.
*/
void
EditPropsDialog::InitProps(GenericEntry* pEntry)
{
fPathName = pEntry->GetPathName();
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 != nil);
pCombo->InitStorage(256, 256 * 8);
for (int type = 0; type < 256; type++) {
const char* str;
char buf[10];
if (fAllowedTypes == kAllowedPascal) {
/* not the most efficient way, but it'll do */
for (int j = 0; j < NELEM(kPascalTypes); j++) {
if (kPascalTypes[j] == type)
break;
}
if (j == NELEM(kPascalTypes))
continue;
} else if (fAllowedTypes == kAllowedDOS) {
for (int j = 0; j < NELEM(kDOSTypes); j++) {
if (kDOSTypes[j] == type)
break;
}
if (j == NELEM(kDOSTypes))
continue;
}
str = PathProposal::FileTypeString(type);
if (str[0] == '$')
sprintf(buf, "??? $%02X", type);
else
sprintf(buf, "%s $%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("???");
pCombo->SetCurSel(comboIdx);
pCombo->SetItemData(comboIdx, 256);
}
}
CString dateStr;
pWnd = GetDlgItem(IDC_PROPS_CREATEWHEN);
ASSERT(pWnd != nil);
FormatDate(fProps.createWhen, &dateStr);
pWnd->SetWindowText(dateStr);
pWnd = GetDlgItem(IDC_PROPS_MODWHEN);
ASSERT(pWnd != nil);
FormatDate(fProps.modWhen, &dateStr);
pWnd->SetWindowText(dateStr);
//WMSG2("USING DATE '%s' from 0x%08lx\n", dateStr, fProps.modWhen);
CEdit* pEdit = (CEdit*) GetDlgItem(IDC_PROPS_AUXTYPE);
ASSERT(pEdit != nil);
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();
}
/*
* Convert values.
*/
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;
appName.LoadString(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("The file and creator types must be exactly"
" 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("The AuxType field must be a valid 4-digit"
" 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;
msg.LoadString(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 = "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("%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);
}
/*
* 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.
*/
void
EditPropsDialog::OnTypeChange(void)
{
static const char* kUnknownFileType = "Unknown file type";
CComboBox* pCombo;
CWnd* pWnd;
int fileType, fileTypeIdx;
long auxType;
const char* descr = nil;
pCombo = (CComboBox*) GetDlgItem(IDC_PROPS_FILETYPE);
ASSERT(pCombo != nil);
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 == nil)
descr = kUnknownFileType;
}
pWnd = GetDlgItem(IDC_PROPS_TYPEDESCR);
ASSERT(pWnd != nil);
pWnd->SetWindowText(descr);
/* DOS aux type only applies to BIN */
if (!fReadOnly && fAllowedTypes == kAllowedDOS) {
pWnd = GetDlgItem(IDC_PROPS_AUXTYPE);
pWnd->EnableWindow(fileType == kFileTypeBIN);
}
}
/*
* Called when something is typed in one of the HFS type boxes.
*/
void
EditPropsDialog::OnHFSTypeChange(void)
{
assert(fAllowedTypes == kAllowedHFS);
}
/*
* Called initially and when switching modes.
*/
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 */
WMSG0("Switching to HFS mode\n");
//fHFSMode = true;
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 != nil);
pWnd->SetWindowText("(HFS type)");
OnHFSTypeChange();
} else {
/* switch to ProDOS mode */
WMSG0("Switching to ProDOS mode\n");
//fHFSMode = false;
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();
}
}
/*
* 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
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);
}
/*
* Get the aux type.
*
* Returns -1 if something was wrong with the string (e.g. empty or has
* invalid chars).
*/
long
EditPropsDialog::GetAuxType(void)
{
CWnd* pWnd = GetDlgItem(IDC_PROPS_AUXTYPE);
ASSERT(pWnd != nil);
CString aux;
pWnd->GetWindowText(aux);
const char* str = aux;
char* end;
long val;
if (str[0] == '\0') {
WMSG0(" HEY: blank aux type, returning -1\n");
return -1;
}
val = strtoul(aux, &end, 16);
if (end != str + strlen(str)) {
WMSG1(" HEY: found some garbage in aux type '%s', returning -1\n",
(LPCTSTR) aux);
return -1;
}
return val;
}
/*
* Context help request (question mark button).
*/
BOOL
EditPropsDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* User pressed the "Help" button.
*/
void
EditPropsDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_EDIT_PROPS, HELP_CONTEXT);
}

88
app/EditPropsDialog.h Normal file
View File

@ -0,0 +1,88 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Edit file properties.
*/
#ifndef __EDITPROPSDIALOG__
#define __EDITPROPSDIALOG__
#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:
// overrides
virtual BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg void OnTypeChange(void);
afx_msg void OnHFSTypeChange(void);
afx_msg void OnHelp(void);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
void UpdateSimpleAccess(void);
void UpdateHFSMode(void);
long GetAuxType(void);
//void ShowHFSType(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 /*__EDITPROPSDIALOG__*/

211
app/EnterRegDialog.cpp Normal file
View File

@ -0,0 +1,211 @@
/*
* 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 != nil);
//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 != nil);
pEdit->SetLimitText(120);
pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_COMPANY);
ASSERT(pEdit != nil);
pEdit->SetLimitText(120);
pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_REG);
ASSERT(pEdit != nil);
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))
{
WMSG3("Correct key entered: '%s' '%s' '%s'\n",
(LPCTSTR)fUserName, (LPCTSTR)fCompanyName, (LPCTSTR)fRegKey);
} else {
WMSG0("Incorrect key entered, rejecting\n");
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();
}
}
/*
* 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
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 != nil);
pEdit->GetWindowText(userStr);
unsigned short crc;
crc = gMyApp.fRegistry.ComputeStringCRC(userStr);
userStr.Format("%04X", crc);
pWnd = GetDlgItem(crcID);
ASSERT(pWnd != nil);
pWnd->SetWindowText(userStr);
/*
* Update the OK button.
*/
pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_USER);
ASSERT(pEdit != nil);
pEdit->GetWindowText(userStr);
pEdit = (CEdit*) GetDlgItem(IDC_REGENTER_REG);
ASSERT(pEdit != nil);
pEdit->GetWindowText(regStr);
pWnd = GetDlgItem(IDOK);
ASSERT(pWnd != nil);
pWnd->EnableWindow(!userStr.IsEmpty() && !regStr.IsEmpty());
}
/*
* Handle changes in the three edit fields.
*/
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);
}
/*
* User pressed the "Help" button.
*/
void
EnterRegDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_ENTER_REG_DATA, HELP_CONTEXT);
}
/*
* 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
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*/

50
app/EnterRegDialog.h Normal file
View File

@ -0,0 +1,50 @@
/*
* 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 __ENTERREGDIALOG__
#define __ENTERREGDIALOG__
#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.
*/
class EnterRegDialog : public CDialog {
public:
EnterRegDialog(CWnd* pParent = nil) : CDialog(IDD_REGISTRATION, pParent)
{ fDepth = 0; }
virtual ~EnterRegDialog(void) {}
CString fUserName;
CString fCompanyName;
CString fRegKey;
static int GetRegInfo(CWnd* pWnd);
private:
// overrides
virtual BOOL OnInitDialog(void);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg void OnUserChange(void);
afx_msg void OnCompanyChange(void);
afx_msg void OnRegChange(void);
afx_msg void OnHelp(void);
void HandleEditChange(int editID, int crcID);
MyEdit fMyEdit;
int fDepth;
DECLARE_MESSAGE_MAP()
};
#endif /*__ENTERREGDIALOG__*/

View File

@ -0,0 +1,208 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Support for ExtractOptionsDialog.
*/
#include "stdafx.h"
#include "ExtractOptionsDialog.h"
#include "HelpTopics.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 != nil);
/* set the count string using a string table entry */
if (fSelectedCount == 1) {
countFmt.LoadString(IDS_EXT_SELECTED_COUNT);
pWnd->SetWindowText(countFmt);
} else {
countFmt.LoadString(IDS_EXT_SELECTED_COUNTS_FMT);
selStr.Format((LPCTSTR) countFmt, fSelectedCount);
pWnd->SetWindowText(selStr);
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
}
/*
* Convert values.
*
* Should probably verify that fFilesToExtract is not set to kExtractSelection
* when fSelectedCount is zero.
*/
void
ExtractOptionsDialog::DoDataExchange(CDataExchange* 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);
}
/*
* Reconfigure controls for best preservation of Apple II formats.
*/
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();
}
/*
* Reconfigure controls for easiest viewing under Windows.
*/
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();
}
/*
* Enable or disable the "Convert high ASCII" button based on the current
* setting of the radio button above it.
*/
void
ExtractOptionsDialog::OnChangeTextConv(void)
{
CButton* pButton = (CButton*) GetDlgItem(IDC_EXT_CONVEOLNONE);
ASSERT(pButton != nil);
bool convDisabled = (pButton->GetCheck() == BST_CHECKED);
CWnd* pWnd = GetDlgItem(IDC_EXT_CONVHIGHASCII);
ASSERT(pWnd != nil);
pWnd->EnableWindow(!convDisabled);
}
/*
* They want to choose the folder from a tree.
*/
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 != nil);
pEditWnd->GetWindowText(editPath);
chooseDir.SetPathName(editPath);
if (chooseDir.DoModal() == IDOK) {
const char* ccp = chooseDir.GetPathName();
WMSG1("New extract path chosen = '%s'\n", ccp);
pEditWnd->SetWindowText(ccp);
}
}
/*
* Context help request (question mark button).
*/
BOOL
ExtractOptionsDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp((DWORD) lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // yes, we handled it
}
/*
* User pressed the "Help" button.
*/
void
ExtractOptionsDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_EXT_OPTIONS, HELP_CONTEXT);
}

View File

@ -0,0 +1,87 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Choose options related to file extraction.
*/
#ifndef __EXTRACT_OPTIONS_DIALOG__
#define __EXTRACT_OPTIONS_DIALOG__
#include "../util/UtilLib.h"
#include "resource.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) {
//WMSG0("~ExtractOptionsDialog()\n");
}
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);
virtual void DoDataExchange(CDataExchange* pDX);
afx_msg void OnConfigPreserve(void);
afx_msg void OnConfigConvert(void);
afx_msg void OnChangeTextConv(void);
afx_msg void OnChooseFolder(void);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg void OnHelp(void);
MyBitmapButton fChooseFolderButton;
int fSelectedCount;
DECLARE_MESSAGE_MAP()
};
#endif /*__EXTRACT_OPTIONS_DIALOG__*/

1421
app/FileNameConv.cpp Normal file

File diff suppressed because it is too large Load Diff

139
app/FileNameConv.h Normal file
View File

@ -0,0 +1,139 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* File name conversion.
*/
#ifndef __FILENAMECONV__
#define __FILENAMECONV__
#include "GenericArchive.h"
#define kUnknownTypeStr "???"
/*
* 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 = ":BOGUS:";
fStoredFssep = '[';
fFileType = 256;
fAuxType = 65536;
fThreadKind = 0;
fLocalPathName = ":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) {
fStoredPathName = pEntry->GetPathName();
fStoredFssep = pEntry->GetFssep();
//if (fStoredFssep == '\0') // e.g. embedded DOS 3.3 volume
// fStoredFssep = kDefaultStoredFssep;
fFileType = pEntry->GetFileType();
fAuxType = pEntry->GetAuxType();
//fThreadKind set from SelectionEntry
// reset the "output" fields
fLocalPathName = ":HOSED:";
fLocalFssep = ']';
// I expect these to be as-yet unset; check it
ASSERT(!fPreservation);
ASSERT(!fAddExtension);
ASSERT(!fJunkPaths);
}
// init the "add to archive" side
void Init(const char* localPathName) {
//ASSERT(basePathName[strlen(basePathName)-1] != kLocalFssep);
//fLocalPathName = localPathName + strlen(basePathName)+1;
fLocalPathName = localPathName;
fLocalFssep = kLocalFssep;
// reset the "output" fields
fStoredPathName = ":HOSED:";
fStoredFssep = '[';
fFileType = 0;
fAuxType = 0;
fThreadKind = GenericEntry::kDataThread;
// I expect these to be as-yet unset; check it
ASSERT(!fPreservation);
ASSERT(!fAddExtension);
ASSERT(!fJunkPaths);
}
// Convert a partial pathname from the archive to a local partial path.
void ArchiveToLocal(void);
// Same thing, other direction.
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
unsigned long fFileType;
unsigned long 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;
/*
* Misc utility functions.
*/
static const char* FileTypeString(unsigned long fileType);
static const char* FileTypeDescription(long fileType, long auxType);
private:
void Win32NormalizeFileName(const char* srcp, long srcLen,
char fssep, char** pDstp, long dstLen);
void NormalizeFileName(const char* srcp, long srcLen,
char fssep, char** pDstp, long dstLen);
void NormalizeDirectoryName(const char* srcp, long srcLen,
char fssep, char** pDstp, long dstLen);
void AddPreservationString(const char* pathBuf, char* extBuf);
void AddTypeExtension(const char* pathBuf, char* extBuf);
void ReplaceFssep(char* str, char oldc, char newc, char newSubst);
void LookupExtension(const char* ext);
bool ExtractPreservationString(char* pathName);
void InterpretExtension(const char* pathName);
void DenormalizePath(char* pathBuf);
void StripDiskImageSuffix(char* pathName);
};
#endif /*__FILENAMECONV__*/

1362
app/GenericArchive.cpp Normal file

File diff suppressed because it is too large Load Diff

690
app/GenericArchive.h Normal file
View File

@ -0,0 +1,690 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Generic Apple II archive handling.
*
* These are abstract base classes.
*/
#ifndef __GENERIC_ARCHIVE__
#define __GENERIC_ARCHIVE__
#include "Preferences.h"
#include "../util/UtilLib.h"
#include "../diskimg/DiskImg.h"
#include "../prebuilt/NufxLib.h"
#include "../reformat/Reformat.h"
#include <time.h>
#include <string.h>
// this shouldn't be in a header file, but in here it's probably okay
using namespace DiskImgLib;
class ActionProgressDialog;
class AddFilesDialog;
class RecompressOptionsDialog;
class SelectionSet;
struct Win32dirent;
class GenericArchive;
const int kFileTypeTXT = 0x04;
const int kFileTypeBIN = 0x06;
const int kFileTypeSRC = 0xb0;
const int kFileTypeINT = 0xfa;
const int kFileTypeBAS = 0xfc;
/*
* Set of data allowed in file property "set file info" calls.
*/
typedef struct FileProps {
unsigned long fileType;
unsigned long auxType;
unsigned long access;
time_t createWhen;
time_t modWhen;
} FileProps;
/*
* Options for converting between file archives and disk archives.
*/
class XferFileOptions {
public:
XferFileOptions(void) :
fTarget(nil), fPreserveEmptyFolders(false), fpTargetFS(nil)
{}
~XferFileOptions(void) {}
/* where the stuff is going */
GenericArchive* fTarget;
/* these really only have meaning when converting disk to file */
//bool fConvDOSText;
//bool fConvPascalText;
bool fPreserveEmptyFolders;
/* only useful when converting files to a disk image */
//CString fStoragePrefix;
DiskImgLib::DiskFS* fpTargetFS;
};
/*
* Generic description of an Apple II file.
*
* Everything returned by the basic "get" calls for display in the ContentList
* must be held in local storage (i.e. not just pointers into DiskFS data).
* Otherwise, we run the risk of doing some DiskFS updates and having weird
* things happen in the ContentList.
*/
class GenericEntry {
public:
GenericEntry(void);
virtual ~GenericEntry(void);
/* kinds of files found in archives */
enum RecordKind {
kRecordKindUnknown = 0,
kRecordKindDisk,
kRecordKindFile,
kRecordKindForkedFile,
kRecordKindDirectory,
kRecordKindVolumeDir,
};
/*
* Threads we will view or extract (threadMask). This is no longer used
* for viewing files, but still plays a role when extracting.
*/
enum {
// create one entry for each matching thread
kDataThread = 0x01,
kRsrcThread = 0x02,
kDiskImageThread = 0x04,
kCommentThread = 0x08,
// grab any of the above threads
kAnyThread = 0x10,
// set this if we allow matches on directory entries
kAllowDirectory = 0x20,
// and volume directory entries
kAllowVolumeDir = 0x40,
// set to include "damaged" files
kAllowDamaged = 0x80,
};
/* EOL conversion mode for threads being extracted */
typedef enum ConvertEOL {
kConvertUnknown = 0, kConvertEOLOff, kConvertEOLOn, kConvertEOLAuto
} ConvertEOL;
typedef enum EOLType {
kEOLUnknown = 0, kEOLCR, kEOLLF, kEOLCRLF
};
/* high ASCII conversion mode for threads being extracted */
typedef enum ConvertHighASCII {
kConvertHAUnknown = 0, kConvertHAOff, kConvertHAOn, kConvertHAAuto
} ConvertHighASCII;
/* ProDOS access flags, used for all filesystems */
enum {
kAccessRead = 0x01,
kAccessWrite = 0x02,
kAccessInvisible = 0x04,
kAccessBackup = 0x20,
kAccessRename = 0x40,
kAccessDelete = 0x80
};
/* features supported by underlying archive */
typedef enum Feature {
kFeatureCanChangeType,
kFeaturePascalTypes,
kFeatureDOSTypes,
kFeatureHFSTypes,
kFeatureHasFullAccess,
kFeatureHasSimpleAccess, // mutually exclusive with FullAccess
kFeatureHasInvisibleFlag,
} Feature;
// retrieve thread (or filesystem) data
virtual int ExtractThreadToBuffer(int which, char** ppText, long* pLength,
CString* pErrMsg) const = 0;
virtual int ExtractThreadToFile(int which, FILE* outfp, ConvertEOL conv,
ConvertHighASCII convHA, CString* pErrMsg) const = 0;
// This helps us retain the ContentList selection across a Reload(). Only
// necessary for read-write archives, since those are the only ones that
// ever need to be reloaded. Value must be nonzero to be used.
virtual long GetSelectionSerial(void) const = 0;
/* are we allowed to change the file/aux type of this entry? */
/* (may need to generalize this to "changeable attrs" bitmask) */
virtual bool GetFeatureFlag(Feature feature) const = 0;
long GetIndex(void) const { return fIndex; }
void SetIndex(long idx) { fIndex = idx; }
const char* GetPathName(void) const { return fPathName; }
void SetPathName(const char* path);
const char* GetFileName(void);
const char* GetFileNameExtension(void); // returns e.g. ".SHK"
void SetSubVolName(const char* name);
const char* GetSubVolName(void) const { return fSubVolName; }
const char* GetDisplayName(void) const; // not really "const"
char GetFssep(void) const { return fFssep; }
void SetFssep(char fssep) { fFssep = fssep; }
long GetFileType(void) const { return fFileType; }
void SetFileType(long type) { fFileType = type; }
long GetAuxType(void) const { return fAuxType; }
void SetAuxType(long type) { fAuxType = type; }
long GetAccess(void) const { return fAccess; }
void SetAccess(long access) { fAccess = access; }
time_t GetCreateWhen(void) const { return fCreateWhen; }
void SetCreateWhen(time_t when) { fCreateWhen = when; }
time_t GetModWhen(void) const { return fModWhen; }
void SetModWhen(time_t when) { fModWhen = when; }
RecordKind GetRecordKind(void) const { return fRecordKind; }
void SetRecordKind(RecordKind recordKind) { fRecordKind = recordKind; }
const char* GetFormatStr(void) const { return fFormatStr; }
void SetFormatStr(const char* str) { fFormatStr = str; } // arg not copied, must be static!
LONGLONG GetCompressedLen(void) const { return fCompressedLen; }
void SetCompressedLen(LONGLONG len) { fCompressedLen = len; }
LONGLONG GetUncompressedLen(void) const {
return fDataForkLen + fRsrcForkLen;
}
//void SetUncompressedLen(LONGLONG len) { fUncompressedLen = len; }
LONGLONG GetDataForkLen(void) const { return fDataForkLen; }
void SetDataForkLen(LONGLONG len) { fDataForkLen = len; }
LONGLONG GetRsrcForkLen(void) const { return fRsrcForkLen; }
void SetRsrcForkLen(LONGLONG len) { fRsrcForkLen = len; }
DiskImg::FSFormat GetSourceFS(void) const { return fSourceFS; }
void SetSourceFS(DiskImg::FSFormat fmt) { fSourceFS = fmt; }
bool GetHasDataFork(void) const { return fHasDataFork; }
void SetHasDataFork(bool val) { fHasDataFork = val; }
bool GetHasRsrcFork(void) const { return fHasRsrcFork; }
void SetHasRsrcFork(bool val) { fHasRsrcFork = val; }
bool GetHasDiskImage(void) const { return fHasDiskImage; }
void SetHasDiskImage(bool val) { fHasDiskImage = val; }
bool GetHasComment(void) const { return fHasComment; }
void SetHasComment(bool val) { fHasComment = val; }
bool GetHasNonEmptyComment(void) const { return fHasNonEmptyComment; }
void SetHasNonEmptyComment(bool val) { fHasNonEmptyComment = val; }
bool GetDamaged(void) const { return fDamaged; }
void SetDamaged(bool val) { fDamaged = val; }
bool GetSuspicious(void) const { return fSuspicious; }
void SetSuspicious(bool val) { fSuspicious = val; }
GenericEntry* GetPrev(void) const { return fpPrev; }
void SetPrev(GenericEntry* pEntry) { fpPrev = pEntry; }
GenericEntry* GetNext(void) const { return fpNext; }
void SetNext(GenericEntry* pEntry) { fpNext = pEntry; }
// Utility functions.
const char* GetFileTypeString(void) const;
static bool CheckHighASCII(const unsigned char* buffer,
unsigned long count);
static ConvertEOL DetermineConversion(const unsigned char* buffer,
long count, EOLType* pSourceType, ConvertHighASCII* pConvHA);
static int GenericEntry::WriteConvert(FILE* fp, const char* buf,
size_t len, ConvertEOL* pConv, ConvertHighASCII* pConvHA,
bool* pLastCR);
protected:
static void SpacesToUnderscores(char* buf);
private:
char* fPathName;
const char* fFileName; // points within fPathName
const char* fFileNameExtension; // points within fPathName
char fFssep;
char* fSubVolName; // sub-volume prefix, or nil if none
char* fDisplayName; // combination of sub-vol and path
long fFileType;
long fAuxType;
long fAccess;
time_t fCreateWhen;
time_t fModWhen;
RecordKind fRecordKind; // forked file, disk image, ??
const char* fFormatStr; // static str; compression or fs format
//LONGLONG fUncompressedLen;
LONGLONG fDataForkLen; // also for disk images
LONGLONG fRsrcForkLen; // set to 0 when nonexistent
LONGLONG fCompressedLen; // data/disk + rsrc
DiskImg::FSFormat fSourceFS; // if DOS3.3, text files have funky format
bool fHasDataFork;
bool fHasRsrcFork;
bool fHasDiskImage;
bool fHasComment;
bool fHasNonEmptyComment; // set if fHasComment and it isn't empty
bool fDamaged; // if set, don't try to open file
bool fSuspicious; // if set, file *might* be damaged
long fIndex; // serial index, for sorting "unsorted" view
GenericEntry* fpPrev;
GenericEntry* fpNext;
};
/*
* Generic representation of a collection of Apple II files.
*
* This raises the "reload" flag whenever its data is reloaded. Any code that
* keeps pointers to stuff (e.g. local copies of pointers to GenericEntry
* objects) needs to check the "reload" flag before dereferencing them.
*/
class GenericArchive {
public:
GenericArchive(void) {
fPathName = nil;
fNumEntries = 0;
fEntryHead = fEntryTail = nil;
fReloadFlag = true;
//fEntryIndex = nil;
}
virtual ~GenericArchive(void) {
//WMSG0("Deleting GenericArchive\n");
DeleteEntries();
delete fPathName;
}
virtual GenericEntry* GetEntries(void) const {
return fEntryHead;
}
virtual long GetNumEntries(void) const {
return fNumEntries;
}
//virtual GenericEntry* GetEntry(long num) {
// ASSERT(num >= 0 && num < fNumEntries);
// if (fEntryIndex == nil)
// CreateIndex();
// return fEntryIndex[num];
//}
typedef enum {
kResultUnknown = 0,
kResultSuccess, // open succeeded
kResultFailure, // open failed
kResultCancel, // open was cancelled by user
kResultFileArchive, // found a file archive rather than disk image
} OpenResult;
// Open an archive and do fun things with the innards.
virtual OpenResult Open(const char* filename, bool readOnly,
CString* pErrMsg) = 0;
// Create a new archive with the specified name.
virtual CString New(const char* filename, const void* options) = 0;
// Flush any unwritten data to disk
virtual CString Flush(void) = 0;
// Force a re-read from the underlying storage.
virtual CString Reload(void) = 0;
// Do we allow modification?
virtual bool IsReadOnly(void) const = 0;
// Does the underlying storage have un-flushed modifications?
virtual bool IsModified(void) const = 0;
virtual bool GetReloadFlag(void) { return fReloadFlag; }
virtual void ClearReloadFlag(void) { fReloadFlag = false; }
// One of these for every sub-class. This is used to ensure that, should
// we need to down-cast an object, we did it correctly (without needing
// to include RTTI support).
typedef enum {
kArchiveUnknown = 0,
kArchiveNuFX,
kArchiveBNY,
kArchiveACU,
kArchiveDiskImage,
} ArchiveKind;
virtual ArchiveKind GetArchiveKind(void) = 0;
// Get a nice description for the title bar.
virtual void GetDescription(CString* pStr) const = 0;
// Do a bulk add.
virtual bool BulkAdd(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts) = 0;
// Do a disk add.
virtual bool AddDisk(ActionProgressDialog* pActionProgress,
const AddFilesDialog* pAddOpts) = 0;
// Create a subdirectory.
virtual bool CreateSubdir(CWnd* pMsgWnd, GenericEntry* pParentEntry,
const char* newName) = 0;
// Test a set of files.
virtual bool TestSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0;
// Delete a set of files.
virtual bool DeleteSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0;
// Rename a set of files.
virtual bool RenameSelection(CWnd* pMsgWnd, SelectionSet* pSelSet) = 0;
virtual CString TestPathName(const GenericEntry* pGenericEntry,
const CString& basePath, const CString& newName, char newFssep) const = 0;
// Rename a volume (or sub-volume)
virtual bool RenameVolume(CWnd* pMsgWnd, DiskFS* pDiskFS,
const char* newName) = 0;
virtual CString TestVolumeName(const DiskFS* pDiskFS,
const char* newName) const = 0;
// Recompress a set of files.
virtual bool RecompressSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
const RecompressOptionsDialog* pRecompOpts) = 0;
// Transfer files out of this archive and into another.
typedef enum {
kXferOK = 0, kXferFailed = 1, kXferCancelled = 2, kXferOutOfSpace = 3
} XferStatus;
virtual XferStatus XferSelection(CWnd* pMsgWnd, SelectionSet* pSelSet,
ActionProgressDialog* pActionProgress,
const XferFileOptions* pXferOpts) = 0;
// Get, set, or delete the comment on an entry.
virtual bool GetComment(CWnd* pMsgWnd, const GenericEntry* pEntry,
CString* pStr) = 0;
virtual bool SetComment(CWnd* pMsgWnd, GenericEntry* pEntry,
const CString& str) = 0;
virtual bool DeleteComment(CWnd* pMsgWnd, GenericEntry* pEntry) = 0;
// Set ProDOS file properties (e.g. file type, access flags).
virtual bool SetProps(CWnd* pMsgWnd, GenericEntry* pEntry,
const FileProps* pProps) = 0;
// Preferences have changed, update library state as needed.
virtual void PreferencesChanged(void) = 0;
// Determine an archive's capabilities. This is specific to the object
// instance, so this must not be made a static function.
typedef enum {
kCapUnknown = 0,
kCapCanTest, // NuFX, BNY
kCapCanRenameFullPath, // NuFX, BNY
kCapCanRecompress, // NuFX, BNY
kCapCanEditComment, // NuFX
kCapCanAddDisk, // NuFX
kCapCanConvEOLOnAdd, // Disk
kCapCanCreateSubdir, // Disk
kCapCanRenameVolume, // Disk
} Capability;
virtual long GetCapability(Capability cap) = 0;
// Get the pathname of the file we opened.
const char* GetPathName(void) const { return fPathName; }
// Generic utility function.
static CString GenDerivedTempName(const char* filename);
static int ComparePaths(const CString& name1, char fssep1,
const CString& name2, char fssep2);
void AddEntry(GenericEntry* pEntry);
/*
* This class holds details about a file that we're adding.
*
* It's based on the NuFileDetails class from NufxLib (which used to be
* used everywhere).
*/
class FileDetails {
public:
FileDetails(void);
virtual ~FileDetails(void) {}
/*
* Automatic cast to NuFileDetails. The NuFileDetails structure will
* have a pointer to at least one of our strings, so structures
* filled out this way need to be short-lived. (Yes, this is
* annoying, but it's how NufxLib works.)
*/
operator const NuFileDetails() const;
/*
* Provide operator= and copy constructor. This'd be easier without
* the strings.
*/
FileDetails& operator=(const FileDetails& src) {
if (&src != this)
CopyFields(this, &src);
return *this;
}
FileDetails(const FileDetails& src) {
CopyFields(this, &src);
}
/*
* What kind of file this is. Files being added to NuFX from Windows
* can't be "BothForks" because each the forks are stored in
* separate files. However, files being transferred from a NuFX
* archive, a disk image, or in from the clipboard can be both.
*
* (NOTE: this gets embedded into clipboard data. If you change
* these values, update the version info in Clipboard.cpp.)
*/
typedef enum FileKind {
kFileKindUnknown = 0,
kFileKindDataFork,
kFileKindRsrcFork,
kFileKindDiskImage,
kFileKindBothForks,
kFileKindDirectory,
} FileKind;
/*
* Data fields. While transitioning from general use of NuFileDetails
* (v1.2.x to v2.0) I'm just going to leave these public.
*/
//NuThreadID threadID; /* data, rsrc, disk img? */
FileKind entryKind;
CString origName;
CString storageName; /* normalized (NOT FS-normalized) */
//NuFileSysID fileSysID;
DiskImg::FSFormat fileSysFmt;
unsigned short fileSysInfo; /* fssep lurks here */
unsigned long access;
unsigned long fileType;
unsigned long extraType;
unsigned short storageType; /* "Unknown" or disk block size */
NuDateTime createWhen;
NuDateTime modWhen;
NuDateTime archiveWhen;
private:
static void CopyFields(FileDetails* pDst, const FileDetails* pSrc);
};
// Transfer files, one at a time, into this archive from another.
virtual void XferPrepare(const XferFileOptions* pXferOpts) = 0;
virtual CString XferFile(FileDetails* pDetails, unsigned char** pDataBuf,
long dataLen, unsigned char** pRsrcBuf, long rsrcLen) = 0;
virtual void XferAbort(CWnd* pMsgWnd) = 0;
virtual void XferFinish(CWnd* pMsgWnd) = 0;
static void UNIXTimeToDateTime(const time_t* pWhen, NuDateTime *pDateTime);
protected:
virtual void DeleteEntries(void);
/* NuLib2-derived recursive directory add functions */
void ReplaceFssep(char* str, char oldc, char newc, char newSubst);
NuError GetFileDetails(const AddFilesDialog* pAddOpts, const char* pathname,
struct stat* psb, FileDetails* pDetails);
Win32dirent* OpenDir(const char* name);
Win32dirent* ReadDir(Win32dirent* dir);
void CloseDir(Win32dirent* dir);
NuError Win32AddDirectory(const AddFilesDialog* pAddOpts,
const char* dirName, CString* pErrMsg);
NuError Win32AddFile(const AddFilesDialog* pAddOpts,
const char* pathname, CString* pErrMsg);
NuError AddFile(const AddFilesDialog* pAddOpts, const char* pathname,
CString* pErrMsg);
/*
* Each implementation must provide this. It's called from the generic
* AddFile function with the high-level add options, a partial pathname,
* and a FileDetails structure filled in using Win32 calls.
*
* One call to AddFile can result in multiple calls to DoAddFile if
* the subject of the AddFile call is a directory (and fIncludeSubdirs
* is set).
*
* DoAddFile is not called for subdirectories. The underlying code must
* create directories as needed.
*
* In some cases (such as renaming a file as it is being added) the
* information in "*pDetails" may be modified.
*/
virtual NuError DoAddFile(const AddFilesDialog* pAddOpts,
FileDetails* pDetails) = 0;
void SetPathName(const char* pathName) {
delete fPathName;
if (pathName != nil) {
fPathName = new char[strlen(pathName)+1];
strcpy(fPathName, pathName);
} else
fPathName = nil;
}
bool fReloadFlag; // set after Reload called
private:
//virtual void CreateIndex(void);
//CString fNewPathHolder;
//CString fOrigPathHolder;
char* fPathName;
long fNumEntries;
GenericEntry* fEntryHead;
GenericEntry* fEntryTail;
//GenericEntry** fEntryIndex;
};
/*
* One entry in a SelectionSet.
*/
class SelectionEntry {
public:
SelectionEntry(GenericEntry* pEntry) {
fpEntry = pEntry;
//fThreadKind = threadKind;
//fFilter = filter;
//fReformatName = "";
fpPrev = fpNext = nil;
}
~SelectionEntry(void) {}
int Reformat(ReformatHolder* pHolder);
GenericEntry* GetEntry(void) const { return fpEntry; }
//int GetThreadKind(void) const { return fThreadKind; }
//int GetFilter(void) const { return fFilter; }
//const char* GetReformatName(void) const { return fReformatName; }
SelectionEntry* GetPrev(void) const { return fpPrev; }
void SetPrev(SelectionEntry* pPrev) { fpPrev = pPrev; }
SelectionEntry* GetNext(void) const { return fpNext; }
void SetNext(SelectionEntry* pNext) { fpNext = pNext; }
private:
GenericEntry* fpEntry;
//int fThreadKind; // data, rsrc, etc (threadMask)
//int fFilter; // fAllowedFilters, really
//const char* fReformatName; // name of formatting actually applied
SelectionEntry* fpPrev;
SelectionEntry* fpNext;
};
class ContentList;
/*
* A set of selected files.
*
* Each entry represents one item that can be displayed, such as a data
* fork, resource fork, or comment thread. Thus, a single file may have
* multiple entries in the set.
*/
class SelectionSet {
public:
SelectionSet(void) {
fNumEntries = 0;
fEntryHead = fEntryTail = fIterCurrent = nil;
}
~SelectionSet(void) {
DeleteEntries();
}
// create the set from the selected members of a ContentList
void CreateFromSelection(ContentList* pContentList, int threadMask);
// create the set from all members of a ContentList
void CreateFromAll(ContentList* pContentList, int threadMask);
// get the head of the list
SelectionEntry* GetEntries(void) const { return fEntryHead; }
void IterReset(void) {
fIterCurrent = nil;
}
// move to the next or previous entry as part of iterating
SelectionEntry* IterPrev(void) {
if (fIterCurrent == nil)
fIterCurrent = fEntryTail;
else
fIterCurrent = fIterCurrent->GetPrev();
return fIterCurrent;
}
SelectionEntry* IterNext(void) {
if (fIterCurrent == nil)
fIterCurrent = fEntryHead;
else
fIterCurrent = fIterCurrent->GetNext();
return fIterCurrent;
}
SelectionEntry* IterCurrent(void) {
return fIterCurrent;
}
bool IterHasPrev(void) const {
if (fIterCurrent == nil)
return fEntryTail != nil;
else
return (fIterCurrent->GetPrev() != nil);
}
bool IterHasNext(void) const {
if (fIterCurrent == nil)
return fEntryHead != nil;
else
return (fIterCurrent->GetNext() != nil);
}
int GetNumEntries(void) const { return fNumEntries; }
// count the #of entries whose display name matches "prefix"
int CountMatchingPrefix(const char* prefix);
// debug dump
void Dump(void);
private:
void AddToSet(GenericEntry* pEntry, int threadMask);
void AddEntry(SelectionEntry* pEntry);
void DeleteEntries(void);
int fNumEntries;
SelectionEntry* fIterCurrent;
SelectionEntry* fEntryHead;
SelectionEntry* fEntryTail;
};
#endif /*__GENERIC_ARCHIVE__*/

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

BIN
app/Graphics/CiderPress.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
app/Graphics/FileViewer.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
app/Graphics/NewFolder.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

BIN
app/Graphics/binary2.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
app/Graphics/diskimage.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
app/Graphics/fslogo.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
app/Graphics/hdrbar.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

BIN
app/Graphics/list-pics.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 B

BIN
app/Graphics/nufx.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
app/Graphics/toolbar1.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
app/Graphics/tree_pics.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
app/Graphics/vol_pics.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 374 B

BIN
app/Help/CIDERPRESS.HLP Normal file

Binary file not shown.

56
app/Help/CiderPress.cnt Normal file
View File

@ -0,0 +1,56 @@
:Base CiderPress.hlp>main
:Title CiderPress Help
1 Introduction
2 Welcome!=Topic1
2 Features=Topic47
2 Getting Help=Topic284
2 How (and Why) to Register=Topic46
2 Credits=Topic14
1 Using CiderPress
2 Available Commands=Topic48
2 Commands
3 Selecting Commands=Topic52
3 Opening, Closing, and Creating Files=Topic51
3 Opening a Volume=Topic241
3 Archive Info=Topic258
3 Working With the File List=Topic50
3 Printing=Topic53
3 Viewing Files=Topic54
3 Adding Files and Disks=Topic55
3 Creating Subdirectories=Topic263
3 Extracting Files=Topic39
3 Copying and Pasting=Topic273
3 Testing Archives=Topic56
3 Rename Entries=Topic42
3 Delete Entries=Topic57
3 Re-Compress Entries=Topic58
3 Edit Comment=Topic43
3 Edit File Attributes=Topic203
3 Convert Disk Image to File Archive=Topic215
3 Convert File Archive to Disk Image=Topic216
3 Import from Cassette=Topic111
3 Import BASIC Program=Topic112
2 Tools
3 Disk Sector Viewer=Topic3
3 Disk Image Converter=Topic187
3 Bulk Disk Image Converter=Topic233
3 SST Image Merge=Topic201
3 Windows Volume Copier=Topic245
3 EOL Scanner=Topic272
3 2MG Properties Editor=Topic277
2 Preferences
3 General Preferences=Topic19
3 Disk Image Preferences=Topic259
3 Compression Preferences=Topic29
3 File Viewer Preferences=Topic23
3 File Preferences=Topic28
1 Appendix
2 About Disk Images=Topic18
2 Embedded DOS Volumes=Topic21
2 File Format Converters=Topic22
2 Disassembly Notes=Topic109
2 File Extensions=Topic45
2 File Attribute Preservation=Topic68
2 Compression Algorithms=Topic69
2 About Removable Media (CF, floppy, CD-ROM)=Topic244
2 Administrator Privileges=Topic262

BIN
app/Help/CiderPress.hmp Normal file

Binary file not shown.

43
app/HelpTopics.h Normal file
View File

@ -0,0 +1,43 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Constants for help topics.
*/
#ifndef __HELP_TOPICS__
#define __HELP_TOPICS__
#define HELP_TOPIC_WELCOME 10
#define HELP_TOPIC_DISKEDIT 13
#define HELP_TOPIC_PREFS_GENERAL 19
#define HELP_TOPIC_DISK_IMAGES 20
#define HELP_TOPIC_PREFS_FVIEW 23
#define HELP_TOPIC_CREDITS 24
#define HELP_TOPIC_FILE_VIEWER 25
#define HELP_TOPIC_PREFS_FILES 28
#define HELP_TOPIC_PREFS_COMPRESSION 29
#define HELP_TOPIC_CHOOSE_FOLDER 38
#define HELP_TOPIC_EXT_OPTIONS 39
#define HELP_TOPIC_ADD_FILES_DLG 41
#define HELP_TOPIC_RENAME_ENTRY 42
#define HELP_TOPIC_EDIT_COMMENT 43
#define HELP_TOPIC_EDIT_ASSOC 44
#define HELP_TOPIC_ORDERING_INFO 46
#define HELP_TOPIC_ENTER_REG_DATA 49
#define HELP_TOPIC_IMPORT_CASSETTE 111
#define HELP_TOPIC_IMPORT_BASIC 112
#define HELP_TOPIC_DISK_CONV 187
#define HELP_TOPIC_EDIT_PROPS 203
#define HELP_TOPIC_BULK_DISK_CONV 233
#define HELP_TOPIC_OPEN_VOLUME 241
#define HELP_TOPIC_VOLUME_COPIER 245
#define HELP_TOPIC_IMAGE_CREATOR 247
#define HELP_TOPIC_CHOOSE_TARGET 257
#define HELP_TOPIC_ARCHIVE_INFO 258
#define HELP_TOPIC_PREFS_DISK_IMAGE 259
#define HELP_TOPIC_RENAME_VOLUME 268
#define HELP_TOPIC_EOL_SCAN 272
#endif /*__HELP_TOPICS__*/

359
app/ImageFormatDialog.cpp Normal file
View File

@ -0,0 +1,359 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Implementation of ImageFormatDialog class.
*/
#include "stdafx.h"
#include "ImageFormatDialog.h"
#include "HelpTopics.h"
#include "../diskimg/DiskImg.h"
BEGIN_MESSAGE_MAP(ImageFormatDialog, CDialog)
ON_BN_CLICKED(IDC_DECONF_HELP, OnHelp)
ON_WM_HELPINFO()
END_MESSAGE_MAP()
/*
* Conversion tables.
*
* If you add something else, remember to turn off "sort" in the drop list.
*
* The tables contain only the formats we currently support, and are in the
* order in which we want to present them to the user.
*
* THOUGHT: drop "name" from the tables, and use a DiskImg::ToString lookup
* to get the text string. That way we'd be consistent.
*/
typedef struct ImageFormatDialog::ConvTable {
int enumval; // a DiskImg::enum type
const char* name;
} ConvTable;
const int kLastEntry = -1;
/* DiskImg::OuterFormat */
static const ConvTable gOuterFormats[] = {
{ DiskImg::kOuterFormatUnknown, "Unknown format" },
{ DiskImg::kOuterFormatNone, "(none)" },
// { DiskImg::kOuterFormatCompress, "UNIX compress" },
{ DiskImg::kOuterFormatGzip, "gzip" },
// { DiskImg::kOuterFormatBzip2, "bzip2" },
{ DiskImg::kOuterFormatZip, "Zip archive" },
{ kLastEntry, nil }
};
/* DiskImg::FileFormat */
static const ConvTable gFileFormats[] = {
{ DiskImg::kFileFormatUnknown, "Unknown format" },
{ DiskImg::kFileFormatUnadorned, "Unadorned raw data" },
{ DiskImg::kFileFormat2MG, "2MG" },
{ DiskImg::kFileFormatNuFX, "NuFX (ShrinkIt)" },
{ DiskImg::kFileFormatDiskCopy42, "DiskCopy 4.2" },
// { DiskImg::kFileFormatDiskCopy60, "DiskCopy 6.0" },
// { DiskImg::kFileFormatDavex, "Davex volume image" },
{ DiskImg::kFileFormatSim2eHDV, "Sim //e HDV" },
{ DiskImg::kFileFormatDDD, "DDD" },
{ DiskImg::kFileFormatTrackStar, "TrackStar image" },
{ DiskImg::kFileFormatFDI, "FDI image" },
// { DiskImg::kFileFormatDDDDeluxe, "DDDDeluxe" },
{ kLastEntry, nil }
};
/* DiskImg::PhysicalFormat */
static const ConvTable gPhysicalFormats[] = {
{ DiskImg::kPhysicalFormatUnknown, "Unknown format" },
{ DiskImg::kPhysicalFormatSectors, "Sectors" },
{ DiskImg::kPhysicalFormatNib525_6656, "Raw nibbles (6656-byte)" },
{ DiskImg::kPhysicalFormatNib525_6384, "Raw nibbles (6384-byte)" },
{ DiskImg::kPhysicalFormatNib525_Var, "Raw nibbles (variable len)" },
{ kLastEntry, nil }
};
/* DiskImg::SectorOrder */
static const ConvTable gSectorOrders[] = {
{ DiskImg::kSectorOrderUnknown, "Unknown ordering" },
{ DiskImg::kSectorOrderProDOS, "ProDOS block ordering" },
{ DiskImg::kSectorOrderDOS, "DOS sector ordering" },
{ DiskImg::kSectorOrderCPM, "CP/M block ordering" },
{ DiskImg::kSectorOrderPhysical, "Physical sector ordering" },
{ kLastEntry, nil }
};
/* DiskImg::FSFormat */
static const ConvTable gFSFormats[] = {
{ DiskImg::kFormatUnknown, "Unknown filesystem" },
{ DiskImg::kFormatGenericDOSOrd, "Generic DOS sectors" },
{ DiskImg::kFormatGenericProDOSOrd, "Generic ProDOS blocks" },
{ DiskImg::kFormatGenericPhysicalOrd, "Generic raw sectors" },
{ DiskImg::kFormatGenericCPMOrd, "Generic CP/M blocks" },
{ DiskImg::kFormatProDOS, "ProDOS" },
{ DiskImg::kFormatDOS33, "DOS 3.3" },
{ DiskImg::kFormatDOS32, "DOS 3.2" },
{ DiskImg::kFormatPascal, "Pascal" },
{ DiskImg::kFormatMacHFS, "HFS" },
// { DiskImg::kFormatMacMFS, "MFS" },
// { DiskImg::kFormatLisa, "Lisa" },
{ DiskImg::kFormatCPM, "CP/M" },
{ DiskImg::kFormatMSDOS, "MS-DOS FAT" },
// { DiskImg::kFormatISO9660, "ISO-9660" },
{ DiskImg::kFormatUNIDOS, "UNIDOS (400K DOS x2)" },
{ DiskImg::kFormatOzDOS, "OzDOS (400K DOS x2)" },
{ DiskImg::kFormatCFFA4, "CFFA (4 or 6 partitions)" },
{ DiskImg::kFormatCFFA8, "CFFA (8 partitions)" },
{ DiskImg::kFormatMacPart, "Macintosh partitioned disk" },
{ DiskImg::kFormatMicroDrive, "MicroDrive partitioned disk" },
{ DiskImg::kFormatFocusDrive, "FocusDrive partitioned disk" },
{ DiskImg::kFormatRDOS33, "RDOS 3.3 (16-sector)" },
{ DiskImg::kFormatRDOS32, "RDOS 3.2 (13-sector)" },
{ DiskImg::kFormatRDOS3, "RDOS 3 (cracked 13-sector)" },
{ kLastEntry, nil }
};
/*
* Initialize our members by querying the associated DiskImg.
*/
void
ImageFormatDialog::InitializeValues(const DiskImg* pImg)
{
fOuterFormat = pImg->GetOuterFormat();
fFileFormat = pImg->GetFileFormat();
fPhysicalFormat = pImg->GetPhysicalFormat();
fSectorOrder = pImg->GetSectorOrder();
fFSFormat = pImg->GetFSFormat();
if (pImg->ShowAsBlocks())
fDisplayFormat = kShowAsBlocks;
else
fDisplayFormat = kShowAsSectors;
if (!pImg->GetHasBlocks() && !pImg->GetHasSectors())
fDisplayFormat = kShowAsNibbles;
fHasSectors = pImg->GetHasSectors();
fHasBlocks = pImg->GetHasBlocks();
fHasNibbles = pImg->GetHasNibbles();
// "Unknown" formats default to sectors, but sometimes it's block-only
if (fDisplayFormat == kShowAsSectors && !fHasSectors)
fDisplayFormat = kShowAsBlocks;
fInitialized = true;
}
/*
* Configure the combo boxes.
*/
BOOL
ImageFormatDialog::OnInitDialog(void)
{
ASSERT(fInitialized);
LoadComboBoxes();
return CDialog::OnInitDialog(); // do DDX/DDV
}
/*
* Load the combo boxes with every possible entry, and set the current
* value appropriately.
*
* While we're at it, initialize the "source" edit text box and the
* "show as blocks" checkbox.
*/
void
ImageFormatDialog::LoadComboBoxes(void)
{
CWnd* pWnd;
CButton* pButton;
pWnd = GetDlgItem(IDC_DECONF_SOURCE);
ASSERT(pWnd != nil);
pWnd->SetWindowText(fFileSource);
if (fQueryDisplayFormat) {
pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASSECTORS);
ASSERT(pButton != nil);
pButton->SetCheck(fDisplayFormat == kShowAsSectors);
if (!fHasSectors)
pButton->EnableWindow(FALSE);
pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASBLOCKS);
ASSERT(pButton != nil);
pButton->SetCheck(fDisplayFormat == kShowAsBlocks);
if (!fHasBlocks)
pButton->EnableWindow(FALSE);
pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASNIBBLES);
ASSERT(pButton != nil);
pButton->SetCheck(fDisplayFormat == kShowAsNibbles);
if (!fHasNibbles)
pButton->EnableWindow(FALSE);
} else {
/* if we don't need to ask, don't show the buttons */
pWnd = GetDlgItem(IDC_DECONF_VIEWAS);
pWnd->DestroyWindow();
pWnd = GetDlgItem(IDC_DECONF_VIEWASBLOCKS);
pWnd->DestroyWindow();
pWnd = GetDlgItem(IDC_DECONF_VIEWASSECTORS);
pWnd->DestroyWindow();
pWnd = GetDlgItem(IDC_DECONF_VIEWASNIBBLES);
pWnd->DestroyWindow();
}
LoadComboBox(IDC_DECONF_OUTERFORMAT, gOuterFormats, fOuterFormat);
LoadComboBox(IDC_DECONF_FILEFORMAT, gFileFormats, fFileFormat);
LoadComboBox(IDC_DECONF_PHYSICAL, gPhysicalFormats, fPhysicalFormat);
LoadComboBox(IDC_DECONF_SECTORORDER, gSectorOrders, fSectorOrder);
LoadComboBox(IDC_DECONF_FSFORMAT, gFSFormats, fFSFormat);
}
/*
* Load the strings from ConvTable into the combo box, setting the
* entry matching "default" as the current entry.
*/
void
ImageFormatDialog::LoadComboBox(int boxID, const ConvTable* pTable, int dflt)
{
CComboBox* pCombo;
// const ConvTable* pBaseTable = pTable;
int current = -1;
int idx, idxShift;
pCombo = (CComboBox*) GetDlgItem(boxID);
ASSERT(pCombo != nil);
idx = idxShift = 0;
while (pTable[idx].enumval != kLastEntry) {
/* special-case the generic FS formats */
if (pTable == gFSFormats && !fAllowGenericFormats &&
DiskImg::IsGenericFormat((DiskImg::FSFormat)pTable[idx].enumval))
{
WMSG1("LoadComboBox skipping '%s'\n", pTable[idx].name);
idxShift++;
} else {
// Note to self: AddString returns the combo box item ID;
// should probably use that instead of doing math.
pCombo->AddString(pTable[idx].name);
pCombo->SetItemData(idx - idxShift, pTable[idx].enumval);
}
if (pTable[idx].enumval == dflt)
current = idx - idxShift;
idx++;
}
if (current != -1) {
WMSG3(" Set default for %d/%d to %d\n", boxID, dflt, current);
pCombo->SetCurSel(current);
} else {
WMSG2(" No matching default for %d (%d)\n", boxID, dflt);
}
}
/*
* Find the enum value for the specified index.
*/
int
ImageFormatDialog::ConvComboSel(int boxID, const ConvTable* pTable)
{
CComboBox* pCombo;
int idx, enumval;
pCombo = (CComboBox*) GetDlgItem(boxID);
ASSERT(pCombo != nil);
idx = pCombo->GetCurSel();
if (idx < 0) {
/* nothing selected?! */
ASSERT(false);
return 0;
}
// enumval = pTable[idx].enumval;
enumval = pCombo->GetItemData(idx);
ASSERT(enumval >= 0 && enumval < 100);
if (pTable != gFSFormats) {
ASSERT(enumval == pTable[idx].enumval);
}
WMSG3(" Returning ev=%d for %d entry '%s'\n",
enumval, boxID, pTable[idx].name);
return enumval;
}
/*
* Handle the "OK" button by extracting values from the dialog and
* verifying that reasonable settings are in place.
*/
void
ImageFormatDialog::OnOK(void)
{
CButton* pButton;
if (fQueryDisplayFormat) {
pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASSECTORS);
ASSERT(pButton != nil);
if (pButton->GetCheck())
fDisplayFormat = kShowAsSectors;
pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASBLOCKS);
ASSERT(pButton != nil);
if (pButton->GetCheck())
fDisplayFormat = kShowAsBlocks;
pButton = (CButton*) GetDlgItem(IDC_DECONF_VIEWASNIBBLES);
ASSERT(pButton != nil);
if (pButton->GetCheck())
fDisplayFormat = kShowAsNibbles;
}
/* outer format, file format, and physical format are immutable */
fSectorOrder = (DiskImg::SectorOrder)
ConvComboSel(IDC_DECONF_SECTORORDER, gSectorOrders);
fFSFormat = (DiskImg::FSFormat)
ConvComboSel(IDC_DECONF_FSFORMAT, gFSFormats);
if (fSectorOrder == DiskImg::kSectorOrderUnknown) {
MessageBox("You must choose a sector ordering.", "Error",
MB_OK | MB_ICONEXCLAMATION);
return;
}
if (fFSFormat == DiskImg::kFormatUnknown &&
!fAllowUnknown)
{
MessageBox("You must choose a filesystem format. If not known,"
" use one of the 'generic' entries.",
"Error", MB_OK | MB_ICONEXCLAMATION);
return;
}
CDialog::OnOK();
}
/*
* F1 key hit, or '?' button in title bar used to select help for an
* item in the dialog.
*/
BOOL
ImageFormatDialog::OnHelpInfo(HELPINFO* lpHelpInfo)
{
WinHelp(lpHelpInfo->iCtrlId, HELP_CONTEXTPOPUP);
return TRUE; // indicate success??
}
/*
* User pressed the "Help" button.
*/
void
ImageFormatDialog::OnHelp(void)
{
WinHelp(HELP_TOPIC_DISK_IMAGES, HELP_CONTEXT);
}

81
app/ImageFormatDialog.h Normal file
View File

@ -0,0 +1,81 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Dialog asking the user to confirm certain details of a disk image.
*/
#ifndef __IMAGEFORMATDIALOG__
#define __IMAGEFORMATDIALOG__
//#include <afxwin.h>
#include "resource.h"
#include "../diskimg/DiskImg.h"
using namespace DiskImgLib;
/*
* The default values can be initialized individually or from a prepped
* DiskImg structure.
*/
class ImageFormatDialog : public CDialog {
public:
ImageFormatDialog(CWnd* pParentWnd = NULL) :
CDialog(IDD_DECONF, pParentWnd)
{
fInitialized = false;
fFileSource = "";
fAllowUnknown = false;
fOuterFormat = DiskImg::kOuterFormatUnknown;
fFileFormat = DiskImg::kFileFormatUnknown;
fPhysicalFormat = DiskImg::kPhysicalFormatUnknown;
fSectorOrder = DiskImg::kSectorOrderUnknown;
fFSFormat = DiskImg::kFormatUnknown;
fDisplayFormat = kShowAsBlocks;
fQueryDisplayFormat = true;
fAllowGenericFormats = true;
fHasSectors = fHasBlocks = fHasNibbles = false;
}
// initialize values from a DiskImg
void InitializeValues(const DiskImg* pImg);
bool fInitialized;
CString fFileSource;
bool fAllowUnknown; // allow "unknown" choice?
DiskImg::OuterFormat fOuterFormat;
DiskImg::FileFormat fFileFormat;
DiskImg::PhysicalFormat fPhysicalFormat;
DiskImg::SectorOrder fSectorOrder;
DiskImg::FSFormat fFSFormat;
enum { kShowAsBlocks=0, kShowAsSectors=1, kShowAsNibbles=2 };
int fDisplayFormat;
void SetQueryDisplayFormat(bool val) { fQueryDisplayFormat = val; }
void SetAllowGenericFormats(bool val) { fAllowGenericFormats = val; }
protected:
//virtual void DoDataExchange(CDataExchange* pDX);
virtual BOOL OnInitDialog(void);
void OnOK(void);
afx_msg virtual void OnHelp(void);
afx_msg virtual BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
struct ConvTable;
void LoadComboBoxes(void);
void LoadComboBox(int boxID, const ConvTable* pTable, int dflt);
int ConvComboSel(int boxID, const ConvTable* pTable);
bool fQueryDisplayFormat;
bool fAllowGenericFormats;
bool fHasSectors;
bool fHasBlocks;
bool fHasNibbles;
DECLARE_MESSAGE_MAP()
};
#endif /*__IMAGEFORMATDIALOG__*/

2710
app/Main.cpp Normal file

File diff suppressed because it is too large Load Diff

435
app/Main.h Normal file
View File

@ -0,0 +1,435 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Application UI classes.
*/
#ifndef __MAIN__
#define __MAIN__
#include "ContentList.h"
#include "GenericArchive.h"
#include "PrefsDialog.h"
#include "ActionProgressDialog.h"
#include "ProgressCounterDialog.h"
#include "AddFilesDialog.h"
#include "ExtractOptionsDialog.h"
#include "ConvFileOptionsDialog.h"
#include "DiskConvertDialog.h"
#include "FileNameConv.h"
//#include "ProgressCancelDialog.h"
/* user-defined window messages */
#define WMU_LATE_INIT (WM_USER+0)
#define WMU_START (WM_USER+1) // used by ActionProgressDialog
typedef enum {
kFilterIndexNuFX = 1,
kFilterIndexBinaryII = 2,
kFilterIndexACU = 3,
kFilterIndexDiskImage = 4,
kFilterIndexGeneric = 5, // *.* filter used
} FilterIndex;
struct FileCollectionEntry; // fwd
/*
* The main UI window.
*/
class MainWindow : public CFrameWnd
{
public:
MainWindow(void);
~MainWindow(void);
// Overridden functions
BOOL PreCreateWindow(CREATESTRUCT& cs);
//BOOL OnCreateClient( LPCREATESTRUCT lpcs, CCreateContext* pContext );
void GetClientRect(LPRECT lpRect) const;
// get a pointer to the preferences
const Preferences* GetPreferences(void) const { return &fPreferences; }
Preferences* GetPreferencesWr(void) { return &fPreferences; }
// apply an update from the Preferences pages
void ApplyNow(PrefsSheet*);
// get the text of the next file in the selection list
int GetPrevFileText(ReformatHolder* pHolder, CString* pTitle);
int GetNextFileText(ReformatHolder* pHolder, CString* pTitle);
// update the progress meter
void SetProgressBegin(void);
int SetProgressUpdate(int percent, const char* oldName,
const char* newName);
void SetProgressEnd(void);
// update the progress counter
bool SetProgressCounter(const char* fmt, long val);
// handle a double-click in the content view
void HandleDoubleClick(void);
// do some idle processing
void DoIdle(void);
// return the title to put at the top of a printout
CString GetPrintTitle(void);
// raise flag to abort the current print job
void SetAbortPrinting(bool val) { fAbortPrinting = val; }
bool GetAbortPrinting(void) const { return fAbortPrinting; }
static BOOL CALLBACK PrintAbortProc(HDC hDC, int nCode);
bool fAbortPrinting;
// track printer choice
HANDLE fhDevMode;
HANDLE fhDevNames;
// set flag to abort current operation
//void SetAbortOperation(bool val) { fAbortOperation = val; }
//bool fAbortOperation;
// pause, for debugging
void EventPause(int duration);
ContentList* GetContentList(void) const { return fpContentList; }
void SetActionProgressDialog(ActionProgressDialog* pActionProgress) {
fpActionProgress = pActionProgress;
}
void SetProgressCounterDialog(ProgressCounterDialog* pProgressCounter) {
fpProgressCounter = pProgressCounter;
}
GenericArchive* GetOpenArchive(void) const { return fpOpenArchive; }
int GetFileParts(const GenericEntry* pEntry,
ReformatHolder** ppHolder) const;
// force processing of pending messages
BOOL PeekAndPump();
// make a happy noise after successful execution of a command
void SuccessBeep(void);
// make a not-so-happy noise
void FailureBeep(void);
// remove a file, returning a helpful message on failure
CString RemoveFile(const char* fileName);
// choose the place to put a file
bool ChooseAddTarget(DiskImgLib::A2File** ppTargetSubdir,
DiskImgLib::DiskFS** ppDiskFS);
// try a disk image override dialog
int TryDiskImgOverride(DiskImg* pImg, const char* fileSource,
DiskImg::FSFormat defaultFormat, int* pDisplayFormat,
bool allowUnknown, CString* pErrMsg);
// copy all blocks from one disk image to another
DIError CopyDiskImage(DiskImg* pDstImg, DiskImg* pSrcImg, bool bulk,
bool partial, ProgressCancelDialog* pPCDialog);
// does the currently open archive pathname match?
bool IsOpenPathName(const char* path);
// raise a flag to cause a full reload of the open file
void SetReopenFlag(void) { fNeedReopen = true; }
static void ConfigureReformatFromPreferences(ReformatHolder* pReformat);
static ReformatHolder::SourceFormat ReformatterSourceFormat(DiskImg::FSFormat format);
// save a buffer of data as a file in a disk image or file archive
static bool SaveToArchive(GenericArchive::FileDetails* pDetails,
const unsigned char* dataBuf, long dataLen,
const unsigned char* rsrcBuf, long rsrcLen,
CString& errMsg, CWnd* pDialog);
static const char kOpenNuFX[];
static const char kOpenBinaryII[];
static const char kOpenACU[];
static const char kOpenDiskImage[];
static const char kOpenAll[];
static const char kOpenEnd[];
private:
static const char* kModeNuFX;
static const char* kModeBinaryII;
static const char* kModeACU;
static const char* kModeDiskImage;
// Command handlers
afx_msg int OnCreate(LPCREATESTRUCT lpcs);
afx_msg LONG OnLateInit(UINT, LONG);
//afx_msg LONG OnCloseMainDialog(UINT, LONG);
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnGetMinMaxInfo(MINMAXINFO* pMMI);
afx_msg void OnPaint(void);
//afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt);
afx_msg void OnSetFocus(CWnd* pOldWnd);
afx_msg BOOL OnHelpInfo(HELPINFO* lpHelpInfo);
afx_msg BOOL OnQueryEndSession(void);
afx_msg void OnEndSession(BOOL bEnding);
afx_msg LRESULT OnFindDialogMessage(WPARAM wParam, LPARAM lParam);
//afx_msg LONG OnHelp(UINT wParam, LONG lParam);
afx_msg void OnFileNewArchive(void);
afx_msg void OnFileOpen(void);
afx_msg void OnFileOpenVolume(void);
afx_msg void OnUpdateFileOpenVolume(CCmdUI* pCmdUI);
afx_msg void OnFileReopen(void);
afx_msg void OnUpdateFileReopen(CCmdUI* pCmdUI);
afx_msg void OnFileSave(void);
afx_msg void OnUpdateFileSave(CCmdUI* pCmdUI);
afx_msg void OnFileClose(void);
afx_msg void OnUpdateFileClose(CCmdUI* pCmdUI);
afx_msg void OnFileArchiveInfo(void);
afx_msg void OnUpdateFileArchiveInfo(CCmdUI* pCmdUI);
afx_msg void OnFilePrint(void);
afx_msg void OnUpdateFilePrint(CCmdUI* pCmdUI);
afx_msg void OnFileExit(void);
afx_msg void OnEditCopy(void);
afx_msg void OnUpdateEditCopy(CCmdUI* pCmdUI);
afx_msg void OnEditPaste(void);
afx_msg void OnUpdateEditPaste(CCmdUI* pCmdUI);
afx_msg void OnEditPasteSpecial(void);
afx_msg void OnUpdateEditPasteSpecial(CCmdUI* pCmdUI);
afx_msg void OnEditFind(void);
afx_msg void OnUpdateEditFind(CCmdUI* pCmdUI);
afx_msg void OnEditSelectAll(void);
afx_msg void OnUpdateEditSelectAll(CCmdUI* pCmdUI);
afx_msg void OnEditInvertSelection(void);
afx_msg void OnUpdateEditInvertSelection(CCmdUI* pCmdUI);
afx_msg void OnEditPreferences(void);
afx_msg void OnEditSort(UINT id);
afx_msg void OnUpdateEditSort(CCmdUI* pCmdUI);
afx_msg void OnActionsView(void);
afx_msg void OnUpdateActionsView(CCmdUI* pCmdUI);
afx_msg void OnActionsOpenAsDisk(void);
afx_msg void OnUpdateActionsOpenAsDisk(CCmdUI* pCmdUI);
afx_msg void OnActionsAddFiles(void);
afx_msg void OnUpdateActionsAddFiles(CCmdUI* pCmdUI);
afx_msg void OnActionsAddDisks(void);
afx_msg void OnUpdateActionsAddDisks(CCmdUI* pCmdUI);
afx_msg void OnActionsCreateSubdir(void);
afx_msg void OnUpdateActionsCreateSubdir(CCmdUI* pCmdUI);
afx_msg void OnActionsExtract(void);
afx_msg void OnUpdateActionsExtract(CCmdUI* pCmdUI);
afx_msg void OnActionsTest(void);
afx_msg void OnUpdateActionsTest(CCmdUI* pCmdUI);
afx_msg void OnActionsDelete(void);
afx_msg void OnUpdateActionsDelete(CCmdUI* pCmdUI);
afx_msg void OnActionsRename(void);
afx_msg void OnUpdateActionsRename(CCmdUI* pCmdUI);
afx_msg void OnActionsEditComment(void);
afx_msg void OnUpdateActionsEditComment(CCmdUI* pCmdUI);
afx_msg void OnActionsEditProps(void);
afx_msg void OnUpdateActionsEditProps(CCmdUI* pCmdUI);
afx_msg void OnActionsRenameVolume(void);
afx_msg void OnUpdateActionsRenameVolume(CCmdUI* pCmdUI);
afx_msg void OnActionsRecompress(void);
afx_msg void OnUpdateActionsRecompress(CCmdUI* pCmdUI);
afx_msg void OnActionsConvDisk(void);
afx_msg void OnUpdateActionsConvDisk(CCmdUI* pCmdUI);
afx_msg void OnActionsConvFile(void);
afx_msg void OnUpdateActionsConvFile(CCmdUI* pCmdUI);
afx_msg void OnActionsConvToWav(void);
afx_msg void OnUpdateActionsConvToWav(CCmdUI* pCmdUI);
afx_msg void OnActionsConvFromWav(void);
afx_msg void OnUpdateActionsConvFromWav(CCmdUI* pCmdUI);
afx_msg void OnActionsImportBAS(void);
afx_msg void OnUpdateActionsImportBAS(CCmdUI* pCmdUI);
afx_msg void OnToolsDiskEdit(void);
afx_msg void OnToolsDiskConv(void);
afx_msg void OnToolsBulkDiskConv(void);
afx_msg void OnToolsSSTMerge(void);
afx_msg void OnToolsVolumeCopierVolume(void);
afx_msg void OnToolsVolumeCopierFile(void);
afx_msg void OnToolsEOLScanner(void);
afx_msg void OnToolsTwoImgProps(void);
afx_msg void OnToolsDiskImageCreator(void);
afx_msg void OnHelpContents(void);
afx_msg void OnHelpWebSite(void);
afx_msg void OnHelpOrdering(void);
afx_msg void OnHelpAbout(void);
afx_msg void OnRtClkDefault(void);
void ProcessCommandLine(void);
void ResizeClientArea(void);
void DrawEmptyClientArea(CDC* pDC, const CRect& clientRect);
int TmpExtractAndOpen(GenericEntry* pEntry, int threadKind,
const char* modeStr);
int TmpExtractForExternal(GenericEntry* pEntry);
void DoOpenArchive(const char* pathName, const char* ext,
int filterIndex, bool readOnly);
int LoadArchive(const char* filename, const char* extension,
int filterIndex, bool readOnly, bool createFile);
int DoOpenVolume(CString drive, bool readOnly);
void SwitchContentList(GenericArchive* pOpenArchive);
void CloseArchiveWOControls(void);
void CloseArchive(void);
void SetCPTitle(const char* pathname, GenericArchive* pArchive);
void SetCPTitle(void);
GenericEntry* GetSelectedItem(ContentList* pContentList);
void HandleView(void);
void DeleteFileOnExit(const char* name);
void ReopenArchive(void);
/* some stuff from Actions.cpp */
//int GetFileText(SelectionEntry* pSelEntry, ReformatHolder* pHolder,
// CString* pTitle);
void GetFilePart(const GenericEntry* pEntry, int whichThread,
ReformatHolder* pHolder) const;
void DoBulkExtract(SelectionSet* pSelSet,
const ExtractOptionsDialog* pExtOpts);
bool ExtractEntry(GenericEntry* pEntry, int thread,
ReformatHolder* pHolder, const ExtractOptionsDialog* pExtOpts,
bool* pOverwriteExisting, bool* pOvwrForAll);
int OpenOutputFile(CString* pOutputPath, const PathProposal& pathProp,
time_t arcFileModWhen, bool* pOverwriteExisting, bool* pOvwrForAll,
FILE** pFp);
bool DoBulkRecompress(ActionProgressDialog* pActionProgress,
SelectionSet* pSelSet, const RecompressOptionsDialog* pRecompOpts);
void CalcTotalSize(LONGLONG* pUncomp, LONGLONG* pComp) const;
/* some stuff from Clipboard.cpp */
CString CreateFileList(SelectionSet* pSelSet);
static CString DblDblQuote(const char* str);
long GetClipboardContentLen(void);
HGLOBAL CreateFileCollection(SelectionSet* pSelSet);
CString CopyToCollection(GenericEntry* pEntry, void** pBuf, long* pBufLen);
void DoPaste(bool pasteJunkPaths);
CString ProcessClipboard(const void* vbuf, long bufLen,
bool pasteJunkPaths);
CString ProcessClipboardEntry(const FileCollectionEntry* pCollEnt,
const char* pathName, const unsigned char* buf, long remLen);
/* some stuff from Tools.cpp */
int DetermineImageSettings(int convertIdx, bool addGzip,
DiskImg::OuterFormat* pOuterFormat, DiskImg::FileFormat* pFileFormat,
DiskImg::PhysicalFormat* pPhysicalFormat,
DiskImg::SectorOrder* pSectorOrder);
void BulkConvertImage(const char* pathName, const char* targetDir,
const DiskConvertDialog& convDlg, CString* pErrMsg);
int SSTOpenImage(int seqNum, DiskImg* pDiskImg);
int SSTLoadData(int seqNum, DiskImg* pDiskImg, unsigned char* trackBuf,
long* pBadCount);
long SSTGetBufOffset(int track);
long SSTCountBadBytes(const unsigned char* sctBuf, int count);
void SSTProcessTrackData(unsigned char* trackBuf);
void VolumeCopier(bool openFile);
bool EditTwoImgProps(const char* fileName);
void PrintListing(const ContentList* pContentList);
// set when one of the tools modifies the file we have open
bool fNeedReopen;
CToolBar fToolBar;
CStatusBar fStatusBar;
// currently-open archive, if any
GenericArchive* fpOpenArchive;
// name of open archive, for display only -- if this is a temporary
// file launched from another instance of CP, this won't be the name
// of an actual file on disk.
CString fOpenArchivePathName; // for display only
// archive viewer, open when file is open
// NOTE: make a super-class for a tree-structured display or other
// kinds of display, so we can avoid the if/then/else. Rename
// ContentList to DetailList or FlatList or something.
ContentList* fpContentList;
// currently selected set of goodies; used when viewing, extracting, etc.
//SelectionSet* fpSelSet;
// action progress meter, if any
ActionProgressDialog* fpActionProgress;
// progress counter meter, if any
ProgressCounterDialog* fpProgressCounter;
// modeless standard "find" dialog
CFindReplaceDialog* fpFindDialog;
CString fFindLastStr;
bool fFindDown;
bool fFindMatchCase;
bool fFindMatchWholeWord;
// our preferences
Preferences fPreferences;
/*
* Manage a list of files that must be deleted before we exit.
*/
class DeleteList {
private:
class DeleteListNode {
public:
DeleteListNode(const CString& name) : fName(name),
fPrev(nil), fNext(nil) {}
~DeleteListNode(void) {}
DeleteListNode* fPrev;
DeleteListNode* fNext;
CString fName;
};
public:
DeleteList(void) { fHead = nil; }
~DeleteList(void) {
WMSG1("Processing DeleteList (head=0x%08lx)\n", fHead);
DeleteListNode* pNode = fHead;
DeleteListNode* pNext;
while (pNode != nil) {
pNext = pNode->fNext;
if (unlink(pNode->fName) != 0) {
WMSG2(" WARNING: delete of '%s' failed, err=%d\n",
pNode->fName, errno);
} else {
WMSG1(" Deleted '%s'\n", pNode->fName);
}
delete pNode;
pNode = pNext;
}
WMSG0("Processing DeleteList completed\n");
}
void Add(const CString& name) {
DeleteListNode* pNode = new DeleteListNode(name);
if (fHead != nil) {
fHead->fPrev = pNode;
pNode->fNext = fHead;
}
fHead = pNode;
WMSG1("Delete-on-exit '%s'\n", (LPCTSTR) name);
}
DeleteListNode* fHead;
};
DeleteList fDeleteList;
DECLARE_MESSAGE_MAP()
};
#define GET_MAIN_WINDOW() ((MainWindow*)::AfxGetMainWnd())
#define SET_PROGRESS_BEGIN() ((MainWindow*)::AfxGetMainWnd())->SetProgressBegin()
#define SET_PROGRESS_UPDATE(perc) \
((MainWindow*)::AfxGetMainWnd())->SetProgressUpdate(perc, nil, nil)
#define SET_PROGRESS_UPDATE2(perc, oldName, newName) \
((MainWindow*)::AfxGetMainWnd())->SetProgressUpdate(perc, oldName, newName)
#define SET_PROGRESS_END() ((MainWindow*)::AfxGetMainWnd())->SetProgressEnd()
#define SET_PROGRESS_COUNTER(val) \
((MainWindow*)::AfxGetMainWnd())->SetProgressCounter(nil, val)
#define SET_PROGRESS_COUNTER_2(fmt, val) \
((MainWindow*)::AfxGetMainWnd())->SetProgressCounter(fmt, val)
#define GET_PREFERENCES() ((MainWindow*)::AfxGetMainWnd())->GetPreferences()
#define GET_PREFERENCES_WR() ((MainWindow*)::AfxGetMainWnd())->GetPreferencesWr()
#endif /*__MAIN__*/

221
app/MyApp.cpp Normal file
View File

@ -0,0 +1,221 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* The application object.
*/
#include "stdafx.h"
#include "../util/UtilLib.h"
#include "MyApp.h"
#include "Registry.h"
#include "Main.h"
#include "DiskArchive.h"
#include <process.h>
/* magic global that MFC finds (or that finds MFC) */
MyApp gMyApp;
#if defined(_DEBUG_LOG)
FILE* gLog = nil;
int gPid = -1;
#endif
/*
* Constructor. This is the closest thing to "main" that we have, but we
* should wait for InitInstance for most things.
*/
MyApp::MyApp(LPCTSTR lpszAppName) : CWinApp(lpszAppName)
{
const int kStaleLog = 8 * 60 * 60;
//fclose(fopen("c:\\cp-myapp.txt", "w"));
time_t now;
now = time(nil);
#ifdef _DEBUG_LOG
PathName debugPath(kDebugLog);
time_t when = debugPath.GetModWhen();
if (when > 0 && now - when > kStaleLog) {
/* log file is more than 8 hours old, remove it */
/* [consider opening it and truncating with chsize() instead, so we
don't hose somebody's custom access permissions. ++ATM 20041015] */
unlink(kDebugLog);
}
gLog = fopen(kDebugLog, "a");
if (gLog == nil)
abort();
::setvbuf(gLog, nil, _IONBF, 0);
gPid = ::getpid();
fprintf(gLog, "\n");
if (when > 0) {
WMSG2("(Log file was %.3f hours old; logs are reset after %.3f)\n",
(now - when) / 3600.0, kStaleLog / 3600.0);
}
#endif
WMSG5("CiderPress v%d.%d.%d%s started at %.24s\n",
kAppMajorVersion, kAppMinorVersion, kAppBugVersion,
kAppDevString, ctime(&now));
int tmpDbgFlag;
// enable memory leak detection
tmpDbgFlag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
tmpDbgFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag(tmpDbgFlag);
WMSG0("Leak detection enabled\n");
}
/*
* This is the last point of control we have.
*/
MyApp::~MyApp(void)
{
DiskArchive::AppCleanup();
NiftyList::AppCleanup();
WMSG0("SHUTTING DOWN\n\n");
#ifdef _DEBUG_LOG
if (gLog != nil)
fclose(gLog);
#endif
}
/*
* It all begins here.
*
* Create a main window.
*/
BOOL
MyApp::InitInstance(void)
{
//fclose(fopen("c:\\cp-initinstance.txt", "w"));
//_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
m_pMainWnd = new MainWindow;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
WMSG0("Happily in InitInstance!\n");
/* find our .EXE file */
//HMODULE hModule = ::GetModuleHandle(NULL);
char buf[MAX_PATH];
if (::GetModuleFileName(nil /*hModule*/, buf, sizeof(buf)) != 0) {
WMSG1("Module name is '%s'\n", buf);
fExeFileName = buf;
char* cp = strrchr(buf, '\\');
if (cp == nil)
fExeBaseName = "";
else
fExeBaseName = fExeFileName.Left(cp - buf +1);
} else {
WMSG1("BIG problem: GetModuleFileName failed (err=%ld)\n",
::GetLastError());
}
LogModuleLocation("riched.dll");
LogModuleLocation("riched20.dll");
LogModuleLocation("riched32.dll");
#if 0
/* find our .INI file by tweaking the EXE path */
char* cp = strrchr(buf, '\\');
if (cp == nil)
cp = buf;
else
cp++;
if (cp + ::lstrlen(_T("CiderPress.INI")) >= buf+sizeof(buf))
return FALSE;
::lstrcpy(cp, _T("CiderPress.INI"));
free((void*)m_pszProfileName);
m_pszProfileName = strdup(buf);
WMSG1("Profile name is '%s'\n", m_pszProfileName);
if (!WriteProfileString("SectionOne", "MyEntry", "test"))
WMSG0("WriteProfileString failed\n");
#endif
SetRegistryKey(fRegistry.GetAppRegistryKey());
//WMSG1("Registry key is '%s'\n", m_pszRegistryKey);
//WMSG1("Profile name is '%s'\n", m_pszProfileName);
WMSG1("Short command line is '%s'\n", m_lpCmdLine);
//WMSG1("CP app name is '%s'\n", m_pszAppName);
//WMSG1("CP exe name is '%s'\n", m_pszExeName);
WMSG1("CP help file is '%s'\n", m_pszHelpFilePath);
WMSG1("Command line is '%s'\n", ::GetCommandLine());
//if (!WriteProfileString("SectionOne", "MyEntry", "test"))
// WMSG0("WriteProfileString failed\n");
/*
* If we're installing or uninstalling, do what we need to and then
* bail immediately. This will hemorrhage memory, but I'm sure the
* incredibly robust Windows environment will take it in stride.
*/
if (strcmp(m_lpCmdLine, _T("-install")) == 0) {
WMSG0("Invoked with INSTALL flag\n");
fRegistry.OneTimeInstall();
exit(0);
} else if (strcmp(m_lpCmdLine, _T("-uninstall")) == 0) {
WMSG0("Invoked with UNINSTALL flag\n");
fRegistry.OneTimeUninstall();
exit(1); // tell DeployMaster to continue with uninstall
}
fRegistry.FixBasicSettings();
return TRUE;
}
/*
* Show where we got something from. Handy for checking DLL load locations.
*
* If "name" is nil, we show the EXE info.
*/
void
MyApp::LogModuleLocation(const char* name)
{
HMODULE hModule;
char fileNameBuf[256];
hModule = ::GetModuleHandle(name);
if (hModule != nil &&
::GetModuleFileName(hModule, fileNameBuf, sizeof(fileNameBuf)) != 0)
{
// GetModuleHandle does not increase ref count, so no need to release
WMSG2("Module '%s' loaded from '%s'\n", name, fileNameBuf);
} else {
WMSG1("Module '%s' not loaded\n", name);
}
}
/*
* Do some idle processing.
*/
BOOL
MyApp::OnIdle(LONG lCount)
{
BOOL bMore = CWinApp::OnIdle(lCount);
//if (lCount == 0) {
// WMSG1("IDLE lcount=%d\n", lCount);
//}
/*
* If MFC is done, we take a swing.
*/
if (bMore == false) {
/* downcast */
((MainWindow*)m_pMainWnd)->DoIdle();
}
return bMore;
}

52
app/MyApp.h Normal file
View File

@ -0,0 +1,52 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* The application object.
*/
#ifndef __MYAPP__
#define __MYAPP__
#include "Registry.h"
#if defined(_DEBUG_LOG)
//#define kDebugLog "C:\\test\\cplog.txt"
#define kDebugLog "C:\\cplog.txt"
#endif
/* CiderPress version numbers */
#define kAppMajorVersion 3
#define kAppMinorVersion 0
#define kAppBugVersion 0
#define kAppDevString ""
/*
* Windows application object.
*/
class MyApp: public CWinApp
{
public:
MyApp(LPCTSTR lpszAppName = NULL);
virtual ~MyApp(void);
MyRegistry fRegistry;
const char* GetExeFileName(void) const { return fExeFileName; }
const char* GetExeBaseName(void) const { return fExeBaseName; }
private:
// Overridden functions
virtual BOOL InitInstance(void);
virtual BOOL OnIdle(LONG lCount);
void LogModuleLocation(const char* name);
CString fExeFileName;
CString fExeBaseName;
};
extern MyApp gMyApp;
#endif /*__MYAPP__*/

166
app/NewDiskSize.cpp Normal file
View File

@ -0,0 +1,166 @@
/*
* CiderPress
* Copyright (C) 2007 by faddenSoft, LLC. All Rights Reserved.
* See the file LICENSE for distribution terms.
*/
/*
* Functions and data to support the "new disk size" radio buttons.
*/
#include "stdafx.h"
#include "NewDiskSize.h"
#include "resource.h"
/*
* Number of blocks in the disks we create.
*
* These must be in ascending order.
*/
/*static*/ const NewDiskSize::RadioCtrlMap NewDiskSize::kCtrlMap[] = {
{ IDC_CONVDISK_140K, 280 },
{ IDC_CONVDISK_800K, 1600 },
{ IDC_CONVDISK_1440K, 2880 },
{ IDC_CONVDISK_5MB, 10240 },
{ IDC_CONVDISK_16MB, 32768 },
{ IDC_CONVDISK_20MB, 40960 },
{ IDC_CONVDISK_32MB, 65535 },
{ IDC_CONVDISK_SPECIFY, kSpecified },
};
static const kEditBoxID = IDC_CONVDISK_SPECIFY_EDIT;
/*
* Return the #of entries in the table.
*/
/*static*/ unsigned int
NewDiskSize::GetNumSizeEntries(void)
{
return NELEM(kCtrlMap);
}
/*
* Return the "size" field from an array entry.
*/
/*static*/ long
NewDiskSize::GetDiskSizeByIndex(int idx)
{
ASSERT(idx >= 0 && idx < NELEM(kCtrlMap));
return kCtrlMap[idx].blocks;
}
/*static*/ void
NewDiskSize::EnableButtons(CDialog* pDialog, BOOL state /*=true*/)
{
CWnd* pWnd;
for (int i = 0; i < NELEM(kCtrlMap); i++) {
pWnd = pDialog->GetDlgItem(kCtrlMap[i].ctrlID);
if (pWnd != nil)
pWnd->EnableWindow(state);
}
}
/*
* Run through the set of radio buttons, disabling any that don't have enough
* space to hold the ProDOS volume with the specified parameters.
*
* The space required is equal to the blocks required for data plus the blocks
* required for the free-space bitmap. Since the free-space bitmap size is
* smaller for smaller volumes, we have to adjust it for each.
*
* Pass in the total blocks and #of blocks used on a particular ProDOS volume.
* This will compute how much space would be required for larger and smaller
* volumes, and enable or disable radio buttons as appropriate. (You can get
* these values from DiskFS::GetFreeBlockCount()).
*/
/*static*/ void
NewDiskSize::EnableButtons_ProDOS(CDialog* pDialog, long totalBlocks,
long blocksUsed)
{
CButton* pButton;
long usedWithoutBitmap = blocksUsed - GetNumBitmapBlocks_ProDOS(totalBlocks);
bool first = true;
WMSG3("EnableButtons_ProDOS total=%ld used=%ld usedw/o=%ld\n",
totalBlocks, blocksUsed, usedWithoutBitmap);
for (int i = 0; i < NELEM(kCtrlMap); i++) {
pButton = (CButton*) pDialog->GetDlgItem(kCtrlMap[i].ctrlID);
if (pButton == nil) {
WMSG1("WARNING: couldn't find ctrlID %d\n", kCtrlMap[i].ctrlID);
continue;
}
if (kCtrlMap[i].blocks == kSpecified) {
pButton->SetCheck(BST_UNCHECKED);
pButton->EnableWindow(TRUE);
CWnd* pWnd = pDialog->GetDlgItem(kEditBoxID);
pWnd->EnableWindow(FALSE);
continue;
}
if (usedWithoutBitmap + GetNumBitmapBlocks_ProDOS(kCtrlMap[i].blocks) <=
kCtrlMap[i].blocks)
{
pButton->EnableWindow(TRUE);
if (first) {
pButton->SetCheck(BST_CHECKED);
first = false;
} else {
pButton->SetCheck(BST_UNCHECKED);
}
} else {
pButton->EnableWindow(FALSE);
pButton->SetCheck(BST_UNCHECKED);
}
}
UpdateSpecifyEdit(pDialog);
}
/*
* Compute the #of blocks needed to hold the ProDOS block bitmap.
*/
/*static*/long
NewDiskSize::GetNumBitmapBlocks_ProDOS(long totalBlocks) {
ASSERT(totalBlocks > 0);
const int kBitsPerBlock = 512 * 8;
int numBlocks = (totalBlocks + kBitsPerBlock-1) / kBitsPerBlock;
return numBlocks;
}
/*
* Update the "specify size" edit box.
*/
/*static*/ void
NewDiskSize::UpdateSpecifyEdit(CDialog* pDialog)
{
CEdit* pEdit = (CEdit*) pDialog->GetDlgItem(kEditBoxID);
int i;
if (pEdit == nil) {
ASSERT(false);
return;
}
for (i = 0; i < NELEM(kCtrlMap); i++) {
CButton* pButton = (CButton*) pDialog->GetDlgItem(kCtrlMap[i].ctrlID);
if (pButton == nil) {
WMSG1("WARNING: couldn't find ctrlID %d\n", kCtrlMap[i].ctrlID);
continue;
}
if (pButton->GetCheck() == BST_CHECKED) {
if (kCtrlMap[i].blocks == kSpecified)
return;
break;
}
}
if (i == NELEM(kCtrlMap)) {
WMSG0("WARNING: couldn't find a checked radio button\n");
return;
}
CString fmt;
fmt.Format("%ld", kCtrlMap[i].blocks);
pEdit->SetWindowText(fmt);
}

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