              Column 17 - PC/ST RESOURCE CONVERTER
              ------------------------------------


This the seventeenth installment of ST PRO GEM, the first to feature a 
complete GEM application. The program converts resource files between 
the ST (68000) and IBM PC (Intel) formats used by the respective 
versions of GEM and the Resource Construction Set.

The Resource Converter will, for the most part, be of direct use only if 
you are doing cross-development of GEM software on these two systems. In 
this case, you will want to read this entire article, starting with the 
"What It Does" heading. You may also be interested in an article on 
PC/ST conversion written by Mark Skapinker of Batteries Included, which 
appears in the Fall issue of START magazine (available September 1986).

If you are not doing cross machine development, you can still get 
something out of this column. Reading the program code and following the 
discussion, you will find practical examples of using GEM dialogs and 
following the internal structure of a resource file. Finally, there is a 
good deal of standard code for GEM initialization, AES utility 
functions, and GEMDOS file handling which you can extract for your own 
uses. If you fall in this category, skip the first parts of the column, 
and resume reading at the "How it Works" heading.

The files and their contents for this column are:

    RSCVMAIN.C  - This is the C source for main routine
    RSCVFILE.C  - This is the C source of GEMDOS utilities     RCLIB.C - 
                C source of AES utility functions
    RSCONV.PRG  - Linked binary of converter
    RSCONV.RSC  - Binary resource image (ST format)
    RSCONV.DFN  - Symbol file for resource (ST format for RCS 2.0)
    RSCONV.H    - Object/tree name file for resource     RSCVLINK.SH- C-
                shell script for linking RSCONV
    SYMLINK.SH  - Another script, used by RSCVLINK.SH

The final two files will be of use only if you are using Dave 
Beckemeyer's Micro C-Shell environment. If not, you can simply translate 
them to .BAT and/or .INP form for use with the Atari batch program and 
linker.


WHAT IT DOES

A converter program is necessary because of the differing order of 
storage on the 68000 and Intel chips: the order of significance of bytes 
within words is reversed, as well as the order of words within long (32-
bit) quantities. Attempting to load an unconverted resource on a GEM 
system with the other architecture will result in a crash, because all 
of the pointer and integer fields will be incorrect.

In addition, the format of symbol definition (DEF) files differs between 
the PC and ST implementations of the RCS in its first version. In the 
latest version from DRI, the formats do correspond, and the files now 
carry an extent name of DFN. However, the byte swapping problem must be 
corrected in either format for the symbols to be correctly loaded.

As final touch, the converter also checks to be sure that a resource 
being loaded from the PC has its bit images synchronized to an even byte 
boundary. If this is not corrected, the ST will suffer a bus error when 
attempted to address the images with a word type operation, which will 
certainly happen in either the RCS or the application itself on the 
target system.


OPERATION

When the Resource Converter loads, it presents an initial dialog box. 
(The converter does not use the menu bar.) The dialog has action buttons 
at the bottom labelled "Help", "Convert" and "Quit". Clicking the Help 
button will obtain a screen which summarizes these operating 
instructions. The Quit button terminates the program. Clicking Convert 
initiates the program's action, according to the options you have set in 
the rest of the dialog.

The top two buttons in the dialog establish the direction of the 
conversion. If you have built a resource on the PC and want to move it 
to the ST, click Intel->68000. If you are moving the file from the ST to 
the PC, click 68000->Intel.

The next two lines in the dialog establish the file extent names which 
will be used for the conversion. The input extent names are always on 
the left, no matter which conversion mode you selected above. By 
default, they are RSC and DEF. The output extents, on the right, are RS2 
and DF2 by default. The RSC/RS2 file extents are used for the resource, 
and DEF/DF2 extents are for the symbol file. Be careful that the input 
files you use are actually of the type (Intel or 68000) which you 
specified. If you get it backwards, the ST will most likely crash, 
though the input files will not be harmed.

The three buttons below the file extents determine the format of the 
input and output symbol files. If you click on DEF, the Resource 
Converter will assume that the input and output symbol files are in the 
old (that is, version 1.1) format used by the RCS. If you click on DFN, 
the program expects the new (version 2.0) symbol format. If the input 
symbol file extent is still set to "DEF", clicking this button will also 
change it appropriately. (Note that the DEF and DFN formats are in fact 
IDENTICAL on the PC ONLY. To "change formats" ON THE PC, you need only 
change the file's extent name, for example, rename FOOBAZ.DEF to 
FOOBAZ.DFN. On the ST, there is a real difference,and you must use the 
DRI DEF2DFN.PRG utility to go from old to new.)

