Copyright: Copyright © STAC ELECTRONICS, 1993


Stacker Developer's Tool Kit 3.0
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Developer's Toolkit Functions
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Informational Functions

General Functions

Compression Functions

Drive State Manipulation Functions

Read Compressed Data Functions

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


General Info Menu
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Developing C programs for DOS

Developing C programs for Windows

Developing Turbo Pascal programs

Developing Assembly programs

Stacker Developer's Tool Kit Data Size Conventions

Errors returned from the Developer's Tool Kit functions

Informational Functions

General Functions

Compression Functions

Drive State Manipulation Functions

Read Compressed Data Functions

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Informational Functions
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Informational Functions

stacker_status

stacker_driveInfo

stacker_driverInfo

stacker_getCurrentDriveLetter

stacker_getBootTimeDriveLetter

stacker_fileCompressionRatio

stacker_fastFileCR

General Functions

Compression Functions

Drive State Manipulation Functions

Read Compressed Data Functions

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


General Functions
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Informational Functions

General Functions

stacker_freeApiSelectors

Compression Functions

Drive State Manipulation Functions

Read Compressed Data Functions

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Compression Funtions
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Informational Functions

General Functions

Compression Functions

stacker_pack

stacker_unpack

Drive State Manipulation Functions

Read Compressed Data Functions

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Drive State Functions
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Informational Functions

General Functions

Compression Functions

Drive State Manipulation Functions

stacker_flushDriver

stacker_unmountVolume

stacker_hostDriveMode

stacker_undoHostDriveMode

stacker_writeProtect

stacker_unWriteProtect

Read Compressed Data Functions

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Read Compressed Data Functions
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

General Information

Informational Functions

General Functions

Compression Functions

Drive State Manipulation Functions

Read Compressed Data Functions

stacker_initializeRccBuffer

stacker_getFatEntry

stacker_readCompressedCluster

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Important Considerations with Stacker 3.1 and Beyond
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

Stacker 3.1

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

GLOSSARY


Testing Considerations Menu
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

General testing considerations

If your program writes to Stacker drives

Tests for the Compressed Data Functions

Tests for Stacker Converted Drives

Testing installation programs with Stacker

Non-Developer's Tool Kit specific tests

INSTALL PROGRAMS AND STACKER

GLOSSARY


Install Programs and Stacker Menu
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

Does Your Installation Program Need the Developer's Tool Kit?

What happens when Compressing a boot drive?

Making installation programs work with Stacker

Program logic for a Stacker aware installation program

GLOSSARY


Compressing a Boot Drive Menu
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

Does Your Installation Program Need the Developer's Tool Kit?

What happens when Compressing a boot drive?

The Problem with Batch Files, The Path, and Configured Software

Stacker and the boot process for DOS's prior to MS DOS 6.0

Modification of CONFIG.SYS and AUTOEXEC.BAT

after Compressing with Stacker 3.0 or before

Stacker 3.1 and the Boot process for MS DOS 6.0

Making installation programs work with Stacker

Program logic for a Stacker aware installation program

GLOSSARY


Installation Programs working with Stacker
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

Does Your Installation Program Need the Developer's Tool Kit?

What happens when Compressing a boot drive?

Making installation programs work with Stacker

Modifying CONFIG.SYS or AUTOEXEC.BAT

Checking how much disk space is available before copying files

Program logic for a Stacker aware installation program

GLOSSARY


Program logic for a Stacker aware installation program
INTRODUCTION

WHAT'S NEW?

THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS

IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND

TESTING CONSIDERATIONS

INSTALL PROGRAMS AND STACKER

Does Your Installation Program Need the Developer's Tool Kit?

What happens when Compressing a boot drive?

Making installation programs work with Stacker

Program logic for a Stacker aware installation program

Main body of the installation program

Copy_Files_And_Update_Config() - Function

Gather_Drive_Info() - Function

GLOSSARY


 Introduction

Welcome to the Stacker Developer's Tool Kit for Windows and DOS, a gateway to the internals of Stacker, one of the most exciting and powerful personal computer products in the world today! Better yet, you have the ability to add the features of Stacker's high performance, high quality data compression to your own DOS and Windows programs. Now you can join the other developers such as those at Central Point, Colorado Memory Systems, Symantec and Traveling Software who have already been using Stac's data compression in their products for years.

At Stac we try our very hardest to be responsive to the needs of our customers. Thus, you can be assured that we work diligently with many software and hardware developers to make certain that the Stacker Developer's Tool Kit includes the most useful features that we can put into it. You will find that  in this release of the Stacker Developer's Tool Kit these features can be grouped into three major categories:

Features that help you develop software that is Stacker compatible. Using the Developer's Tool Kit you can now determine if Stacker is installed on a system, what version of Stacker is running, and which drives in the system are compressed. If the Stacker drives have been swapped you can find out what drives they have been swapped with. You can determine if a Stacker coprocessor is installed, which coprocessor it is, and the hardware address where it is installed. If a Stacker drive replaces its host drive your software can now have direct access to the underlying host drive if it needs it. This and much more has been provided to help you enhance your product's compatibility. We think the most common use of these features will be in installation programs which want to detect Stacker and optimally configure the software that they are installing for a Stacker based system. You'll probably think of lots of other things to do with all this information.

Direct access to Stacker's compression and decompression engines. Use Stacker's high performance compression and decompression to improve the performance of your product. Transmit data in half the time.  Store it in half the space. Reduce memory usage by keeping infrequently used data structures compressed and decompressing them only when needed. Speed up your software by keeping overlays compressed in EMS or XMS and then decompressing them when needed instead paying the seek time it costs to read them from disk.

If you are already using one of Stac's LZS221 or DCS221 high performance software compression modules, and do not require their multi-session or destination flush features for your application, you will want to investigate using the Stacker Developer's Tool Kit as a replacement. The Tool Kit's stacker_pack and stacker_unpack functions now work even if Stacker is not installed in a system. Without Stacker these functions are fifty percent faster than LZS221 or DCS221. If Stacker or Stac's LZS hardware is present in the system stacker_pack and stacker_unpack automatically use Stac's LZS Server technology to detect it and make use of it, providing performance that is two to twenty five times faster than LZS221 or DCS221 depending on the version of Stacker or LZS hardware that is installed.

The ability to read compressed data directly from a Stacker drive. Now you can transfer compressed data directly from a Stacker drive, to tape, to backup floppies, across a modem, or over a network without having to pay the performance penalty of having Stacker first decompress the data. The data format is the same Stacker LZS compressed data format that is supported by all of our compression products. This means that when you need access to the compressed data you can decompress it using our 9703, 9704, 9705, 9706, 9730 or 9731 compression chips, our LZS221 compression libraries (available in 8088, 80386, 68000 and i960 assembly as well as C), or the Stacker Developer's Tool Kit. This is also the same compressed data format as the QIC-122 data compression standard which is used in quarter inch tape drives.

This Stacker Developer's Tool Kit manual is divided into the following sections:

CHAPTER 1: THE STACKER DEVELOPER'S TOOL KIT FUNCTIONS describes each of the functions provided in the Stacker Developer's Tool Kit. For each function an example, in the C programming language, is provided which shows how to use it. This chapter also tells how to use the Stacker Developer's Tool Kit with assembly and Pascal programs as well as C.

CHAPTER 2: IMPORTANT CONSIDERATIONS FOR STACKER 3.1 AND BEYOND describes items that have changed with recent versions of Stacker. This chapter is a must for all developers who are going to use the Stacker Developer's Tool Kit in their application(s).

CHAPTER 3: TESTING CONSIDERATIONS suggests several Stacker configurations which you should consider when you are testing your software to make sure that it works as you intend.

CHAPTER 4: INSTALLATION PROGRAMS AND STACKER discusses the issues relevant to writing installation programs that work properly on systems with compressed boot drives. This section is complemented by source code for an example installation program on the Tool Kit diskette.

GLOSSARY: Defines several of the terms used in this manual.

Along with the library modules for the Tool Kit itself, source and executables for several example programs are also included on the Tool Kit diskette. There is also a README.DOC file which describes all of the files on the Tool Kit diskette. You should review the README.DOC file for any changes which did not make it into this revision of the Developer's Guide.

Should you have questions about how to use the Stacker Developer's Tool Kit in your own applications or if you have suggestions on how we can make it better, we would be happy to hear from you. You can reach us on CompuServe. For public information you can access the Stac Forum by typing GO STAC at any "!" prompt. There are Stacker Developer's Tool Kit areas under the Message and Library sections. For private email, our user ID is 70674,3364. If you do not have a CompuServe account, call (800) 524-3388, and ask for representative #391 to request a free Introductory Membership.


What's New in Version 3.0?
 What's New?

A significant number of enhancements have been made to this version of the Stacker Developer's Tool Kit. Here's a list of what's new:

Windows Functionality - Windows protected mode versions of all of the Tool Kit functions have been added, as well as a new stacker_freeApiSelectors() function which is used to free up the selectors allocated by the Stacker Developer's Tool Kit. All of the Tool Kit functions for Windows are contained in the STACKERW.LIB library file.

Build-In tunable Compression and Decompression - Stand-alone compression and decompression functionality has been added to the stacker_pack and stacker_unpack functions so that they now work even if Stacker is not present in a system. These functions also make use of Stac's LZS Server technology. This means that if Stacker or Stac's LZS compression hardware is present in a system these functions automatically detect and use it. The stacker_pack function is also tunable allowing your software or your customers to trade speed for compression ratio to optimize performance.

File Compression Ratio Support - Two new functions for gathering information about file compression ratios have been added. The first, stacker_fileCompressionRatio(), takes a full file path as a parameter and is very easy to use. The second, stacker_fastFileCR(), takes the starting cluster number of the file. It requires a little more knowledge about DOS internals, but is much faster.

More Specific Error Codes - Several new error codes which provide more specific information about the nature of possible errors have been added.

Better Handling of Read Errors - The stacker_readCompressedCluster() function now detects and reports read errors if they occur.

Stacker 3.1 support - This version of the Tool Kit adds support for Stacker 3.1 and its full integration with MS DOS 6.0. See the chapter entitled "Important Considerations For Stacker 3.1 And Beyond."


 The Stacker Developer's Tool Kit Functions

The Stacker Developer's Tool Kit is a linkable software interface to the Stacker device driver, STACKER.COM. When Stacker is installed, this interface uses Stac's LZS Server technology to provide access to Stacker's LZS data compression and decompression engine (either the software compression engine or one of the high-performance Stacker coprocessor cards, whichever is currently installed on the system). If Stacker is not installed the Developer's Tool Kit will automatically and transparently use its built-in compression and decompression functions. The Stacker Developer's Tool Kit is compatible with the normal operation of Stacker and will not interfere with disk compression and decompression operations.

This version of the Stacker Developer's Tool Kit works with all software and coprocessor versions of Stacker starting with version 1.00 and including all shipping versions of Stacker at the time this document was written.

The Stacker Developer's Tool Kit can be linked into programs using several different development tools. It is compatible with Microsoft C versions 6.00 and 7.00, Microsoft Visual C++, Borland C versions 2.0 thru 3.1, Microsoft Assembler version 6.0, Turbo Assembler version 3.1, Turbo Pascal version 6.0 and Borland Pascal version 7.0. The C compilers were tested in all models from tiny to huge.

The DOS subdirectory of the Stacker Developer's Tool Kit diskette contains all of the files for the DOS version of the Developer's Tool Kit including the Stacker Developer's Tool Kit for DOS library file, STACKER.LIB. To use the Stacker Developer's Tool Kit with a DOS program that must be compiled in tiny model, substitute STACKERT.LIB for STACKER.LIB. The files for the Windows version of the Developer's Tool Kit are in the WINDOWS subdirectory on the Stacker Developer's Tool Kit diskette. The Stacker Developer's Tool Kit for Windows library file is STACKERW.LIB.

We have included many example programs with the Stacker Developer's Tool Kit. Please see the README.DOC file on the Stacker Developer's Tool Kit disk for a list of the files. The example programs have compile/assemble instructions at the top of each file.


C Programs
 Developing C programs for DOS

To use the Stacker Developer's Tool Kit functions in a C program for DOS, you must include STACKER.H, using #include, and then link in STACKER.LIB. You do not need to add any special compile time switches. The following examples will show you how to link the STACKER.LIB file into the TEST_API program using either Borland C or Microsoft C:

Borland C and C++:

bcc test_api.c stacker.lib

Borland C with C++ options:

bcc -P test_api.c stacker.lib

Microsoft C and C++:

cl /F 2000 test_api.c stacker.lib

Microsoft C with C++ options:

cl /F 2000 /Tp test_api.c stacker.lib

NOTE:	For tiny model programs use STACKERT.LIB instead of STACKER.LIB. The sample program TEST_API.C will not compile under tiny model.

The "/F 2000" parameter for Microsoft C sets the stack size large enough for the stacker_pack and stacker_unpack functions. If you are not using these Tool Kit functions then you don't need the /F 2000 parameter. The need for this parameter can also be eliminated by using a temporary stack. See the description of the stacker_pack and stacker_unpack functions for more information.


C for Windows Programs
 Developing C programs for Windows

To use the Stacker Developer's Tool Kit functions in a C program for Windows, you must include STACKERW.H, using #include, and then link in STACKERW.LIB. You do not need to add any special compile time switches. The following examples will show you how to link the STACKERW.LIB file into the EXAMWAPI program using either Borland C or Microsoft C:

Microsoft C and C++:

rc /r examwapi.rc

cl /c /GA /F 2000 examwapi.c

link examwapi, , NUL, slibcew libw stackerw, examwapi.def

rc examwapi.res

Borland C and C++:

rc -r examwapi.rc

bcc -c -W examwapi.c

tlink -Twe -c c0ws examwapi, examwapi, NUL, cws import stackerw, examwapi.def

rc examwapi.res


Turbo Pascal Programs
 Developing Turbo Pascal programs

To use the Stacker Developer's Tool Kit functions in a Turbo Pascal program, add STACKER to the USES statement. STACKER can be added anywhere in the USES line since the Stacker Developer's Tool Kit is not dependent on any of the Pascal functions. The following is a before and after example:

Before:

USES CRT, DOS;

After:

USES CRT, DOS, STACKER;

The CRT and DOS units were only included as part of this example and are not necessary for the Stacker Developer's Tool Kit.

When you compile your program, the STACKER.PAS unit will be linked into your program. The first time you do this, you should choose the BUILD option to build STACKER.TPU when it compiles your program.


 Developing Assembly programs

To use the Stacker Developer's Tool Kit functions in an Assembly language program, add an include line in your program to include the STACKER.INC file. This file sets up all of the data structures necessary for the Stacker Developer's Tool Kit function calls. After assembling your program you will need to link it with the appropriate Stacker library, STACKER.LIB for small through huge memory models and STACKERT.LIB for tiny memory model.

Please note that all of the Stacker Developer's Tool Kit functions use Pascal calling conventions. This means that the parameters are passed on the stack from left to right. The return value for all of the Stacker Developer's Tool Kit functions is returned in the AX register. To make your code as small as possible you should comment out all external definitions that are not used in the STACKER.INC file. This will prevent the linker from linking in unused functions.

The following is an example as to how to assemble and link TESTCMP.ASM:

With Borland's TASM:

tasm testcmp

tlink testcmp, , , stacker.lib

With Microsoft's MASM:

masm testcmp

link testcmp, , , stacker

Please see the sample program TESTCMP.ASM for examples on how to call the Stacker Developer's Tool Kit functions.


Data Size Conventions
 Stacker Developer's Tool Kit Data Size Conventions

The following data size assumptions are made with respect to the data types specified in this document:

s_char	8 bits signed

s_uchar	8 bits unsigned

s_short	16 bits signed

s_ushort	16 bits unsigned

s_return	16 bits signed

s_drvStkd	16 bits signed

s_long	32 bits signed

s_ulong	32 bits unsigned

s_ptr_ptr	Far pointer to a far pointer to void


Errors returned from various function calls
 Errors returned from the Developer's Tool Kit functions

All Stacker Developer's Tool Kit function calls return values of type s_return. The possible return values, with descriptions, are:

SE_ACCESS_DENIED	An Access Denied error occurred during a block device call.

SE_DATA_DID_NOT_COMPRESS	The data did not compress.

SE_DATA_DID_NOT_UNPACK	The data did not decompress.

SE_DEST_BUF_2_LG	The destination buffer is too large.

SE_DRIVE_NOT_IN_HOST_MODE	An attempt has been made to take a Stacker drive out of host drive mode when it is not in host drive mode.

SE_DRIVE_NOT_STACKED	The drive you are attempting to access with the Stacker Developer's Tool Kit is not a Stacker drive.

SE_DRIVE_NOT_MOUNTED	The drive you are attempting to access with the Stacker Developer's Tool Kit is not a mounted Stacker drive.

SE_FILE_NOT_FOUND	Could not find the file.

SE_INVALID_DATA	An Invalid Data error occurred during a block device call.

SE_INVALID_DATA_PASSED	Invalid data was passed to the function.

This can occur if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status().

SE_INVALID_FUNCTION	An Invalid Function error occurred during a block device call.

SE_INVALID_GLOBAL_DATA	One or more of the global fields for the Stacker Developer's Tool Kit is invalid.

This can show up if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status().

SE_INVALID_HANDLE	An Invalid Handle error occurred during a block device call.

SE_LZS_HW_BUSY   	The Stacker hardware was busy during a pack or unpack call.

SE_NO_DATA_TO_COMPRESS	There was no data to compress.

SE_SOURCE_BUF_2_LG	The source buffer is too large.

SE_STACKER_NOT_INSTALLED	The Stacker device driver is not resident.

SE_SUCCESSFUL	The function call was successful.

SE_UNABLE_TO_FIND_DPB	Could not find the Drive Parameter Block (DPB) for the drive.

SE_UNABLE_TO_FLUSH_DRIVER	Could not flush the Stacker device driver for the drive.

SE_UNABLE_TO_FREE_SELECTOR	Could not free one of the global selectors used in the Developer's Tool Kit (Windows programs only).

SE_UNEXPECTED_ERROR	An unexpected error occurred.

This can occur if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status().

Critical errors which are detected by Stacker, or the device driver for the physical device hosting Stacker, will be reported via DOS's standard critical error handling mechanisms. Your software should install an Int 24h critical error handler to detect and report such errors.


 stacker_status

Purpose:	Returns the status of the Stacker driver.

Declaration:	s_return far pascal stacker_status( s_ushort far *status );

Parameters:	The pointer to an s_ushort in which to store the status of the Stacker device driver.

Prerequisites:  None.

Remarks:	An initial call to the stacker_status() function of the Developer's Tool Kit must be made before calling any of the other functions. Attempting to use any of the other functions in the Stacker Developer's Tool Kit without first making a call to stacker_status() will cause the function to fail. The only exception to this is calling the stacker_driverInfo() function. This is because stacker_driverInfo() calls stacker_status() before doing its work.

You do not need to call stacker_status() more than once. You can call it anywhere in your program, as long as you do so before calling any other Stacker Developer's Tool Kit functions.

Note: Under Windows the stacker_freeApiSelectors function must be called before your program terminates to free up the protected mode selectors allocated by stacker_status.

Return value:

SE_SUCCESSFUL	The function call was successful.

SE_STACKER_NOT_INSTALLED	The Stacker device driver is not installed.

Values

Passed back:

Variable name:  *status

STACKER_NOT_INSTALLED	Stacker is not installed.

STACKER_SW_INSTALLED	Stacker software is installed.

STACKER_HW_INSTALLED	Stacker hardware is installed.

