TweetFollow Us on Twitter

PICTScavenger

Picture Resource Scavenger

By David T. Craig, Kansas City, MO

INTRODUCTION

This article describes a MPW Pascal 3.0 unit that scavenges QuickDraw picture resources from corrupted resource files.

This unit produces as output a standard Macintosh text file containing the source listing for all the scavenged picture resources in a format compatible with Apple's Rez tool along with the actual scavenged picture resources which may be viewed by a program like Apple's ResEdit.

All non-corrupted picture resources (type ‘PICT’) within a file will be found by this program. This program makes no assumptions about the resource file. As such, this program ignores any format that the resource may contain. For example, the Resource Header is not assumed to be intact and is not used.

Progress information may also be displayed by this unit during the scavenging process. Currently, this information consists of simple phrases which detail the scavenger's progress on a picture basis. This information is only displayed if the program that uses this unit is running under the Apple MPW Shell. If the MPW Shell is not active, then no progress information appears.

A file containing PICT resources may become corrupted in many ways. Once corrupted, a file's resources can not be accessed. If Apple's ResEdit program can not open a file, then you are seriously in need of this PICT scavenger. Most resource files which become corrupt generally have an invalid Resource Header and Resource Map. These two areas within a resource file point to the actual resource data.

This unit should be viewed from a global Macintosh perspective as a first step in providing a set of modules that reconstruct corrupted data from Macintosh files. This concept is not new and has been around, as I far as I know, since the Xerox Alto in the mid 1970's. In this author's opinion, these types of services should exist at two levels. The first level is the lowest level and involves device data integrity. This means disk drives. Apple Computer should provide this level of service via its Macintosh Operating System. If a file becomes corrupted the OS should detect this fact, inform the user, and attempt to regain the corrupted data. Apple in its wonderful Lisa computer provided such a feature via the Lisa Scavenger and Tag Bytes on disk media. Apple has not pursued this avenue with the Macintosh. See Apple Macintosh Technical Note #94 for Tag Byte information. The second level of data integrity involves the integrity of file contents. This level of service can only be provided by the authors of Macintosh applications and their related documents. Implementing this service would require checksums (which includes CRCs), redundant file data structures, and built-in application recovery methods.

USING THIS UNIT

This unit contains two public routines. Routine PICT_Unit_Version returns a string representing the version number and compilation date and time of this unit. Routine PICT_Scavenger performs the actual scavenging process. The Pascal interface for this routine follows:

{1}

