mirror of
https://github.com/fadden/ciderpress.git
synced 2024-10-06 01:56:20 +00:00
initial import
This commit is contained in:
commit
0d7d1b67e0
107
CP.dsw
Normal file
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
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
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
2516
DIST/NList.Data.TXT
Normal file
File diff suppressed because it is too large
Load Diff
11
DIST/ReadMe.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
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
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
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
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);
|
||||