Example:	stacker_status example   (EXAM_1.C on the Stacker Developer's Tool Kit diskette).


 stacker_driveInfo

Purpose:	The drive info call is used to determine if a drive is a Stacker drive or not. If it is a Stacker drive then several useful pieces of information about the drive are returned.

Declaration:	s_return far pascal stacker_driveInfo(struct stacker_drive_info_struct far *driveInfo );

Structures:

struct stacker_drive_info_struct

{

s_char	driveLetter;

s_drvStkd	driveIsStacked;

s_ushort	driveState;

stacker_BPB	stacker;

s_ushort	stackerUnitNum;

s_char	bootDriveLetter;

s_char	hostDriveLetter;

s_char	stacvolFileName[9];

s_char	stacvolFileExt[4];

s_ushort	stacvolVersion;

s_ulong	stacvolFileSize;

s_ulong	stacvolMainStore;

s_ptr_ptr	hostInterrupt;

s_ptr_ptr	hostStrategy;

s_ulong	stackerDeviceHdr;

s_ushort	hostUnitNum;

stacker_BPB	host;

s_uchar	flagBits;

s_uchar	bitsPerFATEntry;

s_ushort	rccBufferSize;

s_ulong	erasedSectorCount;

s_ulong	freeSectorCount;

s_ushort	freeClusterCount;

s_ushort	adjSecPerClust;

};

The following supplemental structure and typedef are also provided:

typedef struct stacker_BPB_struct

{

s_ushort	bytesPerSec;

s_uchar	secPerCluster;

s_ushort	reservedSec;

s_uchar	numberFATs;

s_ushort	rootEntries;

s_ushort	shortTotalSec;

s_uchar	mediaDescriptor;

s_ushort	secPerFAT;

s_ushort	secPerTrack;

s_ushort	numberHeads;

s_ulong	numberHiddenSec;

s_ulong	totalSec;

} stacker_BPB;

Parameters:	The pointer to a struct of type stacker_drive_info_struct must be passed to stacker_driveInfo(). Input fields in stacker_drive_info_struct:

driveLetter	Drive letter to return information for.

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	The stacker_driveInfo() function should only be called after a successful call has been made to stacker_status() or stacker_driverInfo().

Return value:

SE_SUCCESSFUL	The function call was successful.

SE_DRIVE_NOT_STACKED	The drive specified by the driveLetter element of driveInfo is not a Stacker drive.

SE_INVALID_GLOBAL_DATA	One of the global variables used in the Stacker Developer's Tool Kit is invalid. Do not trust the values in the stacker_drive_info_struct structure. This is probably because you have not called stacker_status() or stacker_driverinfo().

Values

Passed back:

Variable name:  stacker_drive_info_struct  (filled only upon success)

driveIsStacked	This element will provide information about how the drive is compressed. The following values can be in this element:

STACKER_DRIVE_NOT_STACKED
The drive is not a Stacker compressed drive.

STACKER_STACKED_DRIVE
The drive is a Stacker compressed drive.

STACKER_AUTOMOUNT_DRIVE
The drive is an auto-mounting Stacker drive. This feature is only available with version 2.02 and higher Stacker device drivers. You can get the Stacker device driver version using the stacker_driverInfo() function.

NOTE:	The following structure elements will only be filled in if the drive specified by driveLetter is a Stacker drive.

driveState	If the specified drive is a Stacker drive and it is mounted then this element will be set to STACKER_MOUNTED.

NOTE:	If the specified drive is a Stacker drive, but it is not mounted, then the values in the remaining elements of the structure may be invalid.

stacker	This element is a structure which contains the DOS Bios Parameter Block (BPB) information for the Stacker drive. See the description of the stacker_BPB_struct below for more information on this element.

stackerUnitNum	The Stacker device driver unit number for the Stacker drive specified by driveLetter.

bootDriveLetter	The boot time drive letter of the host drive, before any drive swapping that may have taken place.

hostDriveLetter	The current drive letter of the drive that contains the STACVOL file for the Stacker drive accounting for any drive swapping that may have taken place.

stacvolFileName	The file name of the STACVOL file for the specified drive.

stacvolFileExt	The file extension of the STACVOL file for the specified drive.

stacvolVersion	100 times the version number of Stacker that was used to create the STACVOL file for the specified drive.

Example:	Version 2.01 would be reported as 201.

stacvolFileSize	The size of the STACVOL file hosting this Stacker drive.

stacvolMainStore	The total size of the data storage area in the STACVOL file for this drive, in bytes.

hostInterrupt	The address of Stacker's pointer to the device driver interrupt routine for the drive which contains the STACVOL file for the specified drive.

hostStrategy	The address of Stacker's pointer to the device driver strategy routine for the drive which contains the STACVOL file for the specified drive.

stackerDeviceHdr	Pointer to the Stacker device driver header. This is set to NULL and is reserved for use only by the stacker_hostDriveMode() and stacker_undoHostDriveMode() functions.

hostUnitNum	The device driver unit number for the drive which contains the STACVOL file for the specified drive.

host	This element is a structure which contains the DOS Bios Parameter Block (BPB) information for the drive that contains the STACVOL file for the specified Stacker drive. See the description of the stacker_BPB_struct below for more information on this element.

flagBits	These bit flags provide information about the state of the stacker drive. The following bit flags are provided:

STACKER_REPLACED	bit 2, (0x04).
This bit will be set if the Stacker drive replaces the underlying host drive.

STACKER_READONLY	bit 3, (0x08).
This bit will be set if the Stacker drive is currently in a read only or write protected state.

STACKER_DAMAGED	bit 4, (0x10).
This bit will be set if the Stacker device driver detected damage to the Stacker drive at the time it was mounted. SCHECK (CHECK for Stacker 3.0 and beyond) should be run to repair the drive.

bitsPerFATEntry	The number of bits per FAT entry for the Stacker drive, either 12 or 16.

rccBufferSize	The size in bytes of the scratch buffer which must be allocated for the read compressed cluster function.

erasedSectorCount	The number of erased sectors on the Stacker drive. These are sectors that were last used by files or directories, that are currently erased. Stacker tracks these sectors to allow files and directories to be unerased.  See NOTE under freeSectorCount.

freeSectorCount	The number of free sectors on the Stacker drive. These are sectors that are not in use by any files, including deleted files or directories.

NOTE:  The erasedSectorCount plus the freeSectorCount equals the total number of sectors that are available for storing new information in compressed form. Multiply the sum of these two fields by the bytesPerSec element of the stacker_BPB struct to obtain the total physical bytes available on the drive.

freeClusterCount	The number of logical free clusters. This element will only be filled in if the version of the Stacker driver that's loaded is 3.00 or higher. The logical clusters free in this element is the same as the number of clusters free from int 21, function 36H if Stacker did not trap this interrupt.

adjSecPerClust	The adjusted sectors per cluster that Stacker uses to calculate a file's compression ratio.

The supplemental stacker_BPB_struct structures contain the following elements if the drive specified by driveLetter is a Stacker drive:

bytesPerSec	The number of bytes per sector for the drive.

secPerCluster	The number of sectors per cluster for the drive.

reservedSec	The number of reserved sectors for the drive.

numberFATs	The number of copies of the File Allocation Table (FAT) for the drive.

rootEntries	The maximum number of root directory entries for the given drive.

shortTotalSec	For drives logically smaller than 32MB in size, this element contains the total number of logical sectors on the drive.

If the drive is logically larger than 32MB in size then this element will be zero and the total number of logical sectors will be found in the totalSec element.

mediaDescriptor	The media descriptor for the drive.

secPerFAT	The number of sectors reserved for each copy of the drive's File Allocation Table.

secPerTrack	The number of sectors per track for the drive.

numberHeads	The number of read/write heads for the drive.

numberHiddenSec	This element contains the number of hidden sectors for the drive.

totalSec	This element contains the total number of sectors for the drive if the drive is larger than 32MB in size.

Example:	stacker_driveInfo example   (EXAM_2.C on the Stacker Developer's Tool Kit diskette).


 stacker_driverInfo

Purpose:	Gets information about the Stacker device driver.

Declaration:	s_return far pascal stacker_driverInfo(struct stacker_driver_info_struct far *driverInfo);

Structures:

struct stacker_driver_info_struct

{

s_ushort	driverVersion;

s_ushort	hardwareAddress;

s_ushort	lzsServerType;

s_ushort	usingEMS;

s_ushort	loadedHigh;

s_ushort	preLoaded;

s_ushort	availRepDrives;

};

Parameters:	A pointer to a stacker_driver_info_struct structure must be passed to stacker_driverInfo(). None of the elements of this structure need to be initialized before calling stacker_driverInfo().

Prerequisites:  None.

Remarks:	This function can be called in place of stacker_status() before calling other functions in the Developer's Tool Kit.

Return value:	The possible values returned by this function are:

SE_SUCCESSFUL	The function call was successful.

SE_STACKER_NOT_INSTALLED	The Stacker device driver is not installed.  Note that although Stacker itself may not be installed an LZS Server may be present in the system.  If so the lzsServerType element described below will be filled in appropriately.

Values

Passed back:

Variable name:  stacker_driver_info_struct (filled only upon success)

driverVersion	The Stacker driver version times 100.

hardwareAddress	The hardware address.

0xC400 - 0xDFFF = XT/8 coprocessor.

0x0200 - 0x02FF = AT/16 coprocessor.

1 = MC/16 coprocessor.

0 = Stacker software only installed.

lzsServerType	The type of Stac LZS Server present. This element is only used in Stacker versions starting at 3.1. A value of 0xFFFF indicates that there is no LZS Server installed. Zero is a software LZS Server. As new Stac hardware is developed new values will be assigned.

usingEMS	A boolean value telling you if the Stacker driver is using EMS for its cache and buffers. This element is non-zero if Stacker is using EMS.

loadedHigh	A boolean value telling you if the Stacker driver is loaded high. This element is non-zero if Stacker is loaded above 640k.

preLoaded	A boolean value telling you if the Stacker device driver was preloaded (loaded before the CONFIG.SYS file). This element is non-zero if Stacker is preloaded. Stacker 3.1 and above only.

availRepDrives	The number of available replaceable Stacker drives. Stacker version 2.0 and above only.

Example:	stacker_driverInfo example   (EXAM_3.C on the Stacker Developer's Tool Kit diskette.).


 stacker_getCurrentDriveLetter

Purpose:	Gets the current drive letter of a swapped drive given the drive letter assignment at boot time.

Declaration:	s_return far pascal stacker_getCurrentDriveLetter(
s_char beforeSwapDriveLetter,
s_char far *currentDriveLetter
);

Parameters:

beforeSwapDriveLetter	Letter of the drive at boot time. The drive letter does not have to be upper case.

*currentDriveLetter	Pointer to a s_char field in which to store the current drive letter.

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	This function is useful in determining what the letter of the boot drive is after swapping.

The function stacker_getBootTimeDriveLetter() provides complementary functionality.

Note: For purposes of the Stacker Developer's Tool Kit "boot time" is defined as the time at which the CONFIG.SYS processing begins. See the section titled "Important Considerations For Stacker 3.1 And Beyond" for more information.

Return value:

SE_SUCCESSFUL	The function call was successful.

After a successful call, *currentDriveLetter will contain the current drive letter, after swapping, of the given boot time drive. currentDriveLetter will contain a '?' if an invalid drive was passed in the beforeSwapDriveLetter parameter.

SE_INVALID_GLOBAL_DATA	One or more of the global fields for the Stacker Developer's Tool Kit is invalid. This can occur if you have not made a previous call to stacker_status() or stacker_driverInfo().

Example:	stacker_getCurrentDriveLetter example   (EXAM_4.C on the Stacker Developer's Tool Kit diskette.).


 stacker_getBootTimeDriveLetter

Purpose:	Gets the boot time drive letter of a swapped drive given the current drive letter assignment.

Declaration:	s_return far pascal stacker_getBootTimeDriveLetter(
s_char	currentDriveLetter,
s_char far	*beforeSwapDriveLetter
);

Parameters:

currentDriveLetter	Current drive letter assignment. The drive letter does not have to be upper case.

*beforeSwapDriveLetter	Pointer to a s_char field in which to store the boot time drive letter.

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	The function stacker_getCurrentDriveLetter() provides complementary functionality.

Note: For purposes of the Stacker Developer's Tool Kit "boot time" is defined as the time at which the CONFIG.SYS processing begins. See the section titled "Important Considerations For Stacker 3.1 And Beyond" for more information.

Return value:

SE_SUCCESSFUL	The function call was successful.

After a successful call, *beforeSwapDriveLetter will contain the boot time drive letter, before swapping, of the given current drive letter assignment. beforeSwapDriveLetter will contain a '?' if an invalid drive was passed in the currentDriveLetter parameter.

SE_INVALID_GLOBAL_DATA	One or more of the global fields for the Stacker Developer's Tool Kit is invalid. This can occur if you have not made a previous call to stacker_status() or stacker_driverInfo().

Example:	stacker_getBootTimeDriveLetter example   (EXAM_5.C on the Stacker Developer's Tool Kit diskette.).


 stacker_fileCompressionRatio

Purpose:	Gets the information needed to calculate the compression ratio of a file on a Stacker drive.

Declaration:	s_return far pascal stacker_fileCompressionRatio(
struct stacker_drive_info_struct
far  *driveInfo,
s_char	far  *rccBuffer,
s_char	far  *fileName,
s_ulong	far  *compressedSectors,
s_ulong	far  *rawSectors,
);

Parameters:

*driveInfo	Pointer to a stacker_drive_info_struct structure. Since this function uses the information in driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before getting the compression ratio information for a file. You do not have to call the stacker_driveInfo() function each time you get the file compression ratio information, but you must call it to reinitialize the drive info structure before getting the file compression ratio information from a different drive. See stacker_driveInfo() for more information.

*rccBuffer	Pointer to a scratch buffer for this function to use. The space for this scratch buffer is allocated by your program. The size of this buffer is provided by the drive info function and is approximately 10 times the number of bytes per sector for the drive. After allocating space for this buffer the stacker_initializeRccBuffer() function must be used to initialize it. You do not have to call the stacker_initializeRccBuffer() function each time you get the file compression ratio information, but you must call it to reinitialize the Read Compressed Cluster buffer before getting the file compression ratio information from a different drive. See stacker_initializeRccBuffer() for more information.

*fileName	Pointer to a null terminated string holding the file name, including full path, of the file. This field must be filled in prior to making a call to stacker_fileCompressionRatio().

*compressedSectors	Pointer to a s_ulong field in which to put the number of compressed sectors the file is using. stacker_fileCompressionRatio() will fill in this field upon a successful return.

*rawSectors	Pointer to a s_ulong field in which to put the number of raw sectors the file would be using on a non-Stacker drive. stacker_fileCompressionRatio() will fill in this field upon a successful return.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo(), followed by stacker_initializeRccBuffer().

Remarks:	Use the following formula to calculate the compression ratio of the given file:

compressionRatio = ((rawSectors * 100 / compressedSectors) + 5) / 10

After this calculation the compressionRatio field will store the compression ratio times 10. This prevents the need to use floating point math. The '*' above is used to represent multiplication and '/' is used for division.

Note: The number of raw sectors used by a file and thus the compression ratio for the file is based on the adjusted sectors per cluster for the Stacker drive. The adjusted sectors per cluster for a drive will be stored in the drive info structure in the adjSecPerClust element. See the Glossary for a description of adjusted sectors per cluster.

Return value:

SE_SUCCESSFUL	The function call was successful.

After a successful call the parameters *compressedSectors and *rawSectors will contain the information needed to calculate the compression ratio of the given file.

SE_DRIVE_NOT_STACKED	The drive you are attempting to get the file compression ratio information from is not a Stacker drive.

SE_INVALID_DATA_PASSED	Invalid data was passed to the function.

This can occur if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status().

SE_FILE_NOT_FOUND	The file specified could not be found.

SE_INVALID_FUNCTION	A DOS invalid function error occurred while attempting to access the Stacker driver.

SE_ACCESS_DENIED	A DOS access denied error occurred while attempting to access the Stacker driver.

SE_INVALID_HANDLE	A DOS invalid handle error occurred while attempting to access the Stacker driver.

SE_INVALID_DATA	A DOS invalid data error occurred while attempting to access the Stacker driver.

SE_UNEXPECTED_ERROR	An unexpected error occurred while attempting to access the Stacker driver.

Example:	stacker_fileCompressionRatio example   (TESTFCR.C on the Stacker Developer's Tool Kit diskette.).


 stacker_fastFileCR

Purpose:	Like stacker_fileCompressionRatio() this function gets the information needed to calculate the compression ratio of a file on a Stacker drive. However, it is much faster than the stacker_fileCompressionRatio() function. This function is different from stacker_fileCompressionRatio() in that you must pass it the starting cluster number for the file, instead of the file name. This function also requires that you pass it the size of the file.

Declaration:	s_return far pascal stacker_fastFileCR(
struct stacker_drive_info_struct
     far	*driveInfo,
s_char	     far	*rccBuffer,
s_ushort	startingCluster,
s_ulong	fileSize,
s_ulong	     far	*compressedSectors,
s_ulong	     far	*rawSectors,
);

Parameters:

*driveInfo	Pointer to a stacker_drive_info_struct structure. Since this function uses the information in driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before getting the compression ratio information for a file. You do not have to call the stacker_driveInfo() function each time you get the file compression ratio information, but you must call it to reinitialize the drive info structure before getting the file compression ratio information from a different drive. See stacker_driveInfo() for more information.

*rccBuffer	Pointer to a scratch buffer for this function to use. The space for this scratch buffer is allocated by your program. The size of this buffer is provided by the drive info function and is approximately 10 times the number of bytes per sector for the drive. After allocating space for this buffer the stacker_initializeRccBuffer() function must be used to initialize it. You do not have to call the stacker_initializeRccBuffer() function each time you get the file compression ratio information, but you must call it to reinitialize the Read Compressed Cluster buffer before getting the file compression ratio information from a different drive. See stacker_initializeRccBuffer() for more information about rcc buffers.

startingCluster	The starting cluster number of the file.

fileSize	The size of the file that you want the compression ratio of.

*compressedSectors	Pointer to a s_ulong field in which to put the number of compressed sectors the file is using. stacker_fileCompressionRatio() will fill in this field upon a successful return.

*rawSectors	Pointer to a s_ulong field in which to put the number of raw sectors the file would be using on a non-Stacker drive. stacker_fastFileCR() will fill in this field upon a successful return.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo(), followed by stacker_initializeRccBuffer().

Remarks:	Use the following formula to calculate the compression ratio of the given file:

compressionRatio = ((rawSectors * 100 / compressedSectors) + 5) / 10

After this calculation the compressionRatio field will store the compression ratio times 10. This prevents the need to use floating point math. The '*' above is used to represent multiplication and '/' is used for division.

Note: The number of raw sectors used by a file and thus the compression ratio for the file is based on the adjusted sectors per cluster for the Stacker drive. The adjusted sectors per cluster for a drive will be stored in the drive info structure in the adjSecPerClust element. See the Glossary for a description of adjusted sectors per cluster.

Return value:

SE_SUCCESSFUL	The function call was successful.

After a successful call the *compressedSectors and *rawSectors parameters will contain the information needed to calculate the compression ratio of the given file.

SE_DRIVE_NOT_STACKED	The drive you are attempting to get the file compression ratio information from is not a Stacker drive.

SE_INVALID_DATA_PASSED	Invalid data was passed to the function.

This can occur if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status().

SE_INVALID_FUNCTION	A DOS invalid function error occurred while attempting to access the Stacker driver.

SE_ACCESS_DENIED	A DOS access denied error occurred while attempting to access the Stacker driver.

SE_INVALID_HANDLE	A DOS invalid handle error occurred while attempting to access the Stacker driver.

SE_INVALID_DATA	A DOS invalid data error occurred while attempting to access the Stacker driver.

SE_UNEXPECTED_ERROR	An unexpected error occurred while attempting to access the Stacker driver.

Example:	stacker_fastFileCR example   (TESTFCRF.C on the Stacker Developer's Tool Kit diskette.).


 stacker_freeApiSelectors

Purpose:	Free the global selectors used by the Stacker Developer's Tool Kit.

Declaration:	s_return far pascal stacker_freeApiSelectors(void);

Parameters:	None.

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	This function is required for Windows programs which use the Stacker Developer's Tool Kit.

The stacker_status() and stacker_driverInfo() functions allocate and initialize some global selectors for the Stacker Developer's Tool Kit. These selectors must be freed before your program terminates and after you are done making call to the Stacker Developer's Tool Kit functions. This function will free any allocated selectors.

This function is not available for DOS programs.

Return value:

SE_SUCCESSFUL	The function call was successful.

SE_UNABLE_TO_FREE_SELECTOR	This function was unable to free one or more of the global selectors.

Example:	See EXAMWAPI.C on the Stacker Developer's Tool Kit diskette.

Example:	stacker_freeApiSelectors example   (EXAMWAPI.C on the Stacker Developer's Tool Kit diskette.).


 stacker_pack

Purpose:	Compress data using the Stacker device driver (if the Stacker device driver is detected) or using the Tool Kit's own built in compression code.

Declaration:	s_return far pascal stacker_pack(
    const s_char far	*source_pointer,
    s_ushort	source_count,
    s_char far	*dest_pointer,
    s_ushort	dest_count,
    s_ushort far	*compressed_size,
    s_ushort	flags
    );

Parameters:

*source_pointer	Pointer to the buffer in DOS memory holding the bytes to be compressed. It is the calling program's responsibility to normalize the buffer pointers so the end of the buffer does not need to be accessed through a different segment than the start of the buffer. This should not be a problem in Windows applications because the GlobalDosAlloc() function provides normalized pointers to the memory it allocates.

source_count	The number of bytes to compress starting from source_pointer. This must be less than or equal to 63,488 (62k). If this value is greater than 62k stacker_pack() will return SE_SOURCE_BUF_2_LG.

*dest_pointer	Pointer to the buffer in DOS memory to hold the compressed data. It is the calling program's responsibility to normalize the buffer pointers so the end of the buffer does not need to be accessed through a different segment than the start of the buffer. This should not be a problem in Windows applications because the GlobalDosAlloc() function provides normalized pointers to the memory it allocates.

dest_count	Size of the destination buffer. This must be less than or equal to 63,488 (62K). If this value is greater than 62K, stacker_pack() will return SE_DEST_BUF_2_LG.

*compressed_size	Pointer to a field in which to store the size of the compressed data.

flags	Setting bit 0 (the parity bit) causes a parity check byte to be placed after the end of the compressed data. You can use the symbolic names STACKER_PARITY and STACKER_NO_PARITY to turn parity on and off.

Using this parameter you can tune the performance of the compression engine. The following values have been defined:

STACKER_PACK_P1 (fastest)
STACKER_PACK_P2
STACKER_PACK_P3
STACKER_PACK_P4
STACKER_PACK_P5
STACKER_PACK_P6
STACKER_PACK_P7
STACKER_PACK_P8
STACKER_PACK_P9 (best compression)

In Windows applications you can set this to STACKER_USE_WIN_MEM to force using the Tool Kit's built in LZS engine, allowing you to use Windows memory for the buffers instead of DOS memory.

In DOS applications you can set this to STACKER_FORCE_LZSPLUS to force using the Tool Kit's built in LZS engine.

NOTE: Forcing the Stacker Developer's Tool Kit to use the built in LZS engine will be significantly slower than allowing it to use Stacker if it is present.

Do NOT attempt to use a value other than the values defined for this parameter. An invalid value in this parameter will cause unpredictable results (probably locking up the system).

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	You must call stacker_status() or stacker_driverInfo() before making a call to stacker_pack(), otherwise stacker_pack() will return SE_INVALID_GLOBAL_DATA when you attempt to compress a block of data.

This function will use the Stacker device driver for compressing the data if there is a Stacker device driver present. Otherwise, it will use its own built in compression code to compress the data.

For maximum performance on high end systems the buffers referenced by source_pointer and dest_pointer should be double word aligned.

Since compression adds some overhead and some data cannot be compressed, it is possible for the compressed size to be larger than the uncompressed size. This is called expansion. The maximum possible data expansion using Stacker LZS compression is 12.5% of the uncompressed data size. If the compressed data does not fit in the destination buffer this function will return SE_DATA_DID_NOT_COMPRESS. It is possible to have the data expand and still be successful, as long as the destination buffer is large enough for the expanded data. If the data expands, *compressed_size will be larger than source_count.

The Stacker parity is simply an XOR of all of the uncompressed bytes, the value of which is stored after the end-of-compression marker. If you intend to use parity with stacker_unpack() you will need to use parity with stacker_pack(). You cannot compress the data without parity and then decompress the data with parity. You can, however, compress with parity and then decompress without parity.

Using parity will slow performance slightly if the Stacker software version is used, but the typical degradation is less than 5%. Since Stac's LZS compression chips support parity directly, there is no significant performance impact when using parity along with a Stacker Coprocessor.

NOTE:  The stacker_pack() and stacker unpack() functions require 8K of stack space. To reduce the amount of stack space required, you can create a temporary stack that the pack and unpack functions can use. The code dependent on the USE_TEMP_STACK compile time switch in the example at the end of the stacker_unpack description shows how to set up a temporary stack for the stacker_pack() and stacker_unpack() functions.

Return value:

SE_SUCCESSFUL	The function call was successful.

SE_INVALID_DATA_PASSED	Some of the data passed in is invalid.

SE_INVALID_GLOBAL_DATA	One or more of the global fields for the Stacker Developer's Tool Kit is invalid. This can occur if you have not made a previous call to stacker_status() or stacker_driverInfo().

SE_SOURCE_BUF_2_LG	The source buffer is too large. The source_count parameter must be <= 62K (62 * 1024).

SE_DEST_BUF_2_LG	The destination buffer is too large. The dest_count parameter must be <= 62K
(62 * 1024).

SE_DATA_DID_NOT_COMPRESS	The data did not compress. It either did not fit in the destination buffer or there was some other error while attempting to compress the data. *compressed_size will equal 0.

SE_LZS_HW_BUSY	The hardware being used for the compression is busy. Do something else for a little bit and then try again.

SE_UNEXPECTED_ERROR	There was an unexpected error. Do not trust the values returned by this function. This error is extremely rare and should never happen. If it does, the system is probably unstable because the Stacker device driver has been corrupted.

Example:	See example for stacker_unpack().

Example:	stacker_pack and stacker_unpack example   (EXAM_6.C on the Stacker Developer's Tool Kit diskette.).


 stacker_unpack

Purpose:	Decompress data using the Stacker device driver (if the Stacker device driver is detected) or using its own decompression code.

Declaration:	s_return far pascal stacker_unpack(
      const s_char far	*source_pointer,
      s_ushort	source_count,
      s_char far	*dest_pointer,
      s_ushort	dest_count,
      s_ushort far	*uncompressed_size,
      s_ushort	flags
      );

Parameters:

*source_pointer	Pointer to the buffer in DOS memory holding the bytes to be decompressed. It is the calling program's responsibility to normalize the buffer pointers so the end of the buffer does not need to be accessed through a different segment than the start of the buffer. This should not be a problem in Windows applications because the GlobalDosAlloc() function provides normalized pointers to the memory it allocates.

source_count	The size of the buffer containing the data to be decompressed. This must be less than or equal to 63,488 (62k). If this value is greater than 62K, stacker_unpack() will return SE_SOURCE_BUF_2_LG.

*dest_pointer	Pointer to the buffer in DOS memory that will receive the decompressed data. It is the calling program's responsibility to normalize the buffer pointers so the end of the buffer does not need to be accessed through a different segment than the start of the buffer. This should not be a problem in Windows applications because the GlobalDosAlloc() function provides normalized pointers to the memory it allocates.

dest_count	Size of the destination buffer. This must be less than or equal to 63,488 (62K). If this value is greater than 62K, stacker_unpack() will return SE_DEST_BUF_2_LG.

*uncompressed_size	Pointer to a field in which to store the size of the uncompressed data.

flags	Setting bit 0 (the parity bit) enables verification of the decompressed data against the parity check byte generated by setting bit 0 of the flags parameter of the stacker_pack() function. You can use the symbolic names STACKER_PARITY and STACKER_NO_PARITY to turn parity on and off. Bits 1 to 15 of the flags parameter are reserved, and MUST be set to 0.

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	You must make an initial call to stacker_status() or stacker_driverInfo() before making a call to stacker_unpack(), otherwise stacker_unpack() will return SE_INVALID_GLOBAL_DATA when you attempt to decompress a block of data.

This function will use the Stacker device driver or LZS hardware for decompressing the data if it is present in the system. Otherwise, it will use its own decompression code to decompress the data.

For maximum performance on high end systems the buffers referenced by source_pointer and dest_pointer should be double word aligned.

The Stacker parity is simply an XOR of all of the decompressed bytes and the value is stored after the end-of-compression marker. If you intend to use parity with stacker_unpack() you will need to use parity with stacker_pack(). You cannot compress the data without generating a parity check byte and then decompress the data with parity checking enabled. You can, however, compress with parity and then decompress without parity.

Using parity will slow performance slightly if the Stacker software version is used, but the typical degradation is less than 5%. Since Stac's LZS compression chips support parity directly, there is no significant performance impact when using parity along with a Stacker Coprocessor.

NOTE:  The stacker_pack() and stacker unpack() functions require 6.5K of stack space. To reduce the amount of stack space required, you can create a temporary stack that the pack and unpack functions can use. The code dependent on the USE_TEMP_STACK compile time switch in the example at the end of this function description shows how to set up a temporary stack for the stacker_pack() and stacker_unpack() functions.

Return value:

SE_SUCCESSFUL	The function call was successful.

SE_INVALID_GLOBAL_DATA	One or more of the global fields for the Stacker Developer's Tool Kit is invalid. This can occur if you have not made a previous call to stacker_status() or stacker_driverInfo().

SE_SOURCE_BUF_2_LG	The source buffer is too large. The source_count parameter must be <= 62K (62 * 1024).

SE_DEST_BUF_2_LG	The destination buffer is too large. The dest_count parameter must be <= 62K
(62 * 1024).

SE_DATA_DID_NOT_UNPACK	The data could not be decompressed. It either did not fit in the destination buffer or there was some other error while attempting to compress the data.

SE_LZS_HW_BUSY	The hardware being used for the decompression is busy. Do something else for a little while and then try again.

SE_UNEXPECTED_ERROR	There was an unexpected error. Do not trust the values returned by this function. This error is extremely rare and should never happen. If it does, the system is probably unstable because the Stacker device driver has been corrupted in memory.

Example:	stacker_pack and stacker_unpack example   (EXAM_6.C on the Stacker Developer's Tool Kit diskette.).


 stacker_flushDriver

Purpose:	Flushes the Stacker device driver buffers for a Stacker compressed drive.

Declaration:	s_return far pascal stacker_flushDriver( s_char   driveLetter );

Parameters:

driveLetter	The letter of the Stacker drive to flush.

Prerequisites:  stacker_status() or stacker_driverInfo().

Remarks:	This function should only fail if the drive is write-protected. Any other failures represent major problems, such as a program writing over some memory used by the Developer's Tool Kit or the Stacker driver.

You must make an initial call to stacker_status() or stacker_driverInfo() before making a call to stacker_flushDriver().

Return value:

SE_SUCCESSFUL	The drive was flushed successfully.

SE_INVALID_GLOBAL_DATA	Unable to access some data specific to the Stacker Developer's Tool Kit. This can happen if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status() or stacker_driverInfo().

SE_INVALID_FUNCTION	A DOS invalid function error occurred while attempting to access the Stacker driver.

SE_ACCESS_DENIED	A DOS access denied error occurred while attempting to access the Stacker driver.

SE_INVALID_HANDLE	A DOS invalid handle error occurred while attempting to access the Stacker driver.

SE_INVALID_DATA	A DOS invalid data error occurred while attempting to access the Stacker driver.

SE_UNEXPECTED_ERROR	An unexpected error occurred. This can occur if you attempt to make a call to a Developer's Tool Kit function without first making a call to stacker_status() or stacker_driverInfo().

Example:	stacker_flushDriver example   (EXAM_7.C on the Stacker Developer's Tool Kit diskette.).


 stacker_unmountVolume

Purpose:	Unmounts a Stacker compressed drive.

Declaration:	s_return far pascal stacker_unmountVolume( struct stacker_drive_info_struct far* driveInfo );

Parameters:

*driveInfo	A pointer to a stacker_drive_info_struct structure for the drive to unmount. Since this function uses the information in driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before unmounting the volume. See stacker_driveInfo() for more information.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo().

Remarks:	The stacker_unmountVolume() function should only be called after a successful call has been made to stacker_driveInfo().

This function changes the current directory for the drive being unmounted to the root directory when it unmounts the drive.

You should be careful to make sure that there are no open files on the Stacker drive before unmounting it.

Return value:

SE_SUCCESSFUL	Volume unmounted successfully.

SE_DRIVE_NOT_STACKED	Unable to unmount the drive because the drive is not a Stacker drive.

SE_DRIVE_NOT_MOUNTED	Unable to unmount the drive because the Stacker drive is not mounted.

SE_UNABLE_TO_FIND_DPB	Unable to unmount the drive because it could not find the Drive Parameter Block (DPB) for the drive. The only reason this could happen is if something corrupted the DOS "list of lists." If this error occurs, the system is probably unstable.

Example:	stacker_unMountVolume example   (EXAM_8.C on the Stacker Developer's Tool Kit diskette.).


 stacker_hostDriveMode

Purpose:	Puts a mounted Stacker drive in host drive mode.

Declaration:	s_return far pascal stacker_hostDriveMode(
struct stacker_drive_info_struct far *driveInfo,
s_char                                   far *hostDriveLetter
);

Parameters:	*driveInfo	A pointer to a stacker_drive_info_struct structure for the drive to put in host mode. Since this function uses the information from the driveInfo() function,  you must  make a call to stacker_driveInfo() to gather information on the drive before putting the drive in Host Drive Mode. Do not make a second call to stacker_driveInfo() before taking the drive out of Host Drive Mode. If you make a second call to stacker_driveInfo() while a drive is in Host Drive Mode, you will not be able to take the drive out of Host Drive Mode.  See stacker_driveInfo() for more information.

*hostDriveLetter	Pointer to an s_char in which to store the drive letter for accessing the host drive.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo().

Remarks:	Stacker drives can be mounted in three different ways: 1) on a new drive letters, 2) swapped with other drives (as "swapped" drives), or 3) so that they replace or take over the drive letters for their host drives (i.e. as "replaced drives").  This very powerful function allows full access to the host drive of a mounted Stacker drive no matter which way the Stacker drive was mounted, and without having to unmount it and then re-mount it when you are done. After a call to this function the host drive will be accessible by way of the drive letter returned in the hostDriveLetter field passed into this function. This is especially useful when a Stacker drive replaces its host drive so that the host drive would otherwise be inaccessible.

Using stacker_hostDriveMode() will reset the current directory for the given drive to the root directory of that drive. You should take care to save and restore the current directory of the Stacker drive around calls to stacker_hostDriveMode() and stacker_undoHostDriveMode().

WARNING:	The Stacker drive is not accessible while the drive is in host mode. Therefore, you must make sure that no files are open on the Stacker drive that may get written to while the drive is in host mode. This is a critical time and you should leave the drive in host mode for as short of time as possible. You should be particularly mindful of this in multitasking environments such as Microsoft Windows.

The stacker_hostDriveMode() function should only be called after a successful call has been made to stacker_driveInfo() to initialize the stacker_drive_info_struct for the drive.

Use the stacker_undoHostDriveMode() to restore normal access to the Stacker drive and take it out of host drive mode.

Return value:	SE_SUCCESSFUL	The drive is now in host mode.

SE_DRIVE_NOT_STACKED	Unable to put the drive in host mode because the drive is not a Stacker drive.

SE_DRIVE_NOT_MOUNTED	Unable to put the drive in host mode because the Stacker drive is not mounted.

SE_UNABLE_TO_FLUSH_DRIVER	Unable to put the drive in Host Drive Mode because the information buffered by the Stacker device driver for the drive could not be flushed to disk. This should never happen, but if it does, make a call to the stacker_flushDriver() function on the same drive to get a better reason for the failure.

SE_UNABLE_TO_FIND_DPB	Unable to put the drive in Host Drive Mode because it could not find the Drive Parameter Block (DPB) for the drive. The only reason this could happen is if something corrupted the DOS "list of lists." If this error occurs, the system is probably unstable.

Example:	See the sample program HOSTMODE.C.

Example:	stacker_hostDriveMode example   (HOSTMODE.C on the Stacker Developer's Tool Kit diskette.).


 stacker_undoHostDriveMode

Purpose:	Takes a Stacker drive out of host drive mode.

Declaration:	s_return far pascal stacker_undoHostDriveMode(struct stacker_drive_info_struct far *driveInfo);

Parameters:

*driveInfo	A pointer to a stacker_drive_info_struct structure for the drive to take out of host mode. Since this function uses the information from the driveInfo() function,  you must make a call to stacker_driveInfo() to gather information on the drive before putting the drive in Host Drive Mode. Do not make a second call to stacker_driveInfo() before taking the drive out of Host Drive Mode. If you make a second call to stacker_driveInfo() while a drive is in Host Drive Mode, you will not be able to take the drive out of Host Drive Mode. See stacker_driveInfo() for more information.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo, and lastly stacker_hostDriveMode().

Remarks:	After using stacker_hostDriveMode() to put a mounted Stacker drive in host drive mode, use stacker_undoHostDriveMode() to restore normal access to the Stacker drive.

Using stacker_undoHostDriveMode() will reset the current directory for the given drive to the root directory of that drive. You should take care to save and restore the current directory of the Stacker drive around calls to stacker_hostDriveMode() and stacker_undoHostDriveMode().

Return value:

SE_SUCCESSFUL	The drive is out of host mode.

SE_DRIVE_NOT_STACKED	Unable to take the drive out of host mode because the drive is not a Stacker drive. This could only happen if driveInfo points to a drive info structure whose driveLetter element contains a drive letter for a non-Stacker drive.

SE_DRIVE_NOT_IN_HOST_MODE	driveInfo->driveLetter is a Stacker drive that is not in host mode.

SE_UNABLE_TO_FIND_DPB	Unable to take the drive out of Host Drive Mode because it could not find the Drive Parameter Block (DPB) for the drive. The only reason this could happen is if something corrupted the DOS "list of lists." If this error occurs, the system is probably unstable.

Example:	stacker_undoHostDriveMode example   (HOSTMODE.C on the Stacker Developer's Tool Kit diskette.).


 stacker_writeProtect

Purpose:	Write protects a mounted Stacker drive.

Declaration:	s_return far pascal stacker_writeProtect(struct stacker_drive_info_struct far *driveInfo);

Parameters:

*driveInfo	A pointer to a stacker_drive_info_struct structure for the drive to write-protect. Since this function uses the information pointed to by driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before write-protecting the Stacker drive. See stacker_driveInfo() for more information.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo().

Remarks:	The stacker_writeProtect() function should only be called after a successful call has been made to stacker_driveInfo() to initialize the stacker_drive_info_struct for the drive.

This function allows you to logically write-protect a mounted Stacker drive. You should make sure that all files are closed before write protecting the drive.

Return value:

SE_SUCCESSFUL	The drive is now in write-protected mode.

SE_DRIVE_NOT_STACKED	Unable to write-protect the drive because it is not a Stacker drive.

SE_DRIVE_NOT_MOUNTED	Unable to write-protect the drive because the Stacker drive is not mounted.

SE_UNABLE_TO_FLUSH_DRIVER	Unable to write-protect the drive because it was unable to successfully flush the Stacker device driver for the drive. This should never happen, but if it does, make a call to the stacker_flushDriver() function on the same drive to get a better reason for the failure.

Example:	stacker_writeProtect example   (EXAM_9.C on the Stacker Developer's Tool Kit diskette.).


 stacker_unWriteProtect

Purpose:	Clears the write-protect status of a mounted Stacker drive.

Declaration:	s_return far pascal stacker_unWriteProtect(struct stacker_drive_info_struct far *driveInfo );

Parameters:

*driveInfo	A pointer to a stacker_drive_info_struct structure for the drive to be write-protected. Since this function uses the information pointed to by driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before removing the write-protect status. See stacker_driveInfo() for more information.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo().

Remarks:	The stacker_unWriteProtect() function should only be called after a successful call has been made to stacker_driveInfo() to initialize the stacker_drive_info_struct for the drive.

This function allows you to clear the write-protect status of a mounted Stacker drive. Stacker automatically write-protects any Stacker drives that are damaged or physically write-protected when Stacker mounts them.

WARNING:	Writing to a damaged Stacker drive can cause unpredictable results, including damage to data on the drive. To check if the drive is damaged, see if the STACKER_DAMAGED bit in the flagBits element of the stacker_drive_info_struct for the drive is set.

Return value:

SE_SUCCESSFUL	The drive is now writeable.

SE_DRIVE_NOT_STACKED	Unable to clear the write-protect status of the drive because it is not a Stacker drive.

SE_DRIVE_NOT_MOUNTED	Unable to clear the write-protect status of the drive because the Stacker drive is not mounted.

Example:	stacker_unWriteProtect example   (EXAM_10.C on the Stacker Developer's Tool Kit diskette.).


 stacker_initializeRccBuffer

Purpose:	Initializes the Read Compressed Cluster buffer.

Declaration:	s_return far pascal stacker_initializeRccBuffer(
void 	 far  *rccBuffer,
struct stacker_drive_info_struct far  *driveInfo
);

Parameters:

*rccBuffer	Pointer to a buffer in which to store internal data for the stacker_getFatEntry() and stacker_readCompressedCluster() functions. The size needed for this buffer is provided by the stacker_driveInfo() function and is approximately 10 times the number of bytes per sector for the drive. You do not need to call the stacker_initializeRccBuffer() function before each call to a function that uses the Read Compressed Cluster buffer, but you must call it to reinitialize the Read Compressed Cluster buffer before using it on a different drive.

*driveInfo	Pointer to a stacker_drive_info_struct. Filled in by calling program. Since this function uses the information in driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before initializing the Read Compressed Cluster buffer. See stacker_driveInfo() for more information.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo().

Remarks:	This function must be called after allocating memory for the Read Compressed Cluster buffer and before making a call to any of the Developer's Tool Kit functions that use the Read Compressed Cluster buffer. This function only needs to be called once to initialize the Read Compressed Cluster buffer before making any calls to any of the Developer's Tool Kit functions that use the Read Compressed Cluster buffer.

Return value:

SE_SUCCESSFUL	The buffer was initialized successfully.

SE_INVALID_DATA_PASSED	The driveInfo data passed into this function is invalid.

Example:	The sample program RCCTEST.C shows how to use stacker_initializeRccBuffer(), stacker_getFatEntry() and stacker_readCompressedCluster() to read a file in compressed form.

Example:	stacker_initializeRccBuffer example   (RCCTEST.C on the Stacker Developer's Tool Kit diskette.).


 stacker_getFatEntry

Purpose:	Gets the FAT entry for a cluster on a Stacker drive.

Declaration:	s_return far pascal stacker_getFatEntry(
s_ushort	clusterNumber,
struct stacker_drive_info_struct far	*driveInfo,
s_char far	*rccBuffer,
s_ushort far	*fatEntry
);

Parameters:

clusterNumber	The cluster number to get the FAT entry for.

*driveInfo	Pointer to a stacker_drive_info_struct structure. Since this function uses the information in driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before getting a FAT entry for the cluster on a Stacker drive. You do not have to call the stacker_driveInfo() function each time you get a FAT entry, but you must call it to reinitialize the drive info structure or initialize a new drive info structure before reading from a different drive. See stacker_driveInfo() for more information.

*rccBuffer	Pointer to a scratch buffer for this function to use. The size needed for this buffer is provided by the drive info function and is approximately 10 times the number of bytes per sector for the drive. You do not have to call the stacker_initializeRccBuffer() function each time you get a FAT entry, but you must call it to reinitialize the Read Compressed Cluster buffer before reading from a different drive. See stacker_initializeRccBuffer() for more information.

*fatEntry	Pointer to an s_ushort to store the FAT entry for clusterNumber.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo(), followed by stacker_initializeRccBuffer().

Remarks:	This function is provided to facilitate walking the FAT of a Stacker drive. It is designed to be used in conjunction with the stacker_readCompressedCluster() function to read files and directories directly from a Stacker drive in compressed form. Files read this way can later be decompressed using the stacker_unpack() function, one of Stac's LZS221 compression libraries, or Stac's data compression chips.

The calling program fills the rccBuffer field with the address of the scratch buffer it has allocated for this function. The size needed for this buffer is provided by the stacker_driveInfo() function.

See chapters 8 and 9 of the "DOS Programmer's Reference" written by Terry Dettmann and published by QUE Corporation for more information on the DOS FAT file system.

Return value:

SE_SUCCESSFUL	The function call was successful.

After a successful call to stacker_getFatEntry *fatEntry will contain the FAT entry for the specified cluster.

SE_INVALID_FUNCTION	An invalid function error occurred while attempting to access the Stacker device driver.

SE_ACCESS_DENIED	An access denied error occurred while attempting to access the Stacker device driver.

SE_INVALID_HANDLE	An invalid handle error occurred while attempting to access the Stacker device driver.

SE_INVALID_DATA	An invalid data error occurred while attempting to access the Stacker device driver.

SE_UNEXPECTED_ERROR	An unexpected error occurred while attempting to access the Stacker device driver.

Example:	See the sample program RCCTEST.C for an example of how to use stacker_getFatEntry() and stacker_readCompressedCluster() to read a file in compressed form.

Example:	stacker_getFatEntry example   (RCCTEST.C on the Stacker Developer's Tool Kit diskette.).


 stacker_readCompressedCluster

Purpose:	Transfers compressed data for a cluster to a buffer.

Declaration:	s_return far pascal stacker_readCompressedCluster(
struct stacker_drive_info_struct far *driveInfo,
s_ushort	clusterNumber,
s_char far	*compDataBuffer,
s_char far	*rccBuffer,
s_ushort far	*compByteCount,
s_ushort far	*compDataFormat
s_ushort	flags
);

Parameters:

*driveInfo	Pointer to a stacker_drive_info_struct structure. Since this function uses the information in driveInfo, you must make a call to stacker_driveInfo() to gather information on the drive before reading compressed data from the Stacker drive. You do not have to call the driveInfo function each time you read a compressed cluster, but you must call it to reinitialize the drive info structure or initialize a new drive info structure before reading from a different drive. See stacker_driveInfo() for more information.

clusterNumber	Number of the cluster to read.

*compDataBuffer	Pointer to the buffer that receives the compressed data. This buffer should be large enough to hold one cluster plus one sector.

*rccBuffer	Pointer to a scratch buffer for this function to use. The size needed for this buffer is provided by the drive info function and is approximately 10 times the number of bytes per sector for the drive. You do not have to call the stacker_initializeRccBuffer() function each time you read a new cluster, but you must call it to reinitialize the Read Compressed Cluster buffer before reading from a different drive. See stacker_initializeRccBuffer() for more information.

*compByteCount	Pointer to a s_ushort that will store how many bytes were read.

*compDataFormat	Pointer to a s_ushort that will store the compressed data format. See below.

flags	Reserved.  Must be set to 0.

Prerequisites:  stacker_status() or stacker_driverInfo(), then stacker_driveInfo() followed by stacker_initializeRccBuffer().

Remarks:	This function reads a compressed cluster from a Stacker drive. It is designed to be used in conjunction with the stacker_getFatEntry() function to read files and directories directly from a Stacker drive in compressed form. Files read this way can later be decompressed using the stacker_unpack() function, one of Stac's LZS221 compression libraries, or Stac's data compression chips.

Compressed data for the specified clusterNumber is transferred into the destination buffer pointed to by compDataBuffer. The size of destination buffer must be equal to or larger than the cluster size of the Stacker drive specified by driveLetter plus the size of one sector. An example would be a buffer that could hold 17 sectors when the drive has 16 sectors per cluster.

The calling program fills the rccBuffer field with the address of the scratch buffer it has allocated for this function. The size needed for this buffer is provided by the stacker_driveInfo() function.

The compByteCount field is filled in by the function to indicate the number of bytes of compressed data transferred into the destination buffer rounded up to the next multiple of 512 bytes.  (512  is the sector size and the minimum amount Stacker can read from or write to the disk.) A result value of zero indicates that no data is stored for the cluster specified by clusterNumber.

The compDataFormat field indicates the format of the data transferred into the destination buffer by the function. The following are the possible values for compDataFormat:

STACKER_LZS_FORMAT	Stacker LZS

STACKER_INCOMPRESSABLE	Data that cannot be compressed

STACKER_NOT_COMPRESSED	Uncompressed

Other	Reserved

NOTE:	If compDataFormat is STACKER_NOT_COMPRESSED you can compress the cluster using the stacker_pack() function in the Stacker Developer's Tool Kit.  If compDataFormat is STACKER_INCOMPRESSABLE there is no need to compress it using the stacker_pack() function because the data will probably expand.

Return value:

SE_SUCCESSFUL	The function call was successful.

SE_INVALID_DATA_PASSED	A value passed into this function is invalid.

SE_DRIVE_NOT_STACKED	Unable to read the compressed cluster because the drive is not a Stacker drive.

SE_INVALID_FUNCTION	A DOS invalid function error occurred while attempting to access the Stacker driver.

SE_ACCESS_DENIED	A DOS access denied error occurred while attempting to access the Stacker driver.

SE_INVALID_HANDLE	A DOS invalid handle error occurred while attempting to access the Stacker driver.

SE_INVALID_DATA	A DOS invalid data error occurred while attempting to access the Stacker driver.

SE_UNEXPECTED_ERROR	An unexpected error occurred while attempting to access the Stacker device driver.

NOTE:	The following fields will only be filled in upon a successful call to this function.

compDataBuffer	The compressed data that was read in.

compByteCount	The number of bytes read. 0 if the cluster does not contain data.

compDataFormat	The compressed data format. See above.

Example:	The sample program COMPCLU.C shows how to read compressed clusters. RCCTEST.C shows how to use stacker_readCompressedCluster() and stacker_getFatEntry() to read a file in compressed form.

Example:	stacker_readCompressedCluster example   (RCCTEST.C on the Stacker Developer's Tool Kit diskette.).


 Important Considerations For Stacker 3.1 and Beyond

As we create new versions of Stacker some things will change. This chapter describes these changes and how they can affect how your program works under different versions of Stacker. Please take a moment to read through these changes and make sure that your application does not break under different versions of Stacker.


 Stacker 3.1

Stacker 3.1 for MS DOS 6.0 is different than any previous version of Stacker in that the Stacker device driver actually loads before the CONFIG.SYS file.  This is an excellent improvement in that you no longer have two CONFIG.SYS and AUTOEXEC.BAT files (one on the Stacker drive and one on the host drive) since the Stacker drives are mounted before the CONFIG.SYS file loads.  Because the Stacker drives are mounted and swapped before the CONFIG.SYS file loads the Stacker drive mapping is reported differently with Stacker 3.1.  Here are a some examples:

Swapmap with Stacker 3.0 device driver:

Drive A: was drive A: at boot time	[ Auto-mounting Stacker drive ]

Drive B: was drive B: at boot time

Drive C: was drive D: at boot time	[ D:\STACVOL.DSK = 94.9MB ]

Drive D: was drive C: at boot time

Drive E: was drive E: at boot time	[ D:\STACVOL.000 = 15.7MB ]

Swapmap with Stacker 3.1 device driver:

Drive A: was drive A: at boot time	[ Auto-mounting Stacker drive ]

Drive B: was drive B: at boot time

Drive C: was drive C: at boot time	[ D:\STACVOL.DSK = 94.9MB ]

Drive D: was drive D: at boot time

Drive E: was drive E: at boot time	[ D:\STACVOL.000 = 15.7MB ]

As you can see the drive mapping for Stacker 3.1 is different from Stacker 3.0.  This is because Stacker 3.1 loads before the CONFIG.SYS file and the drives are swapped before the CONFIG.SYS file loads.  Thus, with Stacker 3.1 and MS DOS 6.0 "boot time" means the time at which CONFIG.SYS processing begins.  Notice that the information in square brackets on the right side of the listings is the same under both versions of Stacker.  This is because the Stacker device driver keeps the swap data in two different areas in the device driver.  The left side (the drive letters) is kept in a Swapmap table and the right side is kept as part of the STACVOL file name.

The Stacker Developer's Tool Kit has several methods for determining the swap information.  The functions are listed below.

Swap information using the Swapmap table:

stacker_getCurrentDriveLetter()

stacker_getBootTimeDriveLetter()

Swap information using the STACVOL file name:

stacker_driveInfo() - using the hostDriveLetter element in the stacker_drive_info_struct structure.

If you are currently using one of the two swap information functions that use the Swapmap table to determine the host drive letter of a Stacker drive you will want to change your program to use the stacker_driveInfo() function instead.  If you only need to access the host drive for a short period of time then you may want to put the Stacker drive in Host Drive Mode using the stacker_hostDriveMode() function.  Please see the description of stacker_hostDriveMode() for more information on Host Drive Mode.

You are probably wondering when you will need to use the different swap information functions.  Here is a general description of when to use the different functions.

stacker_getCurrentDriveLetter() - Used in installation programs to determine where the "real" CONFIG.SYS file is located.  Under Stacker 3.0 and before it will give you the host drive and under Stacker 3.1 it will give you the Stacker drive.

stacker_driveInfo() - This will always give you the host drive letter for the Stacker drive.  This should be used by programs that need to read from or write to the host drive of a Stacker drive, such as disk repair utilities and disk defragmenters.

stacker_hostDriveMode() - This function will return a drive letter you can use to access the host drive of a Stacker drive.  If the Stacker drive is replaced (i.e. the Stacker drive uses the same drive letter as its host drive) it will temporarily unmount the Stacker drive.  You can then use stacker_undoHostDriveMode() to make the Stacker drive visible again.  Note that the Stacker drive is not accessible when it is in Host Drive Mode.

There is a new element in the stacker_driver_info_struct structure called preLoaded that will tell you if the Stacker device driver was preloaded (loaded before the CONFIG.SYS file).  Please see the description of the stacker_driverInfo() function for more information on the preLoaded structure element.


 Testing considerations

There are several Stacker specific conditions to consider when writing programs that use the Stacker Developer's Tool Kit. This section contains an outline of many test configurations that you should run your programs through to make sure that they work perfectly with Stacker.


 General testing considerations

Stacker not installed: Make sure that your programs are tested on computers that do not have Stacker loaded. Your program should only make calls to stacker_status(), stacker_driverInfo(), stacker_pack(), and stacker_unpack() if Stacker is not installed on the system.

Swapped Stacker drives: If your program needs to access the host (physical) drive, you should make sure that it does not get confused by Stacker's drive letter swapping. The Developer's Tool Kit provides support for determining which drive letters have been swapped and the drive letter assignments before and after swapping.

Replaced Stacker drives (started with Stacker 2.00): If your program needs to access the host (physical) drive, you will need to use the stacker_hostDriveMode() and stacker_undoHostDriveMode() functions. These functions allow you to access the host drive for a given Stacker drive through a drive letter passed back by the stacker_hostDriveMode() function. You must make sure that no background programs attempt to write to files on the Stacker drive while it is in host drive mode. If they do, it will produce a critical error.

Removable Stacker drives (started with Stacker 2.00): One of the problems with removable media is that the user can change disks while your program is running. You should try this to make sure your program handles this problem gracefully.

Various DOS versions: Stacker and the Stacker Developer's Tool Kit support MS and PC DOS versions 3.0 and higher and DR DOS 5.0 and higher.

32K cluster size Stacker drives (started with Stacker 3.00): This cluster size is only allowed on volumes greater than 64MB in size and can be selected through the advanced options section of the Stacker installation program.

16K cluster size Stacker drives (started with Stacker 3.00): This cluster size is only allowed on volumes greater than 32MB in size and can be selected through the advanced options section of the Stacker installation program.

8K cluster size Stacker drives: This is the default and most common cluster size for Stacker drives.

4K cluster size Stacker drives: Even though this is not a common cluster size for the Stacker drive it is still supported and used by many Stacker customers. When creating your Stacker drive you can select the cluster size under the advanced options in the Stacker installation program.

Auto-mounting Stacker drives (started with Stacker OEM version 2.02): Removable Stacker drives can be automatically mounted every time you change them if auto-mounting is enabled. To enable auto-mounting add "/AUTO" to the STACKER.COM device line in your CONFIG.SYS file. To set up a drive as an auto-mounting drive add the drive letter, with a colon, to the STACKER.COM device line.

Example:

Before:

device=c:\stacker\stacker.com c:\stacvol.dsk @

After:

device=c:\stacker\stacker.com c:\stacvol.dsk @ a: /auto

STACKER.EXE (started with Stacker OEM version 2.02): This version of the Stacker driver can be loaded from the command line and used to read Stacker compressed floppy diskettes and other Stacker compressed removable media on computers that do not have Stacker installed.

Different versions of Stacker: The current version of the Stacker Developer's Tool Kit works with all DOS versions of Stacker, including all products in development. Stacker 3.1 was the most recent shipping version at the time this document was written.

Load Stacker high with various memory managers.

Load the Stacker device driver before the CONFIG.SYS file: Starting with Stacker 3.1 the Stacker device driver can load before the CONFIG.SYS file if you are using MS DOS 6.0. This affects the drive swapping because the compressed Stacker drives are mounted and swapped before the CONFIG.SYS file loads. Please see the chapter titled "Important Considerations For Stacker 3.1 and beyond".


 If your program writes to Stacker drives

Physical disk full conditions: A Stacker drive is physically full when all of the data storage space in the STACVOL file for the drive has been used up. This can occur when the data that is written to a Stacker drive compresses at or below the drive's expected compression ratio. When a Stacker drive is created the actual amount of physical space available for storing data in the STACVOL file is multiplied by the expected compression ratio to determine the logical size of the Stacker drive. Thus, if a 60MB Stacker drive was created from a 20MB STACVOL file then the expected compression ratio for the Stacker drive is 3 to 1. The default expected compression ratio for Stacker drives is 2 to 1. However, at the time a Stacker drive is created the user can specify an expected compression ratio anywhere in the range from 1.1 to 1 up to 8 to 1. After the drive has been created the user can change the expected compression ratio to a value between 1.1 to 1 and 16 to 1 using the SDEFRAG program.  (At 16 to 1 a 20MB STACVOL file would appear as a 320MB Stacker drive.)

To setup a physical disk full situation for testing you will need to create a Stacker drive with a high expected compression ratio. To do this run the Stacker setup program and choose the custom setup options. Under the advanced options choose an expected compression ratio of 8 to 1. After the Stacker drive is created fill it with .COM and .EXE files (these typically do not compress very well). When attempting to write to the disk your program should determine that the disk is full and handle this situation gracefully.

NOTE:  If you attempt to write data to a Stacker drive and the new data does not compress as well as the data already on the drive then a physically full condition may occur during the disk write.  The Stacker device driver reports disk full conditions during a write to the disk as 0 bytes written, but no errors. Your program should always make sure that there is enough space available before writing to the disk and it should check how many bytes were written to make sure that all of the data was written.

Logical disk full conditions: A Stacker drive is logically full when all of the cluster entries in its FAT have been used up. This can occur when the data that is written to a Stacker drive compresses at or above the drive's expected compression ratio. Since all data varies in compressibility, it is possible to run out of logical disk space when there is space physically available in the STACVOL file.

To setup this situation for testing you will need to create a Stacker drive with a low expected compression ratio. To do this run the Stacker setup program and choose the custom setup options. Under the advanced options choose an expected compression ratio of 1.1 to 1. After the Stacker drive is created fill it with very compressible files (such as database or ASCII files). The CHECK (SCHECK for Stacker 2.0 and before) utility will tell you the compression ratio of all of the files on the drive. This compression ratio should be greater than 2.0 to 1. Once the disk is full according to CHKDSK, run SCHECK to see if the STACVOL file has space available even though the projected bytes free is 0. Stacker intercepts DOS Interrupt 21 function 36 (used to determine the number of clusters available on the drive) to report the projected number of clusters available on the drive, so the DOS DIR command should also report that the disk is full. When attempting to write to the disk your program should determine that the disk is full and handle this situation gracefully.

Write-protected Stacker drives: With Stacker the drive can be write-protected by the Stacker driver, in addition to the normal method of physically write protecting the drive. This only happens if the Stacker device driver detects that the Stacker drive is damaged as it is mounted. The Stacker Developer's Tool Kit gives you the ability to remove the write-protect, but THIS SHOULD NEVER BE DONE SO THAT YOU CAN JUST WRITE TO A FILE. It was only added to the Stacker Developer's Tool Kit so that disk repair utilities can repair damaged Stacker drives.  An easy way to test this feature is to physically write protect a Stacker drive on removable media.


 Tests for the Compressed Data Functions

If your program uses the stacker_readCompressedCluster(), stacker_pack(), or stacker_unpack() functions you should test with files and data that vary in size and compressibility. Since nearly all files compress differently you should test your programs with everything from very compressible files (such as ASCII files) to uncompressable files (such as .ZIP files). In addition to testing on files that compress differently, you should also test on very small (less than one sector) and very large (greater than one cluster) files.

If your program uses the Read Compressed Data Functions you should test with Stacker converted drives as described on the next page.


 Tests for Stacker Converted Drives

Stacker 3.1 added the ability to convert drives that have been compressed with some other vendors compression products, such as Microsoft's DoubleSpace® Utility, to Stacker drives.  This conversion has two phases.  The first stage is performed by Stacker's SETUP program.  This first step takes only a few seconds.  It replaces the existing file system data structures with the redundant and safer data structures of the Stacker compressed file system, but does not convert the compressed data itself to the Stacker LZS compressed data format.  Although the existing data is not converted to Stacker LZS data any new data that is written to the drive will be compressed in Stacker LZS format.  The second part of the conversion is completed when you run SDEFRAG, the Stacker disk optimizer,  on the converted volume.  SDEFRAG detects that the drive has had the initial conversion and finishes the full conversion by recompressing all of the data with the Stacker LZS compression algorithm.

When using the stacker_readCompressedCluster() function on a drive that has had the initial conversion performed by SETUP, but has not been fully converted with SDEFRAG, any non-LZS compressed data that is read from the drive will automatically be decompressed and reported as STACKER_NOT_COMPRESSED data.  If you want you can then compress this data to Stacker LZS format using the stacker_pack() compression function.

The stacvolVersion as reported by the stacker_driveInfo() function is 1.00 to 1.99 for drives created with Stacker 1.XX.   It is 2.00 to 2.99 for drives created by Stacker 2.XX, or drives created with Stacker 1.XX and converted to 2.XX format by Stacker 2.XX's SDEFRAG utility, or for removable Stacker drives with 4K or 8K cluster created under Stacker 3.XX.  The stacvolVersion is 3.00 to 3.99 for drives created with Stacker version 3.XX, including removable Stacker drives with 16K or 32K clusters, and for drives which were created with a different compression product, but have been completely converted to Stacker drives with Stacker 3.1's SETUP and SDEFRAG utilities.  The stacvolVersion is 4.00 to 4.99 for drives which for drives which were created with a different compression product and have only undergone the first phase of the conversion to a Stacker drive with Stacker' 3.1's SETUP utility.

You should make sure that you test your program on drives that have been through the first part of this conversion, but have not had SDEFRAG finish the full conversion.  You should then have SDEFRAG finish the conversion and then test your program again.


 Testing installation programs with Stacker

You should make sure to test your software's installation program on a Stacker drive.  There are a number of important issues regarding programs that install software when Stacker is running on a system.  For example the following questions should be answered:  "How can I guarantee that the software I'm installing will fit on the compressed drive?", "My device driver needs to be installed before Stacker, how do I know which is the right drive to install it to?", and "I understand that some versions of Stacker maintain two copies of the CONFIG.SYS and AUTOEXEC.BAT files.  How does this affect my program which needs to update these files?"  For a complete discussion of the issues relating to Stacker and installation programs read the chapter titled "Install Programs and Stacker".


 Non-Developer's Tool Kit specific tests

Below are some STACKER.COM device driver switches that do not directly impact the Stacker Developer's Tool Kit, but you should test with to improve the robustness of your programs:

/EMS  : This tells the Stacker device driver to use EMS memory for disk buffers.

/B=xxxx  : This is for Stacker hardware only and specifies the hardware address for the type of Stacker coprocessor you are using.

/M=xx  : This is the Stacker cache size. The maximum cache size is 64K bytes. The minimum is zero.

/C=x  : This changes the default cluster size from 8K to either 4K, 16K or 32K. In the case of 4K clusters, all of the Stacker drives must have a cluster size of 4K. For 16K or 32K default clusters, none of the drives have to have the larger clusters. 16K and 32K cluster support is only available with Stacker 3.0 and higher.

See the documentation for the latest version of Stacker for a more complete description of these switches.


 Install Programs and Stacker

This section has been specifically designed to assist developers who write installation programs. It discusses all issues regarding Stacker and installation programs, and it shows how to use the Stacker Developer's Tool Kit to ensure complete compatibility. C source code for an example Stacker compatible installation program is included on the Stacker Developer's Tool Kit diskette in the file INSTALL.C. This program actually installs a very simple device driver that is loaded by CONFIG.SYS. An executable form of this program, INSTALL.EXE, has also been included on the diskette.


Installation Programs
 Does your installation program need the Developer's Tool Kit?

Below is a check list to see if you need to add the Stacker Developer's Tool Kit to your installation program to make it work perfectly with Stacker. If you answer "Yes" to any of the questions, you should add the Stacker Developer's Tool Kit to your installation program.

1.	Does your installation program modify the CONFIG.SYS or AUTOEXEC.BAT files?

2.	Does the program you're installing require that any device drivers be loaded in CONFIG.SYS before the Stacker device drivers?

3.	Does your program check how much disk space is available before copying its files to the destination path?


 What happens when Compressing a Boot Drive?

There are many things that happen when you compress a boot drive. First, Stacker's CREATE utility (SCREATE with versions 2.0 and before) creates a small empty STACVOL file on the drive. Next, it compresses the files on the drive and copies them into the STACVOL file. After each copy operation the newly compressed data is verified to make sure that it can be read without error. After verification the original files are deleted from the drive. Not all of the files on the drive get deleted. Stacker leaves system files, files referenced in CONFIG.SYS, hidden files and some special files that Stacker knows must remain on the host drive. After each set of file deletes the STACVOL file is enlarged to fill the space which has just been freed up to make room for more compressed data. The process of compressing, copying to the STACVOL file, verifying, deleting from the uncompressed drive, and enlarging the STACVOL file then repeats over and over until all of the data that is destined to end up on the Stacker drive has been compressed.  At this point roughly half the drive is empty and half of it is filled with the STACVOL file. The CONFIG.SYS file is then modified to load Stacker on the next reboot and mount the newly created STACVOL file as a Stacker drive on the next available DOS drive letter. After the changes have been made to CONFIG.SYS, Stacker runs a standard DOS disk defragmenter to defragment the STACVOL file itself while the drive is half empty and the defragmenter has lots of room to work with. Lastly the STACVOL file is enlarged to its full size, the media under the STACVOL file checked for errors, and the PC is rebooted to mount the new Stacker drive. For each Stacker drive the physical drive containing the STACVOL file for the drive is referred to as the "host drive" for that Stacker drive. By default Stacker will leave 1MB of uncompressed space on the host drive for any new device drivers and their support files which may be installed in the future.


Batch Files, The Path, and Configured Software
 The Problem with Batch Files, The Path,

    and Configured Software

The fact that the Stacker drive mounts on a new drive letter can cause several problems when you go to execute your programs on it. This is because the program is now located on a different drive letter than it was originally installed on. If the program gets executed from a batch file the batch file will be looking on the wrong drive for the program files. Additionally any items in the PATH statement that refer to directories on the drive that has been compressed will now reference the wrong drive letter for those directories. Stacker's SSWAP program was created to solve this problem.

SSWAP.COM is a program that interchanges the drive letters of two drives. By using SSWAP to exchange the drive letters of the Stacker drive and its host drive, Stacker assures that your files will be accessible by way of the same drive letter as they were originally installed on.


  Stacker and the boot process for DOS's prior to MS DOS 6.0

When Stacker is used with MS DOS 3.2 - MS DOS 5.0, PC DOS 3.2 - PC DOS 5.0, Compaq DOS 3.31, DR DOS 5.0, or DR DOS 6.0 the Stacker device driver is loaded as part of DOS's processing of the list of devices in CONFIG.SYS.  As the  computer boots and executes the lines in CONFIG.SYS, the Stacker drives will be unavailable until the Stacker device driver is loaded and it mounts the STACVOL files as new drives. Prior to this point the Stacker drives don't exist as drives yet. They are just large hidden files on the physical disk.

Consequently, very careful attention must be paid to the assignment of drive letters during the execution of CONFIG.SYS. Consider the following example CONFIG.SYS file:

DEVICE=C:\DOS\HIMEM.SYS

DEVICE=C:\STACKER\STACKER.COM C:\STACVOL.DSK

DEVICE=C:\STACKER\SSWAP.COM C:\STACVOL.DSK /SYNC

DEVICE=C:\DOS\SMARTDRV.SYS

In this example HIMEM.SYS, STACKER.COM and SSWAP.COM need to be on the host (uncompressed) drive. The SMARTDRV.SYS program would need to be on the Stacker drive. As the CONFIG.SYS file gets executed you can see that the Stacker device driver gets loaded after HIMEM.SYS. It is only after this point that the Stacker drive can be accessed. However, the SSWAP command must be run before you can access the Stacker drive using the same drive letter as the original boot drive. If you load your driver after the STACKER.COM driver, but before the SSWAP.COM program, you would not know which drive letter to reference the driver on. This is because Stacker uses the next available drive letter for the Stacker drive and this can vary by adding a RAM drive or any other new drives to the system.

When an existing drive and its data are compressed to create a new Stacker drive all device drivers on that drive that are listed in the CONFIG.SYS file are copied to the Stacker drive and left behind on the host drive. This makes sure that the device drivers are available when the computer boots.

A problem occurs when device drivers need additional support or overlay files. Since Stacker cannot leave all files on both drives and it does not know what support files are needed by the different device drivers, it will copy the support files to the compressed Stacker drive. This can create a problem if the device driver that needs these support files is loaded before the Stacker device driver in the CONFIG.SYS file because these support files are not accessible until after the Stacker device driver loads and the Stacker drive is mounted.

By the time the AUTOEXEC.BAT file gets executed all of the Stacker drives should be mounted and swapped. This is the default for Stacker. However, it is possible to mount and swap from the AUTOEXEC.BAT file or the command line, as long as the Stacker device driver has been loaded in the CONFIG.SYS file prior to attempting to mount.


Modification of CONFIG.SYS and AUTOEXEC after Compressing with Stacker 3.0 or before
 Modification of CONFIG.SYS or AUTOEXEC.BAT

    after Compressing with Stacker 3.0 or before

Another issue that arises with compressed boot drives is that most programs modify the CONFIG.SYS and AUTOEXEC.BAT files on the C: drive, without asking the user what their real boot drive is, or accounting for any drive letter swapping. Because of this Stacker creates and maintains two copies of CONFIG.SYS and AUTOEXEC.BAT, one on the Stacker drive and one on the host drive.

Starting with Stacker version 2.00 a boot file synchronizer feature was added to SSWAP.COM. This feature allows programs to modify the boot files (CONFIG.SYS and AUTOEXEC.BAT) on a compressed boot drive and then the next time the computer is rebooted the synchronizer updates the boot files on the host boot drive.

The /SYNC, or synchronizer option, for SSWAP.COM is the default for new installations of Stacker version 2.00 and higher. However, this feature is not automatically enabled if the user upgraded from version 1.X to version 2.X and you should not assume that this feature is enabled by simply checking the Stacker driver version.

NOTE: The Stacker Developer's Tool Kit does not have a function to determine if a drive is being synchronized.

In addition to synchronizing the CONFIG.SYS and AUTOEXEC.BAT files, /SYNC will also make sure that COMMAND.COM is the same on both drives. This helps prevent problems when a user changes or deletes COMMAND.COM from either of the drives.

Stacker 3.0 enhanced the /SYNC option by synchronizing all the device drivers that are listed in CONFIG.SYS.  However because previous versions did not synchronize all device drivers in CONFIG.SYS it is necessary that the user and developers make sure that updates to all device drivers and their support files are placed on the correct drives. The upgrade/installation program may modify the drivers on the Stacker drive, but the device line in CONFIG.SYS may be before the Stacker device line, causing the old drivers to be loaded. See "Copying device drivers to the destination drive" below.


 Stacker 3.1 and the Boot process for MS DOS 6.0

Prior to MS DOS 6.0 the boot sequence for an IBM compatible PC was as follows:  First the microprocessors reset interrupt vector starts execution in the boot ROM.  This code performs a number of start up tests and then looks to see if there is a bootable disk drive available.  If there is, sector zero, the boot sector, is read, loaded into memory and execution is transferred to the boot code read from the disk.  This code then begins to install DOS by loading the system file IBMDOS or MSDOS.SYS from the disk into memory and then transfers execution to it.  MSDOS.SYS then loads the second system file IO.SYS and begins building up the basic system IO services.  Next CONFIG.SYS is processed and the device driver it lists are loaded and each is assigned as many drive letters as it requests.  Next COMMAND.COM is loaded to provide the internal DOS commands like COPY, and lastly AUTOEXEC.BAT is processed to perform any commands that the user wants to have performed each time the computer boots.

For the first time in many years, a change to this boot order was introduced with MS DOS 6.0.  To avoid the need to keep and synchronize two copies of CONFIG.SYS and AUTOEXEC.BAT when a disk compression device driver is used to compress the boot drive MS DOS 6.0 loads the compression driver as a third system file, in addition to MSDOS.SYS and IO.SYS, which is loaded before CONFIG.SYS is processed.  This file is called DBLSPACE.SYS.  Since this file is loaded and the compression driver is available before CONFIG.SYS is processed, CONFIG.SYS and AUTOEXEC.BAT can both reside on just the compressed part of the boot drive.  Because of this there is only one copy of CONFIG.SYS and AUTOEXEC.BAT and the whole problem of synchronization goes away.

Stacker 3.1 supplies its own version of the DBLSPACE.SYS file which under MS DOS 6.0 can be loaded before CONFIG.SYS is processed.  In its default mode of operation under MS DOS 6.0 the Stacker 3.1  device driver will be loaded when Stac's version of the DBLSPACE.SYS file is loaded before CONFIG.SYS is processed.  This is called "preloading".  Although preloading is Stacker 3.1's default mode of operation under MS DOS 6.0, the old method of loading STACKER.COM in CONFIG.SYS and swapping and synchronizing the uncompressed and compressed boot drives still works as well.  This was done to provide additional compatibility with certain device drivers that must load before Stacker.

You can use the preLoaded element of the stacker_driver_info_struct returned by the stacker-driverInfo() function to determine if Stacker was loaded before CONFIG.SYS or not.


 Making installation programs work with Stacker

There are three things you can do to make your installation program work better with Stacker. First, if your program modifies CONFIG.SYS or AUTOEXEC.BAT, the boot drive is a Stacker drive, and Stacker was not preloaded under MS DOS 6.0 or later, then your program will need to modify the CONFIG.SYS file on the host drive and the AUTOEXEC.BAT file on both the Stacker drive and its host drive. Second, if your program updates device drivers or system files and the drive is a Stacker drive it will need to do so on the host drive of the Stacker drive. Third, if the drive your program is copying its files to is a Stacker drive you should check how much disk space is available on the Stacker drive based on how well your files compress.


 Modifying CONFIG.SYS or AUTOEXEC.BAT

A Stacker drive is not available until it has been mounted by the Stacker device driver. Thus, if the boot drive has been swapped or replaced with a Stacker drive and Stacker is not preloading under MS DOS 6.0 or later you should always modify the CONFIG.SYS file on the uncompressed portion of the boot drive and the AUTOEXEC.BAT file on both the compressed and uncompressed portions of the boot drive. Note that under an OS/2 and MS DOS 6.0 dual boot system Stacker will not preload when the PC is booted under OS/2.  If Stacker is installed on the system, the C: drive may not be the original DOS assigned boot drive. Because of this you should use the Stacker Developer's Tool Kit to access the real boot drive.

To check if the boot drive is a Stacker drive you should make a call to the stacker_driveInfo() function. This function takes a pointer to a stacker_drive_info_struct structure and will tell you if the boot drive is a Stacker drive or not.  Please see stacker_driveInfo() in the Stacker Developer's Tool Kit Reference portion of this manual for more information about this function.

Compressed Boot Drives

If the boot drive is a Stacker drive you will need to use the stacker_hostDriveMode() function to access its host drive (the uncompressed portion of the boot drive). This function will provide a drive letter by which you can access the host drive of the Stacker drive.  See stacker_hostDriveMode() in the Stacker Developer's Tool Kit Functions portion of this manual.


Disk Space Availability
 Checking how much disk space is available before copying files

If the files you are copying to the Stacker drive do not compress as well as the data that is already on the drive it is possible for DOS to report that there is plenty of space for your program, but then fail part of the way through the copy with a disk full error. This is because Stacker intercepts DOS Interrupt 21 function 36 and reports the number of projected clusters available based on the overall compression ratio of the files already on the disk. To get around this problem you can use the stacker_driveInfo() function to determine how much physical space is available on a Stacker drive. The physical bytes free can then be multiplied by the expected compression ratio of your files to see if there is enough room for all of your files.

Determining the Physical Bytes Free on a Stacker Drive

To determine the physical space available (also known as the minimum space available) on a Stacker drive make a call to the stacker_driveInfo() function for the drive you want to check. Next add the freeSectorCount and erasedSectorCount fields returned in the drive info structure. Multiply this total by the number of bytes per sector for the drive. This total is the minimum amount of disk space available under the worst case ("worst case" meaning that the new files you are copying to the drive don't compress at all).

Determining the Compressed Physical Space Requirements of Your Files

To determine the actual physical space requirements of your files, after accounting for compression, copy all of your files to an empty Stacker drive and observe the STACVOL file space usage as reported by the middle row of the right column of SCHECK /D. See the example SCHECK output below. This is the number of bytes that you must have physically free to fit all of your files on a Stacker drive.

NOTE: Starting from Stacker 3.0 the SCHECK program is called CHECK.

Example SCHECK output showing Stacker drive cluster size and physical bytes used in bold.

NOTE: The compression ratio of the files will vary and you should always plan for the worst case. To get the worst case compressibility , use a Stacker drive with 4K clusters and tuning setting of P=0 or P=1 to do the test for your software's compressibility.

Copying Device Drivers to the Destination Drive

If Stacker is not being preloaded then, since device drivers load in the CONFIG.SYS file and have the ability to load before the Stacker device driver, you should make sure that all of your drivers and their support files (such as overlays) are on both the Stacker drive and its host (uncompressed) drive.  Some device drivers, such as memory managers, need to be at the beginning of the CONFIG.SYS file so that they load before any of the programs that may need them. These programs should always be copied to the host (uncompressed) drive. Other drivers that are added to the end of the CONFIG.SYS file can just be copied to the Stacker drive (i.e.: the compressed portion of the drive).

Stacker Destination Drives

To see if a drive is a Stacker drive, make a call to stacker_driveInfo(). There is a boolean field in the drive info structure named driveIsStacked that will be true (non-zero) if the drive is a Stacker drive. If the destination drive is a Stacker drive and Stacker is not being preloaded, or if the system can be booted under OS/2 and use Stacker for OS/2, you will need to find the host (uncompressed) drive and copy any drivers that get referenced in the beginning of the CONFIG.SYS file to it. Once you have determined that the drive is a Stacker drive, you will need to put the Stacker drive in host drive mode before you can copy any files to its host (uncompressed) drive. You can access the host drive through the drive letter returned by the stacker_hostDriveMode() function using your normal disk I/O functions. After you are finished accessing the host drive you will need to take the drive out of host drive mode using the stacker_undoHostDriveMode() function. See stacker_hostDriveMode() in the Stacker Developer's Tool Kit Reference portion of this manual.

Note that if Stacker is being preloaded under MS DOS 6.0 and the PC is not an OS/2 dual boot system you should copy and drivers or software that is accessed by CONFIG.SYS and AUTOEXEC.BAT to the compressed part of the boot drive.  Assuming there is room available, if you want to keep your installation program simple you can just copy any CONFIG.SYS device drivers you install to both the compressed boot drive and its host drive.


 Program logic for a Stacker aware installation program

The following logic will show you how you can use the Stacker Developer's Tool Kit to create a Stacker aware installation program, with or without Stacker on the system. From a high level viewpoint the functionality of this logic is as follows:

Initialize all variables.

Gather the drive information (bytes free and specific Stacker information).

Copy all files to the destination drive.

Copy all files required for CONFIG.SYS to the host boot drive.

Update CONFIG.SYS/AUTOEXEC.BAT files on the boot drives.

The sample installation program, illustrated by the pseudo code below, takes the destination path as a command line argument. This may not be the best way to get input from a user, but it makes the code simple for this example. To install a demonstration of the Stacker Developer's Tool Kit to the C:\APIDEMO directory type in the following after compiling the INSTALL.C program and linking it with the Stacker Developer's Tool Kit:

INSTALL C:\APIDEMO

To be of assistance to developers who are writing installation programs in a language other than C the following pseudo code shows a large amount of detail. For those who are developing in C an exact implementation of this logic can be found in the sample program INSTALL.C which is included on the Stacker Developer's Tool Kit diskette. This program assumes that the computer was booted from drive C:. Please see the comments in the INSTALL.C program for suggestions on how to make this program more robust.


 Main body of the Installation program

Define local variables:

errorLevel	s_ushort

bootDriveFree	s_ulong

destDriveFree	s_ulong

destHostDriveFree	s_ulong

bootDriveLetter	s_char

destPath	81 byte array of s_char

hostPath	81 byte array of s_char

destDriveInfoRec	stacker_drive_info_struct

bootDriveInfoRec	stacker_drive_info_struct

Set errorLevel to SUCCESSFUL_INSTALLATION

Set bootDriveFree, destDriveFree and destHostDriveFree to 0

Set bootDriveLetter to 'C'

Display the program title to the user

Validate the command line parameters

If no command line parameters were passed

or the user wants help

or an invalid command line parameter was passed

{

If an invalid command line parameter was passed in

{

Set errorLevel to INVALID_PARAMETER

Display an error message to the user

}

Give the user help

}

Else

{

Tell user that you're starting the installation

If there is no backslash at the end of the destination path

{

Add a backslash to the destination path

}

Move the destination drive letter to the driveLetter field

in its drive info structure

Move the boot drive letter to the driveLetter field in its

drive info structure

Gather the drive information for the destination and boot

drives (see pseudo code for Gather_Drive_Info() below)

Set errorLevel to the status of the above function call

If there is enough space available on all of the drives

and errorLevel is set to SUCCESSFUL_INSTALLATION

{

Copy files and update CONFIG.SYS (see pseudo code for

Copy_Files_And_Update_Config() below)

Set errorLevel to the value returned by the above

copy operation

}

Else

{

If errorLevel is set to SUCCESSFUL_INSTALLATION

{

Give user an error message

Set errorLevel to INSUFFICIENT_DISK_SPACE

}

}

}

Display an exit message to the user

Return with errorLevel


Copy_Files_And_Update_Config() Function
 Copy_Files_And_Update_Config() - Function to

    copy files and update CONFIG.SYS

Define local variables:

destDriveInHostMode	s_ushort

bootDriveInHostMode	s_ushort

result	s_ushort

errorLevel	s_ushort

hostDriveLetter	s_char

bootHostDriveLetter	s_char

currentDirectory	Array of s_char that's S_MAX_PATH in size

apiResult	s_return

driverInfo	stacker_driver_info_struct

Set destDriveInHostMode and bootDriveInHostMode to FALSE

Copy all files to the destination drive

Set apiResult to stacker_driverInfo()

If successful

{

If apiResult is successful and the destination drive is a

Stacker drive and it is mounted and the Stacker driver is

not preloaded

{

Save the current path for the destination Stacker drive

Put the destination Stacker drive in host mode

If successful

{

Turn on the destDriveInHostMode flag

}

Else

{

Give user an error message

Set errorLevel to UNABLE_TO_GO_IN_HOST_MODE

}

}

If errorLevel is SUCCESSFUL_INSTALLATION

{

Copy the device driver files to the host drive

Set the result field to the return value of the above

copy operation

If destDriveInHostMode is TRUE

{

Take the drive out of host mode

If not successful

{

Give user an error message

Set errorLevel to UNABLE_TO_UNDO_HOST_MODE

}

Set destDriveInHostMode to FALSE

Restore the path for the destination Stacker drive

}

If result is successful

{

If apiResult is successful and the boot drive is a

Stacker drive and it is mounted and the Stacker driver

is not preloaded.

{

Save the current path for the boot Stacker drive

Put the boot Stacker drive in host mode

If successful

{

Turn on the bootDriveInHostMode flag

}

Else

{

Give user an error message

Set errorLevel to UNABLE_TO_GO_IN_HOST_MODE

}

}

If errorLevel SUCCESSFUL_INSTALLATION

{

Update the CONFIG.SYS/AUTOEXEC.BAT files on

the boot drive

Set the result field to the return value of the

above update operation

If bootDriveInHostMode is TRUE

{

Take the drive out of host mode

If not successful

{

Give user an error message

Set errorLevel to UNABLE_TO_UNDO_HOST_MODE

}

Set bootDriveInHostMode to FALSE

Restore the boot Stacker drive

}

If result of updating the CONFIG.SYS/AUTOEXEC.BAT is not successful

{

Give user an error message

Set errorLevel to BAD_SYSTEM_FILE_UPDATE

}

}

Else

{

Give user an error message

Set errorLevel to BAD_COPY_TO_HOST_DRIVE

}

}

}

}

Else

{

Give user an error message

Set errorLevel to BAD_COPY_TO_DESTINATION_DRIVE

}

Return with errorLevel


Gather_Drive_Info() Function
 Gather_Drive_Info() - Function to gather the drive info

Define local variables:

result	s_return

currentDirectory	Array of s_char that's S_MAX_PATH in size

hostDriveLetter	s_char

errorLevel	s_ushort

statusCode	s_ushort

Set errorLevel to SUCCESSFUL_INSTALLATION

Set statusCode to STACKER_NOT_INSTALLED

Display message to user saying that you're gathering information

Set destDriveFree to the number of bytes free for the destination

drive according to DOS

Set destHostDriveFree to destDriveFree

Set bootDriveFree to the number of bytes free for the bootDrive

according to DOS

Set destSpaceNeeded to the minimum space needed to copy the

program to the destination drive

Set statusCode to stacker_status()

If Stacker software is installed

or Stacker hardware is installed

{

Get Stacker drive information for the destination drive

If successful

and the drive is a Stacker drive

and the Stacker drive is mounted

{

Set destDriveFree to the actual physical bytes available

in the Stacker drive

Set destSpaceNeeded to the minimum physical space needed to

copy the program files to a Stacker drive

Save the current path for the destination Stacker drive

Put destination drive in host mode

If successful

{

Set destHostDriveFree to the number of bytes free on the

host drive for the destination drive (see pseudo code for

s_getStackerDiskFree() below)

Take the destination drive out of host drive mode

If not successful

{

Give user an error message

Set errorLevel to UNABLE_TO_UNDO_HOST_MODE

}

}

Else

{

Give user an error message

Set errorLevel to UNABLE_TO_GO_IN_HOST_MODE

}

Restore the path on the destination Stacker drive

}

Determine what drive was C: at boot time and put the value in bootDriveLetter

Get Stacker drive information for the boot drive

If successful

and the drive is a Stacker drive

and the Stacker drive is mounted

{

Save the current directory for the boot Stacker drive

Put the boot drive in host mode

If successful

{

Set bootDriveFree to the number of bytes free on the host

drive for the boot Stacker drive

Take the boot drive out of host mode

If not successful

{

Give user an error message

Set errorLevel to UNABLE_TO_UNDO_HOST_MODE

}

}

Else

{

Give user an error message

Set errorLevel to UNABLE_TO_GO_IN_HOST_MODE

}

Restore the path on the boot Stacker drive

}

}

Return with errorLevel


 Glossary

actual compression ratio

The number of sectors that DOS would have required to store all of the files on a Stacker drive divided by the number of sectors Stacker actually used to store those files.  This overall compression ratio for a Stacker drive is calculated using the Stacker drive cluster size, not the adjusted sectors per cluster described below.

adjusted sectors per cluster

Stacker calculates file compression ratios by dividing the number of sectors DOS would have taken to store a file on the Stacker drive divided by the number of sectors Stacker actually used when storing the file.  In general the cluster size on the Stacker drive is used to determine the number of sectors that DOS would have used to store the file.  However if a user has selected to create a Stacker drive with very a very large cluster size (16K or 32) on a small to medium size drive (less than 512MB) then the compression ratios for small files would seem inflated because DOS is being made to look unrealistically inefficient in its storage.  To compensate for this Stacker uses an adjusted sectors per cluster number to calculate how much space DOS would have taken to store a file on a Stacker drive.  The adjusted cluster size is 4K or 8K for Stacker drives with 4K or 8K clusters respectively.  It is 8K if the Stacker drive cluster size is 16K or 32K and the host drive cluster size is 8K or less.  It is the smaller of the host drive cluster size or the Stacker drive cluster size if the Stacker drive and its host drive both have cluster sizes greater than 8K.  The adjusted sectors per cluster is the adjusted cluster size divided by the Stacker drive sector size.

BPB	Bios Parameter Block. This DOS structure details certain particulars about the drive it is on, such as the number of sectors on the drive and the cluster size.

boot time	When Stacker 3.1 is preloaded "boot time" as referred to in this manual means the time when CONFIG.SYS processing starts.  Otherwise it generally refers to the state of the machine just before any drive swapping takes place by SSWAP in the CONFIG.SYS file.

cluster	The smallest allocation unit of disk space that DOS uses for file storage. Clusters consist of a number of sectors. (Stacker requires that each sector be 512 bytes in size). For hard disks clusters typically contain from four to sixty-four sectors depending on the size of the disk.

compression ratio	For Stacker drives the compression ratio is defined as the ratio between the number of sectors that DOS would have required to store a given set of data on the drive and the number of sectors Stacker actually requires to store the same data. For example if DOS would have required thirty-two sectors to store a file, but Stacker only required sixteen, then the compression ratio for that file is 32:16 which simplifies to 2:1.

expected compression ratio

The ratio of logical space to physical space for a Stacker drive. This value is set at the time the Stacker drive is created. For example if SCREATE is used to create a 30MB Stacker drive from 10MB of physical disk space (corresponding to a 10MB STACVOL file), then the expected compression ratio for that drive is 3 to 1. The default expected compression ratio for Stacker drives is 2 to 1. A different value can be selected at the time the drive is created via the "Custom" setup options available in Stacker's SETUP program or via CREATE's "/R=X.X" command line switch. After the Stacker drive has been created the expected compression ratio may be changed by running SDEFRAG /G on the drive. The minimum selectable value for the expected compression ratio of a Stacker drive is 1.1 to 1. The maximum is 16 to 1.

FAT	The File Allocation Table. This table common to all DOS disks, is used to track the status of every cluster on the disk. For each cluster it tells whether the cluster is free, is in use, or contains bad sectors. It also keeps track of which clusters are used by each file on the disk.

host drive	The drive which contains the STACVOL file for a Stacker drive.

logical disk space	= (no. of FAT entries) * (cluster size)

LZS Server	Software or firmware which provides a standard interface for accessing a software or hardware Stacker LZS compression/decompression engine installed in a system.  Stacker itself is an LZS server providing general purpose compression and decompression services within a system.  Starting with version 3.1, Stacker  looks to see if a higher performance (typically hardware) LZS server is installed which it can use to speed up its own operation.

main store	The area on a Stacker drive where all the user's data is stored.

mount	The process of making STACVOL file accessible as a Stacker drive.

physical disk space	The number of bytes in the data storage area (main store) of the Stacker drive.

projected disk space	= (physical disk space) * (current overall compression ratio)

replaced drive	A Stacker drive that replaces its host drive. Example: C: is the host drive. C:\STACVOL.DSK is the STACVOL file. The drive created from C:\STACVOL.DSK becomes drive C:. Because the Stacker drive replaces its host drive, the host drive C: is no longer accessible.

SCHECK	A Stacker utility for repairing Stacker drives much as CHKDSK is used to repair a normal DOS drive. Starting with Stacker 3.0, this is called CHECK.

SDEFRAG	A Stacker utility for defragmenting STACKER drives.

sector	The smallest allocation unit on a DOS disk. For Stacker the sector size must be 512 bytes.

SSWAP.COM	A Stacker utility used to interchange the drive letters of two drives.

STACKER.COM	The Stacker device driver. This program is also used to mount STACVOL files making them accessible as Stacker drives.

STACVOL file	A hidden, system, read-only file which contains all the house keeping data structures for a Stacker drive as well as the user data which is stored on that drive.

swapped drive	A drive who's drive letter has been exchanged with the drive letter of another drive by means of Stacker's SSWAP utility.


 Function List

Informational Functions

stacker_status

stacker_driveInfo

stacker_driverInfo

stacker_getCurrentDriveLetter

stacker_getBootTimeDriveLetter

stacker_fileCompressionRatio

stacker_fastFileCR

General Functions

stacker_freeApiSelectors

Compression Functions

stacker_pack

stacker_unpack

Drive State Manipulation Functions

stacker_flushDriver

stacker_unmountVolume

stacker_hostDriveMode

stacker_undoHostDriveMode

stacker_writeProtect

stacker_unWriteProtect

Read Compressed Data Functions

stacker_initializeRccBuffer

stacker_getFatEntry

stacker_readCompressedCluster

Program logic for a Stacker aware installation program

Main body of the installation program

Copy_Files_And_Update_Config() - Function

Gather_Drive_Info() - Function


stacker_status Example
 stacker_status Example  (EXAM_1.C)

#include <stdio.h>

#include "stacker.h"

int main(void)

{

s_ushort status_code;

s_return return_code;

return_code = stacker_status(&status_code);

if (return_code == SE_SUCCESSFUL)

{

switch (status_code)

{

case STACKER_SW_INSTALLED:

puts("\nStacker software is installed.");

break;

case STACKER_HW_INSTALLED:

puts("\nStacker hardware is installed.");

break;

default:

puts("\nStacker is not installed.");

break;

}

}

else /* stacker_status() function call failed.*/

{

puts("\nCall to stacker_status() failed!");

status_code = -1;

}

return status_code;

}


stacker_driveInfo Example
 stacker_driveInfo Example (EXAM_2.C)

#include <stdio.h>

#include "stacker.h"

int main(void)

{

struct	stacker_drive_info_struct driveInfo;

s_return	result	= 0;

s_ushort	status;

s_char	driveLetter	= 'C';

int	errorLevel	= 0;

result = stacker_status(&status);

if ((result == SE_SUCCESSFUL)

&& (status != STACKER_NOT_INSTALLED))

{

driveInfo.driveLetter = driveLetter;

result = stacker_driveInfo(&driveInfo);

if ((result == SE_SUCCESSFUL)

&& (driveInfo.driveIsStacked == STACKER_STACKED_DRIVE))

{

printf(

"\nDrive %c is a Stacker drive.\n",

driveLetter

);

}

else if (result == SE_DRIVE_NOT_STACKED)

{

printf("\nDrive %c is not a Stacker drive.\n", driveLetter);

}

else

{

puts("\nError calling stacker_driveInfo()!");

errorLevel = 1;

}

}

else

{

errorLevel = 2;

}

return errorLevel;

}


stacker_driverInfo Example
 stacker_driverInfo Example (EXAM_3.C)

#include "stacker.h"

#include <stdio.h>

int main(void)

{

int	errorLevel	= 0;

s_return	result;

struct stacker_driver_info_struct di_rec;

puts("\nStacker API test program:\n");

result = stacker_driverInfo(&di_rec);

if (result != SE_SUCCESSFUL)

{

puts("Stacker is not installed.");

errorLevel = 1;

}

else

{

/*

* Display Stacker driver version.

*/

printf(

" Stacker driver version %u.%02u.\n",

di_rec.driverVersion / 100,

di_rec.driverVersion % 100

);

/*

* Display Stacker hardware information.

*/

if (di_rec.hardwareAddress == 0)

{

puts(" Stacker Software installed.");

}

else

{

if (di_rec.hardwareAddress >= 0xC400)

{

printf(

" Stacker XT/8 installed at address %X.\n",

di_rec.hardwareAddress

);

}

else

{

if (di_rec.hardwareAddress >= 0x200)

{

printf(

" Stacker AT/16 installed at address %X.\n", di_rec.hardwareAddress

);

}

else

{

puts(" Stacker MC/16 installed.");

}

}

}

/*

* Display Stacker EMS usage.

*/

if (di_rec.usingEMS)

{

puts(" Stacker driver is using EMS.");

}

else

{

puts(" Stacker driver is not using EMS.");

}

/*

* Display Stacker's load high status.

*/

if (di_rec.loadedHigh)

{

puts(" Stacker is loaded into upper memory.");

}

else

{

puts(" Stacker is loaded into conventional memory.");

}

/*

* Display Stacker's preload status.

*/

if (di_rec.preLoaded)

{

puts(" Stacker was preloaded (loaded before CONFIG.SYS).");

}

else

{

puts(" Stacker was loaded from the CONFIG.SYS file.");

}

/*

* Display the number of available replaceable Stacker drives.

*/

printf(

" There are %01u available replaceable Stacker drives.\n",

di_rec.availRepDrives

);

}

return errorLevel;

}


stacker_getCurrentDriveLetter Example
 stacker_getCurrentDriveLetter Example (EXAM_4.C)

#include "stacker.h"

#include <stdio.h>

int main(void)

{

s_char	bootTimeDrive	= 'C';

s_char	currentDrive;

s_return	result;

s_ushort	status;

int	returnCode	= 0;

puts("\nStacker API test program:\n");

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

puts(" Stacker is not installed.");

returnCode = 1;

}

else

{

result = stacker_getCurrentDriveLetter(

   bootTimeDrive,

   &currentDrive

   );

if (result != SE_SUCCESSFUL)

{

puts(" ERROR:	Unexpected error occurred.");

returnCode = 2;

}

else

{

printf(

" Drive %c: at boot time is now drive %c:\n",

bootTimeDrive,

currentDrive

);

}

}

return returnCode;

}


stacker_getBootTimeDriveLetter Example
 stacker_getBootTimeDriveLetter Example (EXAM_5.C)

#include "stacker.h"

#include <stdio.h>

int main(void)

{

s_char	bootTimeDrive;

s_char	currentDrive	= 'C';

s_return	result;

s_ushort	status;

int	returnCode	= 0;

puts("\nStacker API test program:\n");

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

puts(" Stacker is not installed.");

returnCode = 1;

}

else

{

result = stacker_getBootTimeDriveLetter(

   currentDrive,

   &bootTimeDrive

   );

if (result != SE_SUCCESSFUL)

{

puts(" ERROR:	Unexpected error occurred.");

returnCode = 2;

}

else

{

printf(

" Drive %c: was drive %c: at boot time.\n",

currentDrive,

bootTimeDrive

);

}

}

return returnCode;

}


stacker_fileCompressionRatio Example
 stacker_fileCompressionRatio Example (TESTFCR.C)

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *   TESTFCR - Program to get the compression ratio of a given file.

 *             (c) 1992 - 1993 Stac Electronics, Carlsbad, CA

 *

 *  To compile this program from the command line using Borland C type:

 *

 *      BCC TESTFCR.C STACKER.LIB

 *

 *  To compile this program from the command line using Microsoft C type:

 *

 *      CL /F 2000 TESTFCR.C STACKER.LIB

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

#if defined(__BORLANDC__)

    #include <alloc.h>

    #include <dir.h>

#else

    #include <malloc.h>

    #include <direct.h>

    #define fnsplit   _splitpath   /* Microsoft C uses _splitpath. */

#endif

#include <ctype.h>

#include <dos.h>

#include <stdio.h>

#include <string.h>

#include "stacker.h"

#define  S_MAXPATH     80

extern unsigned _stklen = 16384;

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  void Get_Help() -- Displays help to the user if no parameters were

 *                     specified or if the user requested it with /?.

 *

 *  Input parameters:  None.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void Get_Help(void)

    {

    puts("Purpose:  Checks the compression ratio of a given file.\n");

    puts("Syntax:   TESTFCR filename.ext\n");

    puts("Options:  filename.ext   The file you would like to check the");

    puts("                         compression ratio of.\n");

    puts("          /?  Displays this help screen.\n");

    puts("Example:  TESTFCR C:\\STACKER\\README.DOC\n");

    puts("Meaning:  Check the compression ratio of Stacker's README.DOC file.");

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  char Get_DriveLetter() -- Gets the drive letter from the file spec

 *                            passed in of the current working drive if

 *                            no drive was specified in the file spec.

 *

 *  Input parameters:  *fileSpec.

 *

 *  Return values:     An upper case drive letter.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

char Get_DriveLetter(char *fileSpec)

    {

    char     driveLetter = NULL;

    unsigned driveNumber;

    char     drive[ 3 ];

    char     dir[ S_MAXPATH ];

    char     fname[ 9 ];

    char     ext[ 5 ];

    fnsplit( fileSpec, drive, dir, fname, ext );

    if (drive[0] == '\0')

        {

        _dos_getdrive(&driveNumber);

        driveLetter = driveNumber + 'A' -1;

        }

    else

        {

        driveLetter = toupper(drive[0]);

        }

    return driveLetter;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  int main() -- Parses the command line and perfoms the main loop of the

 *                program.

 *

 *  Input parameters:  The file name of the file to check the compression

 *                     ratio of.  If /? is passed the user will be given a

 *                     help screen.

 *

 *  Return value:      0 == No errors.

 *                     1 == Stacker not installed.

 *                     2 == Unable to open source file.

 *                     3 == Not a Stacker drive.

 *                     4 == Unable to allocate rccBuffer.

 *                     5 == Unable to get file compression ratio.

 *                     6 == Unable to initialize rccBuffer.

 *                     7 == User wants help.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

int main(int argc, char *argv[])

    {

    char       path[100];

    s_ushort   status_code;              /* status of Stacker driver.     */

    s_ulong    compressedSectors;

    s_ulong    rawSectors;

    s_ulong    adjustedClusterRatio;

    s_return   result;

    int        returnValue       = 0;    /* Default to successful.        */

    void  far *rccBuffer;

    struct stacker_drive_info_struct driveInfo;

    if ((argc <= 1) || (strcmp(argv[1], "/?") == 0))

        {

        Get_Help();

        returnValue = 7;

        }

    else

        {

        result = stacker_status(&status_code);

        if ((status_code != STACKER_SW_INSTALLED)

        && (status_code != STACKER_HW_INSTALLED))

            {

            puts("The STACKER device driver must be loaded to run this program.");

            returnValue = 1;

            }

        else

            {

            strupr(strcpy(path, argv[1]));

            driveInfo.driveLetter = Get_DriveLetter(path);

            result = stacker_driveInfo(&driveInfo);

            if ((result != SE_SUCCESSFUL)

            || (!driveInfo.driveIsStacked))

                {

                printf(

                      "Drive %c: is not a valid Stacker drive.",

                      driveInfo.driveLetter

                      );

                returnValue = 3;

                }

            else

                {

                rccBuffer = malloc(driveInfo.rccBufferSize);

                if (rccBuffer == NULL)

                    {

                    puts("Unable to allocate rccBuffer.");

                    returnValue = 4;

                    }

                else

                    {

                    result = stacker_initializeRccBuffer(

                                                        rccBuffer,

                                                        &driveInfo

                                                        );

                    if (result != SE_SUCCESSFUL)

                        {

                        puts("Unable to initialize rccBuffer.");

                        returnValue = 6;

                        }

                    else

                        {

                        result = stacker_fileCompressionRatio(

                                                             &driveInfo,

                                                             rccBuffer,

                                                             path,

                                                             &compressedSectors,

                                                             &rawSectors

                                                             );

                        if (result != SE_SUCCESSFUL)

                            {

                            puts("Error in call to stacker_fileCompressionRatio.");

                            returnValue = 5;

                            }

                        else

                            {

                            if (compressedSectors)

                                {

                                adjustedClusterRatio = ((rawSectors

                                                     * (unsigned long) 100

                                                     / compressedSectors)

                                                     + 5)

                                                     / (unsigned long) 10;

                                }

                            else

                                {

                                adjustedClusterRatio = 10;

                                }

                            printf(

                                  "\n%s uses %lu compressed sectors and %lu raw sectors.\n",

                                  path,

                                  compressedSectors,

                                  rawSectors

                                  );

                            printf(

                                  "The compression ratio of \"%s\" is %ld.%.1ld:1\n",

                                  path,

                                  adjustedClusterRatio / 10,

                                  adjustedClusterRatio % 10

                                  );

                            }

                        }

                    }

                }

            }

        }

    return returnValue;

    }


stacker_fastFileCR Example
 stacker_fastFileCR Example (TESTFCRF.C)

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *   TESTFCRF - Program to get the compression ratio of a given file.

 *              (c) 1992 - 1993 Stac Electronics, Carlsbad, CA

 *

 *  To compile this program from the command line using Borland C type:

 *

 *      BCC TESTFCRF.C STACKER.LIB

 *

 *  To compile this program from the command line using Microsoft C type:

 *

 *      CL /F 2000 TESTFCRF.C STACKER.LIB

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

#if defined(__BORLANDC__)

    #include <alloc.h>

    #include <dir.h>

#else

    #include <malloc.h>

    #include <direct.h>

    #define  fnsplit  _splitpath   /* Microsoft C uses _splitpath. */

    #define  fnmerge  _makepath    /* Microsoft C uses _makepath.  */

    #define  asm      _asm         /* Microsoft C uses _asm.       */

#endif

#include <ctype.h>

#include <dos.h>

#include <stdio.h>

#include <string.h>

#include "stacker.h"

/*

 *  Defines.

 */

#define MAX_PATH        73

#define FILE_NAME_SIZE  8

#define FILE_EXT_SIZE   3

#define FIND_FIRST_FCB  0x11

#define S_MAXPATH       80

#define TRUE            1

#define FALSE           0

/*

 *  Structres for finding the first cluster for a file.

 */

struct  s_fcb

    {

    char    drive;          /* 0 = default, 1 = A */

    char    fname[8];

    char    ext[3];

    short   cur_Block;

    short   rec_Size;

    long    fsize;

    short   date;

    char    reserved[10];

    char    cur_Rec;

    long    random;

    };

struct  s_xfcb

    {

    char         flag;               /* 0xff means this is an extended fcb. */

    char         reserved[5];

    char         attrib;

    struct s_fcb fcb;

    };

extern unsigned _stklen = 16384;

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  void Get_Help() -- Displays help to the user if no parameters were

 *                     specified or if the user requested it with /?.

 *

 *  Input parameters:  None.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void Get_Help(void)

    {

    puts("Purpose:  Checks the compression ratio of a given file.\n");

    puts("Syntax:   TESTFCRF filename.ext\n");

    puts("Options:  filename.ext   The file you would like to check the");

    puts("                         compression ratio of.\n");

    puts("          /?  Displays this help screen.\n");

    puts("Example:  TESTFCRF C:\\STACKER\\README.DOC\n");

    puts("Meaning:  Check the compression ratio of Stacker's README.DOC file.");

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  char Get_DriveLetter() -- Gets the drive letter from the file spec

 *                            passed in of the current working drive if

 *                            no drive was specified in the file spec.

 *

 *  Input parameters:  *fileSpec.

 *

 *  Return values:     An upper case drive letter.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

char Get_DriveLetter(char *fileSpec)

    {

    char     driveLetter = '\0';

    unsigned driveNumber;

    char     drive[ 3 ];

    char     dir[ S_MAXPATH ];

    char     fname[ 9 ];

    char     ext[ 5 ];

    fnsplit( fileSpec, drive, dir, fname, ext );

    if (drive[0] == '\0')

        {

        _dos_getdrive(&driveNumber);

        driveLetter = driveNumber + 'A' -1;

        }

    else

        {

        driveLetter = toupper(drive[0]);

        }

    return driveLetter;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  s_CopyStringAndPad() -- Copies a string.  Pads destination if source

 *                          is short.

 *

 *  Input parameters:  *destination,

 *                     *source,

 *                     padChar,

 *                     size,

 *

 *  Return values:     Nothing.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void s_CopyStringAndPad(

                       char far *destination,

                       char far *source,

                       char      padChar,

                       int       size

                       )

    {

    while(size > 0 && *source != '\0')

        {

        *destination++ = *source++;

        --size;

        }

    while (size-- > 0)

        {

        *destination++ = padChar;

        }

    return;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  s_FCB_Find_First() -- Gets information for the 1st entry in a selected

 *                        directory.

 *

 *  Input parameters:  *path,

 *                     *searchFCBPtr,

 *                     attrib,

 *

 *  Return values:     TRUE for successful, FALSE if there was an error.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

int s_FCB_Find_First(

                    const    char      *path,

                    struct s_xfcb  far *searchFCBPtr,

                    int                 attrib

                    )

    {

    int      result             = TRUE;

    char     drive[3];

    char     dir[MAX_PATH];

    char     fname[9];

    char     ext[5];

    char     temp[MAX_PATH];

    char     orig_Dir[MAX_PATH];

    unsigned orig_Disk;

    unsigned maxDrives;

    fnsplit(path, drive, dir, fname, ext);

    if (fname[0] == '\0')  //No file name.

        {

        return FALSE;

        }

    getcwd(orig_Dir, MAX_PATH);

    _dos_getdrive(&orig_Disk);

    fnmerge(temp, drive, dir, NULL, NULL);

        {

        int len = _fstrlen(temp);

        /*  Strip off trailing backslashes except on root directory.        */

        if ((len != 1)

        && !(len == 3 && temp[1] == ':')

        && (temp[--len] == '\\'))

            {

            temp[len] = 0;

            }

        }

    chdir(temp);

    searchFCBPtr->flag = 0xFF;                 /* Indicates extended FCB.   */

    if( *drive != 0 )                          /* Set the search drive.     */

        {

        searchFCBPtr->fcb.drive = toupper(*drive) - 'A' + 1;

        }

    else

        {

        searchFCBPtr->fcb.drive = orig_Disk + 1;

        }

    searchFCBPtr->attrib = attrib;          /* Set the search file type.    */

    s_CopyStringAndPad(                     /* Set up the search file name. */

                      searchFCBPtr->fcb.fname,

                      fname,

                      ' ',

                      FILE_NAME_SIZE

                      );

    /* Set up the search file extension.                   */

    /* The "+ (*ext == '.')" is for getting rid of the "." */

    s_CopyStringAndPad(

                      searchFCBPtr->fcb.ext,

                      ext + (*ext == '.'),

                      ' ',

                      FILE_EXT_SIZE

                      );

    asm {

        push    ds                      /* Save registers.                  */

        push    bp

        mov     ah,FIND_FIRST_FCB       /* Specify DOS function.            */

        lds     dx,searchFCBPtr         /* Pass pointer to FCB.             */

        int     0x21                    /* Call the DOS function.           */

        pop     bp                      /* Restore registers.               */

        pop     ds

        cmp     al,0xFF                 /* If al == 0xFF, couldn't find.    */

        jne     foundFirst

        }

    result = FALSE;

foundFirst:

    _dos_setdrive(orig_Disk, &maxDrives);

    chdir(orig_Dir);

    return result;

    }

/*

******************************************************************************

*

* Function Name:        _s_getdta

*

* Function:             Returns the program's disk transfer area.

*

* Input parameters:     None.

*

* Return values:        Returns a far char pointer to the dta.

*

-****************************************************************************/

static char far *_s_getdta(void)

    {

    char far *dta_Address;

    asm {

        mov ah, 02fh

        int 021h

        mov word ptr dta_Address, bx

        mov ax, es

        mov word ptr dta_Address + 2, ax

        }

    return dta_Address;

    }

/*

******************************************************************************

*

* Function Name:        _s_setdta

*

* Function:             Sets the program's disk transfer area.

*

* Input parameters:     *dta, Pointer to the new Disk Transfer Area.

*

* Return values:        None.

*

-****************************************************************************/

void _s_setdta(char far *dta)

    {

    asm     push    ds

    asm     mov     ah, 01Ah

    asm     lds     dx, dta

    asm     int     021H

    asm     pop     ds

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  s_Get_Starting_Cluster() -- Calls s_FCB_Find_First, then digs into DTA

 *                              to find the first cluster number.

 *

 *  Input parameters:  *fname,

 *                     *first_Cluster,

 *

 *  Return values:     TRUE for successful, FALSE if there was an error.

 *                     Fails if s_FCB_Find_First fails to find the file.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

int s_Get_Starting_Cluster(

                          char       *fname,

                          s_ushort   *first_Cluster,

                          s_ulong    *fsize

                          )

    {

    struct s_xfcb      searchFCBPtr;

    struct s_xfcb far *dta_xFCB;

    char          far *origDTA;

    char               tempDTA[128];

    s_return           result;

    origDTA = _s_getdta();              /* Save original DTA pointer.       */

    _s_setdta((char far *)&tempDTA);    /* Set new DTA.                     */

    result = (s_FCB_Find_First(

                              fname,

                              &searchFCBPtr,

                              0xffff

                              ) == TRUE )? SE_SUCCESSFUL: SE_UNEXPECTED_ERROR;

    /***********************************************************************

    * DOS stores a copy of this FCB similar the above FCB in the

    * disk transfer area (DTA).  The copy in the DTA also has the

    * beginning cluster number of the file buried in the "reserved"

    * field.  We just need to index five bytes into this field, and

    * extract the cluster number.

    ************************************************************************/

    dta_xFCB = (struct s_xfcb far *) _s_getdta();

    *first_Cluster = *((unsigned far *)(dta_xFCB->fcb.reserved + 5));

    *fsize = *((unsigned long far *)(dta_xFCB->fcb.reserved + 7));

    _s_setdta(origDTA);                 /* Restore original DTA.            */

    return result;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  int main() -- Parses the command line and perfoms the main loop of the

 *                program.

 *

 *  Input parameters:  The file name of the file to check the compression

 *                     ratio of.  If /? is passed the user will be given a

 *                     help screen.

 *

 *  Return value:      0 == No errors.

 *                     1 == Stacker not installed.

 *                     2 == Unable to open source file.

 *                     3 == Not a Stacker drive.

 *                     4 == Unable to allocate rccBuffer.

 *                     5 == Unable to get file compression ratio.

 *                     6 == Unable to initialize rccBuffer.

 *                     9 == User wants help.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

int main(int argc, char *argv[])

    {

    char       path[100];

    s_ushort   status_code;              /* status of Stacker driver.     */

    s_ushort   firstCluster;             /* First cluster of the file.    */

    s_ulong    fileSize;                 /* Size of the file.             */

    s_ulong    compressedSectors;

    s_ulong    rawSectors;

    s_ulong    adjustedClusterRatio;

    s_return   result;

    int        returnValue       = 0;    /* Default to successful.        */

    void  far *rccBuffer;

    struct stacker_drive_info_struct driveInfo;

    if ((argc <= 1) || (strcmp(argv[1], "/?") == 0))

        {

        Get_Help();

        returnValue = 9;

        }

    else

        {

        result = stacker_status(&status_code);

        if ((status_code != STACKER_SW_INSTALLED)

        && (status_code != STACKER_HW_INSTALLED))

            {

            puts("The STACKER device driver must be loaded to run this program.");

            returnValue = 1;

            }

        else

            {

            strupr(strcpy(path, argv[1]));

            driveInfo.driveLetter = Get_DriveLetter(path);

            result = stacker_driveInfo(&driveInfo);

            if ((result != SE_SUCCESSFUL)

            || (!driveInfo.driveIsStacked))

                {

                printf(

                      "Drive %c: is not a valid Stacker drive.",

                      driveInfo.driveLetter

                      );

                returnValue = 3;

                }

            else

                {

                rccBuffer = malloc(driveInfo.rccBufferSize);

                if (rccBuffer == NULL)

                    {

                    puts("Unable to allocate rccBuffer.");

                    returnValue = 4;

                    }

                else

                    {

                    result = stacker_initializeRccBuffer(

                                                        rccBuffer,

                                                        &driveInfo

                                                        );

                    if (result != SE_SUCCESSFUL)

                        {

                        puts("Unable to initialize rccBuffer.");

                        returnValue = 6;

                        }

                    else

                        {

                        if (s_Get_Starting_Cluster(

                                                  path,

                                                  &firstCluster,

                                                  &fileSize

                                                  ) != TRUE)

                            {

                            puts("Unable to get the first cluster of the file!");

                            returnValue = 7;

                            }

                        else

                            {

                            returnValue = stacker_fastFileCR(

                                                            &driveInfo,

                                                            rccBuffer,

                                                            firstCluster,

                                                            fileSize,

                                                            &compressedSectors,

                                                            &rawSectors

                                                            );

                            if (result != SE_SUCCESSFUL)

                                {

                                puts("Error in call to stacker_fileCompressionRatio.");

                                returnValue = 5;

                                }

                            else

                                {

                                if (compressedSectors)

                                    {

                                    adjustedClusterRatio = ((rawSectors

                                                         * (unsigned long) 100

                                                         / compressedSectors)

                                                         + 5)

                                                         / (unsigned long) 10;

                                    }

                                else

                                    {

                                    adjustedClusterRatio = 10;

                                    }

                                printf(

                                      "\n%s uses %lu compressed sectors and %lu raw sectors.\n",

                                      path,

                                      compressedSectors,

                                      rawSectors

                                      );

                                printf(

                                      "The compression ratio of \"%s\" is %ld.%.1ld:1\n",

                                      path,

                                      adjustedClusterRatio / 10,

                                      adjustedClusterRatio % 10

                                      );

                                }

                            }

                        }

                    }

                }

            }

        }

    return returnValue;

    }


stacker_freeApiSelectors example
 stacker_freeApiSelectors Example (EXAMWAPI.C)

The example for stacker_freeApiSelectors, EXAMWAPI.C, is located on the Stacker Developer's Tool Kit diskette in the Windows directory.

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *  EXAMWAPI.C - Example program for the Stacker API for Windows.

 *  Copyright 1993, Stac Electronics, Carlsbad, California.

 *  All Rights Reserved.

 *

 *  To compile this program from the command line using Borland C type:

 *

 *      rc -r examwapi.rc

 *      bcc -c -W examwapi.c

 *      tlink -Twe -c c0ws examwapi, examwapi, NUL, cws import stackerw,

 *            examwapi.def

 *      rc examwapi.res

 *

 *  To compile this program from the command line using Microsoft C type:

 *

 *      rc /r examwapi.rc

 *      cl /c /GA /F 2000 examwapi.c

 *      link examwapi, , NUL, slibcew libw stackerw, examwapi.def

 *      re examwapi.res

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 */

#define WINVER  0x0300      /* This file is compatible with Windows 3.0.    */

#define STRICT              /* Enforces stricter type matching (optional).  */

#include <windows.h>        /* Required for all Windows applications.       */

#include <string.h>

#include <ctype.h>

#include <dos.h>

#include <stdio.h>

#if defined(__BORLANDC__)

    #include <alloc.h>

    #include <dir.h>

#else

    #include <malloc.h>

    #include <direct.h>

    #define  fnsplit    _splitpath

    #define  fnmerge    _makepath

    #define  asm        _asm

    #define  farmalloc  _fmalloc

    #define  farfree    _ffree

    #define  MK_FP(seg,ofs)((void far *)(((unsigned long)(seg)<<16)+(ofs)))

#endif

#include "stackerw.h"       /* Stacker API header file.                     */

#include "examwapi.h"       /* This program's header file.                  */

/*

 *  Defines.

 */

#define MAX_PATH        73

#define FILE_NAME_SIZE  8

#define FILE_EXT_SIZE   3

#define FIND_FIRST_FCB  0x11

#define S_MAXPATH       80

#define TRUE            1

#define FALSE           0

/*

 *  Structres for finding the first cluster for a file.

 */

struct  s_fcb

    {

    char    drive;          /* 0 = default, 1 = A */

    char    fname[8];

    char    ext[3];

    short   cur_Block;

    short   rec_Size;

    long    fsize;

    short   date;

    char    reserved[10];

    char    cur_Rec;

    long    random;

    };

struct  s_xfcb

    {

    char         flag;               /* 0xff means this is an extended fcb. */

    char         reserved[5];

    char         attrib;

    struct s_fcb fcb;

    };

char    szfileName[256] = {"*.*"};      /* File Input dialog.               */

char    szPathSpec[256] = {"*.*"};      /* File Input dialog.               */

char    driveLetter[3]  = {"C"};        /* Allocate 3 bytes for "drive:\0". */

char    fileName[256]   = {"D:\\COMMAND.COM"};

HWND    hDlgModeless    = 0;

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  WinMain() -- Main loop of the program.

 *

 *  Input parameters:  None used as command line parameters.

 *

 *  Return values:     This program just returns the return value from

 *                     PostQuitMessage.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

#pragma argsused

int PASCAL WinMain(

                  HINSTANCE hInstance,

                  HINSTANCE hPrevInstance,

                  LPSTR  lpCmdLine,

                  int    nCmdShow

                  )

    {

    HWND        hWnd;           /* a handle to a message                    */

    MSG         msg;            /* message                                  */

    WNDCLASS    wndclass;       /* the window class                         */

    char gszAppName[] = "examwapi";/* Application's name                    */

    ghInstance = hInstance;     /* Store instance handle as global variable */

    if (!hPrevInstance)         /* Other instances of app running?          */

        {

        wndclass.style          = CS_HREDRAW | CS_VREDRAW; /* Class style(s).             */

        wndclass.lpfnWndProc    = (WNDPROC)WndProc; /* Function to retrieve messages for. */

        wndclass.cbClsExtra     = 0;                /* No per-class extra data.           */

        wndclass.cbWndExtra     = 0;                /* No per-window extra data.          */

        wndclass.hInstance      = hInstance;        /* Application that owns the class.   */

        wndclass.hIcon          = LoadIcon(hInstance,  gszAppName); /* Icon information.  */

        wndclass.hCursor        = LoadCursor(NULL, IDC_ARROW);      /* Cursor information.*/

        wndclass.hbrBackground  = GetStockObject(WHITE_BRUSH);      /* Default brush.     */

        wndclass.lpszMenuName   = gszAppName;       /* Name of menu resource in .RC file. */

        wndclass.lpszClassName  = gszAppName;       /* Name used in call to CreateWindow. */

        if (!RegisterClass(&wndclass))

            {

            return FALSE;

            }

        }

    hWnd = CreateWindow(

        gszAppName,                  /* See RegisterClass() call.           */

        "Stacker API Example",       /* Text for window title bar.          */

        WS_OVERLAPPEDWINDOW,         /* Window style.                       */

        CW_USEDEFAULT,               /* Default horizontal position.        */

        CW_USEDEFAULT,               /* Default vertical position.          */

        CW_USEDEFAULT,               /* Default width.                      */

        CW_USEDEFAULT,               /* Default height.                     */

        NULL,                        /* Overlapped windows have no parent.  */

        NULL,                        /* Use the window class menu.          */

        hInstance,                   /* Instance handle.                    */

        NULL);                       /* Pointer not needed.                 */

    ShowWindow(hWnd, nCmdShow);      /* Show the window.                    */

    UpdateWindow(hWnd);              /* Sends WM_PAINT message.             */

    while (GetMessage(&msg, NULL, 0, 0)) /* Wait for a message.             */

        {

        if ((hDlgModeless == NULL)

        || (!IsDialogMessage(hDlgModeless, &msg)))

            {

            TranslateMessage(&msg);      /* Translates virtual key codes.   */

            DispatchMessage(&msg);       /* Dispatches message to window.   */

            }

        }

    /*

     *   You must always make a call to stacker_freeApiSelectors() after

     *   you are done using the Stacker API functions!!!

     */

    stacker_freeApiSelectors();

    return msg.wParam;               /* Returns value from PostQuitMessage. */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  GetDriveLetterDialog() -- Dialog to Enter a Drive Letter.

 *

 *  Input parameters:  hDlg, Handle to the dialog box.

 *                     iMessge,

 *                     wParam,

 *                     lParam,

 *

 *  Return values:     TRUE if successful, otherwise FALSE.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

BOOL FAR PASCAL GetDriveLetterDialog(

                                    HWND hDlg,

                                    unsigned iMessage,

                                    WORD wParam,

                                    LONG lParam

                                    )

    {

    switch(iMessage)

        {

        case WM_INITDIALOG:

            DlgDirList(hDlg, "", IDD_DRIVE_LIST, 0, 0x4000);

            break;

        case WM_COMMAND:

            switch(wParam)

                {

                case IDD_DRIVE_LIST:

                    switch(HIWORD(lParam))

                    {

                    case LBN_SELCHANGE:

                        DlgDirSelect(hDlg, driveLetter, IDD_DRIVE_LIST);

                        return TRUE;

                    case LBN_DBLCLK:

                        SetDlgItemText(hDlg, IDD_DRIVE_LIST, driveLetter);

                        SendMessage(hDlg, WM_COMMAND, IDOK, 0L) ;

                    }

                return TRUE;

                case IDOK :

                    GetDlgItemText(hDlg, IDD_DRIVE_LIST, driveLetter, 2);

                    DlgDirSelect(hDlg, driveLetter, IDD_DRIVE_LIST);

                    EndDialog (hDlg, 0);

                    return TRUE;

                case IDCANCEL:

                    driveLetter[0] = NULL;

                    EndDialog(hDlg, FALSE);

                    break;

                }

            return TRUE;

        case WM_DESTROY:

            EndDialog (hDlg, 0);

            return TRUE;

        }

    return FALSE;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  GetFileDialog() -- Dialog to Enter a File Name.

 *

 *  Input parameters:  hDlg, Handle to the dialog box.

 *                     iMessge,

 *                     wParam,

 *                     lParam,

 *

 *  Return values:     TRUE if successful, otherwise FALSE.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

#pragma argsused

BOOL FAR PASCAL GetFileDialog(

                             HWND hDlg,

                             unsigned iMessage,

                             WORD wParam,

                             LONG lParam

                             )

    {

    OFSTRUCT  of;

    switch(iMessage)

        {

        case WM_INITDIALOG:

            DlgDirList(hDlg, "", IDD_FLIST, IDD_FPATH, 0x0000 | 0x0010);

            SendDlgItemMessage(hDlg, IDD_FNAME, CB_RESETCONTENT, 0, 0L);

            SetDlgItemText(hDlg, IDD_FNAME, szPathSpec);

            SendDlgItemMessage(hDlg, IDD_DRIVES, CB_RESETCONTENT, 0, 0L);

            SendDlgItemMessage(hDlg, IDD_DRIVES, CB_DIR, (WORD)(0x4000), (LONG)(LPSTR)"");

            break;

        case WM_COMMAND:

            switch(wParam)

                {

                case IDOK:

                    if (DlgDirSelect(hDlg, szfileName, IDD_FLIST))

                        {

                        lstrcat(szfileName, szPathSpec);

                        DlgDirList(hDlg, szfileName, IDD_FLIST, IDD_FPATH,

                                            0x0000 | 0x0010);

                        SetDlgItemText(hDlg, IDD_FNAME, szPathSpec);

                        }

                    else

                        {

                        GetDlgItemText(hDlg, IDD_FNAME, szfileName, 80);

                        OpenFile(szfileName, &of, OF_PARSE);

                        lstrcpy(fileName, strupr(of.szPathName));

                        strupr(szfileName);

                        EndDialog(hDlg, IDOK);

                        }

                    break;

                case IDCANCEL:

                    EndDialog(hDlg, IDCANCEL);

                    break;

                case IDD_DRIVES :

                    if (HIWORD(lParam) == CBN_SELCHANGE)

                        {

                        DlgDirSelectComboBox(hDlg, szPathSpec, IDD_DRIVES);

                        strcat(szPathSpec, "*.*");

                        DlgDirList(hDlg, szPathSpec, IDD_FLIST, IDD_FPATH, 0x0000 | 0x0010);

                        SetDlgItemText(hDlg, IDD_FNAME, szPathSpec);

                        }

                    break;

                case IDD_FLIST:

                    switch(HIWORD(lParam))

                    {

                    case LBN_SELCHANGE:

                        if (DlgDirSelect(hDlg, szfileName, IDD_FLIST))

                        lstrcat(szfileName, szPathSpec);

                        SetDlgItemText(hDlg, IDD_FNAME, szfileName);

                        break;

                    case LBN_DBLCLK:

                        SetDlgItemText(hDlg, IDD_FNAME, szfileName);

                        SendMessage(hDlg, WM_COMMAND, IDOK, 0L);

                        break;

                    }

                default:

                      return FALSE;

                }

            break;

        case WM_DESTROY:

            EndDialog(hDlg, 0);

            break;

        default:

            return FALSE;

        }

    return TRUE;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  Get_DriveLetter() -- Gets the drive letter from the file spec

 *                       passed in of the current working drive if

 *                       no drive was specified in the file spec.

 *

 *  Input parameters:  *fileSpec,

 *

 *  Return values:     An upper case drive letter.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

char Get_DriveLetter(char *fileSpec)

    {

    char     driveLetter = NULL;

    unsigned driveNumber;

    char     drive[3];

    char     dir[S_MAXPATH];

    char     fname[9];

    char     ext[5];

    fnsplit(fileSpec, drive, dir, fname, ext);

    if (drive[0] == '\0')

        {

        _dos_getdrive(&driveNumber);

        driveLetter = driveNumber + 'A' -1;

        }

    else

        {

        driveLetter = toupper(drive[0]);

        }

    return driveLetter;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  s_CopyStringAndPad() -- Copies a string.  Pads destination if source

 *                          is short.

 *

 *  Input parameters:  *destination,

 *                     *source,

 *                     padChar,

 *                     size,

 *

 *  Return values:     Nothing.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void s_CopyStringAndPad(

                       char far *destination,

                       char far *source,

                       char      padChar,

                       int       size

                       )

    {

    while(size > 0 && *source != '\0')

        {

        *destination++ = *source++;

        --size;

        }

    while (size-- > 0)

        {

        *destination++ = padChar;

        }

    return;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  s_FCB_Find_First() -- Gets information for the 1st entry in a selected

 *                        directory.

 *

 *  Input parameters:  *path,

 *                     *searchFCBPtr,

 *                     attrib,

 *

 *  Return values:     TRUE for successful, FALSE if there was an error.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

int s_FCB_Find_First(

                    const    char      *path,

                    struct s_xfcb  far *searchFCBPtr,

                    int                 attrib

                    )

    {

    int      result             = TRUE;

    char     drive[3];

    char     dir[MAX_PATH];

    char     fname[9];

    char     ext[5];

    char     temp[MAX_PATH];

    char     orig_Dir[MAX_PATH];

    unsigned orig_Disk;

    unsigned maxDrives;

    fnsplit(path, drive, dir, fname, ext);

    if (fname[0] == NULL)  //No file name.

        {

        return FALSE;

        }

    getcwd(orig_Dir, MAX_PATH);

    _dos_getdrive(&orig_Disk);

    fnmerge(temp, drive, dir, NULL, NULL);

        {

        int len = _fstrlen(temp);

        /*  Strip off trailing backslashes except on root directory.        */

        if ((len != 1)

        && !(len == 3 && temp[1] == ':')

        && (temp[--len] == '\\'))

            {

            temp[len] = 0;

            }

        }

    chdir(temp);

    searchFCBPtr->flag = 0xFF;                 /* Indicates extended FCB.   */

    if( *drive != 0 )                          /* Set the search drive.     */

        {

        searchFCBPtr->fcb.drive = toupper(*drive) - 'A' + 1;

        }

    else

        {

        searchFCBPtr->fcb.drive = orig_Disk + 1;

        }

    searchFCBPtr->attrib = attrib;          /* Set the search file type.    */

    s_CopyStringAndPad(                     /* Set up the search file name. */

                      searchFCBPtr->fcb.fname,

                      fname,

                      ' ',

                      FILE_NAME_SIZE

                      );

    /* Set up the search file extension.                   */

    /* The "+ (*ext == '.')" is for getting rid of the "." */

    s_CopyStringAndPad(

                      searchFCBPtr->fcb.ext,

                      ext + (*ext == '.'),

                      ' ',

                      FILE_EXT_SIZE

                      );

    asm {

        push    ds                      /* Save registers.                  */

        push    bp

        mov     ah,FIND_FIRST_FCB       /* Specify DOS function.            */

        lds     dx,searchFCBPtr         /* Pass pointer to FCB.             */

        int     0x21                    /* Call the DOS function.           */

        pop     bp                      /* Restore registers.               */

        pop     ds

        cmp     al,0xFF                 /* If al == 0xFF, couldn't find.    */

        jne     foundFirst

        }

    result = FALSE;

foundFirst:

    _dos_setdrive(orig_Disk, &maxDrives);

    chdir(orig_Dir);

    return result;

    }

/*

+*****************************************************************************

*

* Function Name:        _s_getdta

*

* Function:             Returns the program's disk transfer area.

*

* Arguments:            none

* Globals Used:         none

* Return:               Returns a far char pointer to the dta.

* Globals Altered:      none

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

static char far *_s_getdta(void)

    {

    char far *dta_Address;

    asm {

        mov ah, 02fh

        int 021h

        mov word ptr dta_Address, bx

        mov ax, es

        mov word ptr dta_Address + 2, ax

        }

    return dta_Address;

    }

/*

+*****************************************************************************

*

* Function Name:        _s_setdta

*

* Function:             Sets the program's disk transfer area.

*

* Arguments:            *dta, Pointer to the new Disk Transfer Area.

* Globals Used:         none

* Return:               none

* Globals Altered:      none

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

void _s_setdta(char far *dta)

    {

    asm     push    ds

    asm     mov     ah, 01Ah

    asm     lds     dx, dta

    asm     int     021H

    asm     pop     ds

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  s_Get_Starting_Cluster() -- Calls s_FCB_Find_First, then digs into DTA

 *                              to find the first cluster number.

 *

 *  Input parameters:  *fname,

 *                     *first_Cluster,

 *

 *  Return values:     TRUE for successful, FALSE if there was an error.

 *                     Fails if s_FCB_Find_First fails to find the file.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

int s_Get_Starting_Cluster(

                          char       *fname,

                          s_ushort   *first_Cluster,

                          s_ulong    *fsize

                          )

    {

    struct s_xfcb      searchFCBPtr;

    struct s_xfcb far *dta_xFCB;

    char          far *origDTA;

    char               tempDTA[128];

    s_return           result;

    origDTA = _s_getdta();              /* Save original DTA pointer.       */

    _s_setdta((char far *)&tempDTA);    /* Set new DTA.                     */

    result = (s_FCB_Find_First(

                              fname,

                              &searchFCBPtr,

                              0xffff

                              ) == TRUE )? SE_SUCCESSFUL: SE_UNEXPECTED_ERROR;

    /***********************************************************************

    * DOS stores a copy of this FCB similar the above FCB in the

    * disk transfer area (DTA).  The copy in the DTA also has the

    * beginning cluster number of the file buried in the "reserved"

    * field.  We just need to index five bytes into this field, and

    * extract the cluster number.

    ************************************************************************/

    dta_xFCB = (struct s_xfcb far *) _s_getdta();

    *first_Cluster = *((unsigned far *)(dta_xFCB->fcb.reserved + 5));

    *fsize = *((unsigned long far *)(dta_xFCB->fcb.reserved + 7));

    _s_setdta(origDTA);                 /* Restore original DTA.            */

    return result;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  DisplayStackerStatus() -- Displays the status of the Stacker device

 *                            driver, after getting this information from

 *                            the stacker_status() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void DisplayStackerStatus(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    s_return return_code;           /* Result of stacker_status function.   */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    hDC = GetDC(hWnd);              /* Get device context.                  */

    return_code = stacker_status(&status_code);

    if (return_code == SE_SUCCESSFUL)

        {

        switch(status_code)

            {

            case STACKER_SW_INSTALLED:      /* Stacker Software version.    */

                TextOut(hDC, 10, 20, "Stacker software is installed. ", 31);

                break;

            case STACKER_HW_INSTALLED:      /* Stacker Hardware version.    */

                TextOut(hDC, 10, 20, "Stacker Hardware is enabled.   ", 31);

                break;

            default:                        /* No Stacker installed.        */

                TextOut(hDC, 10, 20, "Stacker is not installed.      ", 31);

                break;

            }

        }

    else

        {

        TextOut(hDC, 10, 20, "Stacker is not installed.      ", 31);

        }

    ReleaseDC(hWnd, hDC);                  /* Release device context.      */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  DisplayDriverInfo() -- Displays the Stacker device driver information,

 *                         after getting this information from the

 *                         stacker_driverInfo() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void DisplayDriverInfo(HWND hWnd)

    {

/*  Windows variables  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables  */

    s_return return_code;           /* Result of the Stacker API functions. */

    struct   stacker_driver_info_struct di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    int      nSubVersion;

    int      nFullVersion;

    hDC = GetDC(hWnd);                     /* Get device context.           */

    /*

     *  Function call to get Stacker driver information.

     */

    return_code = stacker_driverInfo(&di_rec);

    if (return_code == SE_SUCCESSFUL)

        {

        /*

         *  Stacker Driver Version.

         */

        nFullVersion = (di_rec.driverVersion / 100); /* Major Version.      */

        nSubVersion  = (di_rec.driverVersion % 100); /* Minor Version.      */

        msgSize = wsprintf(

            msgBuffer,

            "Stacker Driver Version %u.%02u.",

            nFullVersion,

            nSubVersion

            );

        TextOut(hDC, 10, 20, msgBuffer, msgSize);

        /*

         *  Stacker Hardware Type.

         */

        msgSize = wsprintf(

            msgBuffer,

            "Stacker LZS Server type is: %u (Only used after Stacker 3.0).",

            di_rec.lzsServerType

            );

        TextOut(hDC, 10, 40, msgBuffer, msgSize);

        /*

         *  Stacker Hardware Address.

         */

        if (di_rec.hardwareAddress >= 0XC400)

            {

            msgSize = wsprintf(

                msgBuffer,

                "Stacker XT/8 enabled at address %X.",

                di_rec.hardwareAddress

                );

            TextOut(hDC, 10, 60, msgBuffer, msgSize);

            }

        else

            {

            if (di_rec.hardwareAddress >= 0X200)

                {

                msgSize = wsprintf(

                    msgBuffer,

                    "Stacker AT/16 enabled at address %X.",

                    di_rec.hardwareAddress

                    );

                TextOut(hDC, 10, 60, msgBuffer, msgSize);

                }

            else

                {

                if (di_rec.hardwareAddress == 1)

                    {

                    TextOut(hDC, 10, 60, "Stacker MC/16 installed.      ", 30);

                    }

                else

                    {

                    TextOut(hDC, 10, 60, "Stacker software installed.   ", 30);

                    }

                }

            }

        /*

         *  Stacker's EMS usage status.

         */

        if (di_rec.usingEMS)

            {

            TextOut(hDC, 10, 80,"Stacker driver is using EMS.    ", 32);

            }

        else

            {

            TextOut(hDC, 10, 80,"Stacker driver is not using EMS.", 32);

            }

        /*

         *  Stacker's load high status.

         */

        if (di_rec.loadedHigh)

            {

            TextOut(hDC, 10, 100,"Stacker is loaded into upper memory.              ", 50);

            }

        else

            {

            TextOut(hDC, 10, 100,"Stacker driver is loaded into conventional memory.", 50);

            }

        /*

        *  Stacker's pre-load status.

        */

        if (di_rec.preLoaded)

            {

            TextOut(hDC, 10, 120,"Stacker was pre-loaded (loaded before CONFIG.SYS).", 50);

            }

        else

            {

            TextOut(hDC, 10, 120,"Stacker was loaded from the CONFIG.SYS file.      ", 50);

            }

        /*

        *  Number of available replaceable Stacker drives.

        */

        msgSize = wsprintf(

            msgBuffer,

            "There are %01u available replaceable Stacker drives.",

            di_rec.availRepDrives

            );

        TextOut(hDC, 10, 140, msgBuffer, msgSize);

        }

    else                    /* If stacker_driverInfo() function failed.     */

        {

        MessageBox(hWnd, "Call to stacker_status failed!",

                         "API Example Message",

                         MB_ICONEXCLAMATION | MB_OK);

        }

    ReleaseDC (hWnd, hDC);                     /* Release device context.   */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  DisplayDriveInfo() -- Displays information about the Stacker drive,

 *                        after getting this information from the

 *                        stacker_driveInfo() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void DisplayDriveInfo(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    s_return return_code;           /* Result of the Stacker API functions. */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        di_rec.driveLetter = toupper(*driveLetter);

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_driveInfo(&di_rec);

            if (return_code == SE_SUCCESSFUL)

                {

                /*

                 * Display drive information.

                 */

                if (di_rec.flagBits & STACKER_REPLACED)        /* Replaced. */

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: is a replaced Stacker drive created under version %u.%02u.",

                        di_rec.driveLetter,

                        di_rec.stacvolVersion / 100,

                        di_rec.stacvolVersion % 100

                        );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                else if (toupper(di_rec.driveLetter)

                != toupper(di_rec.hostDriveLetter))            /* Swapped.  */

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: is a swapped Stacker drive created under version %u.%02u.",

                        di_rec.driveLetter,

                        di_rec.stacvolVersion / 100,

                        di_rec.stacvolVersion % 100

                        );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                else                                 /* Extra drive letter. */

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: is a Stacker drive created under version %u.%02u.",

                        di_rec.driveLetter,

                        di_rec.stacvolVersion / 100,

                        di_rec.stacvolVersion % 100

                        );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                msgSize = wsprintf(

                    msgBuffer,

                    "Drive %c: has a minimum of %lu bytes free.",

                    di_rec.driveLetter,

                    (s_ulong) (di_rec.erasedSectorCount

                            + di_rec.freeSectorCount)

                            * di_rec.stacker.bytesPerSec

                    );

                TextOut(hDC, 10, 40, msgBuffer, msgSize);

                msgSize = wsprintf(

                    msgBuffer,

                    "The stacvol file hosting drive %c: is %c:\\%8.8s.%3.3s (%lu bytes).",

                    di_rec.driveLetter,

                    di_rec.hostDriveLetter,

                    (LPSTR)di_rec.stacvolFileName,

                    (LPSTR)di_rec.stacvolFileExt,

                    di_rec.stacvolFileSize

                    );

                TextOut(hDC, 10, 60, msgBuffer, msgSize);

                msgSize = wsprintf(

                    msgBuffer,

                    "The main store of the stacvol file is %lu bytes.",

                    di_rec.stacvolMainStore

                    );

                TextOut(hDC, 10, 80, msgBuffer, msgSize);

                msgSize = wsprintf(

                    msgBuffer,

                    "The Stacked drive %c: has a %u-bit FAT.",

                    di_rec.driveLetter,

                    di_rec.bitsPerFATEntry

                    );

                TextOut(hDC, 10, 100, msgBuffer, msgSize);

                if ((di_rec.flagBits & STACKER_READONLY)

                &&  (di_rec.flagBits & STACKER_DAMAGED))

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: was mounted read-only because it's damaged.",

                        di_rec.driveLetter

                        );

                    TextOut(hDC, 10, 120, msgBuffer, msgSize);

                    }

                else if (di_rec.flagBits & STACKER_READONLY)

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: was mounted read-only, but is not damaged.",

                        di_rec.driveLetter

                        );

                    TextOut(hDC, 10, 120, msgBuffer, msgSize);

                    }

                else if (di_rec.flagBits & STACKER_DAMAGED)

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: was mounted damaged, but is not write-protected.",

                        di_rec.driveLetter

                        );

                    TextOut(hDC, 10, 120, msgBuffer, msgSize);

                    }

                else                               /* Mounted without problems. */

                    {

                    msgSize = wsprintf(

                        msgBuffer,

                        "The Stacked drive %c: was mounted without any problems.",

                        di_rec.driveLetter

                        );

                    TextOut(hDC, 10, 120, msgBuffer, msgSize);

                    }

                }

            else if ((return_code == SE_DRIVE_NOT_STACKED)

            || ((return_code == SE_SUCCESSFUL)

                && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

                {

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Drive %c: is not mounted a Stacker drive.",

                                  di_rec.driveLetter

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            else

                {

                TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  DisplaySwapInfo() -- Displays swapmap information about the Stacker

 *                       drive, after getting this information from the

 *                       stacker_getCurrentDriveLetter() and

 *                       stacker_getBootTimeDriveLetter() API calls.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void DisplaySwapInfo(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    s_return return_code;           /* Result of the Stacker API functions. */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    s_char   currentDriveLetter;    /* The current drive letter of the      */

                                    /* drive being checked.                 */

    s_char   bootTimeDriveLetter;   /* The boot time drive letter of the    */

                                    /* drive being checked.                 */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_getCurrentDriveLetter(

                                                       *driveLetter,

                                                       &currentDriveLetter

                                                       );

            if (return_code == SE_SUCCESSFUL)

                {

                return_code = stacker_getBootTimeDriveLetter(

                                                            *driveLetter,

                                                            &bootTimeDriveLetter

                                                            );

                if (return_code == SE_SUCCESSFUL)

                    {

                    bootTimeDriveLetter = toupper(bootTimeDriveLetter);

                    currentDriveLetter  = toupper(currentDriveLetter);

                    *driveLetter        = toupper(*driveLetter);

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: was drive %c at boot time.",

                        *driveLetter,

                        bootTimeDriveLetter

                        );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    msgSize = wsprintf(

                        msgBuffer,

                        "Drive %c: at boot time is now drive %c.",

                        *driveLetter,

                        currentDriveLetter

                        );

                    TextOut(hDC, 10, 40, msgBuffer, msgSize);

                    }

                else

                    {

                    MessageBox(

                              hWnd,

                              "Call to stacker_getBootTimeDriveLetter failed!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                }

            else

                {

                MessageBox(

                          hWnd,

                          "Call to stacker_getCurrentDriveLetter failed!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  Pack_Unpack() -- Compresses and decompresses a file using the

 *                   stacker_pack() and stacker_unpack() API calls.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

#define USE_DOS_MEM_IN_PACK_UNPACK 1  //Comment this line to use Windows mem.

#define RAW_BUF_SIZE    8192

#define COMP_BUF_SIZE   (100 + RAW_BUF_SIZE + (RAW_BUF_SIZE / 8))

void Pack_Unpack(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    int          testFile;          /* Handle of file to read.              */

    char         msgBuffer[80];     /* Temp buffer to store output string.  */

    int          msgSize;           /* Size of the string in msgBuffer.     */

    s_char  far *winSourceBufPtr=0L;/* Windows pointer to source buffer.    */

    s_char  far *winUncompBufPtr=0L;/* Windows pointer to uncomp buffer.    */

    s_char  far *sourceBufPtr   =0L;/* DOS pointer to source buffer.        */

    s_char  far *compBufPtr     =0L;/* DOS pointer to compressed buffer.    */

    s_char  far *uncompBufPtr   =0L;/* DOS pointer to uncompressed buffer.  */

#if USE_DOS_MEM_IN_PACK_UNPACK

    DWORD        sourceBuf      =0L;/* Source buffer.                       */

    DWORD        compBuf        =0L;/* Compressed buffer.                   */

    DWORD        uncompBuf      =0L;/* Uncompressed buffer.                 */

#endif

    s_ulong      compFileSize   =0L;/* Size of the compressed file.         */

    s_ulong      uncompFileSize =0L;/* Size of the uncompressed file.       */

    s_ushort     compBlockSize;     /* Size of the compressed data.         */

    s_ushort     uncompBlockSize;   /* Size of the uncompressed data.       */

    int          sourceCount;       /* Number of bytes read from file.      */

    int          counter;           /* Loop counter to compare buffers.     */

    s_return     return_code;       /* Result of the Stacker API functions. */

    s_ushort     status_code;       /* Field in which to store status of    */

                                    /* the Stacker device driver.           */

    FARPROC      lpfnGetFileDlgProc;/* Pointer to the dialog function to    */

                                    /* get a file name.                     */

    int          iRet;              /* Return value from dialog function.   */

    lpfnGetFileDlgProc = MakeProcInstance((FARPROC)GetFileDialog, ghInstance);

    iRet = DialogBox(ghInstance, "GET_FILE_DLG", hWnd, (DLGPROC)lpfnGetFileDlgProc);

    FreeProcInstance(lpfnGetFileDlgProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (iRet == IDCANCEL)

        {

        return;

        }

    hDC = GetDC(hWnd);              /* Get device context.                  */

    return_code = stacker_status(&status_code);

    if ((return_code == SE_SUCCESSFUL)

    && (status_code != STACKER_NOT_INSTALLED))

        {

        TextOut(hDC, 10, 20, "Stacker is installed.", 21);

        }

    else

        {

        TextOut(hDC, 10, 20, "Stacker is not installed.", 25);

        }

    msgSize = wsprintf(msgBuffer, "File: %s.", (LPSTR)fileName);

    TextOut(hDC, 10, 40, msgBuffer, msgSize);

#if USE_DOS_MEM_IN_PACK_UNPACK  //Use DOS memory for buffers.

    sourceBuf = GlobalDosAlloc(RAW_BUF_SIZE);

    compBuf   = GlobalDosAlloc(COMP_BUF_SIZE);

    uncompBuf = GlobalDosAlloc(RAW_BUF_SIZE);

    if ((sourceBuf == NULL)

    || (compBuf == NULL)

    || (uncompBuf == NULL))

        {

        MessageBox(

                  hWnd,

                  "Unable to allocate enough DOS memory for buffers!",

                  "API Example Message",

                  MB_ICONEXCLAMATION | MB_OK

                  );

        goto ExitPack_Unpack;

        }

    winSourceBufPtr = (s_char far *)MK_FP(LOWORD(sourceBuf), 0);

    winUncompBufPtr = (s_char far *)MK_FP(LOWORD(uncompBuf), 0);

    sourceBufPtr    = (s_char far *)MK_FP(HIWORD(sourceBuf), 0);

    compBufPtr      = (s_char far *)MK_FP(HIWORD(compBuf), 0);

    uncompBufPtr    = (s_char far *)MK_FP(HIWORD(uncompBuf), 0);

#else  //Use Windows memory.

    sourceBufPtr = farmalloc(RAW_BUF_SIZE);

    compBufPtr   = farmalloc(COMP_BUF_SIZE);

    uncompBufPtr = farmalloc(RAW_BUF_SIZE);

    if ((sourceBufPtr == NULL)

    || (compBufPtr == NULL)

    || (uncompBufPtr == NULL))

        {

        MessageBox(

                  hWnd,

                  "Unable to allocate enough Windows memory for buffers!",

                  "API Example Message",

                  MB_ICONEXCLAMATION | MB_OK

                  );

        goto ExitPack_Unpack;

        }

    winSourceBufPtr = sourceBufPtr;

    winUncompBufPtr = uncompBufPtr;

#endif

    testFile = _lopen((LPSTR)fileName, OF_READ);

    if (testFile == -1)

        {

        wsprintf(msgBuffer, "Unable to open file '%s'!", (LPSTR)fileName);

        MessageBox(

                  hWnd,

                  msgBuffer,

                  "API Example Message",

                  MB_ICONEXCLAMATION | MB_OK

                  );

        goto ExitPack_Unpack;

        }

    while ((sourceCount = _lread(testFile, (LPSTR)winSourceBufPtr, RAW_BUF_SIZE)) >= 1)

        {

#if USE_DOS_MEM_IN_PACK_UNPACK  //Use DOS memory for buffers.

        return_code = stacker_pack(

                                  (const s_char far *)sourceBufPtr,

                                  sourceCount,

                                  compBufPtr,

                                  COMP_BUF_SIZE,

                                  &compBlockSize,

                                  STACKER_PARITY

                                  );

#else   //Use Windows memory for buffers.

        return_code = stacker_pack(

                                  (const s_char far *)sourceBufPtr,

                                  sourceCount,

                                  compBufPtr,

                                  COMP_BUF_SIZE,

                                  &compBlockSize,

                                  STACKER_PARITY + STACKER_USE_WIN_MEM

                                  );

#endif

        if (return_code != SE_SUCCESSFUL)

            {

            MessageBox(

                      hWnd,

                      "Call to stacker_pack failed!",

                      "API Example Message",

                      MB_ICONEXCLAMATION | MB_OK

                      );

            goto ExitPack_Unpack;

            }

#if USE_DOS_MEM_IN_PACK_UNPACK  //Use DOS memory for buffers.

        return_code = stacker_unpack(

                                    (const s_char far *)compBufPtr,

                                    compBlockSize,

                                    uncompBufPtr,

                                    RAW_BUF_SIZE,

                                    &uncompBlockSize,

                                    STACKER_PARITY

                                    );

#else   //Use Windows memory for buffers.

        return_code = stacker_unpack(

                                    (const s_char far *)compBufPtr,

                                    compBlockSize,

                                    uncompBufPtr,

                                    RAW_BUF_SIZE,

                                    &uncompBlockSize,

                                    STACKER_PARITY + STACKER_USE_WIN_MEM

                                    );

#endif

        if (return_code != SE_SUCCESSFUL)

            {

            MessageBox(

                      hWnd,

                      "Call to stacker_unpack failed!",

                      "API Example Message",

                      MB_ICONEXCLAMATION | MB_OK

                      );

            goto ExitPack_Unpack;

            }

        for (counter = 0; counter < sourceCount ; counter++)

            {

            if (winSourceBufPtr[counter] != winUncompBufPtr[counter])

                {

                wsprintf(

                        msgBuffer,

                        "Miscompare at buf[0x%X], 0x%04X:0x%04X\n",

                        counter,

                        FP_SEG(winUncompBufPtr),

                        (FP_OFF(winUncompBufPtr) + counter)

                        );

                MessageBox(

                          hWnd,

                          msgBuffer,

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto ExitPack_Unpack;

                }

            }

        uncompFileSize += sourceCount;

        compFileSize   += compBlockSize;

        msgSize = wsprintf(

            msgBuffer,

            "Compressed %lu bytes of file %s.",

            uncompFileSize,

            (LPSTR)szfileName

            );

        TextOut(hDC, 10, 60, msgBuffer, msgSize);

        }

    if (uncompFileSize >= compFileSize)

        {

        msgSize = wsprintf(

            msgBuffer,

            "%s compressed from %lu bytes to %lu bytes.",

            (LPSTR)szfileName,

            uncompFileSize,

            compFileSize

            );

        }

    else

        {

        msgSize = wsprintf(

            msgBuffer,

            "%s expanded from %lu bytes to %lu bytes.",

            (LPSTR)szfileName,

            uncompFileSize,

            compFileSize

            );

        }

    TextOut(hDC, 10, 60, msgBuffer, msgSize);

ExitPack_Unpack:

#if USE_DOS_MEM_IN_PACK_UNPACK

    if (sourceBuf)

        {

        GlobalDosFree((WORD)sourceBuf);

        }

    if (compBuf)

        {

        GlobalDosFree((WORD)compBuf);

        }

    if (uncompBuf)

        {

        GlobalDosFree((WORD)uncompBuf);

        }

#else

    if (sourceBufPtr)

        {

        farfree(sourceBufPtr);

        }

    if (compBufPtr)

        {

        farfree(compBufPtr);

        }

    if (uncompBufPtr)

        {

        farfree(uncompBufPtr);

        }

#endif

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  FlushDriver() -- Flushes a Stacker drive, using the

 *                   stacker_flushDriver() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void FlushDriver(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    s_return return_code;           /* Result of the Stacker API functions. */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        di_rec.driveLetter = toupper(*driveLetter);

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_driveInfo(&di_rec);

            if (return_code == SE_SUCCESSFUL)

                {

                return_code = stacker_flushDriver(*driveLetter);

                if (return_code != SE_SUCCESSFUL)

                    {

                    MessageBox(

                              hWnd,

                              "Call to stacker_flushDriver failed!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                else

                    {

                    msgSize = wsprintf(

                                      msgBuffer,

                                      "Drive %c: has been flushed.",

                                      di_rec.driveLetter

                                      );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                }

            else if ((return_code == SE_DRIVE_NOT_STACKED)

            || ((return_code == SE_SUCCESSFUL)

                && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

                {

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Drive %c: is not a mounted Stacker drive.",

                                  di_rec.driveLetter

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            else

                {

                TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  UnmountVolume() -- Unmounts a Stacker drive, using the

 *                     stacker_unmountVolume() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void UnmountVolume(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string   */

    int      msgSize;               /* Size of the string in msgBuffer      */

    s_return return_code;           /* Result of the Stacker API functions. */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        di_rec.driveLetter = toupper(*driveLetter);

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_driveInfo(&di_rec);

            if (return_code == SE_SUCCESSFUL)

                {

                return_code = stacker_unmountVolume(&di_rec);

                if (return_code != SE_SUCCESSFUL)

                    {

                    MessageBox(

                              hWnd,

                              "Call to stacker_unmountVolume failed!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                else

                    {

                    msgSize = wsprintf(

                                      msgBuffer,

                                      "Drive %c: is unmounted.",

                                      di_rec.driveLetter

                                      );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                }

            else if ((return_code == SE_DRIVE_NOT_STACKED)

            || ((return_code == SE_SUCCESSFUL)

                && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

                {

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Drive %c: is not a mounted Stacker drive.",

                                  di_rec.driveLetter

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            else

                {

                TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  WriteProtect() -- Write-protects a Stacker drive, using the

 *                    stacker_writeProtect() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void WriteProtect(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    s_return return_code;           /* Result of the Stacker API functions. */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        di_rec.driveLetter = toupper(*driveLetter);

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_driveInfo(&di_rec);

            if (return_code == SE_SUCCESSFUL)

                {

                return_code = stacker_writeProtect(&di_rec);

                if (return_code == SE_SUCCESSFUL)

                    {

                    msgSize = wsprintf(

                                      msgBuffer,

                                      "Drive %c: is write-protected.",

                                      di_rec.driveLetter

                                      );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                else

                    {

                    MessageBox(

                              hWnd,

                              "Call to stacker_writeProtect failed!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                }

            else if ((return_code == SE_DRIVE_NOT_STACKED)

            || ((return_code == SE_SUCCESSFUL)

                && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

                {

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Drive %c: is not a mounted Stacker drive.",

                                  di_rec.driveLetter

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            else

                {

                TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  UnWriteProtect() -- Removes the Write-protect status on a Stacker drive,

 *                      using the stacker_writeProtect() API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void UnWriteProtect(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    s_return return_code;           /* Result of the Stacker API functions. */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        di_rec.driveLetter = toupper(*driveLetter);

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_driveInfo(&di_rec);

            if (return_code == SE_SUCCESSFUL)

                {

                return_code = stacker_unWriteProtect(&di_rec);

                if (return_code == SE_SUCCESSFUL)

                    {

                    msgSize = wsprintf(

                                      msgBuffer,

                                      "The write-protect status on drive %c: "

                                      "has been removed.",

                                      di_rec.driveLetter

                                      );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    }

                else

                    {

                    MessageBox(

                              hWnd,

                              "Call to stacker_unWriteProtect failed!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                }

            else if ((return_code == SE_DRIVE_NOT_STACKED)

            || ((return_code == SE_SUCCESSFUL)

                && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

                {

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Drive %c: is not a mounted Stacker drive.",

                                  di_rec.driveLetter

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            else

                {

                TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  HostDriveMode() -- Places a Stacker drive in Host Drive Mode, prompts

 *                     you with a message box and then takes the drive out

 *                     of Host Drive Mode, using the stacker_hostDriveMode()

 *                     and stacker_undoHostDriveMode() API calls.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void HostDriveMode(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char     msgBuffer[80];         /* Temp buffer to store output string.  */

    int      msgSize;               /* Size of the string in msgBuffer.     */

    s_return return_code;           /* Result of stacker_driveInfo function.*/

    s_char   hostDriveLetter;       /* Drive letter of the host drive.      */

    s_ushort status_code;           /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC  lpfnDialogProc;

    hDC = GetDC(hWnd);              /* Get device context.                  */

    lpfnDialogProc = MakeProcInstance((FARPROC)GetDriveLetterDialog, ghInstance);

    DialogBox(ghInstance, "GET_DRIVE_DLG", hWnd, (DLGPROC)lpfnDialogProc);

    FreeProcInstance(lpfnDialogProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (driveLetter[0] != NULL)

        {

        di_rec.driveLetter = toupper(*driveLetter);

        return_code = stacker_status(&status_code);

        if ((return_code == SE_SUCCESSFUL)

        && (status_code != STACKER_NOT_INSTALLED))

            {

            return_code = stacker_driveInfo(&di_rec);

            if (return_code == SE_SUCCESSFUL)

                {

                return_code = stacker_hostDriveMode(&di_rec, &hostDriveLetter);

                if (return_code == SE_SUCCESSFUL)

                    {

                    msgSize = wsprintf(

                                      msgBuffer,

                                      "The host drive for drive %c: can be accessed through drive %c:.",

                                      di_rec.driveLetter,

                                      hostDriveLetter

                                      );

                    TextOut(hDC, 10, 20, msgBuffer, msgSize);

                    MessageBox(

                              hWnd,

                              "Please click on OK to take the drive out of Host Drive Mode!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    return_code = stacker_undoHostDriveMode(

                                                           &di_rec

                                                           );

                    if (return_code == SE_SUCCESSFUL)

                        {

                        msgSize = wsprintf(

                                          msgBuffer,

                                          "Drive %c: is back to normal.",

                                          di_rec.driveLetter

                                          );

                        TextOut(hDC, 10, 40, msgBuffer, msgSize);

                        }

                    else

                        {

                        MessageBox(

                                  hWnd,

                                  "Call to stacker_undoHostDriveMode failed!",

                                  "API Example Message",

                                  MB_ICONEXCLAMATION | MB_OK

                                  );

                        }

                    }

                else

                    {

                    MessageBox(

                              hWnd,

                              "Call to stacker_hostDriveMode failed!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                }

            else if ((return_code == SE_DRIVE_NOT_STACKED)

            || ((return_code == SE_SUCCESSFUL)

                && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

                {

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Drive %c: is not a mounted Stacker drive.",

                                  di_rec.driveLetter

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            else

                {

                TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

                }

            }

        else

            {

            TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

            }

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  FileCompressionRatio() -- Gets the compression ratio of a given file,

 *                            using the stacker_fileCompressionRatio()

 *                            API call.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void FileCompressionRatio(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char      msgBuffer[80];        /* Temp buffer to store output string.  */

    int       msgSize;              /* Size of the string in msgBuffer.     */

    s_ulong   compressedSectors;    /* The # of compressed sectors.         */

    s_ulong   rawSectors;           /* The # of raw sectors.                */

    s_ulong   adjustedClusterRatio; /* The adjusted cluster ratio.          */

    void  far *rccBuffer;           /* Pointer to a scratch RCC buffer.     */

    s_return  return_code;          /* Result of the Stacker API functions. */

    s_ushort  status_code;          /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC   lpfnGetFileDlgProc;   /* Pointer to the dialog function to    */

                                    /* get a file name.                     */

    int       iRet;                 /* Return value from dialog function.   */

    lpfnGetFileDlgProc = MakeProcInstance((FARPROC)GetFileDialog, ghInstance);

    iRet = DialogBox(ghInstance, "GET_FILE_DLG", hWnd, (DLGPROC)lpfnGetFileDlgProc);

    FreeProcInstance(lpfnGetFileDlgProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (iRet == IDCANCEL)

        {

        return;

        }

    hDC = GetDC(hWnd);              /* Get device context.                  */

    strupr(fileName);

    di_rec.driveLetter = Get_DriveLetter(fileName);

    return_code = stacker_status(&status_code);

    if ((return_code == SE_SUCCESSFUL)

    && (status_code != STACKER_NOT_INSTALLED))

        {

        return_code = stacker_driveInfo(&di_rec);

        if ((return_code == SE_SUCCESSFUL)

        && (di_rec.driveIsStacked == STACKER_STACKED_DRIVE))

            {

            rccBuffer = farmalloc(di_rec.rccBufferSize);

            if (rccBuffer == NULL)

                {

                MessageBox(

                          hWnd,

                          "Unable to allocate rccBuffer!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                }

            else

                {

                return_code = stacker_initializeRccBuffer(

                                                         rccBuffer,

                                                         &di_rec

                                                         );

                if (return_code != SE_SUCCESSFUL)

                    {

                    MessageBox(

                              hWnd,

                              "Unable to initialize rccBuffer!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                else

                    {

                    return_code = stacker_fileCompressionRatio(

                                                        &di_rec,

                                                        rccBuffer,

                                                        fileName,

                                                        &compressedSectors,

                                                        &rawSectors

                                                              );

                    if (return_code == SE_SUCCESSFUL)

                        {

                        if (compressedSectors)

                            {

                            adjustedClusterRatio = ((rawSectors

                                                 * (unsigned long) 100

                                                 / compressedSectors)

                                                 + 5)

                                                 / (unsigned long) 10;

                            }

                        else

                            {

                            adjustedClusterRatio = 10;

                            }

                        msgSize = wsprintf(

                                          msgBuffer,

                                          "%s uses %lu compressed sectors "

                                          "and %lu raw sectors.",

                                          (LPSTR)szfileName,

                                          compressedSectors,

                                          rawSectors

                                          );

                        TextOut(hDC, 10, 20, msgBuffer, msgSize);

                        msgSize = wsprintf(

                                          msgBuffer,

                                          "The compression ratio of \"%s\" "

                                          "is %ld.%.1ld:1",

                                          (LPSTR)szfileName,

                                          adjustedClusterRatio / 10,

                                          adjustedClusterRatio % 10

                                          );

                        TextOut(hDC, 10, 40, msgBuffer, msgSize);

                        }

                    else

                        {

                        MessageBox(

                                  hWnd,

                                  "stacker_fileCompressionRatio call failed!",

                                  "API Example Message",

                                  MB_ICONEXCLAMATION | MB_OK

                                  );

                        }

                    }

                farfree(rccBuffer);

                }

            }

        else if ((return_code == SE_DRIVE_NOT_STACKED)

        || ((return_code == SE_SUCCESSFUL)

            && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

            {

            msgSize = wsprintf(

                              msgBuffer,

                              "Drive %c: is not a mounted Stacker drive.",

                              di_rec.driveLetter

                              );

            TextOut(hDC, 10, 20, msgBuffer, msgSize);

            }

        else

            {

            TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

            }

        }

    else

        {

        TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

        }

ExitFileCompressionRatio:

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  FastFileCompressionRatio() -- Gets the compression ratio of a given

 *                                file, using the stacker_getFatEntry()

 *                                and stacker_fastFileCompressionRatio()

 *                                API calls.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void FastFileCompressionRatio(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char      msgBuffer[80];        /* Temp buffer to store output string.  */

    int       msgSize;              /* Size of the string in msgBuffer.     */

    s_ushort  firstCluster;         /* First cluster of the file.           */

    s_ulong   fileSize;             /* Size of the file.                    */

    s_ulong   compressedSectors;    /* The # of compressed sectors.         */

    s_ulong   rawSectors;           /* The # of raw sectors.                */

    s_ulong   adjustedClusterRatio; /* The adjusted cluster ratio.          */

    void  far *rccBuffer;           /* Pointer to a scratch RCC buffer.     */

    s_return  return_code;          /* Result of the Stacker API functions. */

    s_ushort  status_code;          /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC   lpfnGetFileDlgProc;   /* Pointer to the dialog function to    */

                                    /* get a file name.                     */

    int       iRet;                 /* Return value from dialog function.   */

    lpfnGetFileDlgProc = MakeProcInstance((FARPROC)GetFileDialog, ghInstance);

    iRet = DialogBox(ghInstance, "GET_FILE_DLG", hWnd, (DLGPROC)lpfnGetFileDlgProc);

    FreeProcInstance(lpfnGetFileDlgProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (iRet == IDCANCEL)

        {

        return;

        }

    hDC = GetDC(hWnd);              /* Get device context.                  */

    strupr(fileName);

    di_rec.driveLetter = Get_DriveLetter(fileName);

    return_code = stacker_status(&status_code);

    if ((return_code == SE_SUCCESSFUL)

    && (status_code != STACKER_NOT_INSTALLED))

        {

        return_code = stacker_driveInfo(&di_rec);

        if ((return_code == SE_SUCCESSFUL)

        && (di_rec.driveIsStacked == STACKER_STACKED_DRIVE))

            {

            rccBuffer = farmalloc(di_rec.rccBufferSize);

            if (rccBuffer == NULL)

                {

                MessageBox(

                          hWnd,

                          "Unable to allocate rccBuffer!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                }

            else

                {

                return_code = stacker_initializeRccBuffer(

                                                         rccBuffer,

                                                         &di_rec

                                                         );

                if (return_code != SE_SUCCESSFUL)

                    {

                    MessageBox(

                              hWnd,

                              "Unable to initialize rccBuffer!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    }

                else

                    {

                    if (s_Get_Starting_Cluster(

                                              fileName,

                                              &firstCluster,

                                              &fileSize

                                              ) != TRUE)

                        {

                        MessageBox(

                                  hWnd,

                                  "Unable to get the first cluster "

                                  "of the file!",

                                  "API Example Message",

                                  MB_ICONEXCLAMATION | MB_OK

                                  );

                        }

                    else

                        {

                        return_code = stacker_fastFileCR(

                                                        &di_rec,

                                                        rccBuffer,

                                                        firstCluster,

                                                        fileSize,

                                                        &compressedSectors,

                                                        &rawSectors

                                                        );

                        if (return_code != SE_SUCCESSFUL)

                            {

                            MessageBox(

                                      hWnd,

                                      "Error in call to "

                                      "stacker_fileCompressionRatio!",

                                      "API Example Message",

                                      MB_ICONEXCLAMATION | MB_OK

                                      );

                            }

                        else

                            {

                            if (compressedSectors)

                                {

                                adjustedClusterRatio = ((rawSectors

                                                     * (unsigned long) 100

                                                     / compressedSectors)

                                                     + 5)

                                                     / (unsigned long) 10;

                                }

                            else

                                {

                                adjustedClusterRatio = 10;

                                }

                            msgSize = wsprintf(

                                              msgBuffer,

                                              "%s uses %lu compressed sectors "

                                              "and %lu raw sectors.",

                                              (LPSTR)szfileName,

                                              compressedSectors,

                                              rawSectors

                                              );

                            TextOut(hDC, 10, 20, msgBuffer, msgSize);

                            msgSize = wsprintf(

                                              msgBuffer,

                                              "The compression ratio of \"%s\" "

                                              "is %ld.%.1ld:1",

                                              (LPSTR)szfileName,

                                              adjustedClusterRatio / 10,

                                              adjustedClusterRatio % 10

                                              );

                            TextOut(hDC, 10, 40, msgBuffer, msgSize);

                            }

                        }

                    }

                farfree(rccBuffer);

                }

            }

        else if ((return_code == SE_DRIVE_NOT_STACKED)

        || ((return_code == SE_SUCCESSFUL)

            && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

            {

            msgSize = wsprintf(

                              msgBuffer,

                              "Drive %c: is not a mounted Stacker drive.",

                              di_rec.driveLetter

                              );

            TextOut(hDC, 10, 20, msgBuffer, msgSize);

            }

        else

            {

            TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

            }

        }

    else

        {

        TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

        }

ExitFastFileCompressionRatio:

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  ReadCompressedCluster() -- Reads a compressed cluster, reads the cluster

 *                             through DOS, decompresses the compressed

 *                             cluster and then compares the decompressed

 *                             cluster to the cluster read through DOS,

 *                             using the stacker_getFatEntry(),

 *                             stacker_readCompressedCluster() and

 *                             stacker_unpack() API calls.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void ReadCompressedCluster(HWND hWnd)

    {

/*  Windows variables.  */

    HDC      hDC;                   /* Window handle.                       */

/*  Stacker variables.  */

    struct stacker_drive_info_struct      di_rec;

    char      msgBuffer[80];        /* Temp buffer to store output string.  */

    int       msgSize;              /* Size of the string in msgBuffer.     */

    int       hFile           = -1; /* DOS file handle of the input file.   */

    int       bytesRead;            /* Number of bytes read from DOS.       */

    s_ushort  eof;                  /* End-of-file marker for the drive.    */

    s_ushort  counter;              /* Counter for comparing buffers.       */

    s_ushort  clusterSize;          /* Number of bytes per cluster.         */

    s_ushort  currentCluster;       /* Current cluster of the file.         */

    s_ushort  compByteCount;        /* Number of compressed bytes read.     */

    s_ushort  uncompByteCount;      /* Number of uncompressed bytes.        */

    s_ushort  compDataFormat;       /* Format of the compressed data.       */

    s_ulong   fileSize;             /* Size of the file.                    */

    void  far *rccBuffer       = 0L;/* Pointer to a scratch RCC buffer.     */

    char  far *rawBufferPtr    = 0L;/* Buffer to store data read from DOS.  */

    char  far *compBufferPtr   = 0L;/* Buffer to store compressed data.     */

    char  far *uncompBufferPtr = 0L;/* Buffer to store uncompressed data.   */

    s_return  return_code;          /* Result of the Stacker API functions. */

    s_ushort  status_code;          /* Pointer in which to store status of  */

                                    /* the Stacker device driver.           */

    FARPROC   lpfnGetFileDlgProc;   /* Pointer to the dialog function to    */

                                    /* get a file name.                     */

    int       iRet;                 /* Return value from dialog function.   */

    lpfnGetFileDlgProc = MakeProcInstance((FARPROC)GetFileDialog, ghInstance);

    iRet = DialogBox(ghInstance, "GET_FILE_DLG", hWnd, (DLGPROC)lpfnGetFileDlgProc);

    FreeProcInstance(lpfnGetFileDlgProc);

    /* Only run the function if the user did not hit the Cancel button.     */

    if (iRet == IDCANCEL)

        {

        return;

        }

    hDC = GetDC(hWnd);              /* Get device context.                  */

    strupr(fileName);

    di_rec.driveLetter = Get_DriveLetter(fileName);

    hFile = _lopen((LPSTR)fileName, OF_READ);

    if (hFile == -1)

        {

        wsprintf(msgBuffer, "Unable to open file '%s'!", (LPSTR)fileName);

        MessageBox(

                  hWnd,

                  msgBuffer,

                  "API Example Message",

                  MB_ICONEXCLAMATION | MB_OK

                  );

        goto Exit_RCC;

        }

    return_code = stacker_status(&status_code);

    if ((return_code == SE_SUCCESSFUL)

    && (status_code != STACKER_NOT_INSTALLED))

        {

        return_code = stacker_driveInfo(&di_rec);

        if ((return_code == SE_SUCCESSFUL)

        && (di_rec.driveIsStacked == STACKER_STACKED_DRIVE))

            {

            /****************************************************************

            * Because File allocation Tables (FATs) in DOS can be established

            * with either a 12 bit or a 16 bit format for each entry, the

            * value of an "end-of-file" is not fixed.  To be able to properly

            * interpret what a cluster entry is in DOS, we have to first set

            * up this value.  EOF is 0xff8 to 0xfff for a 12 bit FAT, and

            * 0xfff8 to 0xffff for a 16 bit FAT.

            ****************************************************************/

            if (di_rec.bitsPerFATEntry == 12)

                {

                eof = 0xff8;

                }

            else

                {

                eof = 0xfff8;

                }

            clusterSize = di_rec.stacker.bytesPerSec

                        * di_rec.stacker.secPerCluster;

            rccBuffer = farmalloc(di_rec.rccBufferSize);

            if (rccBuffer == NULL)

                {

                MessageBox(

                          hWnd,

                          "Unable to allocate rccBuffer!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto Exit_RCC;

                }

            rawBufferPtr = farmalloc(clusterSize + di_rec.stacker.bytesPerSec);

            if (rawBufferPtr == NULL)

                {

                MessageBox(

                          hWnd,

                          "Unable to allocate rawBufferPtr!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto Exit_RCC;

                }

            compBufferPtr = farmalloc(clusterSize + di_rec.stacker.bytesPerSec);

            if (compBufferPtr == NULL)

                {

                MessageBox(

                          hWnd,

                          "Unable to allocate compBufferPtr!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto Exit_RCC;

                }

            uncompBufferPtr = farmalloc(clusterSize + di_rec.stacker.bytesPerSec);

            if (uncompBufferPtr == NULL)

                {

                MessageBox(

                          hWnd,

                          "Unable to allocate uncompBufferPtr!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto Exit_RCC;

                }

            return_code = stacker_initializeRccBuffer(

                                                     rccBuffer,

                                                     &di_rec

                                                     );

            if (return_code != SE_SUCCESSFUL)

                {

                MessageBox(

                          hWnd,

                          "Unable to initialize rccBuffer!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto Exit_RCC;

                }

            if (s_Get_Starting_Cluster(

                                      fileName,

                                      &currentCluster,

                                      &fileSize

                                      ) != TRUE)

                {

                MessageBox(

                          hWnd,

                          "Unable to get the first cluster "

                          "of the file!",

                          "API Example Message",

                          MB_ICONEXCLAMATION | MB_OK

                          );

                goto Exit_RCC;

                }

            while (currentCluster < eof)

                {

                return_code = stacker_readCompressedCluster(

                                             &di_rec,

                                             currentCluster,

                                             compBufferPtr,

                                             rccBuffer,

                                             &compByteCount,

                                             &compDataFormat,

                                             0

                                                           );

                if (return_code != SE_SUCCESSFUL)

                    {

                    MessageBox(

                              hWnd,

                              "Error in call to "

                              "stacker_readCompressedCluster!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    goto Exit_RCC;

                    }

                if (compDataFormat == STACKER_LZS_FORMAT)

                    {

                    /*

                     *   Not concerned about speed so just use Windows

                     *   memory for stacker_unpack() buffers.

                     */

                    return_code = stacker_unpack(

                            (const s_char far *)compBufferPtr,

                                                compByteCount,

                                                uncompBufferPtr,

                                                clusterSize,

                                                &uncompByteCount,

                                                STACKER_PARITY + STACKER_USE_WIN_MEM

                                                );

                    if (return_code != SE_SUCCESSFUL)

                        {

                        MessageBox(

                                  hWnd,

                                  "Error in call to "

                                  "stacker_unpack!",

                                  "API Example Message",

                                  MB_ICONEXCLAMATION | MB_OK

                                  );

                        goto Exit_RCC;

                        }

                    }

                else

                    {

                    movedata(

                            FP_SEG(compBufferPtr),

                            FP_OFF(compBufferPtr),

                            FP_SEG(uncompBufferPtr),

                            FP_OFF(uncompBufferPtr),

                            compByteCount

                            );

                    }

                bytesRead = _lread(hFile, (LPSTR)rawBufferPtr, clusterSize);

                if (bytesRead <= 0)

                    {

                    MessageBox(

                              hWnd,

                              "Error reading source "

                              "file!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    goto Exit_RCC;

                    }

                for (counter = 0; counter < bytesRead; counter++)

                    {

                    if (uncompBufferPtr[counter] != rawBufferPtr[counter])

                        {

                        wsprintf(

                                msgBuffer,

                                "Miscompare at buf[0x%X], 0x%04X:0x%04X\n",

                                counter,

                                FP_SEG(uncompBufferPtr),

                                (FP_OFF(uncompBufferPtr) + counter)

                                );

                        MessageBox(

                                  hWnd,

                                  msgBuffer,

                                  "API Example Message",

                                  MB_ICONEXCLAMATION | MB_OK

                                  );

                        goto Exit_RCC;

                        }

                    }

                return_code = stacker_getFatEntry(

                                                 currentCluster,

                                                 &di_rec,

                                                 rccBuffer,

                                                 &currentCluster

                                                 );

                if (return_code != SE_SUCCESSFUL)

                    {

                    MessageBox(

                              hWnd,

                              "Error in call to stacker_getFatEntry!",

                              "API Example Message",

                              MB_ICONEXCLAMATION | MB_OK

                              );

                    goto Exit_RCC;

                    }

                msgSize = wsprintf(

                                  msgBuffer,

                                  "Reading cluster 0x%X",

                                  currentCluster

                                  );

                TextOut(hDC, 10, 20, msgBuffer, msgSize);

                }

            msgSize = wsprintf(

                              msgBuffer,

                              "%s was read successfully!     ",

                              (LPSTR)szfileName

                              );

            TextOut(hDC, 10, 20, msgBuffer, msgSize);

            }

        else if ((return_code == SE_DRIVE_NOT_STACKED)

        || ((return_code == SE_SUCCESSFUL)

            && (di_rec.driveIsStacked != STACKER_STACKED_DRIVE)))

            {

            msgSize = wsprintf(

                              msgBuffer,

                              "Drive %c: is not a mounted Stacker drive.",

                              di_rec.driveLetter

                              );

            TextOut(hDC, 10, 20, msgBuffer, msgSize);

            }

        else

            {

            TextOut(hDC, 10, 20, "stacker_driveInfo() call failed!  ", 34);

            }

        }

    else

        {

        TextOut(hDC, 10, 20, "Stacker is not installed.         ", 34);

        }

Exit_RCC:

    if (hFile != -1)

        {

        _lclose(hFile);

        }

    if (rccBuffer)

        {

        farfree(rccBuffer);

        }

    if (rawBufferPtr)

        {

        farfree(rawBufferPtr);

        }

    if (compBufferPtr)

        {

        farfree(compBufferPtr);

        }

    if (uncompBufferPtr)

        {

        farfree(uncompBufferPtr);

        }

    ReleaseDC(hWnd, hDC);                     /* Release device context.    */

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  ClearScreen() -- Clears the window passed to this function.

 *

 *  Input parameters:  hWnd, Handle to the window to clear.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void ClearScreen(HWND hWnd)

    {

    InvalidateRect(hWnd, NULL, TRUE);

    UpdateWindow(hWnd);

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  DisplayNotAvailable() -- Displays that the functions you picked is not

 *                           available at this time.

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void DisplayNotAvailable(HWND hWnd)

    {

    MessageBox(hWnd, "This function is not available yet!",

                     "API Example Message",

                     MB_OK);

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  WndProc() --

 *

 *  Input parameters:  hWnd, Handle to the current window.

 *                     iMessage,

 *                     wParam,

 *                     iParam,

 *

 *  Return values:     0 if successful.  If unseccessful, it will return

 *                     the value returned by DefWindowProc().

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

long FAR PASCAL WndProc(

                       HWND     hWnd,

                       unsigned iMessage,

                       WORD     wParam,

                       LONG     lParam

                       )

    {

    switch (iMessage)               /* Process windows messages.            */

        {

        case WM_COMMAND:            /* Message from application menu.       */

            ClearScreen(hWnd);

            switch (wParam)

                {

                /* stacker_status() example.                                */

                case IDM_STACKER_STAT:

                    DisplayStackerStatus(hWnd);

                    break;

                /* stacker_driverInfo example.                              */

                case IDM_DRIVER_STAT:

                    DisplayDriverInfo(hWnd);

                    break;

                /* stacker_driveInfo example.                               */

                case IDM_DRIVE_INFO:

                    DisplayDriveInfo(hWnd);

                    break;

                /* stacker_driverInfo example.                              */

                case IDM_SWAP_INFO:

                    DisplaySwapInfo(hWnd);

                    break;

                /* stacker_pack and stacker_unpack example.                 */

                case IDM_PACK_UNPACK:

                    Pack_Unpack(hWnd);

                    break;

                /* stacker_flushDriver example.                             */

                case IDM_FLUSH_DRIVER:

                    FlushDriver(hWnd);

                    break;

                /* stacker_unmountVolume example.                           */

                case IDM_UNMOUNT:

                    UnmountVolume(hWnd);

                    break;

                /* stacker_hostDriveMode and stacker_undoHostDriveMode      */

                /* example.                                                 */

                case IDM_HOST_DRIVE:

                    HostDriveMode(hWnd);

                    break;

                /* stacker_writeProtect example.                            */

                case IDM_WRITE_PROTECT:

                    WriteProtect(hWnd);

                    break;

                /* stacker_unWriteProtect example.                          */

                case IDM_UNWRITE_PROTECT:

                    UnWriteProtect(hWnd);

                    break;

                /* stacker_initializeRccBuffer, stacker_getFatEntry and     */

                /* stacker_readCompressedCluster example.                   */

                case IDM_RCC_TEST:

                    ReadCompressedCluster(hWnd);

                    break;

                /* stacker_fileCompressionRatio example.                    */

                case IDM_FILE_CR:

                    FileCompressionRatio(hWnd);

                    break;

                /* stacker_fastFileCR example.                              */

                case IDM_FAST_FILE_CR:

                    FastFileCompressionRatio(hWnd);

                    break;

                /* QUIT menu option - end of application.                   */

                case IDM_QUIT:

                    DestroyWindow (hWnd);

                    break;

                }

                break;

        case WM_DESTROY:            /*  Stop application.                   */

            PostQuitMessage(0);

            break;

        default:                    /* Default windows message processing.  */

            return DefWindowProc(hWnd, iMessage, wParam, lParam);

        }

        return (0L);

    }


stacker_pack example
  stacker_pack and stacker_unpack Example (EXAM_6.C)

#define USE_TEMP_STACK 1

#include <stdio.h>

#include <string.h>

#include <dos.h>

#include "stacker.h"

#if defined(__BORLANDC__)

#include <alloc.h>

#else

#include <malloc.h>

#define asm _asm

#endif

int main(void)

{

int return_code	= 0;

s_return result;

s_ushort status;

const char source_buffer[]	=

"this is the is the is the text to compress.";

s_char	dest_buffer[50];

s_char	unpack_buffer[50];

s_ushort	source_count;

s_ushort	dest_count;

s_ushort	unpack_count;

s_ushort	flags;

#if USE_TEMP_STACK

char  far  *tempStack;	/* Pointer to the temporary stack.	*/

s_ushort    origSS;	/* Value of original SS register.	*/

s_ushort    origSP;	/* Value of original SP register.	*/

#endif

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

puts("\nStacker is not installed.");

return_code = 1;

}

else

{

#if USE_TEMP_STACK

/* Allocate 8k of temporary stack space.	*/

tempStack = (char far *) malloc(0x2000);

if (tempStack == NULL)

{

puts("\nERROR:  Unable to allocate temporary stack.");

return_code = 3;

}

else

#endif

{

#if USE_TEMP_STACK

origSS = _SS;	/* Save the current stack.	*/

origSP = _SP;

_SS = FP_SEG(tempStack);	/* Point SS:SP to the	*/

_SP = FP_OFF(tempStack);	/* temporary stack.	*/

#endif

/* add 1 to strlen to include NULL at end of string */

source_count = strlen(source_buffer) + 1;

flags = STACKER_NO_PARITY;

result = stacker_pack(
source_buffer,
source_count,
dest_buffer,
sizeof(dest_buffer),
&dest_count,
flags
);

if (result == SE_SUCCESSFUL)

{

result = stacker_unpack(
dest_buffer,
dest_count,
unpack_buffer,
sizeof(unpack_buffer),
&unpack_count,
flags
);

}

if ((strcmp(source_buffer, unpack_buffer) == 0)

&& (source_count == unpack_count)

&& (result == SE_SUCCESSFUL))

{

puts("\nCompression/decompression was successful!");

printf( "Original size of data was %u bytes.\n",

source_count );

printf( "Compressed size of data is %u bytes.\n",

dest_count );

printf( "Uncompressed size of data is %u bytes.\n",

unpack_count );

return_code = 0;

}

else

{

puts("\nError during compression/decompression.");

return_code = 2;

}

#if USE_TEMP_STACK

_SP = origSP;	/* Restore the original stack.	*/

_SS = origSS;

free((char *)tempStack);	/* Free memory for the	*/

/* temporary stack.	*/

#endif

}

return return_code;

}


stacker_flushDriver Example
 stacker_flushDriver Example (EXAM_7.C)

#include <stdio.h>

#include "stacker.h"

int main(void)

{

struct	stacker_drive_info_struct driveInfo;

s_ushort	status;

s_return	result	= 0;

int	errorLevel;

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

puts(" Stacker is not installed.");

errorLevel = 1;

}

else

{

driveInfo.driveLetter = 'C';  /* Flush drive C: */

result = stacker_driveInfo(&driveInfo);

if (result != SE_SUCCESSFUL)

{

if (result == SE_DRIVE_NOT_STACKED)

{

printf (

"\nDrive %c is not a Stacker drive. \n",

driveInfo.driveLetter

);

}

else

{

puts("\nERROR: Problem in call to stacker_driveInfo().");

}

errorLevel = 4;

}

else

{

if ((driveInfo.driveIsStacked)

&& (driveInfo.driveState == STACKER_MOUNTED))

{

result = stacker_flushDriver(driveInfo.driveLetter);

if (result == SE_SUCCESSFUL)

{

printf(

"\nDrive %c: has been flushed.\n",

driveInfo.driveLetter

);

errorLevel = 0;

}

else

{

printf(

"\nERROR: Unable to flush drive %c:.\n",

driveInfo.driveLetter

);

errorLevel = 2;

}

}

else

{

printf(

"\nDrive %c: is not a mounted Stacker drive.\n",

driveInfo.driveLetter

);

errorLevel = 3;

}

}

}

return errorLevel;

}


stacker_unmountVolume Example
 stacker_unmountVolume Example (EXAM_8.C)

#include <stdio.h>

#include "stacker.h"

int main(void)

{

struct stacker_drive_info_struct	driveInfo;

s_ushort	status;

s_return	result	= 0;

int	errorLevel;

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

puts(" Stacker is not installed.");

errorLevel = 1;

}

else

{

driveInfo.driveLetter = 'C';  /* Unmount drive C: */

result = stacker_driveInfo(&driveInfo);

if (result != SE_SUCCESSFUL)

{

if (result == SE_DRIVE_NOT_STACKED)

{

printf(

  "\nDrive %c is not a Stacker drive.\n",

  driveInfo.driveLetter

  );

}

else

{

puts("\nERROR: Problem in call to stacker_driveInfo().");

}

errorLevel = 4;

}

else

{

if ((!driveInfo.driveIsStacked)

|| (driveInfo.driveState != STACKER_MOUNTED))

{

printf(

"\nDrive %c: is not a mounted Stacker drive.\n",

driveInfo.driveLetter

);

errorLevel = 3;

}

else

{

result = stacker_unmountVolume(&driveInfo);

if (result == SE_SUCCESSFUL)

{

printf(

"\nDrive %c: was successfully unmounted.\n",

driveInfo.driveLetter

);

errorLevel = 0;

}

else

{

errorLevel = 2;

printf(

"\nERROR: Unable to unmount drive %c:\n",

driveInfo.	driveLetter

);

/* NOTE:	The following should never happen because	*/

/* 	    it is checked before making the unmount call.	*/

if (result == SE_DRIVE_NOT_STACKED)

{

puts("The drive is not a mounted Stacker drive.");

errorLevel = 5;

}

}

}

}

}

return errorLevel;

}


stacker_hostDriveMode Example
 stacker_hostDriveMode and stacker_undoHostDriveMode

    Example (HOSTMODE.C)

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *   HOSTMODE - Program to place a Stacker drive in host mode, shell to DOS

 *              and then take the drive out of host mode when you type EXIT.

 *

 *              (c) 1992 - 1993 Stac Electronics, Carlsbad, CA

 *

 *  To compile this program from the command line using Borland C type:

 *

 *      BCC HOSTMODE.C STACKER.LIB

 *

 *  To compile this program from the command line using Microsoft C type:

 *

 *      CL /F 2000 HOSTMODE.C STACKER.LIB

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

/*

 *  Include files.

 */

#if defined(__BORLANDC__)                /* Borland C.                    */

#include <dir.h>

#define S_MAX_PATH   MAXPATH

#else

#include <direct.h>

#include <stdlib.h>

#define S_MAX_PATH   _MAX_PATH

#endif

#include <stdio.h>

#include <ctype.h>

#include <string.h>

#include <process.h>

#include <stdlib.h>

#include "stacker.h"

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  void Get_Help() -- Displays help to the user if no parameters were

 *                     specified or if the user requested it with /?.

 *

 *  Input parameters:  None.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void Get_Help(void)

    {

    puts("Purpose:  Test putting Stacker drives in host mode.\n");

    puts("Syntax:   HOSTMODE drive\n");

    puts("Options:  drive - The Stacker drive you would like to put in");

    puts("                  host mode.\n");

    puts("          /?    - Displays this help screen.\n");

    puts("Example:  HOSTMODE B:\n");

    puts("Meaning:  Put drive B: in host mode.");

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  void Save_Path() -- Saves the path for a specified drive.

 *

 *  Input parameters: *currentDirectory, Buffer to hold the path.

 *                     driveLetter, Letter of the drive to get the current

 *                                  path of.

 *                     maxPath, Maximum length of the path.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void Save_Path(

              s_char    *currentDirectory,

              s_char    driveLetter,

              s_ushort  maxPath

              )

    {

    driveLetter = toupper(driveLetter);

    currentDirectory[0] = driveLetter;

    currentDirectory[1] = ':';

    currentDirectory[2] = '\\';

#if defined(__BORLANDC__)                /* Borland C.                    */

    getcurdir(

             driveLetter - 'A' + 1,

             currentDirectory + 3

             );

#else

    _getdcwd(

            driveLetter - 'A' + 1,

            currentDirectory,

            maxPath

            );

#endif

    return;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  void Restore_Path() -- Restores the path for a specified drive.

 *

 *  Input parameters: *currentDirectory, Buffer to hold the path.

 *

 *  Return values:     None.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

void Restore_Path(s_char *newPath)

    {

    chdir(newPath);

    return;

    }

/*

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

 *

 *  int main() -- Parses the command line and perfoms the main loop of the

 *                program.

 *

 *  Input parameters:  The drive to put in host drive mode mode.

 *

 *  Return value:      0 == No errors.

 *                     1 == Stacker not installed.

 *                     2 == Unexpected error.

 *                     3 == Give help.

 *

 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

*/

s_ushort main(int argc, char *argv[])

    {

    s_ushort                              errorLevel       = 2;

    s_char                                currentDirectory[S_MAX_PATH];

    s_char                                hostDriveLetter;

    s_return                              result;

    s_ushort                              status;

    struct stacker_drive_info_struct      di_rec;

    puts("\n\nHOSTMODE - (c) 1992 Stac Electronics, Carlsbad, CA\n");

    if ((argc <= 1) || (strcmp(argv[1], "/?") == 0))

        {

        Get_Help();

        return 3;

        }

    result = stacker_status(&status);

    if ((result != SE_SUCCESSFUL)

    || (status == STACKER_NOT_INSTALLED))

        {

        puts("Stacker is not installed.\n");

        return 1;

        }

    di_rec.driveLetter   = toupper(argv[1][0]);

    result = stacker_driveInfo(&di_rec);

    if ((result != SE_SUCCESSFUL)

    || (!di_rec.driveIsStacked))

        {

        printf(

              "ERROR:  Drive %c: is not a Stacker drive.\n\n",

              di_rec.driveLetter

              );

        return 2;

        }

    Save_Path(currentDirectory, di_rec.driveLetter, S_MAX_PATH);

    result = stacker_hostDriveMode(&di_rec, &hostDriveLetter);

    if (result == SE_SUCCESSFUL)

        {

        printf(

              "Drive %c: is in host drive mode.\n",

              di_rec.driveLetter

              );

        printf(

              "You can now access the host drive for drive %c: through"

              " drive %c:.\n",

              di_rec.driveLetter,

              hostDriveLetter

              );

        printf(

              "Type EXIT to put drive %c: back to normal Stacked mode.\n\n",

              di_rec.driveLetter

              );

        system("command");

        if (stacker_undoHostDriveMode(

                                     &di_rec

                                     ) == SE_SUCCESSFUL)

            {

            printf(

                  "Drive %c: is now back to normal!!!\n\n",

                  di_rec.driveLetter

                  );

            errorLevel = 0;

            }

        else

            {

            printf(

                  "ERROR:  Unable to return drive %c: to normal Stacked mode.\n\n",

                  di_rec.driveLetter

                  );

            }

        Restore_Path(currentDirectory);

        }

    else

        {

        printf(

              "ERROR:  Unable to put drive %c: in host mode.\n\n",

              di_rec.driveLetter

              );

        if (result == SE_DRIVE_NOT_STACKED)

            {

            puts("        It is not a mounted Stacker drive.");

            }

        }

    return errorLevel;

    }


stacker_writeProtect Example
 stacker_writeProtect Example (EXAM_9.C)

#include <stdio.h>

#include <ctype.h>

#include "stacker.h"

s_ushort main(int argc, char *argv[])

{

s_ushort	status;

s_return	result	= 0;

s_ushort	errorLevel;

struct	stacker_drive_info_struct di_rec;

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

printf("Stacker is not installed.\n\n");

errorLevel = 1;

}

else

{

di_rec.driveLetter = toupper(argv[1][0]);

result = stacker_driveInfo(&di_rec);

if ((result != SE_SUCCESSFUL)

|| (!di_rec.driveIsStacked))

{

printf(

"ERROR: Drive %c: is not a Stacker drive.\n\n",

di_rec.driveLetter

);

errorLevel = 2;

}

else

{

result = stacker_writeProtect(&di_rec);

if (result == SE_SUCCESSFUL)

{

printf(

"Drive %c: is write-protected.\n\n",

di_rec.driveLetter

);

errorLevel = 0;

}

else

{

printf(

"ERROR: Unable to write-protect drive %c:\n\n",

di_rec.driveLetter

);

if ((result == SE_DRIVE_NOT_STACKED)

|| (result == SE_DRIVE_NOT_MOUNTED))

{

puts( " It is not a mounted Stacker drive." );

}

errorLevel = 2;

}

}

}

return errorLevel;

}


stacker_unWriteProtect Example
 stacker_unwriteProtect Example (EXAM_10.C)

#include <stdio.h>

#include <ctype.h>

#include "stacker.h"

s_ushort main(int argc, char *argv[])

{

s_ushort	status;

s_return	result	= 0;

s_ushort	errorLevel;

struct	stacker_drive_info_struct di_rec;

result = stacker_status(&status);

if ((result != SE_SUCCESSFUL)

|| (status == STACKER_NOT_INSTALLED))

{

puts("Stacker is not installed.\n");

errorLevel = 1;

}

else

{

/* argv[1][0] is the first character in the first	*/

/* argument on the command line.	*/

di_rec.driveLetter = toupper(argv[1][0]);

result = stacker_driveInfo(&di_rec);

if ((result != SE_SUCCESSFUL)

|| (!di_rec.driveIsStacked))

{

printf(

"ERROR: Drive %c: is not a Stacker drive.\n\n",

di_rec.driveLetter

);

errorLevel = 2;

}

else

{

result = stacker_unWriteProtect(&di_rec);

if (result == SE_SUCCESSFUL)

{

printf(

"The write-protect status of drive %c: has"

" been cleared.\n\n",

di_rec.driveLetter

);

errorLevel = 0;

}

else

{

printf(

"ERROR: Unable to clear the write-protect"

" status of drive %c:\n\n",

di_rec.driveLetter

);

if ((result == SE_DRIVE_NOT_STACKED)

|| (result == SE_DRIVE_NOT_MOUNTED))

{

puts(" It is not a mounted Stacker drive.");

}

errorLevel = 2;

}

}

}

return errorLevel;

}


stacker_initializeRccBuffer Example
 stacker_initializeRccBuffer, stacker_getFatEntry,

    and stacker_readCompressedCluster Example (RCCTEST.C)

/*

*

* Copyright (c) 1993 by Stac Electronics, Carlsbad, California.

******************************************************************************

*

* Filename:         RCCTEST.C

*

* Project:          OEM/ISV

*

* Functional Block:

*

* Comments:         This is an example of how to walk the FAT on a DOS system

*                   so as to read compressed clusters off of a Stacker drive.

*

*                   TO COMPILE:  link this file with PROFILE.C as well as

*                                STACKER.LIB.

*

* To compile this program from the command line using Borland C type:

*

*     BCC RCCTEST.C PROFILE.C STACKER.LIB

*

* To compile this program from the command line using Microsoft C type:

*

*     CL /F 2000 RCCTEST.C PROFILE.C STACKER.LIB

*

*

* Contents:

*   enum Error  main

*   enum Error  Parse_Cmd_Line

*   enum Error  Buffer_Setup_etc

*   enum Error  Read_Clust_and_Compare

*   enum Error  File_Benchmark

*   char far    *s_getdta

*   enum Error  Get_First_Clust

*   int         FCB_Find_First

*   int         s_far_read

*   void        CopyStringAndPad

*   void        Print_Status

*

*

*

****************************************************************************/

#if defined(__BORLANDC__)

    #include <alloc.h>

    #include <dir.h>

    #include <mem.h>

    extern unsigned _stklen = 16384U;

#else

    #include <malloc.h>

    #include <direct.h>

    #include <memory.h>

    #define  asm      _asm         /* Microsoft C uses _asm.       */

    #define  _seg     seg          /* Microsoft C uses seg.        */

    #define farmalloc _fmalloc     /* Microsoft C uses _fmalloc.   */

    #define farfree   _ffree       /* Microsoft C uses _ffree.     */

    #define fnsplit   _splitpath   /* Microsoft C uses _splitpath. */

    #define fnmerge   _makepath    /* Microsoft C uses _makepath.  */

#endif

#include    <ctype.h>

#include    <io.h>

#include    <conio.h>

#include    <fcntl.h>

#include    <stdio.h>

#include    <string.h>

#include    "stacker.h"

#include    "rcctest.h"

#include    "profile.h"

/*

******************************************************************************

*

* Function Name:        main

*

* Function:

*

* Arguments:            int argc        number of cmd line arguments passed in

*                       char *argv[]    a list of command line args passed in

*                                       Valid arguments are:

*                                           "-b"    do a timed benchmark of

*                                                   the operation

*                                           "-t"    do a test run of the the

*                                                   read compressed clusters,

*                                                   comparing result to DOS

* Globals Used:         None

* Return:               Error enum

* Globals Altered:      None

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

enum Error main( int argc, char *argv[] )

    {

    char            fname[100];

    enum Error      result = Ok;

    s_return        return_code;

    s_ushort        status;

    int             hdl;

    int             benchmark = FALSE;

    result = Parse_Cmd_Line( argc, argv, fname, &benchmark );

    if( result == Ok )

        {

        return_code = stacker_status(&status);

        if (return_code == SE_SUCCESSFUL)

            {

            switch( status )

                {

                case STACKER_SW_INSTALLED:

                case STACKER_HW_INSTALLED:

                    break;

                case STACKER_NOT_INSTALLED:

                default:

                    result = Stacker_Not_Installed;

                }

            }

        else

            {

            result = Failed;

            }

        }

    if( result == Ok )

        {

        struct stacker_drive_info_struct drive_Info;

        s_char  far *comp_Buf;

        s_char  far *uncomp_Buf;

        s_char  far *uncomp_Buf2;

        s_char  far *rccBuffer;

        unsigned    clust_Size;

        s_ushort    eof;

        s_ushort    bad_Clust;

        drive_Info.driveLetter = ( fname[ 1 ] == ':' )

                               ? toupper( *fname ) : s_getdisk() + 'A';

        return_code = stacker_driveInfo( &drive_Info );

        if( return_code != SE_SUCCESSFUL)

            {

            result = Failed;

            }

        if( return_code == SE_DRIVE_NOT_STACKED )

            {

            result = Drive_Not_Stacked;

            }

        if( result == Ok && drive_Info.driveState != STACKER_MOUNTED )

            {

            result = Drive_Not_Mounted;

            }

        if( result == Ok )

            {

            Buffer_Setup_etc(

                            &drive_Info,

                            &eof,

                            &bad_Clust,

                            &clust_Size,

                            &comp_Buf,

                            &uncomp_Buf,

                            &uncomp_Buf2,

                            &rccBuffer

                            );

            }

        if( result == Ok )

            {

            printf( "\nFile to test:  \"%s\"\n", fname );

            if( ( hdl = open( fname, O_RDONLY ) ) == -1 )

                {

                result = Open_File_Failed;

                perror( "File Read error or Open error occurred." );

                }

            if( result == Ok )

                {

                if( benchmark == FALSE )

                    {

                    result = Read_Clust_and_Compare(

                                                   fname,

                                                   hdl,

                                                   &drive_Info,

                                                   eof,

                                                   bad_Clust,

                                                   clust_Size,

                                                   comp_Buf,

                                                   uncomp_Buf,

                                                   uncomp_Buf2,

                                                   rccBuffer

                                                   );

                    }

                else

                    {

                    result = File_Benchmark(

                                           fname,

                                           hdl,

                                           &drive_Info,

                                           eof,

                                           bad_Clust,

                                           clust_Size,

                                           comp_Buf,

                                           uncomp_Buf,

                                           uncomp_Buf2,

                                           rccBuffer

                                           );

                    }

                }

            close( hdl );

            }

        if( comp_Buf != NULL )

            {

            farfree( comp_Buf );

            }

        if( uncomp_Buf2!= NULL )

            {

            farfree( uncomp_Buf2 );

            }

        if( uncomp_Buf != NULL )

            {

            farfree( uncomp_Buf );

            }

        if( rccBuffer != NULL )

            {

            farfree ( rccBuffer );

            }

        if( comp_Buf != NULL )

            {

            farfree ( comp_Buf );

            }

        }

    Print_Status( result );

    return( result );

    }

/*

******************************************************************************

*

* Function Name:        s_getdisk()

*

* Function:             Gets the current default drive number.

*

* Arguments:            None.

*

* Globals Used:

* Return:               Drive number.

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

int s_getdisk( void )

    {

    int currentDrive;

#if defined(__BORLANDC__)

    currentDrive = getdisk();

#else

    _dos_getdrive(&currentDrive);

    --currentDrive;

#endif

    return currentDrive;

    }

/*

******************************************************************************

*

* Function Name:        s_setdisk()

*

* Function:             Sets the current drive number.

*

* Arguments:            None.

*

* Globals Used:

* Return:               The maximum number of logical drives in the system.

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

int s_setdisk( int driveNumber )

    {

    int maxDrives;

#if defined(__BORLANDC__)

    maxDrives = setdisk(driveNumber);           /* BC is 0 based, 0=A.  */

#else

    _dos_setdrive(driveNumber + 1, &maxDrives); /* MSC is 1 based, 1=A. */

#endif

    return maxDrives;

    }

/*

******************************************************************************

*

* Function Name:        Buffer_Setup_etc

*

* Function:             Allocates buffers, determines what value

*                       end-of-file and bad clusters are on this drive,

*                       and fills in the drive_Info struct

*

* Arguments:            struct stacker_drive_info_struct *drive_Info

*                       s_ushort *eof

*                       s_ushort *bad_Clust

*                       unsigned *clust_Size

*                       s_char far **comp_Buf

*                       char far **uncomp_Buf

*                       s_char far **uncomp_Buf2

*                       s_char far **rccBuffer

* Globals Used:

* Return:               Error

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

enum Error  Buffer_Setup_etc(

                            struct stacker_drive_info_struct *drive_Info,

                            s_ushort                         *eof,

                            s_ushort                         *bad_Clust,

                            unsigned                         *clust_Size,

                            s_char               far        **comp_Buf,

                            char                 far        **uncomp_Buf,

                            s_char               far        **uncomp_Buf2,

                            s_char               far        **rccBuffer

                            )

    {

    enum Error result = Ok;

    s_return   return_code;

    *clust_Size = drive_Info->stacker.secPerCluster

                * drive_Info->stacker.bytesPerSec;

    /************************************************************************

    * Because File allocation Tables (FATs) in DOS can be established with

    * either a 12 bit or a 16 bit format for each entry, the value of an

    * "end-of-file" and a "bad cluster" entry are not fixed.  To be able to

    * properly interpret what a cluster entry is in DOS, we have to first set

    * up these values.  EOF is 0xff8 to 0xfff for a 12 bit FAT, and 0xfff8 to

    * 0xffff for a 16 bit FAT.  A bad cluster is 0xff7 for a 12 bit FAT, and

    * 0xfff7 for a 16 bit FAT.

    *************************************************************************/

    if( drive_Info->bitsPerFATEntry == 12 )

        {

        *eof        = 0xff8;

        *bad_Clust  = 0xff7;

        }

    else

        {

        *eof        = 0xfff8;

        *bad_Clust  = 0xfff7;

        }

    /*  For holding the compressed data.  Notice the extra sector's        */

    /*  worth of mem required for the stacker_readCompressedCluster call.  */

    *comp_Buf = (s_char far *) farmalloc( *clust_Size + drive_Info->stacker.bytesPerSec );

    /*  Destination for decompressing the compressed data into.            */

    /*  This is never more than a cluster in size.                         */

    *uncomp_Buf2 = (s_char far *)   farmalloc( *clust_Size );

    /*  For reading the file into using DOS.                               */

    *uncomp_Buf = (s_char far *)      farmalloc( *clust_Size );

    /*  This is a scratch pad buffer used for stacker_readCompressedCluster */

    /*  and stacker_getFatEntry;                                            */

    *rccBuffer  = (s_char far *)   farmalloc( drive_Info->rccBufferSize );

    if((*rccBuffer == NULL)

    || (*comp_Buf == NULL)

    || (*uncomp_Buf2 == NULL)

    || (*uncomp_Buf == NULL))

        {

        result = Not_Enough_Mem;

        }

    return_code = stacker_initializeRccBuffer( *rccBuffer, drive_Info );

    if( return_code != SE_SUCCESSFUL )

        {

        result = Failed;

        }

    return( result );

    }

/*

******************************************************************************

*

* Function Name:        Read_Clust_and_Compare

*

* Function:             reads a compressed cluster

*

* Arguments:            *fname        Pointer to the file to process.

*                       hdl           Handle to the open file to process.

*                       *drive_Info   Pointer to a valid drive info struct.

*                       eof           Value used for comparing to find the

*                                     end of file.

*                       bad_Clust     Value used for comparing to recognize

*                                     a bad cluster.

*                       clust_Size    The size of a cluster on this drive.

*                       *comp_Buf     A buffer the size of one cluster plus

*                                     one sector used for holding compressed

*                                     clusters.

*                       *uncomp_Buf   A cluster-sized buffer used for holding

*                                     uncompressed clusters read using DOS.

*                       *uncomp_Buf2  A cluster-sized buffer used as a

*                                     destination when uncompressing data

*                                     from comp_Buf.

*                       *rccBuffer    A buffer the size of

*                                     drive_Info.rccBufferSize used for

*                                     internal caching.

* Globals Used:

* Return:

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:                Refer to File_Benchmark() function to perform timing

*                       benchmarks.

*

*                       When walking the FAT of a file, you should keep track

*                       of the total amount of data read and discard any

*                       bytes beyond the filesize.

*

-****************************************************************************/

enum Error Read_Clust_and_Compare(

                              const        char                *fname,

                              int                              hdl,

                              struct stacker_drive_info_struct *drive_Info,

                              s_ushort                         eof,

                              s_ushort                         bad_Clust,

                              unsigned                         clust_Size,

                              s_char                   far     *comp_Buf,

                              char                     far     *uncomp_Buf,

                              s_char                   far     *uncomp_Buf2,

                              s_char                   far     *rccBuffer

                                 )

    {

    enum Error  result         = Ok;

    s_ushort    compByteCount;

    s_ushort    compDataFormat;

    s_ushort    next_Clust_No;

    s_return    return_code;

    int         read_Size;

    s_ushort    total_Uncompressed_Bytes;

    int         stacker_Result = SE_SUCCESSFUL;

    result = Get_First_Clust( fname, &next_Clust_No );

    if( next_Clust_No == UNUSED_CLUSTER )

        {

        result = File_is_Zero_Bytes_Long;

        }

    for(  ;

       next_Clust_No < eof && result == Ok;

       )

        {

        /*  '1' is never a valid cluster number, and '0' means free       */

        /*  cluster so nothing under two is valid here.                   */

        if((next_Clust_No < MIN_CLUST)

        || (next_Clust_No != bad_Clust)

        && (next_Clust_No < 2))

            {

            result = Invalid_Cluster_Num;

            }

        /*  Read a compressed cluster from the Stacker drive. */

        if( result == Ok )

            {

            stacker_Result = stacker_readCompressedCluster(

                                                          drive_Info,

                                                          next_Clust_No,

                                                          comp_Buf,

                                                          rccBuffer,

                                                          &compByteCount,

                                                          &compDataFormat,

                                                          0

                                                          );

            if( stacker_Result != SE_SUCCESSFUL )

                {

                result = Read_Compressed_Clusters_Failed;

                }

            }

        /*  Read a cluster from the drive using DOS. */

        if( result == Ok )

            {

            if ((read_Size = s_far_read(hdl, uncomp_Buf, clust_Size)) == -1)

                {

                result = Dos_Read_Failed;

                }

            }

        /*  If the cluster was compressed, decompress it so we can compare */

        /*  it to the cluster read through DOS.                            */

        if ((result == Ok) && (compDataFormat == STACKER_LZS_FORMAT))

            {

            return_code = stacker_unpack(

                                        (s_char far *) comp_Buf,

                                        clust_Size,

                                        (s_char far *) uncomp_Buf2,

                                        clust_Size,

                                        &total_Uncompressed_Bytes,

                                        0

                                        );

            if(( return_code != SE_SUCCESSFUL )

            || (total_Uncompressed_Bytes == 0 ))

                {

                result = Un_Compress_Failed;

                }

            }

        else

            {

            total_Uncompressed_Bytes = compByteCount;

            }

        /*  Compare the cluster read through DOS with the cluster we read  */

        /*  in compressed form, and then decompressed.  Notice two items   */

        /*  here.                                                          */

        /*  First, we are making sure that Stacker stored the data in      */

        /*  compressed form, and are using the appropriate buffer in the   */

        /*  comparison.  Non-compressible files are stored in clear-text.  */

        /*  Second, we don't compare beyond the end of the valid data in   */

        /*  either buffer.  This is only an issue with data base type      */

        /*  files that do a seek and write, where entire clusters could be */

        /*  included in the FAT chain without ever having been written to. */

        if( _fmemcmp( uncomp_Buf,

            (compDataFormat == STACKER_NOT_COMPRESSED)? comp_Buf:uncomp_Buf2,

            min( read_Size, total_Uncompressed_Bytes ) ) )

            {

            result = Compare_Failed;

            }

        if ( result == Ok )

            {

            return_code = stacker_getFatEntry(

                                             next_Clust_No,

                                             drive_Info,

                                             rccBuffer,

                                             &next_Clust_No

                                             );

            if( return_code != SE_SUCCESSFUL )

                {

                result = Failed;

                }

            }

        }

    return( result );

    }

/*

******************************************************************************

*

* Function Name:        File_Benchmark

*

* Function:             reads clusters, compares to DOS, and does timing

*                       benchmarks.

*

* Arguments:            *fname         Pointer to the file to process.

*                       hdl            Handle to the open file to process.

*                       *drive_Info    Pointer to a valid drive info struct.

*                       eof            Value used for comparing to find end

*                                      of the file.

*                       bad_Clust      Value used for comparing to recognize

*                                      a bad cluster.

*                       clust_Size     The size of a cluster on this drive.

*                       *comp_Buf      A buffer the size of one cluster plus

*                                      one sector used for holding compressed

*                                      clusters.

*                       *uncomp_Buf    A cluster-sized buffer used for holding

*                                      uncompressed clusters read using DOS.

*                       *uncomp_Buf2   A cluster-sized buffer used as a

*                                      destination when uncompressing data

*                                      from comp_Buf.

*                       *rccBuffer     A buffer the size of

*                                      drive_Info.rccBufferSize used for

*                                      internal caching.

* Globals Used:

* Return:

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:                When walking the FAT of a file, you should keep

*                       track of the total amount of data read and discard

*                       any bytes beyond the file size.

*

-****************************************************************************/

enum Error  File_Benchmark(

                          const       char                 *fname,

                          int                              hdl,

                          struct stacker_drive_info_struct *drive_Info,

                          s_ushort                         eof,

                          s_ushort                         bad_Clust,

                          unsigned                         clust_Size,

                          s_char                far        *comp_Buf,

                          char                  far        *uncomp_Buf,

                          s_char                far        *uncomp_Buf2,

                          s_char                far        *rccBuffer

                          )

    {

    float       pack_Total_Time             = 0;

    float       read_DOS_Total_Time         = 0;

    float       Unpack_Total_Time           = 0;

    float       read_Compressed_Total_Time  = 0;

    enum Error  result                      = Ok;

    s_ushort    compByteCount;

    s_ushort    compDataFormat;

    s_ushort    next_Clust_No;

    s_return    return_code;

    int         read_Size;

    s_ushort    total_Uncompressed_Bytes;

    s_ushort    uncompressed_Bytes;

    int         stacker_Result              = SE_SUCCESSFUL;

    struct profile_Struct   Timer;

    result = Get_First_Clust( fname, &next_Clust_No );

    if( next_Clust_No == UNUSED_CLUSTER )

        {

        result = File_is_Zero_Bytes_Long;

        }

    for(    ;

            next_Clust_No < eof && result == Ok;

            )

        {

        /*  '1' is never a valid cluster number, and '0' means free      */

        /*  cluster so nothing under two is valid here.                  */

        if ((next_Clust_No < MIN_CLUST)

        || (next_Clust_No != bad_Clust)

        && (next_Clust_No < 2))

            {

            result = Invalid_Cluster_Num;

            }

        /*  Read a compressed cluster from the Stacker drive. */

        if( result == Ok )

            {

            Start_Clock( &Timer );

            stacker_Result = stacker_readCompressedCluster(

                                                          drive_Info,

                                                          next_Clust_No,

                                                          comp_Buf,

                                                          rccBuffer,

                                                          &compByteCount,

                                                          &compDataFormat,

                                                          0

                                                          );

            read_Compressed_Total_Time += Total_Ticks( &Timer );

            if( stacker_Result != SE_SUCCESSFUL )

                {

                result = Read_Compressed_Clusters_Failed;

                }

            }

        /*  Read a cluster from the drive using DOS. */

        if( result == Ok )

            {

            Start_Clock( &Timer );

            if ((read_Size = s_far_read(hdl, uncomp_Buf, clust_Size)) == -1)

                {

                result = Dos_Read_Failed;

                }

            read_DOS_Total_Time += Total_Ticks( &Timer );

            }

        /*  If the cluster was compressed, decompress it so we can compare */

        /*  it to the cluster read through DOS.                            */

        if ((result == Ok) && (compDataFormat == STACKER_LZS_FORMAT))

            {

            Start_Clock( &Timer );

            return_code = stacker_unpack(

                                        (s_char far *) comp_Buf,

                                        clust_Size,

                                        (s_char far *) uncomp_Buf2,

                                        clust_Size,

                                        &total_Uncompressed_Bytes,

                                        0

                                        );

            if(( return_code != SE_SUCCESSFUL)

            || ( total_Uncompressed_Bytes == 0 ))

                {

                result = Un_Compress_Failed;

                }

            Unpack_Total_Time += Total_Ticks( &Timer );

            }

        else

            {

            total_Uncompressed_Bytes = compByteCount;

            }

        /*  Compare the cluster read through DOS with the cluster we read  */

        /*  in compressed form, and then decompressed.  Notice two items   */

        /*  here.                                                          */

        /*  First, we are making sure that Stacker stored the data in      */

        /*  compressed form, and are using the appropriate buffer in the   */

        /*  comparison.  Non-compressible files are stored in clear-text.  */

        /*  Second, we don't compare beyond the end of the valid data in   */

        /*  either buffer.  This is only an issue with data base type      */

        /*  files that do a seek and write, where entire clusters could be */

        /*  included in the FAT chain without ever having been written to. */

        if( _fmemcmp( uncomp_Buf,

            (compDataFormat == STACKER_NOT_COMPRESSED)? comp_Buf:uncomp_Buf2,

            min( read_Size, total_Uncompressed_Bytes ) ) )

            {

            result = Compare_Failed;

            }

        if( result == Ok )

            {

            Start_Clock( &Timer );

            return_code = stacker_pack(

                                      (s_char far *) uncomp_Buf2,

                                      clust_Size,

                                      (s_char far *) comp_Buf,

                                      clust_Size,

                                      &uncompressed_Bytes,

                                      0

                                      );

            if(return_code != SE_SUCCESSFUL )

                {

                result = Un_Compress_Failed;

                }

            pack_Total_Time += Total_Ticks( &Timer );

            }

        return_code = stacker_getFatEntry(

                                         next_Clust_No,

                                         drive_Info,

                                         rccBuffer,

                                         &next_Clust_No

                                         );

        if( return_code != SE_SUCCESSFUL )

            {

            result = Failed;

            }

        }

    if( result == Ok )

        {

        printf(

              "Total seconds for reading compressed clusters:\t%3.3f\n"

              "Total seconds for reading DOS clusters:\t\t%3.3f\n"

              "Total seconds for uncompressing clusters:\t%3.3f\n"

              "Total seconds for re-compressing clusters:\t%3.3f\n",

              read_Compressed_Total_Time / 18.2,

              read_DOS_Total_Time / 18.2,

              Unpack_Total_Time / 18.2,

              pack_Total_Time / 18.2

              );

        }

    return( result );

    }

/*

******************************************************************************

*

* Function Name:        Print_Status

*

* Function:             prints the result of the program

*

* Arguments:            Error result    The exit result of the program.

* Globals Used:

* Return:               void

* Globals Altered:      None.

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

void Print_Status( enum Error result )

    {

    int x;

    struct Error_Struct

        {

        enum Error  result;

        char    *str;

        };

    static struct Error_Struct state_List[] =

        {

        { Ok,                       "No Errors\n" },

        { Failed,                   "Read Compressed Cluster Program Failed\n\n"

                                    "Cause:    Possibly a failure to:\n\n"

                                    "              1) Get the Drive Information (stacker_driveInfo)\n"

                                    "              2) Initialize Read Compressed Buffer\n"

                                    "                 (stacker_initializeRccBuffer)\n"

                                    "              3) Get the Fat Entries (stacker_getFatEntry)\n"

                                    "              4) Command line parsing errors "},

        { Compare_Failed,           "The DOS buffer and Stacker-Decompressed buffer compare failed.\n" },

        { Dos_Read_Failed,          "A DOS read error occurred while reading a cluster.\n" },

        { Find_First_Failed,        "An error occurred while finding the first cluster\n"

                                    "          of the specified file.\n" },

        { Open_File_Failed,         "A file read error or open error occurred.\n" },

        { Not_Enough_Mem,           "Not enough conventional memory.\n" },

        { Read_Compressed_Clusters_Failed,  "Failure of the stacker_readCompressedCluster function.\n"},

        { Stacker_Not_Installed,    "The Stacker driver is not Installed.\n"},

        { Give_Help,                "Purpose:  Walks the FAT on a DOS system to read compressed\n"

                                    "          clusters off of a Stacker drive.\n\n"

                                    "Syntax:   RCCTEST filename.ext [options]  \n\n"

                                    "Options:  /?    Display this help screen.\n"

                                    "          -b    Perform a timed benchmark of the operation.\n"

                                    "          -t    Perform a test run of the the read compressed\n"

                                    "                clusters, comparing result to DOS.\n\n"

                                    "Example:  RCCTEST C:\\AUTOEXEC.BAT -b\n\n"

                                    "Meaning:  Read all compressed clusters of the AUTOEXEC.BAT\n"

                                    "          file from the C: drive and give a benchmark report.\n"},

        { Not_Enough_Args ,         "Too Few Arguments in Read Compressed Cluster Program\n\n"

                                    "Purpose:  Walks the FAT on a DOS system to read compressed\n"

                                    "          clusters off of a Stacker drive.\n\n"

                                    "Syntax:   RCCTEST filename.ext [options]  \n\n"

                                    "Options:  /?    Display this help screen.\n"

                                    "          -b    Perform a timed benchmark of the operation.\n"

                                    "          -t    Perform a test run of the the read compressed\n"

                                    "                clusters, comparing result to DOS.\n\n"

                                    "Example:  RCCTEST C:\\AUTOEXEC.BAT -b\n\n"

                                    "Meaning:  Read all compressed clusters of the AUTOEXEC.BAT\n"

                                    "          file from the C: drive and give a benchmark report.\n"},

        { Invalid_Arg,              "Invalid argument passed to RCCTEST.\n\n"

                                    "Purpose:  Walks the FAT on a DOS system to read compressed\n"

                                    "          clusters off of a Stacker drive.\n\n"

                                    "Syntax:   RCCTEST filename.ext [options]  \n\n"

                                    "Options:  /?    Display this help screen.\n"

                                    "          -b    Perform a timed benchmark of the operation.\n"

                                    "          -t    Perform a test run of the the read compressed\n"

                                    "                clusters, comparing result to DOS.\n\n"

                                    "Example:  RCCTEST C:\\AUTOEXEC.BAT -b\n\n"

                                    "Meaning:  Read all compressed clusters of the AUTOEXEC.BAT\n"

                                    "          file from the C: drive and give a benchmark report.\n"},

        { Drive_Not_Stacked,        "The Drive specified is not Stacked.  Please enter\n"

                                    "          a Stacked drive on which to test RCCTEST.EXE.\n" },

        { Drive_Not_Mounted,        "The specified drive is not mounted.\n" },

        { Invalid_Cluster_Num,      "Invalid cluster amount used by the specified file.  \n"

                                    "          The cluster usage must be greater than 1.\n" },

        { Un_Compress_Failed,       "Decompression of the stacked cluster failed.\n" },

        { File_is_Zero_Bytes_Long,  "The file specified is zero bytes long.\n"

                                    "          The file must be greater than zero bytes.\n"},

        { Ok,                       NULL }

        };

    puts("\n\nRCCTEST - (c) 1993 Stac Electronics, Carlsbad, CA\n");

    for( x = 0; state_List[ x ].str; x++ )

        {

        if( state_List[ x ].result == result )

            {

            printf( "Result:   %s\n", state_List[ x ].str );

            break;

            }

        }

    }

/*

******************************************************************************

*

* Function Name:        Parse_Cmd_Line

*

* Function:

*

* Arguments:            argc        The number of args in argv.

*                       *argv[]     Is from main's arg list.

*                       **fname     A pointer to the filename in the argument.

*                       *benchmark  A pointer to an int indicating whether

*                                   the user wants to make timing benchmarks

*                                   on this file, or just test run the code.

*

* Globals Used:

* Return:

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

enum Error Parse_Cmd_Line(

                         int argc,

                         char *argv[],

                         char *fname,

                         int *benchmark

                         )

    {

    enum Error result = Ok;

    if( argc >= 2 )

        {

        int x;

        for( x = 1; x < argc; x++ )

            {

            if( *argv[ x ] == '?' )

                {

                result = Give_Help;

                break;

                }

            if( *argv[ x ] == '/' )

                {

                result = Give_Help;

                break;

                }

            if( *argv[ x ] == '-' )

                {

                switch( argv[ x ][ 1 ] & 0x5f )

                    {

                    case 'B':

                        *benchmark = TRUE;

                        break;

                    case 'T':

                        *benchmark = FALSE;

                        break;

                    default:

                        result = Invalid_Arg;

                        break;

                    }

                }

            else

                {

                memccpy(fname, argv[ x ], '\0', 100);

                }

            }

        }

    else

        {

        result = Not_Enough_Args;

        }

    return( result );

    }

/*

******************************************************************************

*

* Function Name:        Get_First_Clust

*

* Function:             Calls FCB_Find_First, then digs into DTA to find the

*                       first cluster number.

*

* Arguments:            char *fname, s_ushort *first_Cluster

* Globals Used:

* Return:               returns a type Error enum.

* Globals Altered:      None

* Error Handling:       Fails if FCB_Find_First fails to find the file.

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

enum Error Get_First_Clust( const char *fname, s_ushort *first_Cluster )

    {

    struct xfcb searchFCBPtr;

    struct xfcb far *dta_xFCB;

    enum Error result;

    result = ( FCB_Find_First(

                             fname,

                             &searchFCBPtr,

                             0xffff

                             ) == TRUE )? Ok: Find_First_Failed;

    /***********************************************************************

    * DOS stores a copy of this FCB similar the above FCB in the

    * disk transfer area (DTA).  The copy in the DTA also has the

    * beginning cluster number of the file buried in the "reserved"

    * field.  We just need to index five bytes into this field, and

    * extract the cluster number.

    ************************************************************************/

    dta_xFCB = (struct xfcb far *) s_getdta();

    *first_Cluster = *( (unsigned far *) ( dta_xFCB->fcb.reserved + 5 ) );

    return( result );

    }

/*

******************************************************************************

*

* Function Name:    FCB_Find_First

*

* Function:         Gets information for 1st entry in selected directory.

*

* Arguments:

* Globals Used:

* Hardware Inputs:

*

* Return:

* Globals Altered:

* Hardware Outputs:

*

* Error Handling:

*

* Algorithms/Strucures:

*

* Notes:

*

-****************************************************************************/

int FCB_Find_First( const char *path, struct xfcb *searchFCBPtr, int attrib )

    {

    int    result             = TRUE;

    char   drive[ 3 ];

    char   dir[ MAX_PATH ];

    char   fname[ 9 ];

    char   ext[ 5 ];

    char   temp[ MAX_PATH ];

    char   orig_Dir[ MAX_PATH ];

    int    orig_Disk;

    fnsplit( path, drive, dir, fname, ext );

    getcwd( orig_Dir, MAX_PATH );

    orig_Disk = s_getdisk();

    fnmerge( temp, drive, dir, NULL, NULL );

        {

        int len = strlen( temp );

        /*  Strip off trailing backslashes except on root dir. */

        if( ( len != 1 )

        && !( len == 3 && temp[ 1 ] == ':' )

        && ( temp[ --len ] == '\\' ) )

            {

            temp[ len ] = 0;

            }

        }

    chdir( temp );

    searchFCBPtr->flag = 0xFF;  /* Indicates extended FCB.   */

    /* Set the search drive. */

    if( *drive != 0 )

        {

        searchFCBPtr->fcb.drive = toupper( *drive ) - 'A' + 1;

        }

    else

        {

        searchFCBPtr->fcb.drive = orig_Disk + 1;

        }

    /* Set the search file type. */

    searchFCBPtr->attrib = attrib;

    /* Set up the search file name. */

    CopyStringAndPad(   searchFCBPtr->fcb.fname, fname,

                        ' ', FILE_NAME_SIZE );

    /* Set up the search file extension. */

    /* the "+ ( *ext == '.' )" is for getting rid of the "." */

    CopyStringAndPad(

                    searchFCBPtr->fcb.ext,

                    ext + ( *ext == '.' ),

                    ' ',

                    FILE_EXT_SIZE

                    );

    asm {

        mov     ah,FIND_FIRST_FCB       /* Specify DOS function          */

        mov     dx,searchFCBPtr         /* Pass pointer to FCB           */

        push    ds                      /* Save registers                */

        push    bp

        int     0x21                    /* Call the DOS function         */

        pop     bp                      /* Restore registers             */

        pop     ds

        cmp     al,0xFF                 /* If al == 0xFF, couldn't find  */

        jne     foundFirst

        }

    result = FALSE;

foundFirst:

    s_setdisk( orig_Disk );

    chdir( orig_Dir );

    return( result );

    }

/*

******************************************************************************

*

* Function Name:    CopyStringAndPad

*

* Function:         Copies a string. Pads destination if source is short.

*

* Arguments:

* Globals Used:

* Hardware Inputs:

*

* Return:

* Globals Altered:

* Hardware Outputs:

*

* Error Handling:

*

* Algorithms/Strucures:

*

* Notes:

*

-****************************************************************************/

void CopyStringAndPad(

                     char *destination,

                     char *source,

                     char padChar,

                     int size

                     )

    {

    while( size > 0 && *source != '\0' )

        {

        *destination++ = *source++;

        --size;

        }

    while (size-- > 0)

        {

        *destination++ = padChar;

        }

    return;

    }

/*

******************************************************************************

*

* Function Name:        s_far_read

*

* Function:             Does a read into a far buffer

*

* Arguments:            hdl            Handle of open file.

*                       *buffer        Pointer to buffer to store data.

*                       count          Number of bytes to read.

*

* Globals Used:         none

* Return:               -1 on error

* Globals Altered:

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

int s_far_read( int hdl, char far *buffer, unsigned count )

    {

    int ret_val;

    asm {

        mov ah, 0x3f

        mov bx, hdl

        mov cx, count

        push ds

        lds dx, buffer

        int 0x21

        pop ds

        mov ret_val, ax

        jnc no_error

        mov ret_val, -1

        }

no_error:

    return( ret_val );

    }

/*

******************************************************************************

*

* Function Name:        s_getdta

*

* Function:             Returns the program's disk transfer area.

*

* Arguments:            none

* Globals Used:         none

* Return:               Returns a far char pointer to the dta.

* Globals Altered:      none

* Error Handling:

*

* Algorithms/Structures:

*

* Notes:

*

-****************************************************************************/

char far *s_getdta()

    {

    char far *dta_Address;

    asm {

        mov ah, 02fh

        int 021h

        mov word ptr dta_Address, bx

        mov ax, es

        mov word ptr dta_Address + 2, ax

        }

    return( dta_Address );

    }


STACKER.H Menu
 STACKER.H

     Data Types

     Constant Definitions

     Return Codes

     Structures

     Function Prototypes


 STACKER.H  Data Types

        /* Stacker data types.     */

     /* These should be set to match that of your compiler.            */

#define s_char             char      /* 8 bit signed.                     */

#define s_uchar   unsigned char      /* 8 bit unsigned.                   */

#define s_short            short     /* 16 bit signed.                    */

#define s_ushort  unsigned short     /* 16 bit unsigned.                  */

#define s_long             long      /* 32 bit signed.                    */

#define s_ulong   unsigned long      /* 32 bit unsigned.                  */

#define s_ptr_ptr void far * far *   /* Pointer to a pointer to void.     */


 STACKER.H Constant Definitions

/* Possible return values for stacker_status().                   */

#define STACKER_NOT_INSTALLED 0  /* if not installed, no other calls work */

#define STACKER_SW_INSTALLED  1  /* Software version installed            */

#define STACKER_HW_INSTALLED  2  /* Coprocessor version installed         */

/* Can't pack more than this many bytes with a single call        */

#define STACKER_MAX_PACK_SIZE 63488L /* (62*1024)                         */

/* Flags bits for pack/unpack calls                               */

#define STACKER_PARITY        1  /* Generate simple parity check on pack, */

                                 /* check parity on unpack. If this flag  */

                                 /* bit (bit 0) is not set, no parity     */

                                 /* used.  Bits 1 to 7 are reserved.      */

#define STACKER_NO_PARITY     0  /* No parity on pack/unpack calls.       */

#define STACKER_USE_WIN_MEM   0x8000 /* bit 15 is set for Windows memory. */

#define STACKER_FORCE_LZSPLUS 0x8000 /* bit 15 is set to force LZSPlus.   */

#define STACKER_PACK_P1       0x01F8 /* Fastest, least compression.       */

#define STACKER_PACK_P2       0x0020

#define STACKER_PACK_P3       0x0024

#define STACKER_PACK_P4       0x0064

#define STACKER_PACK_P5       0x0068

#define STACKER_PACK_P6       0x00E8 /* Similar to hardware compression.  */

#define STACKER_PACK_P7       0x00EC

#define STACKER_PACK_P8       0x01D0

#define STACKER_PACK_P9       0x01D8 /* Slowest, tightest compression.    */

/* Flags bits for stacker_driveInfo call.                         */

#define STACKER_REPLACED   0x04  /* This is a replaced drive (Ver 2 +).   */

#define STACKER_READONLY   0x08  /* Drive is mounted read only.           */

#define STACKER_DAMAGED    0x10  /* Volume is damaged.                    */

/* Values to match against the driveState field in the drive      */

/* info structure.                                                */

#define STACKER_MOUNTED          20      /* The Stacker drive is mounted. */

#define STACKER_HOST_DRIVE_MODE  0xFFFF  /* The Stacker drive is in host  */

                                         /* drive mode.                   */

/* Compressed types for stacker_readCompressedCluster() call.     */

#define STACKER_NOT_COMPRESSED       0x0101

#define STACKER_LZS_FORMAT           0x0000

#define STACKER_INCOMPRESSABLE       0x0001


 STACKER.H Return Codes

/* Possible return codes from the Developer's Tool Kit calls. */

typedef enum

    {

    SE_SUCCESSFUL             = 1, /* The function call was successful.   */

    SE_STACKER_NOT_INSTALLED  = 0, /* The Stacker driver is not resident. */

    SE_INVALID_DATA_PASSED    = 2, /* Invalid data was passed.            */

    SE_NO_DATA_TO_COMPRESS    = 3, /* There was no data to compress.      */

    SE_INVALID_GLOBAL_DATA    = 4, /* A global field is invalid.          */

    SE_DRIVE_NOT_STACKED      = 5, /* The drive is not Stacked.           */

    SE_INVALID_FUNCTION       = 6, /* An Invalid Funcion error occurred   */

                                   /* during a block device call.         */

    SE_ACCESS_DENIED          = 7, /* An Access Denied error occurred     */

                                   /* during a block device call.         */

    SE_INVALID_HANDLE         = 8, /* An Invalid Handle error occurred    */

                                   /* during a block device call.         */

    SE_INVALID_DATA           = 9, /* An Invalid Data error occurred      */

                                   /* during a block device call.         */

    SE_DRIVE_NOT_MOUNTED      = 10,/* The drive is not mounted.           */

    SE_DRIVE_NOT_IN_HOST_MODE = 11,/* The drive is not in host mode.      */

    SE_FILE_NOT_FOUND         = 12,/* Could not find the file.            */

    SE_DATA_DID_NOT_COMPRESS  = 13,/* The data did not compress.          */

    SE_DATA_DID_NOT_UNPACK    = 14,/* The data did not decompress.        */

    SE_LZS_HW_BUSY            = 15,/* The Stacker hardware was busy       */

                                   /* during a pack or unpack call.       */

    SE_SOURCE_BUF_2_LG        = 16,/* The source buffer is too large.     */

    SE_DEST_BUF_2_LG          = 17,/* The destination buffer is too large.*/

    SE_UNABLE_TO_FIND_DPB     = 18,/* Could not find the Drive Parameter  */

                                   /* Block (DPB) for the drive.          */

    SE_UNABLE_TO_FLUSH_DRIVER = 19,/* Could not flush the Stacker device  */

                                   /* driver for the drive.               */

    SE_UNABLE_TO_FREE_SELECTOR= 20,/* Could not free one of the global    */

                                   /* selectors used in the API.          */

    SE_UNEXPECTED_ERROR       = 0xFF /* An unexpected error occurred.     */

    } s_return;

        /* Values to match against the driveIsStacked field in the        */

        /* drive info structure.                                          */

typedef enum

    {

    STACKER_DRIVE_NOT_STACKED = 0x00,   /* Not a Stacker drive.           */

    STACKER_STACKED_DRIVE     = 0x01,   /* This is a Stacker drive.       */

    STACKER_AUTOMOUNT_DRIVE   = 0xFF    /* Auto-mounting Stacker drive.   */

                                        /* Version 2.02 and above only.   */

    } s_drvStkd;


 STACKER.H Structures

/* Structures used in the Stacker Developer's Tool Kit  */

/* The following pragma makes sure that the following structures get      */

/* created on byte boundries.  Microsoft C defaults to word boundries     */

/* and Borland C defaults to byte boundries.  Make sure to set this back  */

/* to the default for Microsoft C, after the declarations for the Stacker */

/* API structures if your code requires word boundries.                   */

#if defined(__BORLANDC__)

#pragma option -a-

#else

#pragma pack(1)

#endif

/*    stacker_BPB_struct  */

typedef struct  stacker_BPB_struct

    {

    s_ushort    bytesPerSec;        /* Bytes per sector.                  */

    s_uchar     secPerCluster;      /* Sectors per cluster.               */

    s_ushort    reservedSec;        /* Number of reserved sectors.        */

    s_uchar     numberFATs;         /* Number of FAT copies.              */

    s_ushort    rootEntries;        /* Number of root directory entries.  */

    s_ushort    shortTotalSec;      /* Total sectors (0 if drive > 32MB). */

    s_uchar     mediaDescriptor;    /* Media descriptor.                  */

    s_ushort    secPerFAT;          /* Number of sectors per FAT.         */

    s_ushort    secPerTrack;        /* Number of sectors per track.       */

    s_ushort    numberHeads;        /* Number of heads.                   */

    s_ulong     numberHiddenSec;    /* Number of hidden sectors.          */

    s_ulong     totalSec;           /* Total sectors (if drive > 32MB).   */

    }   stacker_BPB;

/*   stacker_drive_info_struct  */

struct stacker_drive_info_struct    /* Gets Stacker info on a given drive.*/

    {

    s_char      driveLetter;        /* Drive to check                     */

    s_drvStkd   driveIsStacked;     /* Result of function call.           */

                                    /* See s_drvStkd enum above.          */

    s_ushort    driveState;         /* Equals STACKER_MOUNTED if the      */

                                    /* Stacker drive is Mounted.          */

    stacker_BPB stacker;            /* BPB of driveLetter:                */

    s_ushort    stackerUnitNum;     /* Unit number of the drive, if it    */

                                    /* is Stacked.                        */

    s_char      bootDriveLetter;    /* Boot time drive letter for the     */

                                    /* Stacker drive.                     */

    s_char      hostDriveLetter;    /* Drive letter of host drive.        */

    s_char      stacvolFileName[9]; /* File name of stacvol file.         */

    s_char      stacvolFileExt[4];  /* File extension of stacvol file.    */

    s_ushort    stacvolVersion;     /* Version of stacvol file.           */

    s_ulong     stacvolFileSize;    /* Size of the stacvol file.          */

    s_ulong     stacvolMainStore;   /* Size of the stacvol main store.    */

    s_ptr_ptr   hostInterrupt;      /* Pointer to host interrupt routine  */

                                    /* pointer.                           */

    s_ptr_ptr   hostStrategy;       /* Pointer to host strategy routine   */

                                    /* pointer.                           */

    s_ulong     stackerDeviceHdr;   /* Pointer to the Stacker device      */

                                    /* driver header.                     */

    s_ushort    hostUnitNum;        /* Host drive unit number.            */

    stacker_BPB host;               /* BPB of host drive.                 */

    s_uchar     flagBits;           /* Drive status byte.                 */

    s_uchar     bitsPerFATEntry;    /* 12 or 16.                          */

    s_ushort    rccBufferSize;      /* Used in STACKER_READ_COMP_CLUSTER  */

    s_ulong     erasedSectorCount;  /* Number of erased sectors.          */

    s_ulong     freeSectorCount;    /* Number of free sectors on drive.   */

    /*  NOTE:  erasedSectorCount + freeSectorCount == number of sectors   */

    /*         available on the drive.                                    */

    s_ushort    freeClusterCount;   /* Number of logical free clusters.   */

    s_ushort    adjSecPerClust;     /* # of adjusted sectors per cluster. */

    };

/*   struct stacker_driver_info_struct  */

struct stacker_driver_info_struct   /* Gets Stacker driver information.   */

    {

    s_ushort driverVersion;         /* Stacker driver version * 100.      */

    s_ushort hardwareAddress;       /* Address used by Stacker coproc.    */

    s_ushort lzsServerType;         /* The Stac LZS Server present.       */

    s_ushort usingEMS;              /* Non-zero if Stacker is using EMS   */

                                    /* for its cache.                     */

    s_ushort loadedHigh;            /* Non-zero if the Stacker driver is  */

                                    /* loaded above 640k.                 */

    s_ushort preLoaded;             /* Non-zero if the Stacker driver is  */

                                    /* loaded before the CONFIG.SYS file. */

    s_ushort availRepDrives;        /* # of available replaceable Stacker */

                                    /* drives.                            */

    };

#if defined(__BORLANDC__)

#pragma option -a.

#endif


 STACKER.H Function Prototypes

/*

******************************************************************************

*

* Function Name:    stacker_status()

*

* Function:         Return Stacker installation status.

*

* Arguments:        The pointer to an s_ushort to store the status of the

*                   Stacker device driver.

*

* Return:           The possible return values for *status are:

*

*                       STACKER_NOT_INSTALLED = Stacker is not installed.

*                       STACKER_SW_INSTALLED  = Stacker software is installed.

*                       STACKER_HW_INSTALLED  = Stacker hardware is installed.

*

*                   The possible values returned by this function are:

*

*                       SE_SUCCESSFUL            = The function call was

*                                                  successful.

*                       SE_STACKER_NOT_INSTALLED = The Stacker device driver

*                                                  is not installed.

*

* Notes:            stacker_status() must be called before attempting to

*                   make a call to stacker_pack() or stacker_unpack().

*                   However, it only needs to be called once.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_status(s_ushort far *status);

#else

    s_return far pascal stacker_status(s_ushort far *status);

#endif

/*

******************************************************************************

*

* Function Name:    stacker_pack()

*

* Function:         Compress data from source to destination.

*

* Arguments:       *source_pointer,  Pointer to the source buffer.

*                   source_count,    Number of bytes to compress.

*                  *dest_pointer,    Pointer to the destination buffer.

*                   dest_count,      Size of the destination buffer.

*                  *compressed_size, Pointer to a field to store the size

*                                    of the compressed data.

*                   flags,           Set to STACKER_PARITY if you want to

*                                    decompress with parity.  Set it to

*                                    STACKER_NO_PARITY if you do not want

*                                    to do a parity check on decompress.

*

* Return:           The possible values returned by this function are:

*

*                   SE_SUCCESSFUL            = The function call was

*                                              successful.

*                   SE_INVALID_DATA_PASSED   = Some of the data passed in is

*                                              invalid.

*                   SE_INVALID_GLOBAL_DATA   = One or more of the global

*                                              fields for the Stacker API is

*                                              invalid.  This can occur if

*                                              you have not made a previous

*                                              call to stacker_status() or

*                                              stacker_driver_info().

*                   SE_SOURCE_BUF_2_LG       = The source buffer is too large.

*                                              source_count must be <= 62k

*                                              (62 * 1024).

*                   SE_DEST_BUF_2_LG         = The destination buffer is too

*                                              large.  dest_count must be

*                                              <= 62k (62 * 1024).

*                   SE_DATA_DID_NOT_COMPRESS = The data was unable to be

*                                              compressed.  It either did not

*                                              fit in the destination buffer,

*                                              or there was some other error

*                                              while attempting to compress

*                                              the data.

*                   SE_LZS_HW_BUSY           = The hardware being used for

*                                              the compression is busy.  Do

*                                              something else to kill some

*                                              time and then try this again.

*                   SE_UNEXPECTED_ERROR      = There was an unexpected error.

*                                              Do not trust the values returned

*                                              by this function.  This error is

*                                              extremely rare and should never

*                                              happen.  If it does, the system

*                                              is probably unstable because the

*                                              Stacker device driver does not

*                                              know where the first Stacker

*                                              drive is.

*

* Notes:            If the flags parameter is set to STACKER_USE_WIN_MEM the

*                   pack function will use its own Stacker LZSPlus code, even

*                   if the Stacker device driver or Stacker hardware is

*                   present.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_pack(

                                    const s_char far *source_pointer,

                                    s_ushort          source_count,

                                    s_char       far *dest_pointer,

                                    s_ushort          dest_count,

                                    s_ushort     far *compressed_size,

                                    s_ushort          flags

                                               );

#else

    s_return far pascal stacker_pack(

                                    const s_char far *source_pointer,

                                    s_ushort          source_count,

                                    s_char       far *dest_pointer,

                                    s_ushort          dest_count,

                                    s_ushort     far *compressed_size,

                                    s_ushort          flags

                                    );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_unpack()

*

* Function:         Decompress data from source to destination.

*

* Arguments:       *source_pointer,    Pointer to the source buffer.

*                   source_count,      Number of bytes to uncompress.

*                  *dest_pointer,      Pointer to the destination buffer.

*                   dest_count,        Size of the destination buffer.

*                  *uncompressed_size, Pointer to a field to store the size

*                                      of the uncompressed data.

*                   flags,             Set to STACKER_PARITY if you want

*                                      to uncompress with parity.  Set it

*                                      to STACKER_NO_PARITY if you do not

*                                      want to do a parity check on

*                                      uncompress.

*

* Return:           The possible values returned by this function are:

*

*                   SE_SUCCESSFUL            = The function call was

*                                              successful.

*                   SE_INVALID_GLOBAL_DATA   = One or more of the global

*                                              fields for the Stacker API is

*                                              invalid.  This can occur if

*                                              you have not made a previous

*                                              call to stacker_status() or

*                                              stacker_driver_info().

*                   SE_SOURCE_BUF_2_LG       = The source buffer is too large.

*                                              source_count must be <= 62k

*                                              (62 * 1024).

*                   SE_DEST_BUF_2_LG         = The destination buffer is too

*                                              large.  dest_count must be

*                                              <= 62k (62 * 1024).

*                   SE_DATA_DID_NOT_UNPACK   = The data was unable to be

*                                              uncompressed.  It either did

*                                              not fit in the destination

*                                              buffer, or there was some other

*                                              error while attempting to

*                                              compress the data.

*                   SE_LZS_HW_BUSY           = The hardware being used for

*                                              the decompression is busy.  Do

*                                              something else to kill some

*                                              time and then try this again.

*                   SE_UNEXPECTED_ERROR      = There was an unexpected error.

*                                              Do not trust the values

*                                              returned by this function.

*

* Notes:            If the flags parameter is set to STACKER_USE_WIN_MEM the

*                   unpack function will use its own Stacker LZSPlus code,

*                   even if the Stacker device driver or Stacker hardware is

*                   present.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_unpack(

                                      const s_char far *source_pointer,

                                      s_ushort          source_count,

                                      s_char       far *dest_pointer,

                                      s_ushort          dest_count,

                                      s_ushort     far *uncompressed_size,

                                      s_ushort          flags

                                                 );

#else

    s_return far pascal stacker_unpack(

                                      const s_char far *source_pointer,

                                      s_ushort          source_count,

                                      s_char       far *dest_pointer,

                                      s_ushort          dest_count,

                                      s_ushort     far *uncompressed_size,

                                      s_ushort          flags

                                      );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_driverInfo()

*

* Function:         Gets driver specific information from the Stacker device

*                   driver.

*

* Arguments:       *driverInfo, Pointer to a stacker_driver_info_struct

*                               structure.

*

* Return:           SE_SUCCESSFUL            = The function call was

*                                              successful.

*                   SE_STACKER_NOT_INSTALLED = The Stacker device driver is

*                                              not installed.

*

*        driverInfo.driverVersion   = Stacker device driver version * 100.

*        driverInfo.hardwareAddress = Address used by Stacker coprocessor.

*        driverInfo.lzsServerType   = The type of Stac LZS Server present.

*                                     A value of 0xFFFF indicates that there

*                                     is no LZS Server installed.  Zero is a

*                                     software LZS Server.  All other values

*                                     represent next generation Stac hardware.

*        driverInfo.usingEMS        = Non-zero if Stacker is using EMS for

*                                     its cache.

*        driverInfo.loadedHigh      = Non-zero if the Stacker driver is

*                                     loaded above 640k.

*        driverInfo.preLoaded       = Non-zero if the Stacker driver is

*                                     loaded before the CONFIG.SYS file.

*        availRepDrives             = The number  of available replaceable

*                                     Stacker drives.

*

* Notes:            This can be called in place of stacker_status() before

*                   calling any of the other API functions because it

*                   checks if Stacker is installed using stacker_status(),

*                   which sets up the needed globals for the Stacker API.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_driverInfo(

                            struct stacker_driver_info_struct far *driverInfo

                                                     );

#else

    s_return far pascal stacker_driverInfo(

                            struct stacker_driver_info_struct far *driverInfo

                                          );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_getCurrentDriveLetter()

*

* Function:         Gets the current drive letter of a swapped drive given

*                   the drive letter assignment at boot time.

*

* Arguments:        beforeSwapDriveLetter, Pre-swapped drive letter.

*                  *currentDriveLetter,    Pointer to an s_char to store the

*                                          current drive letter.

*

* Return:           The possible values returned by this function are:

*

*                   SE_SUCCESSFUL            = The function call was

*                                              successful.

*                   SE_INVALID_GLOBAL_DATA   = One or more of the global

*                                              fields for the Stacker API

*                                              is invalid.  This can occur

*                                              if you have not made a previous

*                                              call to stacker_status() or

*                                              stacker_driver_info().

*

*                   After a successful call, *currentDriveLetter will contain

*                   the current drive letter, after swapping, of the given

*                   boot time drive.

*

* Notes:            This should not be confused with GetBootTimeDriveLetter().

*

*                   This value is returned in the hostDriveLetter field of

*                   the drive info structure after a successful call to

*                   stacker_driveInfo().  This function was included in case

*                   you do not want the overhead of the stacker_driveInfo()

*                   function.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_getCurrentDriveLetter(

                                            s_char      beforeSwapDriveLetter,

                                            s_char far *currentDriveLetter

                                                                );

#else

    s_return far pascal stacker_getCurrentDriveLetter(

                                            s_char      beforeSwapDriveLetter,

                                            s_char far *currentDriveLetter

                                                     );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_getBootTimeDriveLetter()

*

* Function:         Gets the boot time drive letter of a swapped drive

*                   given the current drive letter assignment.

*

* Arguments:        currentDriveLetter,    The current drive letter.

*                  *beforeSwapDriveLetter, Pointer to an s_char to store the

*                                          pre-swapped drive letter.

*

* Return:           The possible values returned by this function are:

*

*                   SE_SUCCESSFUL            = The function call was

*                                              successful.

*                   SE_INVALID_GLOBAL_DATA   = One or more of the global

*                                              fields for the Stacker API

*                                              is invalid.  This can occur

*                                              if you have not made a previous

*                                              call to stacker_status() or

*                                              stacker_driver_info().

*

*                   After a successful call, *beforeSwapDriveLetter will

*                   contain the boot time drive letter, after swapping, of

*                   the given current time drive.

*

* Notes:            This should not be confused with GetCurrentDriveLetter().

*

*                   This value is returned in the bootDriveLetter field of

*                   the drive info structure after a successful call to

*                   stacker_driveInfo().  This function was included in case

*                   you do not want the overhead of the stacker_driveInfo()

*                   function.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_getBootTimeDriveLetter(

                                            s_char      currentDriveLetter,

                                            s_char far *beforeSwapDriveLetter

                                                                 );

#else

    s_return far pascal stacker_getBootTimeDriveLetter(

                                            s_char      currentDriveLetter,

                                            s_char far *beforeSwapDriveLetter

                                                      );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_driveInfo()

*

* Function:         Gets information on a Stacker drive.

*

* Arguments:       *driveInfo, Pointer to a stacker_drive_info_struct

*                              structure.

*

*                   NOTE:  Prior to making this function call you must set

*                          the driveLetter field in driveInfo to the letter

*                          of the drive you want the information of.

*

* Return:           The possible values returned by this function are:

*

*                   SE_SUCCESSFUL          = The function call was successful.

*                   SE_DRIVE_NOT_STACKED   = The drive specified by the

*                                            driveLetter element of driveInfo

*                                            is not a Stacker drive.

*                   SE_INVALID_GLOBAL_DATA = The call to

*                                            stacker_getCurrentDriveLetter()

*                                            failed because the communications

*                                            area pointer is invalid.

*                                            Do not trust the values in the

*                                            stacker_drive_info_struct structure.

*

*                   When successfully called the stacker_driveInfo() function

*                   returns the following information in the fields of the

*                   stacker_drive_info_struct:

*

*         driveInfo.driveIsStacked    = Drive unit number + 1.

*         driveInfo.driveState        = Equals STACKER_MOUNTED if the drive

*                                       is mounted.

*         driveInfo.stacker           = BPB of Stacked drive.

*         driveInfo.stackerUnitNum    = Drive unit number.

*         driveInfo.bootDriveLetter   = Boot time drive letter of the Stacker

*                                       drive.

*         driveInfo.hostDriveLetter   = Drive letter of host drive.

*         driveInfo.stacvolFileName   = File name of stacvol file.

*         driveInfo.stacvolFileExt    = Extension of stacvol file.

*         driveInfo.stacvolVersion    = Version of Stacker that created the

*                                       stacvol file.

*         driveInfo.stacvolFileSize   = The size of the stacvol file hosting

*                                       this Stacker drive.

*         driveInfo.stacvolMainStore  = The size of the main store in the

*                                       stacvol file for this drive.

*         driveInfo.hostInterrupt     = Pointer to the interrupt routine for

*                                       the host drive.

*         driveInfo.hostStrategy      = Pointer to the strategy routine for

*                                       the host drive.

*         driveInfo.stackerDeviceHdr  = Pointer to the Stacker device driver

*                                       header.  This is set to NULL and is

*                                       used by the stacker_hostDriveMode()

*                                       and stacker_undoHostDriveMode()

*                                       functions.

*         driveInfo.hostUnitNum       = Unit number of the host drive.

*         driveInfo.host              = BPB of host drive.

*         driveInfo.flagBits          = STACKER_REPLACED, STACKER_READONLY

*                                       or STACKER_DAMAGED.

*         driveInfo.bitsPerFATEntry   = 12 or 16.

*         driveInfo.rccBufferSize     = Size needed for rcc scratch buffer.

*         driveInfo.erasedSectorCount = The number of erased sectors on a

*                                       Stacker drive.

*         driveInfo.freeSectorCount   = The number of free sectors on a

*                                       Stacker drive.

*         driveInfo.freeClusterCount  = The number of logical free clusters.

*                                       This element will only be filled in

*                                       if the version of the Stacker driver

*                                       that is loaded is 3.00 or higher.

*                                       The logical clusters free in this

*                                       element is the same that CHKDSK

*                                       reports, i.e. what int 21, function

*                                       36 would report if Stacker did not

*                                       trap this interrupt.

*         driveInfo.adjSecPerClust    = The adjusted sectors per cluster

*                                       that Stacker uses to calculate the

*                                       compression ratio of the files on

*                                       this drive.

*

* Notes:  The erasedSectorCount plus the freeSectorCount equals the total

*         number of physical sectors that are available.  Multiplying the

*         sum of these by the sector size will give you the minimum bytes

*         free on the Stacker drive.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_driveInfo(

                             struct stacker_drive_info_struct far *driveInfo

                                                    );

#else

    s_return far pascal stacker_driveInfo(

                             struct stacker_drive_info_struct far *driveInfo

                                         );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_flushDriver()

*

* Function:         Forces the Stacker device driver to flush its information

*                   for a Stacker drive.

*

* Arguments:        driveLetter, The drive letter of the Stacker drive to

*                                flush.

*

* Return:           SE_SUCCESSFUL          = The Stacker drive was flushed

*                                            successfully.

*                   SE_INVALID_GLOBAL_DATA = Unable to access some data

*                                            specific to the Stacker API.

*                                            This can show up if you attempt

*                                            to make a call to an API function

*                                            without first making a call to

*                                            stacker_status().

*

*                   NOTE:  The following errors are returned from the IOCTL

*                          call to the Stacker device driver to flush the

*                          drive information:

*

*                   SE_INVALID_FUNCTION    = An invalid function error

*                                            occurred while attempting to

*                                            access the Stacker driver.

*                   SE_ACCESS_DENIED       = An access denied error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_HANDLE      = An invalid handle error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_DATA        = An invalid data error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_UNEXPECTED_ERROR    = An unexpected error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*

* Notes:            This function should only fail if the drive is

*                   write-protected.  Any other failures represent major

*                   problems, such as a program writing over some memory

*                   used by the API or the Stacker driver.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_flushDriver(s_char driveLetter);

#else

    s_return far pascal stacker_flushDriver(s_char driveLetter);

#endif

/*

******************************************************************************

*

* Function Name:    stacker_unmountVolume()

*

* Function:         Unmounts a mounted compressed Stacker volume.

*

* Arguments:       *driveInfo, Pointer to a stacker_drive_info_struct

*                              structure.

*

* Return:           SE_SUCCESSFUL        = Volume unmounted successfully.

*                   SE_DRIVE_NOT_STACKED = Unable to unmount the drive

*                                          because it is not a Stacker drive.

*                   SE_DRIVE_NOT_MOUNTED = Unable to unmount the drive

*                                          because the Stacker drive is

*                                          not mounted.

*                   SE_UNABLE_TO_FIND_DPB= Unable to unmount the drive

*                                          because it could not find the

*                                          Drive Parameter Block (DPB) for

*                                          the drive.  The only reason this

*                                          could happen is if something

*                                          corrupted the "list of lists".

*                                          If this error occurs the system

*                                          is probably unstable.

*

* Notes:            You must make a successful call to stacker_driveInfo()

*                   before calling this function.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_unmountVolume(

                                             struct stacker_drive_info_struct

                                                 far *driveInfo

                                                        );

#else

    s_return far pascal stacker_unmountVolume(

                                             struct stacker_drive_info_struct

                                                 far *driveInfo

                                             );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_hostDriveMode

*

* Function:         Puts a Stacker drive in host drive mode.

*

* Arguments:       *driveInfo,       Pointer to a stacker_drive_info_struct

*                                    structure.

*                  *hostDriveLetter, Pointer to an s_char to store the drive

*                                    letter to access the host drive.

*

* Return:           SE_SUCCESSFUL             = The drive is now in host mode.

*                   SE_DRIVE_NOT_STACKED      = Unable to put the drive in

*                                               host because the drive is not

*                                               a Stacker drive.

*                   SE_DRIVE_NOT_MOUNTED      = Unable to put the drive in

*                                               host mode because the Stacker

*                                               drive is not mounted.

*                   SE_UNABLE_TO_FLUSH_DRIVER = Unable to put the drive in host

*                                               mode because it was unable to

*                                               successfully flush the Stacker

*                                               device driver for the drive.

*                                               This should never happen, but

*                                               if it does make a call to the

*                                               stacker_flushDriver() function

*                                               on the same drive to get a

*                                               better reason for the failure.

*                   SE_UNABLE_TO_FIND_DPB     = Unable to put the drive in host

*                                               mode because it could not find

*                                               the Drive Parameter Block (DPB)

*                                               for the drive.  The only reason

*                                               this could happen is if

*                                               something corrupted the "list

*                                               of lists".  If this error occurs

*                                               the system is probably unstable.

*

* Notes:            You must make a successful call to stacker_driveInfo()

*                   before calling this function.

*

*                   This function allows you to access the host drive of a

*                   mounted Stacker drive without having to unmount it and

*                   then re-mount when you are done.  After a call to this

*                   function the host drive will be accessable using the

*                   normal Stacker drive letter that you put in

*                   driveInfo->driveLetter before making the

*                   stacker_driveInfo() call.

*

*                   WARNING:  The Stacker drive is not accessable while the

*                             drive is in host mode.  Therefore, you must

*                             make sure that no files are opened that may

*                             get written to while the drive is in host mode.

*                             This is a critical time and you should leave

*                             the drive in host mode for as short of time

*                             as possible.

*

*                   Use the stacker_undoHostDriveMode() to take the Stacker

*                   drive out of host mode.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_hostDriveMode(

                                         struct stacker_drive_info_struct

                                                far *driveInfo,

                                         s_char far *hostDriveLetter

                                                        );

#else

    s_return far pascal stacker_hostDriveMode(

                                         struct stacker_drive_info_struct

                                                far *driveInfo,

                                         s_char far *hostDriveLetter

                                             );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_undoHostDriveMode

*

* Function:         Takes a Stacker drive out of host drive mode.

*

* Arguments:       *driveInfo, Pointer to a stacker_drive_info_struct

*                              structure.

*

* Return:           SE_SUCCESSFUL             = The drive is out of host mode.

*                   SE_DRIVE_NOT_STACKED      = Unable to take the drive out

*                                               of host mode because the drive

*                                               is not a Stacker drive.  This

*                                               could only happen if the

*                                               driveInfo points to a drive

*                                               info structure that has a

*                                               drive letter for a non-Stacker

*                                               drive.

*                   SE_DRIVE_NOT_IN_HOST_MODE = driveInfo->driveLetter is a

*                                               Stacker drive that is not in

*                                               host mode.

*                   SE_UNABLE_TO_FIND_DPB     = Unable to put the drive in host

*                                               mode because it could not find

*                                               the Drive Parameter Block (DPB)

*                                               for the drive.  The only reason

*                                               this could happen is if

*                                               something corrupted the "list

*                                               of lists".  If this error occurs

*                                               the system is probably unstable.

*

* Notes:            You must make a successful call to stacker_driveInfo()

*                   before calling this function.

*

*                   Use the stacker_hostDriveMode() to put the mounted Stacker

*                   drive in host drive mode.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_undoHostDriveMode(

                                         struct stacker_drive_info_struct

                                                far *driveInfo

                                                            );

#else

    s_return far pascal stacker_undoHostDriveMode(

                                         struct stacker_drive_info_struct

                                                far *driveInfo

                                                 );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_writeProtect

*

* Function:         Write protects a Stacker drive.

*

* Arguments:       *driveInfo, Pointer to a stacker_drive_info_struct

*                              structure.

*

* Return:           SE_SUCCESSFUL             = The drive is now in

*                                               write-protected.

*                   SE_DRIVE_NOT_STACKED      = Unable to write-protect the

*                                               drive because the it is not

*                                               a Stacker drive.

*                   SE_DRIVE_NOT_MOUNTED      = Unable to write-protect the

*                                               drive because the Stacker

*                                               drive is not mounted.

*                   SE_UNABLE_TO_FLUSH_DRIVER = Unable to write-protect the

*                                               drive because it was unable to

*                                               successfully flush the Stacker

*                                               device driver for the drive.

*                                               This should never happen, but

*                                               if it does make a call to the

*                                               stacker_flushDriver() function

*                                               on the same drive to get a

*                                               better reason for the failure.

*

* Notes:            You must make a successful call to stacker_driveInfo()

*                   before calling this function.

*

*                   This function allows you to write-protect a mounted

*                   Stacker drive.  It is up to you to make sure that all

*                   files are closed before write protecting the drive.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_writeProtect(

                                            struct stacker_drive_info_struct

                                                   far *driveInfo

                                                       );

#else

    s_return far pascal stacker_writeProtect(

                                            struct stacker_drive_info_struct

                                                   far *driveInfo

                                            );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_unWriteProtect

*

* Function:         Takes the write-protect off of a Stacker drive.

*

* Arguments:       *driveInfo, Pointer to a stacker_drive_info_struct

*                              structure.

*

* Return:           SE_SUCCESSFUL        = The drive is now writeable.

*                   SE_DRIVE_NOT_STACKED = Unable to remove the write-protect

*                                          status of the drive because the it

*                                          is not a Stacker drive.

*                   SE_DRIVE_NOT_MOUNTED = Unable to remove the write-protect

*                                          status of the drive because the

*                                          Stacker drive is not mounted.

*

* Notes:            You must make a successful call to stacker_driveInfo()

*                   before calling this function.

*

*                   This function allows you to remove the write-protect

*                   status of a mounted Stacker drive.  This write-protect

*                   status is put on any Stacker drives that are damaged

*                   or physically write-protected when the Stacker driver

*                   mounts them.

*

*                   WARNING:  Writing to a damaged Stacker drive can cause

*                             unpredictable results, including damage to data

*                             on the drive.  To check if the drive is damaged,

*                             see if the STACKER_DAMAGED bit in the

*                             driveInfo->flagBits field is set.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_unWriteProtect(

                                              struct stacker_drive_info_struct

                                                     far *driveInfo

                                                         );

#else

    s_return far pascal stacker_unWriteProtect(

                                              struct stacker_drive_info_struct

                                                     far *driveInfo

                                              );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_getFatEntry

*

* Function:         Returns the FAT entry for a cluster.

*

* Arguments:        clusterNumber, Cluster number for the FAT entry to return.

*                  *driveInfo,     Pointer to a stacker_drive_info_struct

*                                  structure.

*                  *rccBuffer,     Pointer to a temporary buffer that is

*                                  driveInfo->rccBufferSize in size.

*                  *fatEntry,      Pointer to an s_ushort to store the FAT

*                                  entry for clusterNumber.

*

* Return:           SE_SUCCESSFUL          = The function call was successful.

*

*                   NOTE:  The following errors are returned from the IOCTL

*                          call to the Stacker device driver to get the

*                          specified FAT entry:

*

*                   SE_INVALID_FUNCTION    = An invalid function error

*                                            occurred while attempting to

*                                            access the Stacker driver.

*                   SE_ACCESS_DENIED       = An access denied error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_HANDLE      = An invalid handle error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_DATA        = An invalid data error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_UNEXPECTED_ERROR    = An unexpected error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*

*                   After a successful call to stacker_getFatEntry *fatEntry

*                   will contain the FAT entry for the specified cluster.

*

* Notes:            You must make a call to stacker_driveInfo() for the drive

*                   you will be getting the FAT entry from prior to calling

*                   this function.  You must allocate a buffer using the

*                   driveInfo->rccBufferSize field to determine the size.

*                   The rccBuffer is a FAT cache and its size will vary from

*                   drive to drive.  You should not use the same rccBuffer

*                   for different drives.  You may free up the rccBuffer

*                   memory after you are done using the stacker_getFatEntry()

*                   and stacker_readCompressedCluster() functions for that

*                   specific drive.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_getFatEntry(

                                           s_ushort      clusterNumber,

                                           struct stacker_drive_info_struct

                                                    far *driveInfo,

                                           s_char   far *rccBuffer,

                                           s_ushort far *fatEntry

                                                      );

#else

    s_return far pascal stacker_getFatEntry(

                                           s_ushort      clusterNumber,

                                           struct stacker_drive_info_struct

                                                    far *driveInfo,

                                           s_char   far *rccBuffer,

                                           s_ushort  far *fatEntry

                                           );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_readCompressedCluster()

*

* Function:         Reads a compressed cluster into a buffer.

*

* Arguments:       *driveInfo,      Pointer to a stacker_drive_info_struct.

*                                   Filled in by calling program.

*                   clusterNumber,  # of the logical cluster to read.

*                                   Filled in by calling program.

*                  *compDataBuffer, Pointer to a buffer to store the data.

*                                   This must be at least one cluster + one

*                                   sector in size.

*                                   Filled in by calling program.

*                  *rccBuffer,      Pointer to rcc scratch buffer.  The size

*                                   required by this buffer is returned in

*                                   the call to stacker_driveInfo().

*                                   Filled in by calling program.

*                  *compByteCount,  Pointer to a s_ushort to store how many

*                                   bytes were read to make up the cluster.

*                                   Filled in by this function.

*                  *compDataFormat, Pointer to a s_ushort to store how the

*                                   data was compressed.

*                                   Filled in by this function.

*                   flags,          Reserved, must be set to 0.

*

* Return:           SE_SUCCESSFUL          = The function call was

*                                            successful.

*                   SE_INVALID_DATA_PASSED = A value passed into this

*                                            function is invalid.

*                   SE_DRIVE_NOT_STACKED   = Unable to read the compressed

*                                            cluster because the drive is

*                                            not a Stacker drive.

*

*                   NOTE:  The following errors are returned from the IOCTL

*                          call to the Stacker device driver if a failure

*                          occurs while reading the compressed cluster:

*

*                   SE_INVALID_FUNCTION    = An invalid function error

*                                            occurred while attempting to

*                                            access the Stacker driver.

*                   SE_ACCESS_DENIED       = An access denied error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_HANDLE      = An invalid handle error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_DATA        = An invalid data error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_UNEXPECTED_ERROR    = An unexpected error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*

*                   NOTE: The following fields will only be filled in upon a

*                         successful call to this function.

*

*                  *compDataBuffer, The compressed data that was read in.

*                  *compByteCount,  How many bytes were read to make up the

*                                   cluster.

*                  *compDataFormat, STACKER_NOT_COMPRESSED

*                                   STACKER_LZS_FORMAT

*                                   STACKER_INCOMPRESSABLE

*

* Notes:            The compByteCount field is not always an exact number of

*                   bytes.  It is the number of sectors used by the cluster

*                   multiplied by the size of the sectors.

*

*                   If compDataFormat is STACKER_NOT_COMPRESSED you should

*                   compress the cluster using the stacker_pack() function

*                   in the Stacker Developer's Tool Kit.  If compDataFormat

*                   is STACKER_INCOMPRESSABLE there is no need to compress

*                   it using the stacker_pack() function because the data

*                   will probably expand.

*

*                   You must make a call to stacker_driveInfo() for the drive

*                   you will be getting the FAT entry from prior to calling

*                   this function.  You must allocate a buffer using the

*                   driveInfo->rccBufferSize field to determine the size.

*                   The rccBuffer is a FAT cache and its size will vary from

*                   drive to drive.  You should not use the same rccBuffer

*                   for different drives.  You may free up the rccBuffer

*                   memory after you are done using the stacker_getFatEntry()

*                   and stacker_readCompressedCluster() functions for that

*                   specific drive.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_readCompressedCluster(

                                          struct stacker_drive_info_struct

                                                   far *driveInfo,

                                          s_ushort      clusterNumber,

                                          s_char   far *compDataBuffer,

                                          s_char   far *rccBuffer,

                                          s_ushort far *compByteCount,

                                          s_ushort far *compDataFormat,

                                          s_ushort      flags

                                                               );

#else

    s_return far pascal stacker_readCompressedCluster(

                                          struct stacker_drive_info_struct

                                                   far *driveInfo,

                                          s_ushort      clusterNumber,

                                          s_char   far *compDataBuffer,

                                          s_char   far *rccBuffer,

                                          s_ushort far *compByteCount,

                                          s_ushort far *compDataFormat,

                                          s_ushort      flags

                                                     );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_initializeRccBuffer()

*

* Function:         Initializes the Read Compressed Cluster Buffer.

*

* Arguments:       *rccBuffer, Pointer to a buffer to store internal

*                              data for the stacker_getFatEntry() and

*                              stacker_readCompressedCluster() functions.

*                  *driveInfo, Pointer to a stacker_drive_info_struct.

*                              Filled in by calling program.

*

* Return:           SE_SUCCESSFUL          = The buffer was initialized

*                                            successfully.

*                   SE_INVALID_DATA_PASSED = The driveInfo data passed into

*                                            this function is invalid.

*

* Notes:            This function must be called after allocating memory

*                   for the Read Compressed Cluster Buffer and before

*                   making a call to stacker_getFatEntry() or

*                   stacker_readCompressedCluster().  You do not need to

*                   initialize this buffer every time you use it.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_initializeRccBuffer(

                                           void far *rccBuffer,

                                           struct stacker_drive_info_struct

                                                far *driveInfo

                                                              );

#else

    s_return far pascal stacker_initializeRccBuffer(

                                           void far *rccBuffer,

                                           struct stacker_drive_info_struct

                                                far *driveInfo

                                                   );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_fileCompressionRatio()

*

* Function:         Provides the number of compressed sectors and raw

*                   sectors for the file.

*

* Arguments:       *driveInfo,         Pointer to a stacker_drive_info_struct.

*                                      Filled in by calling program.

*                  *rccBuffer,         Pointer to a buffer to store internal

*                                      data for the stacker_getFatEntry() and

*                                      stacker_readCompressedCluster()

*                                      functions.

*                  *fileName,          Pointer to the name of the file to get

*                                      the compression ratio of.

*                  *compressedSectors, Pointer to a s_ulong to store the

*                                      number of compressed sectors used by

*                                      the file specified in fileName.

*                  *rawSectors,        Pointer to a s_ulong to store the

*                                      number of uncompressed sectors used

*                                      by the file specified in fileName.

*

* Return:           SE_SUCCESSFUL          = The call was successful.

*                   SE_DRIVE_NOT_STACKED   = The drive the file is located on

*                                            is not Stacked.

*                   SE_INVALID_DATA_PASSED = The call to initialize the RCC

*                                            buffer failed.

*                   SE_FILE_NOT_FOUND      = Unable to find the file passed

*                                            into this function.

*

*                   NOTE:  The following errors are returned from the IOCTL

*                          call to the Stacker device driver to get the files

*                          compression ratio information:

*

*                   SE_INVALID_FUNCTION    = An invalid function error

*                                            occurred while attempting to

*                                            access the Stacker driver.

*                   SE_ACCESS_DENIED       = An access denied error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_HANDLE      = An invalid handle error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_DATA        = An invalid data error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_UNEXPECTED_ERROR    = An unexpected error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*

* Notes:            You must make a call to stacker_driveInfo() for the drive

*                   you will be getting the file compression ratio information

*                   from prior to calling this function.  You must allocate a

*                   buffer using the driveInfo->rccBufferSize field to

*                   determine the size.  The rccBuffer is a FAT cache and its

*                   size will vary from drive to drive.  You should not use

*                   the same rccBuffer for different drives.  You may free up

*                   the rccBuffer memory after you are done using the

*                   stacker_fileCompressionRatio() function for that specific

*                   drive.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_fileCompressionRatio(

                                         struct stacker_drive_info_struct

                                                    far *driveInfo,

                                         s_char     far *rccBuffer,

                                         s_char     far *fileName,

                                         s_ulong    far *compressedSectors,

                                         s_ulong    far *rawSectors

                                                               );

#else

    s_return far pascal stacker_fileCompressionRatio(

                                         struct stacker_drive_info_struct

                                                    far *driveInfo,

                                         s_char     far *rccBuffer,

                                         s_char     far *fileName,

                                         s_ulong    far *compressedSectors,

                                         s_ulong    far *rawSectors

                                                    );

#endif

/*

******************************************************************************

*

* Function Name:    stacker_fastFileCR()

*

* Function:         Provides the number of compressed sectors and raw

*                   sectors for the file that starts at startingCluster.

*

* Arguments:       *driveInfo,         Pointer to a stacker_drive_info_struct.

*                                      Filled in by calling program.

*                  *rccBuffer,         Pointer to a buffer to store internal

*                                      data for the stacker_getFatEntry() and

*                                      stacker_readCompressedCluster()

*                                      functions.

*                   startingCluster,   The starting cluster of the file that

*                                      you want the compression ratio of.

*                   fileSize,          The size of the file that you want the

*                                      compression ratio of.

*                  *compressedSectors, Pointer to a s_ulong to store the

*                                      number of compressed sectors used by

*                                      the file specified in fileName.

*                  *rawSectors,        Pointer to a s_ulong to store the

*                                      number of uncompressed sectors used

*                                      by the file specified in fileName.

*

* Return:           SE_SUCCESSFUL          = The call was successful.

*                   SE_DRIVE_NOT_STACKED   = The drive the file is located on

*                                            is not Stacked.

*                   SE_INVALID_DATA_PASSED = The call to initialize the RCC

*                                            buffer failed.

*

*                   NOTE:  The following errors are returned from the IOCTL

*                          call to the Stacker device driver to get the files

*                          compression ratio information:

*

*                   SE_INVALID_FUNCTION    = An invalid function error

*                                            occurred while attempting to

*                                            access the Stacker driver.

*                   SE_ACCESS_DENIED       = An access denied error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_HANDLE      = An invalid handle error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_INVALID_DATA        = An invalid data error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*                   SE_UNEXPECTED_ERROR    = An unexpected error occurred

*                                            while attempting to access the

*                                            Stacker driver.

*

* Notes:            You must make a call to stacker_driveInfo() for the drive

*                   you will be getting the file compression ratio information

*                   from prior to calling this function.  You must allocate a

*                   buffer using the driveInfo->rccBufferSize field to

*                   determine the size.  The rccBuffer is a FAT cache and its

*                   size will vary from drive to drive.  You should not use

*                   the same rccBuffer for different drives.  You may free up

*                   the rccBuffer memory after you are done using the

*                   stacker_fileCompressionRatio() function for that specific

*                   drive.

*

****************************************************************************/

#if defined(__BCPLUSPLUS__) || defined(__cplusplus)

    extern "C" s_return far pascal stacker_fastFileCR(

                                         struct stacker_drive_info_struct

                                                    far *driveInfo,

                                         s_char     far *rccBuffer,

                                         s_ushort        startingCluster,

                                         s_ulong         fileSize,

                                         s_ulong    far *compressedSectors,

                                         s_ulong    far *rawSectors

                                                     );

#else

    s_return far pascal stacker_fastFileCR(

                                          struct stacker_drive_info_struct

                                                     far *driveInfo,

                                          s_char     far *rccBuffer,

                                          s_ushort        startingCluster,

                                          s_ulong         fileSize,

                                          s_ulong    far *compressedSectors,

                                          s_ulong    far *rawSectors

                                          );

#endif


Stacker Popup
  STACKER DEVELOPER'S TOOL KIT

3.1

FOR WINDOWS AND DOS

STACKER [image] LZS [image] Compression

The Data Compression Standard

Stac Electronics

Stacker Developer's Tool Kit

5993 Avenida Encinas

Carlsbad, CA 92008-9708

For Stacker Developer's Tool Kit help on CompuServe type   GO STAC  or  GO STACKER


Who uses Stacker LZS Technology
 Who uses Stacker LZS Technology?

Stacker LZS compression technology has been adopted and licensed by many software developers.  The following is a list of some of the companies and their products which use Stacker LZS compression:

ACE Technologies, DoubleCard Compressed PCMCIA Cards for Palmtop Computers

Central Point Software, CP Backup Software in PC Tools

Cheyenne Software, Tape Backup Software

Colorado Memory Systems, Jumbo Tape Drives

Compaq, Tape Adapter Board

Conner Peripherals, Backup Exec Tape Backup Software and QIC Tape Drives

Digadata, Tape Drive

Exabyte, 750MB Tape Drive

Gigatrend, DAT Tape Drive

Golden Triangle, Macintosh Hard Disk Compression

Maxoptix, Optical Disk Drive

Megatape, SCSI to SCSI Compression Adapter

MSR Development, Tape Backup Software for OS/2

Peripheral Vision, SCSI to SCSI Compression Adapter

Quest Development, Norton Backup

Stac Electronics, Hard disk compression for DOS, Windows, OS/2, and Macintosh

Symantec, Norton Backup

Sytron Corporation, Sytos Plus for DOS, OS/2, and Windows Tape Backup Software

Tecmar, QIC Tape Drive

Transitional Technologies, Tape Drive

Traveling Software, LapLink V

WangDAT, DAT Tape Drive


 Stacker Developer's Tool Kit Credits

Programming	Dave Picco, Vince Blasberg

Manual	Dave Picco, Bruce Behymer, Lee Page

Windows Help File	Vince Blasberg

Graphics	Dan Galkowski, Shaun Harrington, Dave Wyner

Licensing	Alex Gillette, John Witzel, Bob Monsour

Production	Trish Anderson, Lisa Walsh

Engineering Management	Alan Watkins, Stephen Mickelsen, Tom Dilatush

Marketing Management	Bruce Behymer, John Bromhead

Concept	Doug Whiting, Bob Lutz


Copyright Notice

  STACKER DEVELOPER'S TOOL KIT

3.1

FOR WINDOWS AND DOS

STACKER [image] LZS [image] Compression

The Data Compression Standard

Stacker Developer's Tool Kit [image] STAC ELECTRONICS, 1993.

Stacker API and Stacker LZS [image] STAC ELECTRONICS 1990, 1991, 1992, 1993.

U.S. patents 4,701,745,  5,016,009, and 5,126,739.  Other patents pending.

This manual copyright [image] 1993 by Stac Electronics.  All rights reserved.  No part of it may be copied, photocopied, reproduced, translated, or reduced to any electronic medium or machine-readable form, except for internal use, without Stac Electronics' prior written consent.

All Stac products are trademarks or registered trademarks of Stac Electronics.  Other brand and product names are trademarks or registered trademarks of their respective holders.

2002 Version 3.1 (10/93)

Stac Electronics

Stacker Developer's Tool Kit

5993 Avenida Encinas

Carlsbad, CA 92008-9708