The last button option is (OFF). Clicking this button disables the 
symbol file conversion. You may use this option to keep better control 
over versions of your resource. By establishing the main version on 
either the PC or ST, and converting only the resource file, not the 
symbols, you can ensure that no one will use the RCS on the destination 
machine to create a version which is different from the source.

When the Convert button is clicked to begin execution, the standard Item 
Selector is displayed. Use it to find and pick the input resource file 
which you want to convert. The Converter will use its main filename, 
substituting the various extents, to find the input definition file, and 
produce the output file names. If the definition file cannot be found in 
the same directory under the generated name, you will be presented with 
an alert, asking you to abort the conversion, continue without the 
symbol file, or specify a new name and/or directory for the file. If the 
output names coincide with existing files, they will be overwritten.

While the conversion operation is occurring, a progress indicator box 
will be displayed on the screen, with text messages indicating the 
current phase of the operation. When the conversion is complete, you are 
returned to the initial dialog, where you may quit or perform another 
conversion.

One last note on conversion, with a caution: it is possible to move 
files between Intel and 68000 even if you have two different versions of 
the Resource Construction Set, for instance, Version 2.0 on the PC and 
Version 1.1 on the ST. In this situation, you can take advantage of the 
identity between symbol file formats on the PC. Make a copy of the PC 
symbol file into another file with the DEF extent, and run the 
converter. The output should load on the ST version correctly. However, 
if you move symbols between different RCS versions, you MUST NOT use the 
"free strings" and "free images" features of version 2 and then move the 
symbols to version 1. Doing so may result in spurious assignment of the 
"free" symbols to trees and objects, and you will (of course) not be 
able to edit the free images and/or strings in the resource.


HOW IT WORKS

For the ST-only developers now rejoining the discussion, I will now take 
a look at the standard GEM initialization code and the special dialog 
handling techniques which I have used in the Converter. Then we'll look 
at the guts of the code, which threads through the resource to do the 
actual conversion. For those interested in the supporting code 
libraries, the GEMDOS interface utilities in RSCVFILE.C were described 
in column number 15. The progress indicator functions of RCSLIB.C were 
detailed in column 16, and map_tree() and standard dialog handling 
techniques were discussed in columns three through five.

There are a number of useful facts in rscv_init(), the initialization 
code. First, notice that the result of appl_init is NOT assigned 
directly to gl_apid. Due to a bug in the appl_init binding the 
application ID is not returned as the function value. Instead it is 
returned to the global variable gl_apid. This bug wouldn't cause direct 
problems in the resource converter, but it will in any program which 
uses the ID when sending messages to its own pipe.

Next, an alert string to be displayed if the resource is not found has 
been explicitly included, to avoid a "Catch-22" situation. You can 
easily create such a string by building the alert in the RCS, using the 
C output option, and extracting the string from the resulting file.

Rscv_init() then sets up its VDI handle, by getting the physical 
workstation handle from graf_handle, and passing it to v_opnvwk (open 
virtual workstation), which returns the virtual workstation handle for 
use in VDI calls. V_opnvwk also returns the dimensions of the screen, 
which are copied to GRECT scrn_area, and the number of color planes, 
which is used to set up an MFDB for the screen: scrn_mfdb.

The wind_get call for the working area of the DESK window (number zero) 
results in the usable GRECT of the screen. This is different from 
scrn_area, because the menu bar's rectangle is reserved for the system.

You may want to take particular notice of the vst_height call, which 
reloads the character dimension variables. The values returned earlier 
by graf_handle are ALWAYS for the monochrome (high resolution) system 
font, and they are not appropriate for the low and medium resolution 
modes of the ST. Performing the vst_height call AFTER opening the 
virtual workstation will get the correct dimensions.

Some of the environment variables which are set up by rscv_init(), are 
not actually used in the resource converter. I have included them 
because they are part of my "generic" initialization procedures.

Finally, rscv_init() uses Malloc to reserve most of memory as a working 
buffer for the resource conversion. Notice that 4K are left free. You 
must leave at least 2K, and preferably a safety factor, or the AES will 
be unable to allocate memory for the file selector dialog, and your 
application will hang when calling fsel_input. By the way, an open 
virtual workstation uses about 8K, so you should do your Mallocs after 
the workstation call.

We now turn our attention to do_mode(), which handles the main dialog of 
the converter. After getting the root address of the dialog tree, the 
states of global variables conv_def and new_dfn are used to set up the 
radio button array for symbol file conversion. Native_in is used to set 
up the conversion type radio buttons. The last initialization step is to 
use the set_text() utility to link the file extent strings into the 
appropriate editable objects. Notice that this version of set_text() 
allows the string length to be explicitly supplied. For an editable text 
field object (G_FTEXT or G_FBOXTEXT), the length must be one greater 
than the number of blanks in the editing template.