{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... PICT_Scavenger
  • Purpose ..... Scavenge all the picture (PICT) resources 
  •  found in a file and write the picture data to a text file 
  •  containing the picture data as PICT resources and Rez
 •  source code
  • Input .. ps_program_name  - name of program (for text file)
  •       ps_input_file_name - corrupted input file to scavenge
  •       ps_input_file_volume - input file volume reference
  •       ps_output_file_name - text output file for Rez source
  •       ps_output_file_volume - output file volume reference
  •       ps_show_progress  - show scavenging progress flag
  •       ps_pict_header_info - picture header/tail info
  •       ps_starting_id - picture resource starting ID value
  •       ps_decrement_id - decrement resource ID flag
  • Output ..ps_num_found_picts - no. pictures found in input file
  •        ps_num_pict_failures - no. picture scavenge failures
  •        ps_error - error result
  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  PROCEDURE PICT_Scavenger (ps_program_name: Str255;
 ps_input_file_name    : Str255;
 ps_input_file_volume  : INTEGER;
 ps_output_file_name   : Str255;
 ps_output_file_volume : INTEGER;
 ps_show_progress      : BOOLEAN;
 ps_pict_header_info   : PS_t_PHI_List;
 ps_starting_id        : INTEGER;
 ps_decrement_id       : BOOLEAN;
 VAR  ps_num_found_picts    : INTEGER;
 VAR  ps_num_pict_failures  : INTEGER;
 VAR  ps_error              : INTEGER);

The key to this routine is the data type PS_t_PHI_List. This type contains a list of patterns which the unit uses to search for a PICT resource in a resource file. The type has the following structure:

{2}

   PS_t_PHI_List = ARRAY [1..PS_c_MaxPICTVersions{4}] OF
                     PS_t_PICT_Header_Info;

where PS_t_PICT_Header_Info has the structure:

{3}
                                                                        
   PS_t_PICT_Header_Info = RECORD
      hi_version_data        : Ptr;     { header info }
      hi_version_data_length : INTEGER;
      hi_endpict_data        : Ptr;
      hi_endpict_data_length : INTEGER; { footer info }
   END;

To scavenge version 1 pictures use the following values:

hi_version_data points to buffer holding value $1101

hi_version_data_length = 2

hi_endpict_data points to buffer holding $FF

hi_endpict_data_length = 1

To scavenge version 2 pictures use the following values:

hi_version_data points to buffer holding value $001102FF

hi_version_data_length = 4

hi_endpict_data points to buffer holding $00FF

hi_endpict_data_length = 2

Note that up to 4 picture header patterns may be used, but currently only two are needed since only two picture versions exist.

I wrote a simple MPW Shell tool called PICTScavenger in MPW Pascal 3.0 which tests this unit and provides a nice programming framework for tools. This tool has the following command line:

# 4

PICTScavenger [-p] [-s start_id] [-d] input_file [output_file]

The arguments are as follows:

-p: Show progress information. The default is to NOT show any progress information.

-s: Set starting PICT resource ID value to start_id. The default value is 1000.

-d: Decrement resource ID. The default is to increment the ID value.

Argument input_file is the name of the corrupted file. This argument must always exist in the argument list. This name MUST occur before the name of the output file, if one exists. Argument output_file is the name of the output file which will contain a Rez-compatible source listing of the pictures found in the corrupted input file. If no output file is specified, then a default output file is created whose name consists of the input file name suffixed with “.PICT.r”.

When run using a test file named “test” as input the following information appears in a MPW window:

PictScavenger -p test

Macintosh PICT File Scavenger 1.0.0  [9/28/90 - 11:06:12 AM]
David T. Craig (736 Edgewater, Wichita, Kansas 67230)
Not Copyright (c) 1990

PICT Scavenger unit version: 1.1.0  [9/28/90 - 3:25:23 PM]

Scavenging file “test” to Rez file “test.PICT.r” ...

Searching for PICT HEAD $1101 and PICT TAIL $FF

  Scavenging PICT #   1000   [Address $00010E  Length $0001D5]
  Scavenging PICT #   1001   [Address $0002E7  Length $000677]
  Scavenging PICT #   1002   [Address $000962  Length $000089]
  *** PICT scavenge failure  [Address $001800]

Searching for PICT HEAD $001102FF and PICT TAIL $00FF

  Scavenging PICT #   1003   [Address $0009EF  Length $0007A0]
  Scavenging PICT #   1004   [Address $001193  Length $000716]

PICT Scavenger Summary:

  Input  file       : test
  Output file       : test.PICT.r
  Scavenged PICTs   : 5
  Scavenge failures : 1
  Elapsed time      : 0 minute 13 seconds

NOTE: Scavenge failures indicate that a PICT header was found, but the header was not valid. The file may contain a valid picture which this program could not scavenge.

That’s all, folks.

To compile the Rez file use the following command line :

#5

Rez -t ZZZZ test.PICT.r -o ‘test pictures’

FORMAT OF PICTURES IN A RESOURCE FILE

A PICT resource within a resource file has a specific format. All pictures begin with a specific pattern of bytes which differ per picture version. I call this pattern the Picture Header Pattern (PHP). Preceding this pattern exists an 8 byte rectangle which represents the picture frame and a 2 byte integer which represents the picture byte size. This size integer is actually the low order word for the picture’s true size. Preceding the size integer in the file is a 4 byte longint value which contains the physical size of the picture data. All pictures end with a pattern of bytes which I call the Picture Tail Pattern (PTP). This pattern is the End-Of-Picture Opcode. For version 1 pictures the pattern contains one byte, for version 2 it has two bytes.

Since a (non-QuickDraw) picture is worth a thousand words?

+------------------+------------------+------------------+--------------+----------------------+--------------+
| PSize 4 | LSize 2 | Frame 8 | PHP X | Pict Data | PTP Y |
+------------------+------------------+------------------+--------------+----------------------+--------------+

For version 1 pictures, X = 2 and Y = 1
For version 2 pictures, X = 4 and Y = 2

PICTURE RESOURCE SEARCH ALGORITHM

The algorithm used to locate picture resources within a resource file is quite straight forward. This algorithm contains 6 steps:

Step 1: Start at the beginning of the resource fork data

Step 2: Locate the next Picture Header Pattern

Step 3: Verify that the Picture Header Pattern is preceded by a valid rectangle and the word and longword least significant word before the rectangle data are equal

Step 4: Verify that the Picture Tail Pattern and the last opcode in the picture data match

Step 5: If steps 2 to 4 are successful, then a picture has been found in the resource file

Step 6: Repeat steps 2-5 until the physical end-of-file of the resource fork is found

BIBLIOGRAPHY

Resource file format : Inside Macintosh, vol. I, p. 128+

QuickDraw pictures : Inside Macintosh, vol. I, p. 158+

QuickDraw picture opcodes: Apple Technical Note # 21, Inside Macintosh, vol. V, p.92+

PICTURES IN TEST

id = 1000

id = 1001

id = 1002

id = 1003

id = 1004


Listing:  PICTScavenger.make

#   File:       PICTScavenger.make
#   Target:     PICTScavenger
#   Sources:    PICTScavenger.p U_PICT_Scavenger.p

U_PICT_Scavenger.p.o ƒ PICTScavenger.make U_PICT_Scavenger.p
         Pascal  -ov -u U_PICT_Scavenger.p

PICTScavenger.p.o ƒ PICTScavenger.make PICTScavenger.p
         Pascal  -ov -u PICTScavenger.p
         
SOURCES = PICTScavenger.p U_PICT_Scavenger.p
OBJECTS = PICTScavenger.p.o U_PICT_Scavenger.p.o

PICTScavenger ƒƒ PICTScavenger.make {OBJECTS}
        Link -w -c 'MPS ' -t MPST 
                {OBJECTS} 
                "{Libraries}"stubs.o 
                "{Libraries}"Runtime.o 
                "{Libraries}"Interface.o 
                "{PLibraries}"PasLib.o 
                "{PLibraries}"SANELib.o 
                "{Libraries}"ToolLibs.o 
                -o PICTScavenger

### FINIS
Listing:  PICTScavenger.p

{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                         •
  • ----------------------------------------------------    •
  •   APPLE MACINTOSH PICT RESOURCE SCAVENGER MPW TOOL      •
  • ----------------------------------------------------    •
  •                                                         •
  •                    Version 1.0.0                        •
  •                                                         •
  • Author ......... David T. Craig                         •
  • Address ........ 736 Edgewater, Wichita, Kansas 67230   •
  • Date ........... August 1990                            •
  • Language ....... Apple MPW Pascal 3.0                   •
  • Computer ....... Apple Macintosh                        •
  • Environment .... Apple MPW Shell                        •
  •                                                         •
  • PURPOSE:                                                •
  •                                                         •
  • This program is an MPW tool which scavenges PICT (picture) resources 
from a corrupted file.  Since this program is an MPW
tool, it can only be run under the MPW Shell.  Only the resource fork 
of a file is searched for picture resources since this is the only fork 
of a Macintosh file that should contain resources.  •
  •    This program produces as output a standard Macintosh text file 
containing the source listing for all the scavenged picture resources 
in a format compatible with Apple's Rez tool.
  •    To produce a file with actual PICT resources requires compiling 
the text file with Rez.  Rez version 3.0 works well with the output files 
from this program.
  • USING THIS PROGRAM:                                •
  •                                                    •
  •    This program is used from the MPW Shell by typing the tool name 
and a list of arguments for the tool.  This argument list must always 
contain at least the name of the corrupted file whose picture resources 
are desired.  Additional arguments may also exist in the argument list.
  •    The format for the tool and its arguments follow with arguments 
enclosed in [] being optional (argument order is unimportant):    •
  •                                                    •
PICTScavenger [-p] [-s start_id] [-d] input_file [output_file]
  •    The arguments are as follows:                   •
  •                                                    •
  •      -p : Show progress information.  The default is to NOT show 
any progress information. 
  •      -s : Set starting PICT resource ID value to start_id. The default 
value is 1000.                                 •
  •      -d : Decrement resource ID.  The default is to increment the 
ID value.
  •       Argument input_file is the name of the corrupted file.  This 
argument must always exist in the argument list.  This name MUST occur 
before the name of the output file, if one exists.
  •       Argument output_file is the name of the output file which will 
contain a Rez-compatible source listing of the pictures found in the 
corrupted input file.  If no output file is specified, then default output 
file created whose name consists of the input file name suffixed with 
".PICT.r".

  • PICT SCAVENGER ERRORS:                                   •
  •                                                          •
  •    Various errors may occur when using PICTScavenger.  Before a file 
 is scavenged the command line arguments are examined.  Failure of a 
command argument generate one of the following errors:
  • Error Number  Error Meaning                              •
  • ------------  ------------------------------------------ •
  •     1         Number of arguments in command is invalid  •
  •     2         Too many file names specified              •
  •     3         Input file name is missing                 •
  •     4         Starting ID value is invalid               •
  •     5         Invalid option encountered                 •
  •                                                          •
  •    Other errors may also occur.  These will be displayed without 
an error message detailing the type of error.  Refer to the list of Macintosh 
errors as provided by Apple in its Inside Macintosh.

••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                          •
  •        NOT COPYRIGHT (C) 1990 BY DAVID T. CRAIG          •
  •                                                          •
  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

PROGRAM Macintosh_PICT_Scavenger_Tool;

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                    EXTERNAL MODULES                    •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  USES
    MemTypes,   { Macintosh common types }
    OSIntf,     { Macintosh Operating System interface }
    ToolIntf,   { Macintosh ToolBox interface }
    Packages,   { Macintosh Package interface }
    PasLibIntf, { Pascal runtime libary interface }
    IntEnv,     { MPW integrated environment interface }
    CursorCtl,  { MPW shell cursor unit }
    
    {$U U_PICT_Scavenger.p} U_PICT_Scavenger; { the real working code 
}

{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                    COMPILER DIRECTIVES                  •
  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  {$R+ }  { [MPWPASCAL] enable range checking }
  {$SC+}  { [MPWPASCAL] encable short-circuit boolean evaluation }
  {$OV+}  { [MPWPASCAL] enable integer-type overflow checking }

{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                      GLOBAL CONSTANTS                   •
  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  CONST
  
{ general program info (appears if Progress option set) }
    gc_pgm_name      = 'Macintosh PICT File Scavenger';
    gc_pgm_version   = '1.0.0';
    gc_pgm_date      = COMPDATE;
    gc_pgm_time      = COMPTIME;
    gc_pgm_author    = 'David T. Craig';
    gc_pgm_address   = '736 Edgewater, Wichita, Kansas 67230';
    gc_pgm_copyright = 'Not Copyright (c) 1990';
    
      { program error values (passed back to MPW Shell) }
    
    gc_err_min              = 1; { min and max tool internal error codes 
}
    gc_err_max              = 5;
    
    gc_err_bad_argc         = 1; { ArgC value is invalid }
    gc_err_extra_file_name  = 2; { Too many file names specified }
    gc_err_no_input_file    = 3; { No input file name found in command 
}
    gc_err_bad_starting_id  = 4; { Start ID value is invalid }
    gc_err_bad_option       = 5; { Invalid command opt found }
    
    gc_output_file_suffix   = '.PICT.r'; { suffix for default output 
file }
    
    gc_default_starting_id  = 1000; { default starting resource ID }
    
    gc_shell_comment        = '### '; { shell message line prefix }
    
      { scavenge failure notice message }
      
    gc_fail_1 = 'NOTE: Scavenge failures indicate that a PICT header 
was found, ';
    gc_fail_2 = '      but the header was not valid.  The file may contain 
a valid ';
    gc_fail_3 = '      picture which this program could not scavenge.';
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                     GLOBAL TYPES                       •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  TYPE
      { command interpreter (aka Shell) arguments }
    gt_ci_args     = RECORD
       ci_show_progress : BOOLEAN;  { show progress flag }
       ci_starting_id   : INTEGER;  { starting ID value }
       ci_decrement_id  : BOOLEAN;  { decrement ID flag }
       ci_input_file    : Str255;   { input file name }
       ci_output_file   : Str255;   { output file name }
                     END;
    
      { version 1 picture starting and ending opcode info }
    gt_pict_1_info = RECORD
       p1_version : INTEGER; { [2] $1101 }
       p1_endpict : INTEGER; { [1] $FF?? (ignore LSB) }
                     END;
                     
      { version 2 picture starting and ending opcode info }
    gt_pict_2_info = RECORD
       p2_version : LONGINT; { [4] $0011 $02FF }
         p2_endpict : INTEGER; { [2] $00FF }
                     END;
                     
{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                     GLOBAL VARIABLES                  •
  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  VAR
    gv_shell_result : INTEGER; { MPW shell result code }
    gv_command_name : Str255; { name of tool command }
    gv_ci_args : gt_ci_args; { command interpreter arguments }
    gv_num_found_picts : INTEGER; 
 { no. PICTs scavenged from input file }
    gv_num_pict_failures : INTEGER;
 { no. PICT scavenge failures }
    gv_exec_time : LONGINT; 
 { scavenger execution time (seconds) }
    
    gv_pict_1_info : gt_pict_1_info; { version 1 picture info }
    gv_pict_2_info : gt_pict_2_info; { version 2 picture info }
    
    gv_pict_info : PS_t_PHI_List;  { list of picture info }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... show_Error
  • Purpose ..... Display an error message to the user
  • Input ....... the_error_code - error code value
  • Output ...... (none)
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE show_Error (the_error_code : INTEGER);
    VAR
      message : Str255; { english error message for bozo user }
    BEGIN { ------------ show_Error ------------ }
      { setup the error message for the user }
      
      CASE the_error_code OF
        gc_err_bad_argc : message := 'Invalid argument count';
        gc_err_extra_file_name : message := 'Too many file names specified';
        gc_err_no_input_file : message := 'No input file name was found';
        gc_err_bad_starting_id : message := 'Invalid starting ID value 
found';
        gc_err_bad_option : message := 'Invalid command line option found';
        
        OTHERWISE message := 'Unknown Macintosh error code'; 
 { Macintosh error }
      END;
            
      { display the error message to the user }
      WRITELN;
      WRITELN(gc_shell_comment, 'ERROR # ',the_error_code:1,' in ', gv_command_name, 
' (',message,')');
    END;  { ------------ show_Error ------------ }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... uppercase_Phrase
  • Purpose ..... Uppercase the characters in a phrase
  • Input ....... the_phrase - phrase to uppercase (eg: "David")
  • Output ...... the_phrase - uppercased phrase   (eg: "DAVID")
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE uppercase_Phrase (VAR the_phrase : Str255);
    VAR
      ch_index : 1..255; { phrase character index }
    BEGIN { ------------ uppercase_Phrase ------------ }
      IF LENGTH(the_phrase) > 0 THEN
        FOR ch_index := 1 TO LENGTH(the_phrase) DO
          BEGIN
            IF the_phrase[ch_index] IN ['a'..'z'] THEN
              the_phrase[ch_index] :=
                CHR( ORD(the_phrase[ch_index]) - ORD('a') + ORD('A') 
);
          END;
    END;  { ------------ uppercase_Phrase ------------ }
    
{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... parse_Command_Arguments
  • Purpose ..... Parse command arguments from shell command line
  • Input ....... the_arg_count    - number of arguments in line
  • Output ...... the_ci_args      - parsed argument info
  •               the_shell_result - parsing result error code
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE parse_Command_Arguments( the_arg_count : INTEGER; VAR the_ci_args 
: gt_ci_args; VAR the_shell_result : INTEGER);
    CONST
      k_arg_option_symbol = '-'; { option prefix character }
      
      { option characters }
      k_option_progress = 'P';   { option: show progress }
      k_option_start_id = 'S';   { option: starting ID value }
      k_option_dec_id   = 'D';   { option: decrement ID value }
      
    VAR
      arg_index   : INTEGER;  { argument list indexer }
      arg_phrase  : Str255;   { argument phrase from argument list }
      arg_value   : LONGINT;  { value of a numeric argument phrase }
      good_option : BOOLEAN;  { valid option found flag }
      
    BEGIN { --------- parse_Command_Arguments --------- }
      { initialize the command arguments }
      the_ci_args.ci_show_progress := FALSE;
      the_ci_args.ci_starting_id   := gc_default_starting_id;
      the_ci_args.ci_decrement_id  := FALSE;
      the_ci_args.ci_input_file    := '';
      the_ci_args.ci_output_file   := '';
      
      { scan the argument list extracting the arguments and }
      { performing error checking on all the read arguments }
      arg_index := 0;
      
      REPEAT
        BEGIN
          { get the next argument from the argument list }
          arg_index := arg_index + 1;
          
          IF arg_index < the_arg_count THEN
            BEGIN
              arg_phrase := ArgV^[arg_index]^; 
 { got an argument ! }
              
              { test if argument is an option (vs. file name) }
              IF arg_phrase[1] = k_arg_option_symbol THEN
                BEGIN
                  { ####################################### }
                  { ###          ARGUMENT: OPTION       ### }
                  { ####################################### }        
          
                  good_option := FALSE; 
 { assume option is invalid }
                  
                  DELETE(arg_phrase,1,1); 
 { remove option leading symbol }
                  
                  { check for an empty argument option }
                  IF LENGTH(arg_phrase) >= 1 THEN
                    BEGIN
                      uppercase_Phrase(arg_phrase);
                      { ####################### SHOW PROGESS }
                      IF arg_phrase = k_option_progress THEN
                        BEGIN
                          good_option                  := TRUE;
                          the_ci_args.ci_show_progress := TRUE;
                        END;
                      
                      { ################## STARTING ID VALUE }
                      IF (the_shell_result = NoErr) AND
                         (arg_phrase = k_option_start_id) THEN
                        BEGIN
                          good_option := TRUE;
                          arg_index := arg_index + 1;
                          
                          IF arg_index >= the_arg_count THEN
                            the_shell_result := gc_err_bad_starting_id 
 { ERROR }
                          ELSE
                            BEGIN
                              arg_phrase := ArgV^[arg_index]^;
 StringToNum(arg_phrase, arg_value);
                              IF (arg_value < -MAXINT-1) OR (arg_value 
> MAXINT) THEN
                                the_shell_result := gc_err_bad_starting_id 
 { ERROR }
                              ELSE
                                the_ci_args.ci_starting_id := LoWord(arg_value);
                            END;
                        END;
                      
                      { ################ DECREMENT ID VALUE }
                      IF (the_shell_result = NoErr) AND
                         (arg_phrase       = k_option_dec_id) THEN
                        BEGIN
                          good_option                 := TRUE;
                          the_ci_args.ci_decrement_id := TRUE;
                        END;
                    END;

                  { check that the found option is valid }
                  IF NOT(good_option) AND (the_shell_result = NoErr) 
THEN
                    the_shell_result := gc_err_bad_option;
                END
              ELSE 
 { a file name must be present if arg is not an option }
                BEGIN
                  { ######################################## }
                  { ###        ARGUMENT: NON-OPTION      ### }
                  { ######################################## }
                  { NOTE: extract INPUT file first, 
 OUTPUT file second }
                  IF the_ci_args.ci_input_file = '' THEN
                    the_ci_args.ci_input_file := arg_phrase
                  ELSE
                    BEGIN
                      IF the_ci_args.ci_output_file = '' THEN
                        the_ci_args.ci_output_file := arg_phrase
                      ELSE
                        BEGIN
                          the_shell_result := gc_err_extra_file_name;
                        END;
                    END;
                END;
            END;
        END;
      UNTIL (arg_index >= the_arg_count) OR (the_shell_result <> NoErr);
    
      { verify that the input file name is present and assign a default 
name to the output file name if the output file name is missing }
      IF the_shell_result = NoErr THEN
        BEGIN
          IF the_ci_args.ci_input_file = '' THEN
            the_shell_result := gc_err_no_input_file;
          
          IF the_shell_result = NoErr THEN
            BEGIN
              IF the_ci_args.ci_output_file = '' THEN
                the_ci_args.ci_output_file :=
                  CONCAT(the_ci_args.ci_input_file,gc_output_file_suffix);
            END;
        END;
    END;  { ---------- parse_Command_Arguments ---------- }
  
{ •••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... init_Shell_Stuff
  • Purpose ..... Initialize various items for the Shell and the tool
  • Input ....... (none)
  • Output ...... the_tool_name - name of Shell tool
  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE init_Shell_Stuff (VAR the_tool_name : Str255);
    BEGIN { ------------ init_Shell_Stuff ------------ }
      { fetch tool name from MPW Shell command line }
      the_tool_name := ArgV^[0]^;
      
      uppercase_Phrase(the_tool_name); 
 { UC tool name since UC is pretty }
      
      { enable text output line buffering by calling MPW Shell RT library 
}
      PLSetVBuf(OUTPUT,NIL,_IOLBF,0);
    END;  { ------------ init_Shell_Stuff ------------ }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... show_Elapsed_Time
  • Purpose ..... Write a line containing the elapsed time for the tool
  • Input ....... the_elapsed_sec_time - elapsed time in seconds
  • Output ...... (none)
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE show_Elapsed_Time (the_elapsed_sec_time : LONGINT);
    VAR
      elapsed_min : INTEGER; { no. elapsed minutes }
      elapsed_sec : INTEGER; { no. elapsed seconds }
    BEGIN { ------------ show_Elapsed_Time ------------ }
      the_elapsed_sec_time := ABS(the_elapsed_sec_time);
      
      elapsed_min := the_elapsed_sec_time DIV 60;
      elapsed_sec := the_elapsed_sec_time MOD 60;
      
      WRITE(elapsed_min:1,' ');
      
      IF elapsed_min <=1 THEN WRITE('minute ')
                         ELSE WRITE('minutes ');
      
      WRITE(elapsed_sec:1,' ');
      
      IF elapsed_sec <=1 THEN WRITELN('second')
                         ELSE WRITELN('seconds');
    END;  { ------------ show_Elapsed_Time ------------ }
                                 
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                        •
  •                     THE MAIN EVENT                     •
  •                                                        •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  BEGIN { -------- Macintosh_PICT_Scavenger_Tool -------- }
    gv_shell_result := NoErr;
 { initialize MPW shell result code }
    
    init_Shell_Stuff(gv_command_name);
    
    IF NOT(ArgC IN [2..6]) THEN
      BEGIN
        gv_shell_result := gc_err_bad_argc; { !!! ERROR !!! }
        show_Error(gv_shell_result);
      END
    ELSE { MPW Shell command line is valid, so parse the descriptor line 
}
      BEGIN
        { fetch the command arguments from the MPW Shell command line 
}
        
parse_Command_Arguments(ArgC,gv_ci_args,gv_shell_result);
        
        IF gv_shell_result <> NoErr THEN
          show_Error(gv_shell_result) { !!! ERROR !!! }
        ELSE
          BEGIN
          { tell the user a little about this wonderful tool }
            IF gv_ci_args.ci_show_progress THEN
              BEGIN
                WRITELN;
                WRITELN(gc_pgm_name,' ',gc_pgm_version, '  [', gc_pgm_date,' 
- ',gc_pgm_time,']');
                WRITELN(gc_pgm_author,' (',gc_pgm_address,')');
                WRITELN(gc_pgm_copyright);
                WRITELN;
                WRITELN('PICT Scavenger unit version: ',PICT_Unit_Version);
              END;
            
            { perform the actual PICT scavenging process by first setting 
up the picture information list and then calling the module which actually 
performs the scavenging process    }
            gv_pict_1_info.p1_version := $1101;
            gv_pict_1_info.p1_endpict := $FF00; {< ignore LSB }
                     
            gv_pict_2_info.p2_version := $001102FF;
            gv_pict_2_info.p2_endpict := $00FF;
                       
            gv_pict_info[1].hi_version_data := @gv_pict_1_info.p1_version;
            gv_pict_info[1].hi_version_data_length := 2;
            gv_pict_info[1].hi_endpict_data := @gv_pict_1_info.p1_endpict;
            gv_pict_info[1].hi_endpict_data_length := 1;
            
            gv_pict_info[2].hi_version_data := @gv_pict_2_info.p2_version;
            gv_pict_info[2].hi_version_data_length := 4;
            gv_pict_info[2].hi_endpict_data := @gv_pict_2_info.p2_endpict;
            gv_pict_info[2].hi_endpict_data_length := 2;
            
            gv_pict_info[3].hi_version_data := NIL; 
 { >>>>> UNUSED <<<<< }
            gv_pict_info[3].hi_version_data_length := 0;
            gv_pict_info[3].hi_endpict_data        := NIL;
            gv_pict_info[3].hi_endpict_data_length := 0;
            
            gv_pict_info[4] := gv_pict_info[3];
 { >>>>> UNUSED <<<<< }
            
            gv_exec_time := TickCount; 
 { start the execution timer }
            
            { ############################## go for it ... }
            PICT_Scavenger(CONCAT(gv_command_name,' ', gc_pgm_version),
               gv_ci_args.ci_input_file,0,
               gv_ci_args.ci_output_file,0,
               gv_ci_args.ci_show_progress,
               gv_pict_info, gv_ci_args.ci_starting_id,
               gv_ci_args.ci_decrement_id, gv_num_found_picts,
               gv_num_pict_failures, gv_shell_result);
            
            gv_exec_time := ABS(TickCount - gv_exec_time) DIV 60; { end 
the timer }
            
            IF gv_shell_result <> NoErr THEN
              show_Error(gv_shell_result) { !!! ERROR !!! }
            ELSE
              BEGIN
  { tell the user a little about the scavenging process }
                IF gv_ci_args.ci_show_progress THEN
                  BEGIN
                    WRITELN;
                    WRITELN('PICT Scavenger Summary:');
                    WRITELN;
                    WRITELN('  Input  file       : ',gv_ci_args.ci_input_file);
                    WRITELN('  Output file       : ',gv_ci_args.ci_output_file);
                    WRITELN('  Scavenged PICTs   : ',gv_num_found_picts:1);
                    WRITELN('  Scavenge failures : ',gv_num_pict_failures:1);
                    WRITE  ('  Elapsed time      : ');
                                 show_Elapsed_Time(gv_exec_time);
                    WRITELN;
                    
                    IF gv_num_pict_failures > 0 THEN
                      BEGIN
                        WRITELN(gc_fail_1);
                        WRITELN(gc_fail_2);
                        WRITELN(gc_fail_3);
                        WRITELN;
                      END;
                  END;
                
                { tell the user that I'm done (for now) }
                IF gv_ci_args.ci_show_progress THEN
                  BEGIN
                    WRITELN('That''s all, folks.');
                  END;
              END;
          END;
      END;
    
    IF (gv_shell_result >= gc_err_min) AND (gv_shell_result <= gc_err_max) 
THEN
      BEGIN
{ display the command line for this tool since the user does not appear 
to know how to use this command                   }
        
        WRITELN(gc_shell_comment,gv_command_name,
                ' [-p] [-s start_id] [-d] input_file [output_file]');
        WRITELN(gc_shell_comment,'  -p : show progress information');
        WRITE  (gc_shell_comment,'  -s : setup starting PICT resource 
ID value ');
        WRITELN(                         '[',-MAXINT-1:1,'..',MAXINT:1,']');
        WRITELN(gc_shell_comment,'  -d : decrement PICT resource ID value');
      END;
      
    IEExit(gv_shell_result); { tell MPW shell how I'm feeling }
  END.  { --------- Macintosh_PICT_Scavenger_Tool --------- }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                        •
  •                           FINIS                        •
  •                                                        •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

Listing:  U_PICT_Scavenger.p

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                        •
  •   --------------------------------------------------   •
  •   APPLE MACINTOSH PICT RESOURCE SCAVENGER MODULE       •
  •   --------------------------------------------------   •
  •                                                        •
  •               Version 1.1.0                            •
  •                                                        •
  • Author ......... David T. Craig                        •
  • Address ........ 736 Edgewater, Wichita, Kansas 67230  •
  • Date ........... August 1990                           •
  • Language ....... Apple MPW Pascal 3.0                  •
  • Computer ....... Apple Macintosh                       •
  • Environment .... Apple MPW Shell                       •
  •                                                        •
  • PURPOSE:                                               •
  •                                                        •
  •    This module scavenges PICT (picture) resources from a corrupted 
file.  Only the resource fork of a file is searched for picture resources 
since this is the only fork of a Macintosh file that should contain resources.
  •                                                        •
  •    This module produces as output a standard Macintosh text file 
containing the source listing for all the scavenged picture resources 
in a format compatible with Apple's Rez tool. 
  •    To produce a file with actual PICT resources requires compiling 
the text file with Rez.  Rez version 3.0 works well with the output files 
from this program.  One may also use Apple's stand-alone version of Rez 
which is named SARez.  SARez runs under the Finder and does not require 
the MPW development Shell. The output file also contains the acutal scavenged 
picture resources which may be viewed by a program like Apple's ResEdit.
  •                                                        •
  • USING THIS MODULE:                                     •
  •                                                        •
  •    This module contains two public routines.           •
  •                                                        •
  •    Routine PICT_Unit_Version returns a string representing the version 
number and compilation date and time of this module.
  •                                                        •
  •    Routine PICT_Scavenger performs the actual scavenging process. 
   •
  •    Callers should provide it with the name of the corrupted file 
and the name of the output text file to contain the picture source code 
in a format for Rez.                                  

  ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                        •
  •        NOT COPYRIGHT (C) 1990 BY DAVID T. CRAIG        •
  •                                                        •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

UNIT U_PICT_Scavenger;
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }
                                  INTERFACE
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                    EXTERNAL MODULES                    •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  USES
    MemTypes,   { Macintosh common types }
    OSIntf,     { Macintosh Operating System interface }
    ToolIntf,   { Macintosh ToolBox interface }
    Packages,   { Macintosh Package interface }
    PasLibIntf, { Pascal runtime libary interface }
    IntEnv;     { MPW integrated environment interface }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                  COMPILER DIRECTIVES                   •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  {$R+ }  { [MPWPASCAL] enable range checking }
  {$OV+}  { [MPWPASCAL] enable overflow checking }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                GLOBAL MODULE CONSTANTS                 •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  CONST
    PS_c_MaxPICTVersions = 4; 
 { max no. picture versions supported }
    
    PS_c_Err_Minimum = 5000;
 { minimum error value from this module }
    PS_c_Err_Maximum = 5002;
 { maximum error value from this module }
    
    PS_c_Err_PICT_Header_Ptr = 5000;
 { picture list pointer is NIL }
    PS_c_Err_PICT_Header_Length = 5001;
 { picture list length is invalid }
    PS_c_Err_Internal = 5002;
 { internal error (big time trouble) }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                GLOBAL MODULE TYPES                     •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  TYPE
  { pict vers. header pattern info record for HEADER & FOOTER }
    PS_t_PICT_Header_Info = RECORD
      hi_version_data        : Ptr;     { header info }
      hi_version_data_length : INTEGER;
      hi_endpict_data        : Ptr;
      hi_endpict_data_length : INTEGER; { footer info }
                            END;
      { list of picture version pattern headers (currently a fixed size) 
}
      
    PS_t_PHI_List = ARRAY [1..PS_c_MaxPICTVersions] OF PS_t_PICT_Header_Info;
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... PICT_Unit_Version
  • Purpose ..... Return version information about this module as a phrase
  • Input ....... (none)
  • Output ...... PICT_Unit_Version - version info phrase
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  FUNCTION PICT_Unit_Version : Str255;
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... PICT_Scavenger
  • Purpose ..... Scavenge all the picture (PICT) resources found in 
a file and write the picture data to a text file containing the picture 
data as PICT resources and Rez source code
  • Input .. ps_program_name - name of program (for text file)
  •   ps_input_file_name    - corrupted input file to scavenge
  •   ps_input_file_volume  - input file volume reference
  •   ps_output_file_name   - text output file for Rez source
  •   ps_output_file_volume - output file volume reference
  •   ps_show_progress      - show scavenging progress flag
  •   ps_pict_header_info   - picture header/tail info
  •   ps_starting_id - picture resource starting ID value
  •   ps_decrement_id       - decrement resource ID flag
  • Output..ps_num_found_picts - # pictures found in input file
  •   ps_num_pict_failures  - no. picture scavenge failures
  •   ps_error              - error result
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

  PROCEDURE PICT_Scavenger (ps_program_name : Str255;
    ps_input_file_name : Str255;
    ps_input_file_volume : INTEGER;
    ps_output_file_name : Str255;
    ps_output_file_volume : INTEGER;
    ps_show_progress : BOOLEAN;
    ps_pict_header_info : PS_t_PHI_List;
    ps_starting_id : INTEGER;
    ps_decrement_id       : BOOLEAN;
    VAR ps_num_found_picts    : INTEGER;
    VAR ps_num_pict_failures  : INTEGER;
    VAR ps_error              : INTEGER);
                               
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }
                              IMPLEMENTATION
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }
{ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% }

{$S SgPICTScavenger} { segment this guy }
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                GLOBAL PRIVATE CONSTANTS                •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  CONST
    pc_unit_version    = '1.1.0';   { unit version info }
    pc_max_data_length = 32; { max length of PICT header data or endpict 
data }
    pc_cursor_info_sig = $31415926; { signature for cursor info record 
}
    pc_max_cursor      = 4;         { no. spinning cursors }
    pc_debug = FALSE; { internal debugging flag for MPW Shell }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                GLOBAL PRIVATE TYPES                    •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  TYPE
 { info about the output file that holds picture resources }
    pt_file_info      = RECORD
                          fi_file_name   : Str255;
                          fi_file_volume : INTEGER;
                        END;
                        
 { PICT resource HEADER info as contained in a resource file }
    pt_pict_header    = RECORD
      phd_length_long : LONGINT; {[4] true length of res data }
      phd_length_word : INTEGER; {[2] low word of len value }
      phd_frame       : Rect;    { [8] PICT bounding box }
                        END;
  
    pt_hex_byte       = STRING[2]; 
 { hexadecimal strings for dec->hex conversion }
    pt_hex_word       = STRING[4];
    pt_hex_long       = STRING[8];
    
      { busy cursor stuff }
    pt_cursor_data    = ARRAY [0..pc_max_cursor-1] OF Cursor;
    
    pt_cursor_info    = RECORD
      ci_signature : LONGINT;        { init signature value }
      ci_count     : INTEGER;        { spin count }
      ci_cursor    : pt_cursor_data; { cursor data list }
                        END;
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                GLOBAL PRIVATE VARIABLES                •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  VAR
    pv_file_info : pt_file_info; { output file information }
    pv_show_progress : BOOLEAN; { global "show progress" flag }
    pv_cursor_info : pt_cursor_info; { busy cursor info }
    pv_debug : BOOLEAN; { internal debugging flag for MPW }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                        •
  •                 MODULE PRIVATE ROUTINES                •
  •                                                        •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... sbyte_2_USByte
  • Purpose ..... Convert signed byte (-128..127) to unsigned byte (0..255)
  • Input ....... the_sbyte
  • Output ...... sbyte_2_USByte
  • Notes ....... This routine is needed since a Macintosh Ptr type points 
to a signed byte value (-128..127) and this module favors highly unsigned 
bytes (0..255).
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  FUNCTION sbyte_2_USByte (the_sbyte : SignedByte) : CHAR;
    BEGIN { ------------ sbyte_2_USByte ------------ }
      IF the_sbyte < 0 THEN
        sbyte_2_USByte := CHR(the_sbyte + 256)
      ELSE
        sbyte_2_USByte := CHR(the_sbyte);
    END;  { ------------ sbyte_2_USByte ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... my_Rotate_Cursor
  • Purpose ..... Rotate the spinning progress cursor one rotation
  • Input ....... (none)
  • Output ...... (none)
  • Notes ....... If the cursor data has not be initialized, this routine 
automatically initializes the cursor data.  The cursor data is defined 
in this routine and not in a resource.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE my_Rotate_Cursor;
    BEGIN { ------------ my_Rotate_Cursor ------------ }
      IF pv_cursor_info.ci_signature <> pc_cursor_info_sig THEN
        BEGIN
          pv_cursor_info.ci_signature := pc_cursor_info_sig;
          
          pv_cursor_info.ci_count := 0;
          
            { use the Apple standard BEACH BALL cursor }
          WITH pv_cursor_info.ci_cursor[0] DO
            BEGIN
StuffHex(@Data,CONCAT('07C01F303F087F047F04FF02FF02FFFE',
'81FE81FE41FC41FC21F819F007C00000'));
              StuffHex(@Mask,CONCAT('07C01FF03FF87FFC7FFCFFFEFFFEFFFE',
'FFFEFFFE7FFC7FFC3FF81FF007C00000'));
              SetPt(HotSpot,8,8);
            END;
    
          WITH pv_cursor_info.ci_cursor[1] DO
            BEGIN
StuffHex(@Data,CONCAT('07C01FF03FF85FF44FE487C283828102',
'838287C24FE45FF43FF81FF007C00000'));
StuffHex(@Mask,CONCAT('07C01FF03FF87FFC7FFCFFFEFFFEFFFE',
'FFFEFFFE7FFC7FFC3FF81FF007C00000'));
              SetPt(HotSpot,8,8);
            END;
    
          WITH pv_cursor_info.ci_cursor[2] DO
            BEGIN
StuffHex(@Data,CONCAT('07C019F021F841FC41FC81FE81FEFFFE',
'FF02FF027F047F043F081F3007C00000'));
              StuffHex(@Mask,CONCAT('07C01FF03FF87FFC7FFCFFFEFFFEFFFE',
'FFFEFFFE7FFC7FFC3FF81FF007C00000'));
              SetPt(HotSpot,8,8);
            END;
            
          WITH pv_cursor_info.ci_cursor[3] DO
            BEGIN
StuffHex(@Data,CONCAT('07C018302008701C783CFC7EFEFEFFFE',
'FEFEFC7E783C701C2008183007C00000'));
              StuffHex(@Mask,CONCAT('07C01FF03FF87FFC7FFCFFFEFFFEFFFE',
'FFFEFFFE7FFC7FFC3FF81FF007C00000'));
              SetPt(HotSpot,8,8);
            END;
        END;
      
      WITH pv_cursor_info DO
        BEGIN
          IF ci_count = MAXINT THEN
            ci_count := 0
          ELSE
            ci_count := ci_count + 1;
          
          SetCursor(ci_cursor[ci_count MOD pc_max_cursor]);
        END; { WITH pv_cursor_info }
    END;  { ------------ my_Rotate_Cursor ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... show_Progress_Message
  • Purpose ..... Display progress information to the caller
  • Input ....... the_message - progress message to display
  • Output ...... (none)
  • Notes ....... Currently, progress information can only go to the 
MPW Shell.  As such, if the shell is not active, then no progress information 
will be sent.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE show_Progress_Message (the_message : Str255);
    BEGIN { ------------ show_Progress_Message ------------ }
      IF pv_show_progress THEN { caller s wnats progress and Shell exists 
}
        BEGIN
          WRITELN(the_message);
        END;
    END;  { ------------ show_Progress_Message ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... myWrite
  • Purpose ..... Write a phrase to an open text file (no CR on end)
  • Input ....... the_file_ref - open text file reference
  •               the_message  - phrase to write to file
  • Output ...... the_error    - error result
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE myWrite (    the_file_ref : INTEGER;
                         the_message  : Str255;
                     VAR the_error    : INTEGER);
    VAR
      byte_count : LONGINT; { no. bytes to write to file }
    BEGIN { ------------ myWrite ------------ }
      IF the_message <> '' THEN
        BEGIN
          byte_count := LENGTH(the_message);
          the_error  := FSWrite(the_file_ref,
                 byte_count, Ptr( ORD4(@the_message) + 1) );
        END;
    END;  { ------------ myWrite ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... myWriteLn
  • Purpose ..... Write a phrase to an open text file (with a CR on end)
  • Input ....... the_file_ref - open text file reference
  •               the_message  - phrase to write to file
  • Output ...... the_error    - error result
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE myWriteLn (    the_file_ref : INTEGER;
                           the_message  : Str255;
                       VAR the_error    : INTEGER);
    CONST
      k_ascii_cr = CHR(13); { message terminator (good old ASCII CR) 
}
    VAR
      byte_count : LONGINT; { no. bytes to write to file }
    BEGIN { ------------ myWriteLn ------------ }
      the_error := NoErr; { assume all will go well }
      
      { write the message to the file }
      IF the_message <> '' THEN
        BEGIN
          byte_count := LENGTH(the_message);
          the_error  := FSWrite(the_file_ref,
             byte_count, Ptr( ORD4(@the_message) + 1) );
        END;
      
      { write the message terminator to the file also }
      IF the_error = NoErr THEN
        BEGIN
          the_message    := '?';
          the_message[1] := k_ascii_cr;
          
          byte_count := LENGTH(the_message);
          the_error  := FSWrite(the_file_ref,
              byte_count, Ptr( ORD4(@the_message) + 1) );
        END;
    END;  { ------------ myWriteLn ------------ }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... get_System_DateTime
  • Purpose ..... Return the current system date and time as a phrase
  • Input ....... (none)
  • Output ...... the_date_and_time - date and time phrase
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE get_System_DateTime (VAR the_date_and_time : Str255);
    VAR
      mac_clock : LONGINT; { raw Macintosh clock info }
      mac_date  : Str255;  { Macintosh date phrase }
      mac_time  : Str255;  { Macintosh time phrase }
    BEGIN { ------------ get_System_DateTime ------------ }
      GetDateTime(mac_clock);
      IUDateString(mac_clock,LongDate,mac_date);
      IUTimeString(mac_clock,TRUE{WantSeconds},mac_time);
      the_date_and_time := CONCAT(mac_date,' -- ',mac_time);
    END;  { ------------ get_System_DateTime ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... byte2Hex
  • Purpose ..... Convert an unsigned BYTE value to a hex string
  • Input ....... the_byte - value to convert
  • Output ...... the_hex  - hex string equivalent to value
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE byte2Hex (the_byte : CHAR; VAR the_hex : pt_hex_byte);
    VAR
      hex_digits : STRING[16]; { list of hex digits $0..$F }
    BEGIN { ------------ byte2Hex ------------ }
      hex_digits := '0123456789ABCDEF';
      the_hex := '??';
      the_hex[1] := hex_digits[(ORD(the_byte) DIV 16) + 1]; 
 { msb }
      the_hex[2] := hex_digits[(ORD(the_byte) MOD 16) + 1]; 
 { lsb }
    END;  { ------------ byte2Hex ------------ }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... word2Hex
  • Purpose ..... Convert an unsigned WORD value to a hex string
  • Input ....... the_word - value to convert
  • Output ...... the_hex  - hex string equivalent to value
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE word2Hex (the_word : INTEGER; VAR the_hex : pt_hex_word);
    TYPE
      t_trix = PACKED ARRAY [0..1] OF CHAR; { [0] = msb, [1] = lsb }
      
    VAR
      trix     : t_trix;      { word to byte converter }
      hex_byte : pt_hex_byte; { byte as a hex string }
    BEGIN { ------------ word2Hex ------------ }
      the_hex := '????';
      
      trix := t_trix(the_word);
      
      byte2Hex(trix[0],hex_byte); { msb }
      
      the_hex[1] := hex_byte[1];
      the_hex[2] := hex_byte[2];
      
      byte2Hex(trix[1],hex_byte); { lsb }
      
      the_hex[3] := hex_byte[1];
      the_hex[4] := hex_byte[2];
    END;  { ------------ word2Hex ------------ }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... long2Hex
  • Purpose ..... Convert an unsigned LONG value to a hex string
  • Input ....... the_long - value to convert
  • Output ...... the_hex  - hex string equivalent to value
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE long2Hex (the_long : LONGINT; VAR the_hex : pt_hex_long);
    TYPE
      t_trix = PACKED ARRAY [0..1] OF INTEGER; { [0] = msw, [1] = lsw 
}
    VAR
      trix     : t_trix;      { word to byte converter }
      hex_word : pt_hex_word; { word as a hex string }
    BEGIN { ------------ long2Hex ------------ }
      the_hex := '????????';
      trix := t_trix(the_long);
      word2Hex(trix[0],hex_word); { msw }
      the_hex[1] := hex_word[1];
      the_hex[2] := hex_word[2];
      the_hex[3] := hex_word[3];
      the_hex[4] := hex_word[4];
      
      word2Hex(trix[1],hex_word); { lsw }
      the_hex[5] := hex_word[1];
      the_hex[6] := hex_word[2];
      the_hex[7] := hex_word[3];
      the_hex[8] := hex_word[4];
    END;  { ------------ long2Hex ------------ }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... get_Hex_Data
  • Purpose ..... Convert the bytes in a buffer to a hex string
  • Input ....... the_data        - pointer to data to convert
  •               the_data_length - length of data in bytes
  • Output ...... the_hex_data    - hex string equivalent to the data 
bytes
  • Notes ....... If the data contains too many bytes, then only those 
bytes that fit in the output string are converted.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE get_Hex_Data (    the_data        : Ptr;
                              the_data_length : INTEGER;
                          VAR the_hex_data    : Str255);
    VAR
      data_offset : LONGINT;      { data byte offset }
      data_ptr    : Ptr;   { pointer to a single data byte }
      data_char   : CHAR;         { data character }
      data_hex    : pt_hex_byte;
 { data character as a hex string }
    BEGIN { ------------ get_Hex_Data ------------ }
      the_hex_data := ''; { start from scratch }
      
      IF (the_data <> NIL) AND (the_data_length > 0) THEN
        BEGIN
          IF the_data_length >= ((SIZEOF(the_hex_data) - 2) DIV 2) THEN
            the_data_length := ((SIZEOF(the_hex_data) - 2) DIV 2);
          
          FOR data_offset := 0 TO (the_data_length - 1) DO
            BEGIN
              data_ptr := Ptr( ORD4(the_data) + data_offset );
              data_char := sbyte_2_USByte(data_ptr^);
              byte2Hex(data_char,data_hex);
              the_hex_data := CONCAT(the_hex_data,data_hex);
            END; { FOR data_offset }
        END;
    END;  { ------------ get_Hex_Data ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... read_File_Data
  • Purpose ..... Read data from a file given the data offset and length
  • Input ....... the_file_ref     - file to read reference
  •               the_file_address - offset of data to read (0 based)
  •               the_data_length  - byte length of data to read
  •               the_buffer       - buffer to hold the read data
  • Output ...... the_error        - error result
  • Note ........ The buffer for the data MUST be at least as large as
  •               the desired length of the file data.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE read_File_Data (    the_file_ref     : INTEGER;
                                the_file_address : LONGINT;
                                the_data_length  : LONGINT;
                                the_buffer       : Ptr;
                            VAR the_error        : INTEGER);
    BEGIN { ------------ read_File_Data ------------ }
      the_error := NoErr;
      
      the_error := SetFPos(the_file_ref,FSFromStart,the_file_address);
      
      IF the_error = NoErr THEN
        BEGIN
          the_error := FSRead(the_file_ref,the_data_length,the_buffer);
        END;
    END;  { ------------ read_File_Data ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... header_Info_is_Valid
  • Purpose ..... Test if a PICT header/footer pattern contains valid 
data
  • Input ....... the_head_info        - picture info to test
  • Output ...... header_Info_is_Valid - True --> data is valid
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  FUNCTION header_Info_is_Valid (the_head_info : PS_t_PICT_Header_Info) 
: BOOLEAN;
    VAR
      good_header : BOOLEAN; { good data flag }
    BEGIN { ------------ header_Info_is_Valid ------------ }
      good_header := TRUE; { assume all will go well }
      
      WITH the_head_info DO
        BEGIN
          IF hi_version_data = NIL THEN
            good_header := FALSE
          ELSE
            BEGIN
              IF (hi_version_data_length < 1 ) OR
                 (hi_version_data_length > pc_max_data_length) THEN
                good_header := FALSE
              ELSE
                BEGIN
                  IF hi_endpict_data = NIL THEN
                    good_header := FALSE
                  ELSE
                    BEGIN
                      IF (hi_endpict_data_length < 1                 
) OR (hi_endpict_data_length > pc_max_data_length) THEN
                        good_header := FALSE;
                    END;
                END;
            END;
        END; { WITH the_head_info }
      
      header_Info_is_Valid := good_header; { return data status to caller 
}
    END;  { ------------ header_Info_is_Valid ------------ }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... verify_Header_Info
  • Purpose ..... Verify that a picture info list is valid
  • Input ....... the_header_list - picture info list to verify
  • Output ...... the_error       - error result
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE verify_Header_Info_List ( the_header_list : PS_t_PHI_List; 
VAR the_error : INTEGER);
    VAR
      list_index : INTEGER; { picture info list index (1+) }
    BEGIN { --------- verify_Header_Info_List --------- }
      the_error := NoErr;
      
      IF NOT( header_Info_is_Valid(the_header_list[1]) ) THEN
        BEGIN
          the_error := PS_c_Err_PICT_Header_Ptr;
        END
      ELSE
        BEGIN
          list_index := 1; { start at the first list record }
          
          REPEAT
            BEGIN
              list_index := list_index + 1;
              
              IF list_index <= PS_c_MaxPICTVersions THEN
                IF the_header_list[list_index].hi_version_data <> NIL 
THEN
                  IF NOT(header_Info_is_Valid(the_header_list[list_index])) 
THEN
                    the_error := PS_c_Err_PICT_Header_Ptr;
            END;
          UNTIL (list_index >= PS_c_MaxPICTVersions) OR (the_error <> 
NoErr);
        END;
    END;  { ---------- verify_Header_Info_List --------- }
  
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... buffers_Are_Equal
  • Purpose ..... Compare two data buffers for data equality
  • Input ....... buffer_a - pointer to 1st buffer to compare
  •               buffer_b - pointer to 2nd buffer to compare
  •               buffer_length - byte length of both buffers
  • Output ...... buffers_Are_Equal - True --> buffer data is equal
  • Notes ....... Buffer length can not exceed 32k bytes.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  FUNCTION buffers_Are_Equal (buffer_a      : Ptr;
                       buffer_b      : Ptr;
                       buffer_length : INTEGER) : BOOLEAN;
    VAR
      they_are_equal : BOOLEAN; { buffer equality flag }
      offset         : INTEGER; { buffer byte offset }
    BEGIN { ------------ buffers_Are_Equal ------------ }
      they_are_equal := TRUE; { assume equality }
      
      IF buffer_length <= 0 THEN
        they_are_equal := FALSE
      ELSE
        FOR offset := 0 TO (buffer_length - 1) DO
          BEGIN
            IF Ptr(ORD4(buffer_a) + offset)^ <> Ptr(ORD4(buffer_b) + 
offset)^ THEN
              they_are_equal := FALSE;
          END;
      
      buffers_Are_Equal := they_are_equal;
    END;  { ------------ buffers_Are_Equal ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... dump_Hex_Buffer
  • Purpose ..... Dump the byte data within a buffer to a text file as 
a stream off hexadecimal phrases compatible with Apple's Rez tool
  • Input ....... the_buffer_data - buffer data to dump
  •               the_file_ref    - reference of text file to write to
  • Output ...... the_error       - error result
  • Notes ....... Buffer data is dumped in a line-by-line fashion prefaced
  •               by '$"' and terminated by '"'.
  • Example ..... $"11018B0105"
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE  dump_Hex_Buffer (the_buffer_data : Handle;
                                  the_file_ref    : INTEGER;
                              VAR the_error       : INTEGER);
    CONST
      k_bytes_per_line = 40; { no. bytes to write per PICT rez file line 
}
      
      k_line_prefix = '$"'; { prefix phrase for a line of data (Rez) 
}
      k_line_suffix =  '"'; { suffix phrase for a line of data (Rez) 
}
    VAR
      buffer_length : Size; { length of buffer in bytes }
      buffer_offset : LONGINT; { offset within buffer during dump }
      buffer_ptr : Ptr; { pointer to a buffer byte }
      buffer_byte : CHAR; { byte from the buffer }
      num_whole_lines : INTEGER; { no. whole data lines to write }
      bytes_in_last_line : INTEGER; { no. remaining bytes in last line 
}
      line_index : INTEGER; { rez file pict data line indexer }
      hex : pt_hex_byte; { phrase holding a hex byte string }
    BEGIN { ------------ dump_Hex_Buffer ------------ }
      the_error := NoErr; { assume all will go well }
      
      IF the_buffer_data <> NIL THEN
        BEGIN
            { determine the byte length of the buffer data }
          buffer_length := GetHandleSize(the_buffer_data);
          
      { determine how many complete lines will be needed and }
      { how many remaining buffer bytes will be left over    }
          num_whole_lines    := buffer_length DIV k_bytes_per_line;
          bytes_in_last_line := buffer_length MOD k_bytes_per_line;
          
          HLock(the_buffer_data);
          
            { dump the buffer data on a line-by-line basis }
          FOR line_index := 0 TO (num_whole_lines - 1) DO
            BEGIN
              IF (line_index MOD 2) = 0 THEN my_Rotate_Cursor;
         { write the line prefix for use by Apple's Rez tool }
              myWrite(the_file_ref,k_line_prefix,the_error);
              
              IF the_error = NoErr THEN
                BEGIN
                  FOR buffer_offset := 0 TO (k_bytes_per_line - 1) DO
                    BEGIN
                      IF the_error = NoErr THEN
                        BEGIN
                          buffer_ptr := Ptr( ORD4(the_buffer_data^) + 
ORD4(line_index) * k_bytes_per_line + buffer_offset );
                          
     { convert signed byte -128..127 to unsigned byte 0..255 }
                          buffer_byte := sbyte_2_USByte(buffer_ptr^);
                          byte2Hex(buffer_byte,hex);
                          myWrite(the_file_ref,hex,the_error);
                        END;
                    END; { FOR buffer_offset }
                END;
              
         { write the line suffix for use by Apple's Rez tool }
              myWriteLn(the_file_ref,k_line_suffix,the_error);
            END; { FOR line_index }
          
            { dump the remaining buffer bytes after all the lines are 
dumped }
          IF (the_error = NoErr) AND (bytes_in_last_line > 0) THEN
            BEGIN
              myWrite(the_file_ref,k_line_prefix,the_error);
              
              FOR buffer_offset := 0 TO (bytes_in_last_line - 1) DO
                BEGIN
                  IF the_error = NoErr THEN
                    BEGIN
                      buffer_ptr := Ptr( ORD4(the_buffer_data^) + ORD4(num_whole_lines) 
* k_bytes_per_line + buffer_offset );
                      
{ convert signed byte -128..127 to unsigned byte 0..255 }
                      buffer_byte := sbyte_2_USByte(buffer_ptr^);
                      byte2Hex(buffer_byte,hex);
                      myWrite(the_file_ref,hex,the_error);
                    END;
                END; { FOR buffer_offset }
              
              myWriteLn(the_file_ref,k_line_suffix,the_error);
            END;
            
          HUnLock(the_buffer_data);
        END;
    END;  { ------------ dump_Hex_Buffer ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... get_Next_File_Match
  • Purpose ..... Attempt to locate a data match within a file
  • Input ....... the_file_ref      - reference of file to search
  •               the_address       - starting address of search (0+)
  •               the_search_data   - data to search for in the file
  •               the_search_length - length of data to search for
  • Output ...... the_address       - address of the next data match
  •               match_found       - data match found in the file flag
  •               the_eof_found     - end-of-file found flag
  •               the_error         - error result
  • Notes ....... The search data may not exceed a certain length defined 
by the global module private constant pc_max_data_length.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE get_Next_File_Match (the_file_ref: INTEGER;{ i }
              VAR the_address       : LONGINT;  { b }
              the_search_data   : Ptr;      { i }
               the_search_length : INTEGER;  { i }
               VAR match_found       : BOOLEAN;  { o }
               VAR the_eof_found     : BOOLEAN;  { o }
               VAR the_error         : INTEGER); { o }
    VAR
      match_data : Ptr; { extracted file data to match against search 
data }
    BEGIN { ------------ get_Next_File_Match ------------ }
        { setup the output parms }
      the_error     := NoErr;
      match_found   := FALSE;
      the_eof_found := FALSE;
      
     { verify that the input data is valid before preceding }
      IF (the_search_data   =  NIL               ) OR
         (the_address       <  0                 ) OR
         (the_search_length <= 0                 ) OR
         (the_search_length >  pc_max_data_length) THEN
        BEGIN
          the_error := PS_c_Err_Internal;  { !!! ERROR !!! }
        END
      ELSE
        BEGIN
{ create an internal buffer used to hold the file data for }
{ comparison purposes against the search data              }
          match_data := NewPtr(the_search_length);
          the_error  := MemError;
          
          IF the_error = NoErr THEN
            BEGIN
{ scan the data file extracting sequences of bytes of the }
{ same length as the search data until either a match is  }
{ found or the end of the data file is encountered        }
              REPEAT
                BEGIN
                  IF (the_address MOD 200) = 0 THEN my_Rotate_Cursor;
                    
                    { read the next file data to match }
                  read_File_Data (the_file_ref,
                      the_address, the_search_length,
                      match_data, the_error);
                  
                  IF the_error = EOFErr THEN
                    BEGIN
                      the_error     := NoErr; { end-of-file found !!! 
}
                      the_eof_found := TRUE;
                    END
                  ELSE
                    BEGIN
{ compare the extracted data against the search data }
                      IF the_error = NoErr THEN
                        BEGIN
                          IF buffers_Are_Equal (the_search_data, match_data,the_search_length) 
THEN
                            BEGIN
                              match_found := TRUE; { match found !!! 
}
                            END
                          ELSE
                            the_address := the_address + 1; 
 { no match, try again }
                        END;
                    END;
                END;
              UNTIL match_found OR the_eof_found OR (the_error <> NoErr);
                
              DisposPtr(match_data); { don't need this guy }
            END;
        END;
    END;  { ------------ get_Next_File_Match ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... get_Next_PICT
  • Purpose ..... Locate & extract the next valid picture data from a 
file
  • Input ....... the_ifile_ref      - reference of the file to search
  •               the_ofile_ref      - reference of the output text Rez 
file
  •               the_search_address - startiong address in file of the 
search
  •               the_pict_info      - picture info to search for
  • Output ...... the_search_address - address of the found next picture
  •               the_eof_found      - end-of-file found flag
  •               the_pict_data      - found picture data buffer
  •               the_pict_header    - found picture data header info
  •               the_pict_failures  - no. PICTs which are not valid 
PICTs
  •               the_ps_error       - error result
  • Notes ....... Input parm the_ofile_ref is only used to write progress 
information to the output text file in case a partial picture data match 
is found.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE get_Next_PICT (the_ifile_ref: INTEGER; { i }
                the_ofile_ref      : INTEGER; { i }
                VAR the_search_address : LONGINT; { b }
                the_pict_info : PS_t_PICT_Header_Info; { i }
                VAR the_eof_found      : BOOLEAN; { o }
                VAR the_pict_data      : Handle; { o }
                VAR the_pict_header : pt_pict_header; { o }
                VAR the_pict_failures  : INTEGER; { o }
                VAR the_ps_error       : INTEGER); { o }
    VAR
      match_found : BOOLEAN; { data match found in file flag }
      header_address : LONGINT; { address of found picture hdr}
      pict_data_length : LONGINT; { byte length of found picture data 
}
      pict_end_buffer  : Ptr; { pointer to the end of the picture buffer 
}
      pict_end_address : LONGINT; { address of the end of the picture 
data }
      hex : pt_hex_long; { hexadecimal address phrase }
      fail_message : Str255; { failure message for progress }
      fail_message_rez : Str255; {failure message for Rez file}
      wr_error : INTEGER; { progress write file error result }
    BEGIN { ------------ get_Next_PICT ------------ }
        { setup the output parms }
      the_ps_error      := NoErr;
      the_eof_found     := FALSE;
      the_pict_data     := NIL;
      the_pict_failures := 0;
      
{ search the input file looking for matches with the Picture Header Pattern 
and when a match is found verify that the found data really represents 
a picture by verifying the found picture’s resource file data header 
and verifying that the picture data terminates in the correct Picture 
Tail Pattern }
      REPEAT
        BEGIN
            { find the next Picture Header Pattern }
          get_Next_File_Match (the_ifile_ref,
                               the_search_address,
                               the_pict_info.hi_version_data,
                               the_pict_info.hi_version_data_length,
           match_found, the_eof_found, the_ps_error);
                    
{ make certain a PICT was really found (check pict length fields) }
          IF (the_ps_error = NoErr) AND match_found THEN
            BEGIN
{ setup the PICT failure message in case the PICT is invalid }
              long2Hex(the_search_address,hex);
              IF (hex[1] = ‘0’) AND (hex[2] = ‘0’) THEN
                DELETE(hex,1,2);
              fail_message :=
                CONCAT(‘*** PICT scavenge failure  [Address $’,hex,’]’);
              
              fail_message_rez    := CONCAT(‘?/* ‘,fail_message,’ */’);
              fail_message_rez[1] := CHR(13);
              fail_message := CONCAT(‘  ‘,fail_message);
              
              header_address := the_search_address - SIZEOF(pt_pict_header);
              
              { verify that the header address is valid }
              IF header_address <= 0 THEN
                BEGIN
                  show_Progress_Message(fail_message);
                  
                  match_found        := FALSE;
                  the_search_address := the_search_address + 1;
                  the_pict_failures  := the_pict_failures + 1;
                  
                  myWriteLn(the_ofile_ref,fail_message_rez,wr_error);
                END
              ELSE
                BEGIN
{ pciture header info is valid, so read picture header data }
                  read_File_Data (the_ifile_ref,
                    header_address, SIZEOF(the_pict_header),
                    @the_pict_header, the_ps_error);
                  
                  IF the_ps_error = NoErr THEN
                    BEGIN
{ verify that the picture header is valid }
                      WITH the_pict_header DO
                        BEGIN
                          IF phd_length_word <> LoWord(phd_length_long) 
THEN
                            BEGIN
                              match_found        := FALSE;
                              the_search_address := the_search_address 
+ 1;
                              the_pict_failures  := the_pict_failures 
+ 1;
                              
                              myWriteLn(the_ofile_ref,fail_message_rez,wr_error);
                              
                              show_Progress_Message(fail_message);
                            END
                          ELSE
                            BEGIN
                              WITH phd_frame DO
                                IF (Left > Right) OR (Top > Bottom) THEN
                                  BEGIN
                                    match_found := FALSE;
                                    the_search_address := the_search_address 
+ 1;
                                    the_pict_failures  := the_pict_failures 
+ 1;
                                    
                                    myWriteLn(the_ofile_ref,fail_message_rez,wr_error);
show_Progress_Message(fail_message);
                                  END;
                            END;
                        END; { WITH the_pict_header }
                      
{ by now the picture header has been verified and now it }
{ is time to read and verify the picture’s ending data   }
{ against the Picture Tail Pattern data                  }
                      IF match_found THEN
                        BEGIN
                          pict_data_length := the_pict_header.phd_length_long 
- SIZEOF(pt_pict_header) + 4;
                          
{ verify existance of END-OF-PICTURE opcode }
                          WITH the_pict_info DO
                            BEGIN
                              pict_end_buffer := NewPtr(hi_endpict_data_length);
                              the_ps_error    := MemError;
                              
                              IF the_ps_error = NoErr THEN
                                BEGIN
                                  pict_end_address := the_search_address 
+ pict_data_length;
                                  pict_end_address := pict_end_address 
  - hi_endpict_data_length;
                                  
                                  read_File_Data (the_ifile_ref, pict_end_address, 
hi_endpict_data_length, pict_end_buffer, the_ps_error);

                                  IF the_ps_error = NoErr THEN
                                    BEGIN
                                      IF NOT(buffers_Are_Equal
(hi_endpict_data, pict_end_buffer, hi_endpict_data_length)) THEN
                                        BEGIN
                                          match_found := FALSE;
                                          the_search_address := the_search_address 
+ 1;
                                          the_pict_failures  := the_pict_failures 
+ 1;
                                          
myWriteLn(the_ofile_ref,fail_message_rez, wr_error);
show_Progress_Message(fail_message);
                                        END;
                                    END;
                                    
                                  DisposPtr(pict_end_buffer);
                                END;
                            END; { WITH the_pict_info }
                          
{ picture header data and picture tail data are valid }
{ so a picture has been found so read the actual raw  }
{ picture data into the output picture data buffer    }
                          IF (the_ps_error = NoErr) AND match_found THEN
                            BEGIN
                              the_pict_data := NewHandle(pict_data_length);
                              the_ps_error  := MemError;
                              
                              IF (the_ps_error  =  NoErr) AND
                                 (the_pict_data <> NIL  ) THEN
                                BEGIN
                                  HLock(the_pict_data);
                                  
                                  read_File_Data (the_ifile_ref, the_search_address, 
pict_data_length, the_pict_data^, the_ps_error);
                                  
                                  HUnLock(the_pict_data);
                                END;
                            END;
                        END;
                    END;
                END;
            END;
        END;
      UNTIL match_found OR the_eof_found OR (the_ps_error <> NoErr);
    END;  { ------------ get_Next_PICT ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... dump_PICT_Data
  • Purpose ..... Dump a single picture’s data to the output text file 
as a valid Rez tool picture definition and as standard Macintosh PICT 
resources
  • Input ....... the_pict_data    - picture data buffer (only opcodes)
  •               the_pict_header  - picture header data from resource 
file
  •               the_pict_id      - resource id value for the picture
  •               the_pict_address - address of picture header in res 
file
  •               the_file_ref     - reference for the output text file
  • Output ...... the_ps_error     - error result
  • Notes ....... Progress information is displayed to the user concerning 
the picture that is to be written to the output file.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE dump_PICT_Data (the_pict_data: Handle;{ i }
                   the_pict_header   : pt_pict_header; { i }
                   the_pict_id       : INTEGER;        { i }
                   the_pict_address  : LONGINT;        { i }
                   the_file_ref      : INTEGER;        { i }
                   VAR the_ps_error      : INTEGER);   { o }
    LABEL 666; { I/O error exception vector }
    CONST
      k_pict_res_type = ‘PICT’; { standard pict resource type }
    TYPE
      t_trix = ARRAY [0..1] OF INTEGER; { tricky record for unsigned 
longint use }
    VAR
      phrase : Str255; {phrase for output to output text file}
      trix : t_trix;       { tricky longint value }
      hex : pt_hex_long;  { hex string for a longint }
      value_str : Str255; { temporary number value string }
      
      qd_picture_info : Picture; { [DTC:28SEP90] QD picture header data 
}
      qd_picture_size : Size; { [DTC:28SEP90] byte size of data buffer 
}
      current_res_file : INTEGER;{ [DTC:28SEP90] current resource file 
}
      res_file_ref: INTEGER;{ [DTC:28SEP90] resource file for PICT }
      res_file_is_open : BOOLEAN;{ [DTC:28SEP90] resource file is open 
flag }
      new_res_data: Handle;{ [DTC:28SEP90] new resource data }
      existing_res: Handle;{ [DTC:28SEP90] handle to existing resource 
}
      error_close: INTEGER;{ [DTC:28SEP90] closing res file error }
    BEGIN { ------------ dump_PICT_Data ------------ }
      the_ps_error := NoErr; { assume all will go well }
      
 { provide some progress info to the user (iff desired) }
      IF pv_show_progress THEN
        BEGIN
          NumToString(the_pict_id,value_str);
          
          WHILE LENGTH(value_str) < 6 DO
            value_str := CONCAT(‘ ‘,value_str);
            
          phrase := CONCAT(‘  Scavenging PICT # ‘,value_str,’   ‘);
          
          long2Hex(the_pict_address,hex);
          IF (hex[1] = ‘0’) AND (hex[2] = ‘0’) THEN
            DELETE(hex,1,2);
          phrase := CONCAT(phrase,’[Address $’,hex,’  ‘);
          
          long2Hex(the_pict_header.phd_length_long,hex);
          IF (hex[1] = ‘0’) AND (hex[2] = ‘0’) THEN
            DELETE(hex,1,2);
          phrase := CONCAT(phrase,’Length $’,hex,’]’);
          
          show_Progress_Message(phrase);
        END;
        
      { #################### INTERNAL RESOURCE FILE INFO }
      myWriteLn(the_file_ref,’’,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      myWriteLn(the_file_ref,’/*’,the_ps_error);
      
      long2Hex   (the_pict_address,hex);
      NumToString(the_pict_address,value_str);
      phrase := CONCAT(‘# Picture file address: $’,hex,’  ‘,value_str);
      myWriteLn(the_file_ref,phrase,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      long2Hex   (the_pict_header.phd_length_long,hex);
      NumToString(the_pict_header.phd_length_long,value_str);
      phrase := CONCAT(‘# Picture data length : $’,hex,’  ‘,value_str);
      myWriteLn(the_file_ref,phrase,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      myWriteLn(the_file_ref,’*/’,the_ps_error);
      
      myWriteLn(the_file_ref,’’,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      { #################### PICT RESOURCE HEADER LINE }
      NumToString(the_pict_id,phrase);
      phrase := CONCAT(‘resource ‘’PICT’’ (‘,phrase,’, “”, purgeable) 
{‘);
      myWriteLn(the_file_ref,phrase,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      { #################### PICT RESOURCE SIZE LINE }
      trix[0] := 0; { make the pict size an UNSIGNED INTEGER }
      trix[1] := the_pict_header.phd_length_word;
      
      NumToString(LONGINT(trix),phrase);
      phrase := CONCAT(phrase,’,’);
      myWriteLn(the_file_ref,phrase,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      { ############# PICT RESOURCE FRAME LINE (T/L/B/R) }
      NumToString(the_pict_header.phd_frame.Top,value_str);
      phrase := value_str;
      NumToString(the_pict_header.phd_frame.Left,value_str);
      phrase := CONCAT(phrase,’, ‘,value_str);
      NumToString(the_pict_header.phd_frame.Bottom,value_str);
      phrase := CONCAT(phrase,’, ‘,value_str);
      NumToString(the_pict_header.phd_frame.Right,value_str);
      phrase := CONCAT(phrase,’, ‘,value_str);
      
      phrase := CONCAT(‘{‘,phrase,’},’);
      
      myWriteLn(the_file_ref,phrase,the_ps_error);
      IF the_ps_error <> NoErr THEN GOTO 666;
      
      { #################### PICT RESOURCE DATA LIST }
      dump_Hex_Buffer(the_pict_data,the_file_ref,the_ps_error);
      
      { ############## PICT RESOURCE ENDING REZ DELIMITER }
      phrase := ‘};’;
      myWriteLn(the_file_ref,phrase,the_ps_error);
      
      666: { branch here iff an error occurs }
      { ### NOW WRITE THE PICTURE DATA TO THE RESOURCE FORK }
 { first, create a handle holding entire pict record and data }
      IF the_ps_error = NoErr THEN
        BEGIN
{ extend & merge header data to front of picture buffer data }
          qd_picture_info.PicSize  := the_pict_header.phd_length_word;
          qd_picture_info.PicFrame := the_pict_header.phd_frame;
          
          qd_picture_size := GetHandleSize(the_pict_data);
          
          SetHandleSize(the_pict_data,qd_picture_size + SIZEOF(qd_picture_info));
          the_ps_error := MemError;
          
          IF the_ps_error = NoErr THEN
            BEGIN
              HLock(the_pict_data);
              
              BlockMove(Ptr(the_pict_data^),
                  Ptr(ORD4(the_pict_data^) + SIZEOF(qd_picture_info)), 
qd_picture_size);
              
              BlockMove(@qd_picture_info, Ptr(the_pict_data^), SIZEOF(qd_picture_info));
              
              HUnLock(the_pict_data);
            END;
        END;
        
      { reference: “Compatibility: Rules of the Road”      }
      {            Dave Radcliffe, Apple Macintosh DTS     }
      {            Develope magazine, January 1990, p. 61+ }
      res_file_is_open := FALSE; { res file is not open yet }
      
      IF the_ps_error = NoErr THEN
        BEGIN
          current_res_file := CurResFile; { save current res file }
          the_ps_error     := ResError;
          
          IF pv_debug THEN
          WRITELN(Diagnostic,’current_res_file = ‘,current_res_file:1,’ 
‘, ‘the_ps_error = ‘,the_ps_error:1);
          
          IF the_ps_error = NoErr THEN
            BEGIN
              the_ps_error := SetVol(NIL,pv_file_info.fi_file_volume);
              
              IF pv_debug THEN
              WRITELN(Diagnostic,’the_ps_error SetVol = ‘,
                                  the_ps_error:1);
              
              IF the_ps_error = NoErr THEN
                BEGIN
                  res_file_ref := OpenResFile(pv_file_info.fi_file_name);
                  
                  IF res_file_ref = -1 THEN
                    the_ps_error := ResError;
                  
                  IF pv_debug THEN
                  WRITELN(Diagnostic,’the_ps_error OpenResFile = ‘, the_ps_error:1);
                  
                  IF the_ps_error = NoErr THEN
                    res_file_is_open := TRUE; { I’m alive !!! }
                  
                  IF the_ps_error = NoErr THEN
                    BEGIN
                      UseResFile(res_file_ref);
                      the_ps_error := ResError;
                      
                      IF pv_debug THEN
                      WRITELN(Diagnostic,’the_ps_error UseResFile = ‘, 
the_ps_error:1);
                    END;
                END;
              
              IF the_ps_error = NoErr THEN
                BEGIN
{ make certain the resource does not already exist }
                  existing_res := GetResource(k_pict_res_type,the_pict_id);
                  
                  IF pv_debug THEN
                  WRITELN(Diagnostic,’existing_res = ‘,
                                      ORD4(existing_res):1);
                                          
                  IF existing_res <> NIL THEN
                    BEGIN
{ delete existing resource before adding a new one }
                      RmveResource(existing_res);
                      the_ps_error := ResError;
                      
                      IF pv_debug THEN
                      WRITELN(Diagnostic,’the_ps_error RmveResource = 
‘, the_ps_error:1);
                      
                      IF the_ps_error = NoErr THEN
                        BEGIN
{ res removal went well, deallocate res memory }
                          DisposHandle(existing_res);
                          
                          UpdateResFile(res_file_ref);
                          the_ps_error := ResError;
                          
                          IF pv_debug THEN
                          WRITELN(Diagnostic,’the_ps_error UpdateResFile 
= ‘, the_ps_error:1);
                        END;
                    END;
                  
                  IF the_ps_error = NoErr THEN
                    BEGIN
{ res file is now ready for PICT res addition }
                      new_res_data := the_pict_data;
                      the_ps_error := HandToHand(new_res_data);
                      
                      IF pv_debug THEN
                      WRITELN(Diagnostic,’the_ps_error HandToHand = ‘, 
the_ps_error:1);
                                              
                      IF the_ps_error = NoErr THEN
                        BEGIN
                          AddResource(new_res_data,
                           k_pict_res_type, the_pict_id,
                                      ‘’);
                                      
                          the_ps_error := ResError;
                          
                          IF pv_debug THEN
                          WRITELN(Diagnostic,’the_ps_error AddResource 
= ‘, the_ps_error:1);
                                          
                          IF the_ps_error = NoErr THEN
                            BEGIN
                              UpdateResFile(res_file_ref);
                              the_ps_error := ResError;
                              
                              IF pv_debug THEN
                              WRITELN(Diagnostic,’the_ps_error UpdateResFile 
= ‘, the_ps_error:1);
                            END;
                        END;
                    END;
                END;
              
              IF res_file_is_open THEN
                BEGIN
                  CloseResFile(res_file_ref);
                  error_close := ResError;
                  
                  IF pv_debug THEN
                  WRITELN(Diagnostic,’error_close CloseResFile = ‘, error_close:1);
                                       
                  IF the_ps_error = NoErr THEN
                    the_ps_error := error_close;
                END;
                
              UseResFile(current_res_file); 
 { restore original res file }
            END;
        END;
    END;  { ------------ dump_PICT_Data ------------ }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... scavenge_Bad_Pict_Files
  • Purpose ..... Scavenge a corrupted file looking for picture resourcesgiven 
a list of special picture patterns to search for
  • Input ....... the_pict_header_info  - list of patterns to search 
for
  •               the_file_bad_ref      - reference for corrupted input 
file
  •               the_file_good_ref     - reference for output text Rez 
file
  •               the_starting_id       - starting id value of found 
pictures
  •               the_decrement_id      - decrement id value flag
  • Output ...... the_num_found_picts   - no. extracted pictures
  •               the_num_pict_failures - no. found partial pictures
  •               the_error             - error result
  • Notes ....... This routine is the heart of this module !!!
  •               Partial pictures are pictures whose Picture Header 
Pattern was found, but whose other data is invalid.  These “pictures” 
could be valid pictures which are corrupt, or could only be coincidences 
which the user of this module should examine closer.
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE scavenge_Bad_Pict_Files ( the_pict_header_info  : PS_t_PHI_List;
                  the_file_bad_ref      : INTEGER;
                  the_file_good_ref     : INTEGER;
                  the_starting_id       : INTEGER;
                  the_decrement_id      : BOOLEAN;
                  VAR the_num_found_picts   : INTEGER;
                  VAR the_num_pict_failures : INTEGER;
                  VAR the_error             : INTEGER);
    VAR
      pict_search_info_index : INTEGER; { picture info list index }
      pict_search_info : PS_t_PICT_Header_Info; { picture search info 
}
      pict_search_head : Str255; { Pict. Hder Pattern string }
      pict_search_tail: Str255;{ Pict. Tail   Pattern string }
      search_address : LONGINT; { file search address }
      eof_found : BOOLEAN; { end-of-file found flag }
      pict_data : Handle; { picture raw data }
      pict_header : pt_pict_header; { picture resource header }
      pict_failures : INTEGER; { no. found partial pictures }
      phrase : Str255;  { temporary phrase }
    BEGIN { -------- scavenge_Bad_Pict_Files -------- }
      the_error := NoErr; { assume all will go well }
      the_num_found_picts   := 0; { setup PICT found and tried counters 
}
      the_num_pict_failures := 0;
      
{ search corrupted file using each of picture info records   }
{ from picture info list until all list records are searched }
      FOR pict_search_info_index := 1 TO PS_c_MaxPICTVersions DO
        BEGIN
          pict_search_info := the_pict_header_info[pict_search_info_index];
          
          IF (the_error = NoErr) AND (header_Info_is_Valid(pict_search_info)) 
THEN
            BEGIN
              search_address := 0; { start at the file’s beginning }
                              
              WITH pict_search_info DO
                BEGIN
                  get_Hex_Data(hi_version_data,
                   hi_version_data_length, pict_search_head);
                  
                  get_Hex_Data(hi_endpict_data,
                   hi_endpict_data_length, pict_search_tail);
                END; { WITH pict_search_info }
             
{ tell the user about the current picture info search }
              pict_search_head := CONCAT(‘$’,pict_search_head);
              pict_search_tail := CONCAT(‘$’,pict_search_tail);
               
              phrase := CONCAT(‘Searching for PICT HEAD ‘,pict_search_head,’ 
and ‘, ‘PICT TAIL ‘,pict_search_tail);
              
              show_Progress_Message(‘’);
              show_Progress_Message(phrase);
              show_Progress_Message(‘’);
              
{ using the current picture info search for the next valid }
{ picture in corrupted input file and dump all found valid }
{ pictures to output Rez text file as standard PICT rsrcs }
              REPEAT
                BEGIN
                  get_Next_PICT(the_file_bad_ref,
                   the_file_good_ref, search_address,
                   pict_search_info, eof_found,
                   pict_data, pict_header,
                   pict_failures, the_error);
        
                  the_num_pict_failures := the_num_pict_failures + pict_failures;
                  
                  IF (the_error = NoErr) AND (pict_data <> NIL) THEN
                    BEGIN
                      dump_PICT_Data(pict_data,
                       pict_header, the_starting_id,
                       search_address, the_file_good_ref,
                                     the_error);
                      
{ update the PICT resource ID (avoids over/under flow) }
                      IF the_decrement_id THEN
                        BEGIN
                          IF the_starting_id = -MAXINT - 1 THEN
                            the_starting_id := MAXINT
                          ELSE
                            the_starting_id := the_starting_id - 1;
                        END
                      ELSE { inc PICT resource ID value }
                        BEGIN
                          IF the_starting_id = MAXINT THEN
                            the_starting_id := -MAXINT - 1
                          ELSE
                            the_starting_id := the_starting_id + 1;
                        END;
                      
{ update file search address for next possible picture }
                      IF the_error = NoErr THEN
                        BEGIN
                          the_num_found_picts := the_num_found_picts 
+ 1;
                          
                          search_address := search_address + pict_header.phd_length_long;
                        END;
                      
                      DisposHandle(pict_data);
                      
                      IF (the_error = NoErr) AND (MemError <> NoErr) 
THEN
                        the_error := MemError;
                    END;
                END;
              UNTIL eof_found OR (the_error <> NoErr);

            END;
        END; { FOR pict_search_info_index }
    END;  { -------- scavenge_Bad_Pict_Files -------- }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                                                        •
  •                 MODULE PUBLIC ROUTINES                 •
  •                                                        •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }

{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... PICT_Unit_Version
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  FUNCTION PICT_Unit_Version : Str255;
    BEGIN { ------------ PICT_Unit_Version ------------ }
      
      PICT_Unit_Version := CONCAT(pc_unit_version,
            ‘  [‘,COMPDATE,’ - ‘,COMPTIME,’]’);
    END;  { ------------ PICT_Unit_Version ------------ }
    
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  • Routine ..... PICT_Scavenger
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }
  PROCEDURE PICT_Scavenger (ps_program_name       : Str255;
                      ps_input_file_name    : Str255;
                      ps_input_file_volume  : INTEGER;
                      ps_output_file_name   : Str255;
                      ps_output_file_volume : INTEGER;
                      ps_show_progress      : BOOLEAN;
                      ps_pict_header_info   : PS_t_PHI_List;
                      ps_starting_id        : INTEGER;
                      ps_decrement_id       : BOOLEAN;
                      VAR ps_num_found_picts    : INTEGER;
                      VAR ps_num_pict_failures  : INTEGER;
                      VAR ps_error              : INTEGER);
    CONST
      k_file_creator = ‘PFSC’; { scavenged picture file creator and type 
}
      k_file_type    = ‘TEXT’;
      
        { Rez file info phrases }
      k_out_file_divider       = ‘#########################################’;
      k_out_file_alpha_message = ‘# SCAVENGED PICT RESOURCES FOR APPLE 
REZ’;
      k_out_file_omega_message = ‘# FINIS’;
    VAR
      file_bad_ref : INTEGER; { ref for corrupted input file }
      file_good_ref : INTEGER; { ref for output Rez text file }
      mac_clock_info : Str255; { system date/time info phrase }
      out_divider : Str255; { output file divider line phrase }
      out_error : INTEGER; { output  file error result }
      error_close  : INTEGER; { closing file error result }
    BEGIN { ------------ PICT_Scavenger ------------ }
      ps_error := NoErr; { assume all will go well }
      
      ps_num_found_picts   := 0; { no picts found yet }
      ps_num_pict_failures := 0; { no pict scavenge attempts have been 
made yet }
      
      { setup some global module information }
      pv_file_info.fi_file_name   := ps_output_file_name;
      pv_file_info.fi_file_volume := ps_output_file_volume;
      pv_show_progress := ps_show_progress;
      
      { don’t write progress info if MPW Shell is not around }
      pv_debug := pc_debug;
      
      IF IEStandAlone THEN
        BEGIN
          pv_show_progress := FALSE; { no progress info today}
          pv_debug         := FALSE; { no debugging today either }
        END;
      
      { tell the user what files I’m playing with today }
      show_Progress_Message(‘’);
      show_Progress_Message(CONCAT(‘Scavenging file “‘,ps_input_file_name,’” 
‘, ‘to Rez file “‘,ps_output_file_name,’” ...’));
        
      { verify PICT HEADER data }
      verify_Header_Info_List(ps_pict_header_info,ps_error);
      
      IF ps_error = NoErr THEN
        BEGIN
          ps_error := OpenRF(ps_input_file_name,ps_input_file_volume,file_bad_ref);
          IF ps_error = NoErr THEN
            BEGIN
              ps_error := Create(ps_output_file_name, ps_output_file_volume, 
k_file_creator,k_file_type);
              
              IF ps_error = DupFNErr THEN
                BEGIN
                  ps_error := FSDelete(ps_output_file_name,ps_output_file_volume);
                  
                  IF ps_error = NoErr THEN
                    ps_error := Create(ps_output_file_name, ps_output_file_volume, 
k_file_creator,k_file_type);
                END;
              
              IF ps_error = NoErr THEN
                BEGIN
                  CreateResFile(pv_file_info.fi_file_name);
                  ps_error := ResError;
                  IF pv_debug THEN
                  WRITELN(Diagnostic,’ps_error CreateResFile = ‘, ps_error:1);
                  IF ps_error = NoErr THEN
                    ps_error := FSOpen(ps_output_file_name, ps_output_file_volume, 
file_good_ref);
                  
                  IF ps_error = NoErr THEN
                    BEGIN
{ write a header message for the output file }
                      out_divider := CONCAT(k_out_file_divider,k_out_file_divider);
                      myWriteLn(file_good_ref,’/*’,out_error);
myWriteLn(file_good_ref,out_divider,out_error);
myWriteLn(file_good_ref,k_out_file_alpha_message,out_error);
myWriteLn(file_good_ref,out_divider,out_error);
                      myWriteLn(file_good_ref,’*/’,out_error);
                      myWriteLn(file_good_ref,’’,out_error);
                      get_System_DateTime(mac_clock_info);
                      mac_clock_info := CONCAT(‘/* ‘,ps_program_name,’ 
  ‘, mac_clock_info,’ */’);
                      myWriteLn(file_good_ref,mac_clock_info,out_error);
                      myWriteLn(file_good_ref,’’,out_error);
                      myWriteLn(file_good_ref,’#INCLUDE “Types.r”’,out_error);
                      myWriteLn(file_good_ref,’#INCLUDE “SysTypes.r”’,out_error);
                      
                      { perform the heart of this module }
                      scavenge_Bad_Pict_Files(ps_pict_header_info, file_bad_ref, 
file_good_ref, ps_starting_id, ps_decrement_id,
         ps_num_found_picts, ps_num_pict_failures, ps_error);
                      
                { write a footer message to the output file }
                      myWriteLn(file_good_ref,’’,out_error);
                      myWriteLn(file_good_ref,’/*’,out_error);
                      myWriteLn(file_good_ref,out_divider,out_error);
myWriteLn(file_good_ref,k_out_file_omega_message,out_error);
myWriteLn(file_good_ref,out_divider,out_error);
                      myWriteLn(file_good_ref,’*/’,out_error);
                      
                      error_close := FSClose(file_good_ref);
                      
                      IF ps_error = NoErr THEN
                        ps_error := error_close;
                    END;
                END;
                
              error_close := FSClose(file_bad_ref);
              IF ps_error = NoErr THEN
                ps_error := error_close;
            END;
        END;
      SetCursor(Arrow); { make certain ARROW CURSOR appears }
    END;  { ------------ PICT_Scavenger ------------ }
END.
{ ••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
  •                        FINIS                           •
  •••••••••••••••••••••••••••••••••••••••••••••••••••••••••• }


 
AAPL
$565.89
Apple Inc.
+41.14
MSFT
$39.36
Microsoft Corpora
-0.33
GOOG
$524.16
Google Inc.
-2.78

MacTech Search:
Community Search:

Software Updates via MacUpdate

TechTool Pro 7.0.3 - Hard drive and syst...
TechTool Pro is now 7, and this is the most advanced version of the acclaimed Macintosh troubleshooting utility created in its 20-year history. Micromat has redeveloped TechTool Pro 7 to be fully 64... Read more
MacFamilyTree 7.1.6 - Create and explore...
MacFamilyTree gives genealogy a facelift: it's modern, interactive, incredibly fast, and easy to use. We're convinced that generations of chroniclers would have loved to trade in their genealogy... Read more
EtreCheck 1.9.9 - For troubleshooting yo...
EtreCheck is a simple little app to display the important details of your system configuration and allow you to copy that information to the Clipboard. It is meant to be used with Apple Support... Read more
TeamViewer 9.0.28116 - Establish remote...
TeamViewer gives you remote control of any computer or Mac over the Internet within seconds, or can be used for online meetings. Find out why more than 200 million users trust TeamViewer! Free for... Read more
Viber 4.1.0 - Send messages and make cal...
Viber lets you send free messages and make free calls to other Viber users, on any device and network, in any country! Viber syncs your contacts, messages and call history with your mobile device,... Read more
Apple iOS 7.1.1 - The latest version of...
The latest version of iOS can be downloaded through iTunes. Apple iOS 7 brings an all-new design and all-new features. Simplicity Simplicity is often equated with minimalism. Yet true simplicity is... Read more
1Password 4.3 - Powerful password manage...
1Password is a password manager that uniquely brings you both security and convenience. It is the only program that provides anti-phishing protection and goes beyond password management by adding Web... Read more
Lens Blur 1.3.0 - True out-of-focus boke...
Let Lens Blur transform your existing photo into true SLR-quality out-of-focus bokeh effect! Everyone needs a gorgeous personalized background for a social profile, blog, Web/UI design, presentation... Read more
VMware Fusion 6.0.3 - Run Windows apps a...
VMware Fusion allows you to create a Virtual Machine on your Mac and run Windows (including Windows 8.1) and Windows software on your Mac. Run your favorite Windows applications alongside Mac... Read more
BitTorrent Sync 1.3.93 - Sync files secu...
BitTorrent Sync allows you to sync unlimited files between your own devices, or share a folder with friends and family to automatically sync anything. File transfers are encrypted. Your information... Read more

Latest Forum Discussions

See All

The Sandbox Gets Update, Receives New Ca...
The Sandbox Gets Update, Receives New Campaign and New Elements Posted by Tre Lawrence on April 24th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Football Management Simulator One For El...
Football Management Simulator One For Eleven Released Worldwide Today for iOS Posted by Simon Reed on April 24th, 2014 [ permalink ] Free-To-Play football management title One For E | Read more »
Leo’s Fortune Review
Leo’s Fortune Review By Jordan Minor on April 24th, 2014 Our Rating: :: FORTUNATE SONUniversal App - Designed for iPhone and iPad Leo’s Fortune delivers a platforming experience as creative and refined as any console game.   | Read more »
Suited Up (Games)
Suited Up 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Suited Up is a difficult, one-touch platformer that requires players to visualize each jump. The controls in Suited Up are simple,... | Read more »
MyTP One Mountain - Ski, Freeski and Sno...
MyTP One Mountain - Ski, Freeski and Snowboard 1.0.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0.0 (iTunes) Description: As real snow is melting away in the snow parks around the northern hemisphere, it's now time... | Read more »
Tank Battle: East Front 1943 (Games)
Tank Battle: East Front 1943 1.0 Device: iOS Universal Category: Games Price: $1.99, Version: 1.0 (iTunes) Description: Tank Battle: East Front 1943 is the third in the successful ‘Tank Battle: East Front’ series of games for iPhone... | Read more »
Third Eye Crime: Act 1 (Games)
Third Eye Crime: Act 1 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: "You have to see Third Eye Crime" Touch Arcade "Third Eye Crime is unlike anything currently available for mobile... | Read more »
Wayward Souls (Games)
Wayward Souls 1.00 Device: iOS Universal Category: Games Price: $4.99, Version: 1.00 (iTunes) Description: **Buy the game now at an introductory sale price of $4.99 USD. Every time we do a big content update, we will raise the price... | Read more »
Leo's Fortune (Games)
Leo's Fortune 1.0.2 Device: iOS Universal Category: Games Price: $4.99, Version: 1.0.2 (iTunes) Description: Leo’s Fortune is a platform adventure game where you hunt down the cunning and mysterious thief that stole your gold.... | Read more »
iOOTP Baseball 2014 Edition Review
iOOTP Baseball 2014 Edition Review By Carter Dotson on April 23rd, 2014 Our Rating: :: SOLID CONTRACTUniversal App - Designed for iPhone and iPad The long-running baseball simulator returns to mobile with a much-improved entry in... | Read more »

Price Scanner via MacPrices.net

16GB 1st generation iPad mini available for $...
Radio Shack has a select number of refurbished 1st generation 16GB WiFi iPad minis available for $199.99 on their online store. Choose free shipping or free ship-to-store. We expect these to sell out... Read more
13-inch 2.5GHz MacBook Pro on sale for $100 o...
B&H Photo has the 13″ 2.5GHz MacBook Pro on sale for $1099 including free shipping plus NY sales tax only. Their price is $100 off MSRP. Read more
iPad Sales “Lull” A Reality Correction Of Unm...
I have lots of time for Jean-Louis Gassée, the former Apple Computer executive (1981 to 1990) who succeeded Steve Jobs as head of Macintosh development when the latter was dismissed in 1985. Mr.... Read more
Apple Makes OS X Betas Available To All – Wit...
Apple’s OS X Beta Seed Program, which lets you install the latest pre-release builds, try it out, and submit your feedback, is now open to anyone who wants to sign on rather than to developers and... Read more
Apple Releases iOS 7.1.1 Update
The latest iOS 7.1.1 update contains improvements, bug fixes and security updates, including: • Further improvements to Touch ID fingerprint recognition • Fixes a bug that could impact keyboard... Read more
Logitech Announces Thinner, Lighter, More Fle...
Logitech has announced an update to its Ultrathin for iPad Air, iPad mini and iPad mini with Retina display, improving the flexibility and design of its award-winning predecessor with an even thinner... Read more
Logitech Introduces Hinge, Big Bang and Turna...
Logitech has announced expansion of its tablet product line with three new cases – the Logitech Hinge, the Logitech Big Bang and the Logitech Turnaround – each for the iPad Air, iPad mini and iPad... Read more
WaterField’s Rough Rider Leather Messenger Ba...
WaterField Designs have announced the new 15-inch size of their popular Rough Rider leather messenger bag, a vintage-looking bag that combines Old West charm and ruggedness with distinctly modern... Read more
New Mac Pro on sale, save $100 on the 4-Core...
J&R has the new 4-Core Mac Pro in stock today and on sale for $2899 including free shipping plus NY sales tax only. Their price is $100 off MSRP, and it’s the lowest price available for this... Read more
Apple refurbished iMacs available for up to $...
The Apple Store has Apple Certified Refurbished 2013 iMacs available for up to $300 off the cost of new models. Apple’s one-year warranty is standard, and shipping is free. - 27″ 3.4GHz iMac – $1699... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Position Opening at *Apple* - Apple (United...
…customers purchase our products, you're the one who helps them get more out of their new Apple technology. Your day in the Apple Store is filled with a range of Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Inc. Research Data Specialist - Appl...
…of Worldwide Market Research & Intelligence. The team is responsible for conducting Apple branded consumer market research. It is also responsible for analyzing data Read more
*Apple* Automotive Parts Department position...
Apple Automotive is one of the fastest growing dealer…and it shows. Consider making the switch to the Apple Automotive Group today! At Apple Automotive, we Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.