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