Since there are TOUCHEXIT objects in the dialog, we cannot use the 
standard hndl_dial() routine. Instead, the form setup and draw calls are 
coded inline. The actual form_do call is placed inside a loop. If the 
object returned is not one of the TOUCHEXIT objects, the loop is 
terminated.

The two objects DEFYES and DFNYES, which select the old and new symbol 
file formats, respectively, have been made TOUCHEXIT. If either is 
clicked, the radio button processing is done by the AES, but then 
control is returned from form_do. Do_mode() then tests the current input 
symbol file extent. If it is "DEF" and we are switching from old to new 
mode, the extent is changed to "DFN". When switching from new to old 
mode, the reverse check is made. In either case, the disp_obj() utility 
is used to force an immediate redraw before returning to form_do for 
further user input.

When the loop terminates, do_mode() cleans up the screen with form_dial, 
and uses the selected() utility to retrieve the status of the radio 
button arrays and update the global variables. Finally, map_tree() is 
used to apply the deselection utility to all objects in the tree, 
leaving it clean for the next invocation of do_mode().


THE BELLY OF THE BEAST

The routine dconv() contains the code for actually converting the 
resource. About half of do_conv() is devoted to accessing disk files, 
and correctly handling error conditions which might arise. The error 
recovery could perhaps be handled more concisely with artifices like 
"setjmp", but I have left it in-line for the sake of clarity.

Do_conv() also uses a collection of routines whose names begin with 
"swap_" Swap_bytes() and swap_words() are the workhorse routines. They 
simply run through a given area of memory, reversing every pair of bytes 
or words, respectively. (Remember that the word swapper will only work 
on even byte boundaries, or a bus fault will occur.) The other swap 
routines do the fix up for one type of resource structure each. I'll 
take apart a couple in detail, so that you can see the similarities in 
the others.

Now let's follow the do_conv() code. The first item of business is to 
use the get_file() utility, followed by open_file(), to find and open 
the input resource file. Again, you can see column 15 for a a discussion 
of these routines, which will handle DOS errors if they occur.

