initial import
107
CP.dsw
Normal 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
@ -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
@ -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
11
DIST/ReadMe.txt
Normal 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
@ -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
@ -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
@ -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
|
||||
"EXCISE_GPL_CODE" 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. All you
|
||||
really need is the C++ compiler. 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. I chose to stick with 6.0 for
|
||||
CiderPress because you need an additional MSVC DLL when you build with
|
||||
2003. 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 "app" as the build target (Build->Set
|
||||
active configuration), and hit F7. This will build everything except MDC,
|
||||
which you can then build by selecting "mdc" and hitting F7. You
|
||||
can also configure a batch build. In 2003 just select "build
|
||||
solution". The necessary prebuilt libraries should be there for both
|
||||
"debug" and "release" builds. (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). You will
|
||||
get a warning about not being able to find the NiftyList data file unless you
|
||||
copy that from "DIST" into the app source directory.</p>
|
||||
|
||||
|
||||
<p>The distribution comes with prebuilt versions of NufxLib2 and zlib in DLL
|
||||
form. 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. 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 "linux" directory has a few command-line utilities and a simple
|
||||
makefile. 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. 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. 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. Some other code is also provided:</p>
|
||||
<ul>
|
||||
<li><code>iconv infile outfile</code> -- convert an image from one format to
|
||||
another. This was used for testing.</li>
|
||||
<li><code>packddd infile outfile</code> -- the DDD code was originally
|
||||
developed under Linux. This code is here for historical reasons.</li>
|
||||
<li><code>sstasm part1 part2</code> -- the SST re-assembly code was originally
|
||||
developed under Linux. The code is here for historical reasons.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p>The "prebuilt" 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 "make depend" before
|
||||
"make" 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. My goal was to learn how to write a
|
||||
Windows application, so I made no pretense at portability. For better or
|
||||
worse, I avoided the Visual Studio "wizards" for the dialogs.
|
||||
|
||||
<p>Much of the user interface text is in the resource file. Much is not,
|
||||
especially when it comes to error messages. This will need to be addressed
|
||||
if internationalization is attempted.
|
||||
|
||||
<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. 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. 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. This represents a single disk image, which may have
|
||||
sub-images. 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. Paired with a DiskImg, this is roughly equivalent to a GS/OS
|
||||
FST (FileSystem Translator). 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. Represents a file on a DiskFS. This holds the file's
|
||||
name, attributes, track/sector or block lists, and provides a call to open a
|
||||
file.</li>
|
||||
<li>A2FileDescr. Represents an open file. You can read or write
|
||||
data.</li>
|
||||
</ul>
|
||||
<p>Sub-classes are defined in DiskImgDetail.h. Most applications won't
|
||||
need to access this header file. 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. 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 "examine" function of each reformatter in
|
||||
turn. The level of confidence is specified in a range. If it's better than
|
||||
"no", it is presented to the user as an option, ordered by the strength of its
|
||||
convictions. If chosen, the "process" function is called to convert the data.
|
||||
|
||||
<p>Bear in mind that reformatters may be disabled from the preferences menu.
|
||||
Also, when extracting files for easy access in Windows, the "best"
|
||||
reformatter is employed by the extraction code.<p>Most of the code should be portable, though some of it uses the MFC CString class.
|
||||
This could probably be altered to use STL strings or plain.
|
||||
|
||||
|
||||
|
||||
<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
@ -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
@ -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
@ -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, ®, &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
@ -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__*/
|
157
app/ActionProgressDialog.cpp
Normal 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));
|
||||
}
|
65
app/ActionProgressDialog.h
Normal 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
60
app/AddClashDialog.cpp
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
160
app/CassetteDialog.h
Normal 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__*/
|
102
app/ChooseAddTargetDialog.cpp
Normal 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);
|
||||
}
|
47
app/ChooseAddTargetDialog.h
Normal 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
@ -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
@ -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
1079
app/Clipboard.cpp
Normal file
173
app/ConfirmOverwriteDialog.cpp
Normal 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();
|
||||
}
|
||||
}
|
93
app/ConfirmOverwriteDialog.h
Normal 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
136
app/ContentList.h
Normal 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__*/
|
344
app/ConvDiskOptionsDialog.cpp
Normal 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);
|
||||
}
|
53
app/ConvDiskOptionsDialog.h
Normal 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__*/
|
35
app/ConvFileOptionsDialog.cpp
Normal 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);
|
||||
}
|
38
app/ConvFileOptionsDialog.h
Normal 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
@ -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
@ -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__*/
|
82
app/CreateSubdirDialog.cpp
Normal 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
@ -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
@ -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
@ -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
244
app/DiskArchive.h
Normal 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
@ -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
@ -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
315
app/DiskEditDialog.h
Normal 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__*/
|
53
app/DiskEditOpenDialog.cpp
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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, ®, &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
@ -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__*/
|
208
app/ExtractOptionsDialog.cpp
Normal 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);
|
||||
}
|
87
app/ExtractOptionsDialog.h
Normal 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
139
app/FileNameConv.h
Normal 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
690
app/GenericArchive.h
Normal 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__*/
|
BIN
app/Graphics/ChooseFolder.bmp
Normal file
After Width: | Height: | Size: 246 B |
BIN
app/Graphics/CiderPress.ico
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
app/Graphics/FileViewer.ico
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
app/Graphics/NewFolder.bmp
Normal file
After Width: | Height: | Size: 246 B |
BIN
app/Graphics/binary2.ico
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
app/Graphics/diskimage.ico
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
app/Graphics/fslogo.bmp
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
app/Graphics/hdrbar.bmp
Normal file
After Width: | Height: | Size: 374 B |
BIN
app/Graphics/list-pics.bmp
Normal file
After Width: | Height: | Size: 758 B |
BIN
app/Graphics/nufx.ico
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
app/Graphics/toolbar1.bmp
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
app/Graphics/tree_pics.bmp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
app/Graphics/vol_pics.bmp
Normal file
After Width: | Height: | Size: 374 B |
BIN
app/Help/CIDERPRESS.HLP
Normal file
56
app/Help/CiderPress.cnt
Normal 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
43
app/HelpTopics.h
Normal 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
@ -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
@ -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
435
app/Main.h
Normal 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
@ -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
@ -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
@ -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);
|
||||
}
|