The Boolean conv_def is true if we need to open a symbols file. If 
everything goes right, just substituting a new extent name (from 
variable old_def), and doing the file open will get the file. If things 
foul up, do_conv() has to recover gracefully. So, it puts up the three 
button alert NODEF. The third button is an abort option; if it's picked 
we punt, closing the one open file and returning to the main dialog. 
Button one allows a continuation without the definition file (conv_def 
is forced FALSE so the user won't make the same mistake twice). Button 
two says try again, so get_file() is called to pick another file, and 
the whole process repeats.

Once the input file(s) are open, the operation will run to completion, 
assuming no disk errors. The mouse form is switched to an hour glass, 
and a progress indicator box is initialized on the screen.

The first action is to bring in the resource header only. If the 
resource is not native, that is, is not in 68000 format, its bytes must 
be swapped before using any of the counts and offsets. Note: if you've 
never dissected a resource before, this would be a good time take a look 
at the header format given in "gemdefs.h" in the Developer's Kit, and 
the resource structure definitions in "obdefs.h".

After the swap, if necessary, the resource size field can be used. 
Before reading the rest of the resource, the size must be compared 
against the size of the buffer allocated during initialization. If there 
isn't enough room, the NOMEM alert is displayed and control returns to 
the main dialog. (Since the biggest resource is 64K, and the Converter 
is rather small, this shouldn't ever happen. But, some people load up 
lots of desk accessories, so best to be prepared.)

After completing the resource input, an (admittedly kludgy) patch is 
made for the odd-byte-images problem. If the image offset pointer is 
odd, there will be problems on the ST. The fix made here relies on two 
facts. First, all images are an even number of bytes in size. Second, 
the old version of RCS DOES do an even byte synchronization AFTER 
writing the images. Therefore, if the images start on an odd byte, they 
will end on an even byte, and have one unused odd-addressed byte 
immediately following. The fixup strategy is to move all of the images 
up one byte, patch the offset in the header accordingly, and leave the 
img_odd flag set so that the bit image swap routine will also increment 
pointers in the BITBLK and ICONBLKs which use the images.

If the resource being converted is native, we can do the image fixup 
immediately. If not, all of the other swaps have to take first, because 
the long pointers in the BITBLKs and ICONBLKs are in the wrong order. 
(We don't have to swap string data; it's always stored in ascending 
order.)

Do_conv() calls subroutines to swap the tree index, objects, TEDINFOs, 
ICONBLKs, BITBLKs, and free string and images, in that order. We'll look 
at their actual code later. When this is finished, one of two cleanup 
actions is needed. If the resource WAS native, everything is now swapped 
except the header, which is finally reversed. If it used to be foreign 
(Intel), the pointers are now in proper 68000 order, so swap_images() 
can be called successfully.

Now it's time to write the converted resource, taking care that no error 
results. If no symbol file conversion is needed, do_conv() is done. The 
files are closed, the progress indicator completed, and control returns 
to rscv_run, which will set the cursor back to the arrow. Otherwise, 
it's time to handle the symbols file.

The first two bytes in the definition file are always the symbol count. 
They are read into nsym, and a scratch copy is made in reply, and 
swapped. Just which version is written to the output file depends on 
whether it is a DFN or DEF file. The new (DFN) format keeps a similar 
word order on both the ST and PC. For these files, the following code 
amounts to a verbatim copy.

Nsym is now used as the control variable of a loop which reads, 
converts, and writes one symbol entry on each iteration. In the old 
(DEF) file format, there was an extra word in the value field of each 
symbol's entry on the ST. Going from PC to ST, two padding zero bytes 
are added. Going the other way, they are read and discarded. For the old 
format, both the significant word of this field, as well as the 
following word field (the type) are swapped. The ten symbol bytes are 
passed through verbatim in both new and old format.

After swapping (old format) or simply copying (new format) of the symbol 
file is completed, file and progress box cleanup is done, and control 
returns.


THE OL' SWITCHEROO

As promised, let's now go and look at a sampling of the structure swap 
routines: one of the simplest (tree index), one of medium complexity 
(objects), and the worst of the bunch (bit images).

Swap_trees() is responsible for fixing the tree index. Its location is 
found by adding an offset from the header (rsh_trindex) to the base 
address of the resource, stored in head. (Notice that head was defined 
as a byte pointer when the buffer was allocated, so I need to type 
coerce it to a resource header pointer before making structure 
references.) The number of trees is stored in the rsh_ntree header 
field. Each index entry is a long pointer of 32-bits, so the total size 
of the index may be found by multiplication. Finally, the swapping is 
done, first bytes within words, then words within the long pointers. 
(The order doesn't matter - work it out!)

Swap_objs() takes care of swapping fields inside of object structures. 
It begins similarly to swap_trees(), computing the base address of the 
objects from rsh_object, and taking the count from rsh_nobs. Since all 
of the objects are contiguous in a resource produced by RCS, and all 
fields of an object are words or longs, a byte swap is performed on the 
entire block at once. Now there are a couple of tricks. Notice that 
"where" in this routine is typed as an object pointer. As the number of 
objects is counted down in the loop, "where" is incremented. Because of 
its type, its actual pointer value will increase by sizeof(OBJECT) at 
each iteration. Inside the loop, only one long field, the ob_spec, needs 
to be word swapped. When the loop completes, all object structures are 
fixed.

Swap_fstr() and swap_fimg() use code similar to swap_trees(). The other 
resource structures, TEDINFOs, ICONBLKs, and BITBLKs, are transformed by 
code similar to swap_objs(). There is one subtlety here. These 
structures would normally be reaching by following an object's ob_spec 
pointer. To finesse a "swap now or later" ordering problem with the 
objects, I have instead used the resource header to find the structure 
arrays. You SHOULD NOT use this technique at run time, for two reasons. 
First, if you do any object patching on the fly, the correspondence 
between objects and reference strucutres will be destroyed. Second, 
there is no way to name a non-object structure with the RCS, and 
therefore no way to reliably retrieve it with rsrc_gaddr at execution 
time.

Last but not least, swap_images() not only takes care of switching bit 
image words, but also fixes image pointers as necessary if an odd byte 
problem has been detected. For these reasons, swap_images DOES follow 
pointers from the ICONBLKs and BITBLKs. The loop structure of the two 
sections of code will be familiar from swap_ibs() and swap_bbs(). Size 
fields within the structure are used to determine the length of each 
image. If the img_odd flag has been set, the data pointer must be 
incremented BEFORE doing the swap, since the underlying data has already 
been moved.


PHEW!

This column was deliberately long, in hopes that even those with no 
immediate use for this program will gain from the discussion. Do to 
space limits, the feedback is postponed to episode number 18, which will 
be another helping of "interface potpourri". Ingredients will include 
the popup menu code (finally), along with generic code for scrolling 
windows, and building your own scroll bars within dialogs.


----
