home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tricks of the Mac Game Programming Gurus
/
TricksOfTheMacGameProgrammingGurus.iso
/
Information
/
CSMP Digest
/
volume 2
/
csmp-v2-011.txt
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Text File
|
1994-12-08
|
48.0 KB
|
1,557 lines
|
[
TEXT/R*ch
]
C.S.M.P. Digest Wed, 10 Feb 93 Volume 2 : Issue 11
Today's Topics:
Help for a new programmer (CDEF's & Debuging Resource Code)
Proper way to do Self-Modifying code?
help- sound playing w/ bufferCmd
code for Finding Prefs File (long)
The Comp.Sys.Mac.Programmer Digest is moderated by Michael A. Kelly.
The digest is a collection of article threads from the internet newsgroup
comp.sys.mac.programmer. It is designed for people who read c.s.m.p. semi-
regularly and want an archive of the discussions. If you don't know what a
newsgroup is, you probably don't have access to it. Ask your systems
administrator(s) for details. If you don't have access to news, you can
post articles to any newsgroup by mailing your article to
newsgroup@cs.utexas.edu
So, to post an article to comp.sys.mac.programmer, mail your article to
comp-sys-mac-programmer@cs.utexas.edu
Note the '-' instead of '.' in the newsgroup name. Be sure to ask that
replies be emailed to you instead of posted to the group, and give your
email address.
Each issue of the digest contains one or more sets of articles (called
threads), with each set corresponding to a 'discussion' of a particular
subject. The articles are not edited; all articles included in this digest
are in their original posted form (as received by our news server at
cs.uoregon.edu). Article threads are not added to the digest until the last
article added to the thread is at least one month old (this is to ensure that
the thread is dead before adding it to the digest). Article threads that
consist of only one message are generally not included in the digest.
The entire digest is available for anonymous ftp from ftp.cs.uoregon.edu
[128.223.8.8] in the directory /pub/mac/csmp-digest. Be sure to read the
file /pub/mac/csmp-digest/README before downloading any files. The most
recent issues are available from sumex-aim.stanford.edu [36.44.0.6] in the
directory /info-mac/digest/csmp. If you don't have ftp capability, the sumex
archive has a mail server; send a message with the text '$MACarch help' (no
quotes) to LISTSERV@ricevm1.rice.edu for more information.
The digest is also available via email. Just send a note saying that you
want to be on the digest mailing list to mkelly@cs.uoregon.edu, and you will
automatically receive each new issue as it is created. Sorry, back issues
are not available through the mailing list.
Send administrative mail to mkelly@cs.uoregon.edu.
-------------------------------------------------------
From: kevinb@engr.LaTech.edu (Kevin P. Brunner)
Subject: Help for a new programmer (CDEF's & Debuging Resource Code)
Date: 6 Jan 93 04:55:39 GMT
Organization: Louisiana Tech University
I've been working on the mac for the last 6 months, recently converted from
ibm p.c. I'm getting my feet wet with controls and resources. I have been
able to create small apps with standard mac stuff, but would like to design
my own controls. Inside Macintosh does a lot for stating generalities...
"you need this, and one of these, and, oh!, one of these too..." but no
specific examples in this case.
So I've tried creating a CDEF which responds only to the draw message and
returns zero (0) for everything else. It responds, but draws something
completely different than what the code says. The code inverts a block
of pixels in a window (it works when called from the "main" block of code,
but not when the resource is compiled seperately -- I get a rounded box!)
Has anyone written custom controls? If so, do you mind sharing a portion
of your code and development technique? Also, any help on using Think C
to debug resources would be appreciated. I have most of the Inside Mac's and
both Programming the Macintosh's (vol 1 and 2) I have looked elsewhere but
have found nothing with coding examples or techniques. Book or Article
references would be appreciated as well.
Thanks in advance!
kevinb@engr.latech.edu
+++++++++++++++++++++++++++
Organization: Royal Institute of Technology, Stockholm, Sweden
Date: Wed, 6 Jan 1993 14:56:05 GMT
> How to debug CDEF in Think C
Well, you could try and create a CDEF that's only 6 bytes,
load it in, make it contain a JMP instruction and the
address of a function in your application, and flush the
cache (after locking the resource)
Something like:
typedef struct cdef {
unsigned short jmp ;
short * ( addr ) ( ) ;
} cdefStub ;
pascal short
MyRealCDEF ( whatever )
{
Do the stuff
}
/* DO this before creating any controls */
cdefStub stub ;
Handle h = GetReosurce ( 'CDEF' , 4711 ) ;
stub . jmp = 0x4000 ; /* Have to look this up, JMP Immediate */
stub . addr = MyRealCDEF ;
HLockHi ( h ) ; /* Top of heap to avoid fragmentation */
* ( cdefStub * ) * h = stub ;
if ( TrapAvailable ( _HWTrap ) ) {
FlushInstructionCache ( ) ;
}
Now anything going to CDEF 4711 in your program will go to
the function MyRealCDEF, and you can use the source debugger
on it, and reference globals even! Cool!
- --
-- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
Cookie Jar: Vanilla Yoghurt with Crushed Oreos.
---------------------------
From: at15+@andrew.cmu.edu (Andrew Lewis Tepper)
Subject: Proper way to do Self-Modifying code?
Date: 3 Jan 93 10:55:11 GMT
Organization: School of Computer Science, Carnegie Mellon, Pittsburgh, PA
I'm writing a BitBlt routine that must be _very_ fast. I want to do
animation of many large 256 color objects of a game. It seems the
quickest way to do this would be to take an object before the animation
starts, and create a sequence of move instructions that would write to
just those addresses I want. To draw the object I would just load a
register with the apropriate starting address and call it's draw routine.
Is there an accepted way to do this on the Mac? I know that
self-modifying code can cause problems on chips with caches, so I want
to be as "kosher" as possible.
Thanks,
Andy
+++++++++++++++++++++++++++
Date: 4 Jan 93 12:03:53 GMT
Organization: Royal Institute of Technology, Stockholm, Sweden
In <wfFp_jO00WB707kGgq@andrew.cmu.edu> at15+@andrew.cmu.edu (Andrew Lewis Tepper) writes:
>Is there an accepted way to do this on the Mac? I know that
>self-modifying code can cause problems on chips with caches, so I want
>to be as "kosher" as possible.
if ( TrapAvailable ( _HwPriv ) ) {
/* FIC also calls FlushDataCache */
FlushInstructionCache ( ) ;
}
Of course you'll want to put the result of TrapAvailable in
a global at startup. Not to mention first modifying ALL the
figures, then flushing the cache ONCE before starting to
call the code.
However, I think you could write the code quite conflict-free
and as fast using just address-register-relative addressing
and maybe a data register for rowBytes. Modifying the instructions
data takes time to do; I think using a register pair would be
FASTER - and more compatible.
Cheers,
/ h+
- --
-- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
-- I don't fear death, it's dying that scares me.
+++++++++++++++++++++++++++
From: alexr@apple.com (Alexander M. Rosenberg)
Date: 5 Jan 93 18:33:37 GMT
Organization: Hackers Anonymous
In article <1993Jan4.120353.16699@kth.se> Jon Wltte, d88-jwa@dront.nada.kth.se
writes:
> However, I think you could write the code quite conflict-free
> and as fast using just address-register-relative addressing
> and maybe a data register for rowBytes. Modifying the instructions
> data takes time to do; I think using a register pair would be
> FASTER - and more compatible.
Immediate moved bitmaps are _much_ faster than those drawn via table lookups.
This is a technique that they call "Compiled Bitmaps" in rec.games.programmer.
Immediate move instructions are generally faster than using an address
register. It adds up, especially when lots of small bitmaps may be necessary.
The one shot compile may even be possible to do at compile time, depending on
the way the screen bitmap is available on the target machine. The problem with
compiled bitmaps is that they typically are double in size over the plan data,
which is why Andy would want to compile them when the game is launched.
Several games take a long time to start up, like Maelstrom (although this is
probably because Maelstrom keeps all it's sprite data as icl8 resources, and
loading, locking, and detaching that many resources takes time).
- ---------------------------------------------------------------------------
- - Alexander M. Rosenberg - INTERNET: alexr@apple.com - Yoyodyne -
- - 330 Waverley St., Apt B - UUCP:ucbvax!apple!alexr - Propulsion -
- - Palo Alto, CA 94301 - - Systems -
- - (415) 329-8463 - Nobody is my employer so - :-) -
- - (408) 974-3110 - nobody cares what I say. - -
+++++++++++++++++++++++++++
Date: 5 Jan 93 20:25:43 GMT
Organization: Royal Institute of Technology, Stockholm, Sweden
In <1993Jan5.183337.20064@gallant.apple.com> alexr@apple.com (Alexander M. Rosenberg) writes:
>Immediate move instructions are generally faster than using an address
>register. It adds up, especially when lots of small bitmaps may be necessary.
Hmm, well, I'm not convinced. Let's say you move a bitmap. Using an
address register, you will only have to change the register. For a
"compiled" bitmap, you would have to change each and every immediate
move instruction. Not to mention that immediate instructions and
longer, and thus takes more bus cycles in fetch; those bus cycles
do also compete with the cycles spent writing to VRAM. (Some slow
NuBus cards take 7 cycles for a longword write, but then fetching
an 8-byte instruction instead of a 4-byte one takes 4 extra cycles
on an LC)
It all comes out in amortized performance; if the blitting has to be
raving fast, but you have ample of time to precompute the next
image, compiled bitmaps would be OK (except they flush the cache on
040s; that's bad speed-wise) but if you have general throughput
problems, register-relative blitting is DEFINATELY the right thing
to do.
Just pass rowBytes in d0 and the address of the first pixel in
a0, and make VERY SURE that address is longword aligned or you
will suffer much more from misaligned writes than you will from
the difference in register-based and immediate addressing.
>The one shot compile may even be possible to do at compile time, depending on
>the way the screen bitmap is available on the target machine. The problem with
Not on the Mac, of course. However, certain other systems have
a measly register set and the screen card (note the singularis)
on a fixed address; I'm sure the timings you cite are for VGA
and not for anything flexible like the mac QD device model.
Cheers,
/ h+
- --
-- Jon W{tte, h+@nada.kth.se, Mac Hacker Deluxe --
This signature is kept shorter than 4 lines in the interests of UseNet
S/N ratio.
+++++++++++++++++++++++++++
From: kent@lloyd.Camex.COM (Kent Borg)
Date: 5 Jan 93 21:31:38 GMT
Organization: Camex Inc., Boston MA
In article <1993Jan4.120353.16699@kth.se> d88-jwa@dront.nada.kth.se (Jon Wdtte) writes:
>In <wfFp_jO00WB707kGgq@andrew.cmu.edu> at15+@andrew.cmu.edu (Andrew Lewis Tepper) writes:
>
>>Is there an accepted way to do this on the Mac? I know that
>>self-modifying code can cause problems on chips with caches, so I want
>>to be as "kosher" as possible.
I hate being non-Kosher, but just today some of my code was modifying
itself. (How can a proc in the sleep queue find my A5 world anyway?)
My "to do" list said I should check before I trap, so I added:
> if ( TrapAvailable ( _HwPriv ) ) {
>
> /* FIC also calls FlushDataCache */
> FlushInstructionCache ( ) ;
> }
It didn't work.
At least with my .h-files, the spelling is "HWTrap". (And now I need
to type in the source for TrapAvailable()--now I remember why I didn't
do this before.)
Yes, trivial, so instead consider my post as follows: How should one
find one's A5 world from within a sleep queue procedure?
- --
Kent Borg kent@camex.com or kentborg@aol.com (when it is *working*)
H:(617) 776-6899 W:(617) 426-3577
As always, things look better when some costs are left out.
-Economist 3-28-92 p. 94
+++++++++++++++++++++++++++
From: neeri@iis.ethz.ch (Matthias Neeracher)
Date: 7 Jan 93 22:23:29 GMT
Organization: Integrated Systems Laboratory, ETH, Zurich
In article <1993Jan05.163138.20910@lloyd.Camex.COM>, kent@lloyd.Camex.COM (Kent Borg) writes:
> Yes, trivial, so instead consider my post as follows: How should one
> find one's A5 world from within a sleep queue procedure?
I have never written a sleep queue proc, but wouldn't the following work ?
TYPE TheBigSleep =
RECORD
sleepy : SleepQRec;
wonderfulWorld: A5WorldPtr; /* Whatever you want */
END;
...
valium : TheBigSleep;
...
SleepQInstall(@valium);
According to TFM, your sleep routine will be called with a pointer to
valium in A0 that it can cast to the appropriate type. Am I missing anything ?
Matthias
- -----
Matthias Neeracher neeri@iis.ethz.ch
"I wouldn't recommend sex, drugs or insanity for everyone, but
they've always worked for me." -- Hunter S. Thompson
+++++++++++++++++++++++++++
From: kent@lloyd.Camex.COM (Kent Borg)
Date: 8 Jan 93 17:50:14 GMT
Organization: Camex Inc., Boston MA
In article <NEERI.93Jan7232329@iis.ethz.ch> neeri@iis.ethz.ch (Matthias Neeracher) writes:
>In article <1993Jan05.163138.20910@lloyd.Camex.COM>, kent@lloyd.Camex.COM (Kent Borg) writes:
>> Yes, trivial, so instead consider my post as follows: How should one
>> find one's A5 world from within a sleep queue procedure?
>
>I have never written a sleep queue proc, but wouldn't the following work ?
>
>TYPE TheBigSleep =
> RECORD
> sleepy : SleepQRec;
> wonderfulWorld: A5WorldPtr; /* Whatever you want */
> END;
>...
>valium : TheBigSleep;
>...
>SleepQInstall(@valium);
>
>According to TFM, your sleep routine will be called with a pointer to
>valium in A0 that it can cast to the appropriate type. Am I missing anything ?
No, you are not missing anything, I was the one missing. Sure looks
like that will work.
Um, though a healthy dose of "RTFM" is appropiate here, I think there
is also a little of another effect: "It is always in the *first* place
you look, you just weren't looking carefully enough when you looked
there because you didn't yet know how lost it was."
I spotted the A0 note, realized that it was not a pointer to my A5,
nor was it a refcon, so I looked further. By the time I realized that
there was no A5 or refcon sitting waiting for me I had forgotten that
I might use A0 to *find* one. Stupid I am sometimes.
Thanks for the help.
- --
Kent Borg kent@camex.com or kentborg@aol.com (when it is *working*)
H:(617) 776-6899 W:(617) 426-3577
As always, things look better when some costs are left out.
-Economist 3-28-92 p. 94
---------------------------
From: tmorrow@oracle.com (Thomas Morrow)
Subject: help- sound playing w/ bufferCmd
Date: 2 Jan 93 23:19:12 GMT
Organization: Oracle Corporation, Belmont, CA
I am having trouble using SndDoCommand to play a sampled sound header.
I do:
SndCommand mySndCmd;
/* mySndH is a handle to a sound header and data */
/* mySndChan is already properly opened */
mySndCmd.cmd;
mySndCmd.param1 = 0;
mySndCmd.param2 = (long)(*mySndH) & 0xffffff;
SndDoCommand(mySndChan, mySndCmd, false);
This does not work for some reason; I can't figure out why because if
I replace the SndDoCommand with a Snd Play, it works:
SndPlay(mySndChan, mySndH, true);
So the channel and header must be alright. I am new to Mac
programming, and wasn't sure exactly how to set param2 (a long) to the
header address (Ptr)... The IM IV uses ORD4() to do the type
conversion, but I am using Think C rather than Lisa Pascal. My way
seems to work, but what is the elegant way?
Anyway, I use SndChannelStatus to get the status every event loop
cycle, and the SCStatus returned is blank (zero) in all except CPUload
(24), ChannelAttributes (initMono), and ChannelBusy (true). How can
the channel be busy if StartTime == endTime == 0?
Please email responses. I will summarize.
Tom tmorrow@us.oracle.com
- --
- ---------------------------------------------------------------------------
Tom Morrow 3OP4 voice:415-506-2202 Office: 427
tmorrow@us.oracle.com fax:415-506-7292 Application Object Library
- ---------------------------------------------------------------------------
+++++++++++++++++++++++++++
From: REEKES@applelink.apple.com (Jim Reekes)
Date: Wed, 6 Jan 1993 00:36:46 GMT
Organization: Apple Computer, Inc.
In article <TMORROW.93Jan2151912@af4hp.oracle.com>, tmorrow@oracle.com
(Thomas Morrow) wrote:
>
>
> I am having trouble using SndDoCommand to play a sampled sound header.
>
> I do:
> SndCommand mySndCmd;
>
> /* mySndH is a handle to a sound header and data */
> /* mySndChan is already properly opened */
> mySndCmd.cmd;
> mySndCmd.param1 = 0;
> mySndCmd.param2 = (long)(*mySndH) & 0xffffff;
> SndDoCommand(mySndChan, mySndCmd, false);
>
> This does not work for some reason; I can't figure out why because if
> I replace the SndDoCommand with a Snd Play, it works:
>
> SndPlay(mySndChan, mySndH, true);
>
> So the channel and header must be alright. I am new to Mac
> programming, and wasn't sure exactly how to set param2 (a long) to the
> header address (Ptr)... The IM IV uses ORD4() to do the type
> conversion, but I am using Think C rather than Lisa Pascal. My way
> seems to work, but what is the elegant way?
>
> Anyway, I use SndChannelStatus to get the status every event loop
> cycle, and the SCStatus returned is blank (zero) in all except CPUload
> (24), ChannelAttributes (initMono), and ChannelBusy (true). How can
> the channel be busy if StartTime == endTime == 0?
You cannot use the 'snd ' resource for the bufferCmd. You must point to the
SoundHeader within the resource. Also, the code above didn't set the cmd
to bufferCmd. Here's some code anyone can use to find the sound header in a
'snd ' resource. Then change your code above into...
mySndCmd.cmd = bufferCmd;
mySndCmd.param1 = 0;
mySndCmd.param2 = (long) *mySndH + offset;
err = SndDoCommand(mySndChan, &mySndCmd, true);
// find the sound header inside of the given snd handle
OSErr GetBufferOffset(Handle sndHandle, long *offset)
{
short howManyCmds;
Ptr cruisePtr;
OSErr result;
if (sndHandle == nil)
return nilHandleErr;
if (*sndHandle == nil)
return nilHandleErr;
result = noErr;
*offset = 0;
// set the pointer past the first two words of the snd
// this is correct for both format 1 and 2 resources
cruisePtr = *sndHandle + offsetof(SndListResource, modifierPart);
// if it's a format 1, then point past the modifier parts
if ( ((SndListPtr)*sndHandle)->format == firstSoundFormat )
cruisePtr += sizeof(ModRef) *
((SndListPtr)*sndHandle)->numModifiers;
// now pointing at number of cmds
howManyCmds = *((short *)cruisePtr);
cruisePtr += sizeof(howManyCmds);
// cruisePtr is now at the first sound command
// cruise all commands and find a soundCmd or bufferCmd
do {
switch (((SndCmdPtr)cruisePtr)->cmd) {
case (soundCmd | dataOffsetFlag):
case (bufferCmd | dataOffsetFlag):
*offset = ((SndCmdPtr)cruisePtr)->param2;
howManyCmds = 0; // done, get out of loop
break;
default: // catch any other type of
cmd
cruisePtr += sizeof(SndCommand);
howManyCmds -= 1;
break;
}
} while (howManyCmds >= 1); // done with all the
commands
if (*offset == 0) // never found sound header
return badFormat;
return(result);
}
- -----------------------------------------------------------------------
Jim Reekes, Polterzeitgeist | Macintosh Toolbox Engineering
| Sound Manager Expert
Apple Computer, Inc. | "All opinions expressed are mine, and do
20525 Mariani Ave. MS: 81-KS | not necessarily represent those of my
Cupertino, CA 95014 | employer, Apple Computer Inc."
---------------------------
From: Sproul@sproul.sproul.com (Mark Sproul)
Subject: code for Finding Prefs File (long)
Date: 5 Jan 93 18:15:16 GMT
Organization: Sproul Consulting
Below is code for finding and working with prefs file in system 7
prefernce folder and working with said file
There are people looking for this code again, I posted it before.
Since I posted it last, I have made some additions to it. This code
is originally from Inside Mac Comm ToolBox.
============================================================================
FILE: doPrefs.proto.h
============================================================================
void getPrefsFile(Str255, OSType, OSType);
Boolean getPrefsResourceStr(Str255, OSType, Str255);
Boolean getPrefsResourceData(Str255, OSType, char *, long);
void setPrefsResourceStr(Str255, OSType, Ptr, long);
void setCTBpref(Str255);
void DoCommPortSetup(Str255);
============================================================================
FILE: doPrefs.c
============================================================================
/******************************************************************************
* adapted from
* Inside the Macintosh Communications Toolbox
* Page 333
* After initialization, the code shown first checks if a
* preferences folder, which contains tool settings written in preference
* files, already exists. If so, the application uses the settings in this file.
* Otherwise, the code generates a new preferences file.
******************************************************************************
* Modifications
******************************************************************************
* Nov 27, 1991 Original version had a bug in it.
* If the "Preferences" directory already existed, the preferences file
* was placed in the system folder, not in the "Preferences" directory.
* If the directory did NOT exist, everything worked fine.
* I also changed it to check for the existance of the "Preferences" folder
* first instead of trying to create one and letting an duplicate file error
* indicate that it already existed.
* by Mark Sproul
* AppleLink: Sproul.M
* Jan 16, 1992 Working on a general purpose prefs file manipulator
* Jan 25, 1992 Prefs file resources working great
******************************************************************************/
#include <Connections.h>
#include <CommResources.h>
#include "doPrefs.proto.h"
/*
** Global Variables used by all of the prefs routines
*/
long prefDirID; /* Prefs Dir ID number */
long sysfDirID; /* System folder Dir ID number */
short prefVRefNum; /* Prefs Volume Ref number */
short prefFileOKFlag = 0; /* prefs file OK indicator */
/*************************************************************
* This makes sure there is a prefs file in the PREFERENCES directory
* if not, it creates it.
*
* getPrefsFile MUST be called before any atempt at getting the prefs values
*
*************************************************************/
void getPrefsFile(prefsFileName, creatorType, fileType)
Str255 prefsFileName;
OSType creatorType;
OSType fileType;
{
OSErr osErr = noErr;
SysEnvRec theWorld;
CInfoPBPtr infoPB;
WDPBPtr wdPB;
HParmBlkPtr dirPB;
short prefRefNum;
Point where = { 75, 75 };
Str63 toolName;
short procID;
Handle h;
Ptr p;
ConnHandle prefConn;
ConnHandle docConn;
CMBufferSizes sizes = { 0, 0, 0, 0, 0, 0, 0, 0 };
infoPB = (CInfoPBPtr)NewPtrClear(sizeof(*infoPB));
wdPB = (WDPBPtr)NewPtrClear(sizeof(*wdPB));
dirPB = (HParmBlkPtr)NewPtrClear(sizeof(*dirPB));
/* find the system folder's volume reference number and directory ID */
osErr = SysEnvirons(curSysEnvVers, &theWorld);
(*wdPB).ioVRefNum = theWorld.sysVRefNum;
if (noErr == (osErr = PBGetWDInfo(wdPB, false)))
{
/*********************************************************
* 11-27-91 Modified by Mark Sproul
**********************************************************/
/* get the Volume Reference Number and save it */
prefVRefNum = (*wdPB).ioWDVRefNum;
/* get the System directory ID and save it */
sysfDirID = (*wdPB).ioWDDirID;
/* check for the preferences folder */
(*infoPB).hFileInfo.ioFDirIndex = 0;
(*infoPB).hFileInfo.ioVRefNum = prefVRefNum;
(*infoPB).hFileInfo.ioDirID = sysfDirID;
(*infoPB).hFileInfo.ioNamePtr = "\pPreferences";
osErr = PBGetCatInfo(infoPB, false);
/* save the "Preferecnces" dir number */
prefDirID = (*infoPB).hFileInfo.ioDirID;
if (osErr == fnfErr)
{
/* Create "Preferences" folder */
(*dirPB).fileParam.ioVRefNum = prefVRefNum;
(*dirPB).fileParam.ioDirID = sysfDirID;
(*dirPB).fileParam.ioNamePtr = "\pPreferences";
osErr = PBDirCreate(dirPB, false);
prefDirID = (*dirPB).fileParam.ioDirID;
}
/*********************************************************
* end of modifications
**********************************************************/
if (osErr == noErr)
{
/* does the preference file exist? */
(*infoPB).hFileInfo.ioFDirIndex = 0;
(*infoPB).hFileInfo.ioVRefNum = prefVRefNum;
(*infoPB).hFileInfo.ioDirID = prefDirID;
(*infoPB).hFileInfo.ioNamePtr = prefsFileName;
osErr = PBGetCatInfo(infoPB, false);
if (osErr == noErr)
{
/* set flag saying the prefs file is OK */
prefFileOKFlag = 0xAA;
}
if (osErr == fnfErr)
{
/* no, so create a new preference file */
if (noErr == (osErr = HCreate(prefVRefNum, prefDirID, prefsFileName, creatorType, fileType)))
{
HCreateResFile(prefVRefNum, prefDirID, prefsFileName);
if (noErr == (osErr = ResError()))
{
/* open the preference file */
prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
if (prefRefNum == -1)
{
osErr = ResError();
}
else
{
/* create a default connection */
osErr = CRMGetIndToolName(classCM, 1,toolName);
if (noErr == osErr)
{
prefConn = CMNew(CMGetProcID(toolName), cmData, sizes, 0, 0);
/* leave the default setting as the preferance */
/************************************************************
* ORIGINALLY, the code let the user select setup at this point
* I do not want it asking for serial port prefs on startup
* if they are not set.
* ---allow the user to select a prefered tool and configuration
* ---osErr = CMChoose(&prefConn, where, nil);
************************************************************/
/* write the prefered tool name to the preference file */
HLock((Handle) prefConn);
CMGetToolName((**prefConn).procID, toolName);
HUnlock((Handle) prefConn);
h = NewHandle(1 + toolName[0]);
HLock(h);
BlockMove(toolName, *h, GetHandleSize(h));
HUnlock(h);
AddResource(h, 'pTXT', 0, "");
ReleaseResource(h);
/* write the prefered configuration to the preference file */
p = CMGetConfig(prefConn);
h = NewHandle(GetPtrSize(p));
HLock(h);
BlockMove(p, *h, GetHandleSize(h));
HUnlock(h);
AddResource(h, 'cTXT', 0, "");
ReleaseResource(h);
DisposPtr(p);
/* dispose of the connection */
CMDispose(prefConn);
}
/* close the file so that it can be used in a shared environment */
CloseResFile(prefRefNum);
/* set flag saying the prefs file is OK */
prefFileOKFlag = 0xAA;
}
}
}
}
}
}
}
//**************************************
//* get resource String from Prefs file
//*
//***************************************
Boolean getPrefsResourceStr(prefsFileName, perfsResType, returnString)
Str255 prefsFileName;
OSType perfsResType;
Str255 returnString;
{
short prefRefNum;
Handle h;
int i;
Size hSize;
Boolean returnFlag;
h = nil;
returnFlag = false;
/* did the prefs file get opened or created OK */
if (prefFileOKFlag == 0xAA)
{
/* focus on the preference file */
prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
if (prefRefNum != -1)
{
h = Get1Resource(perfsResType, 0);
if (h != nil)
{
hSize = GetHandleSize(h); /* get the size of the handle */
if (hSize > 255) hSize = 255;
HLock(h);
/* had to have this to make it compile under THINK C 5.0 */
for (i=0; i< hSize; i++)
{
returnString[i] = *(*h+i);
}
HUnlock(h);
ReleaseResource(h);
returnFlag = true; //* prefs string is OK
}
CloseResFile(prefRefNum);
}
}
else
{
//* generate prefs file alert message
}
return(returnFlag);
}
//**************************************
//* get resource String from Prefs file
//*
//***************************************
Boolean getPrefsResourceData( Str255 prefsFileName,
OSType perfsResType,
char *returnString,
long maxLen)
{
short prefRefNum;
Handle h;
int i;
Size hSize;
Boolean returnFlag;
h = nil;
returnFlag = false;
/* did the prefs file get opened or created OK */
if (prefFileOKFlag == 0xAA)
{
/* focus on the preference file */
prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
if (prefRefNum != -1)
{
h = Get1Resource(perfsResType, 0);
if (h != nil)
{
hSize = GetHandleSize(h); /* get the size of the handle */
if (hSize > maxLen) hSize = maxLen;
HLock(h);
/* had to have this to make it compile under THINK C 5.0 */
for (i=0; i< hSize; i++)
{
returnString[i] = *(*h+i);
}
HUnlock(h);
ReleaseResource(h);
returnFlag = true; //* prefs string is OK
}
CloseResFile(prefRefNum);
}
}
else
{
//* generate prefs file alert message
}
return(returnFlag);
}
//**************************************
//* save resource to Prefs file
//*
//***************************************
void setPrefsResourceStr(Str255 prefsFileName, OSType perfsResType,char *buffer, long bufLen)
{
short prefRefNum;
Handle h;
int i;
Size hSize, newSize;
h = nil;
/* did the prefs file get opened or created OK */
if (prefFileOKFlag == 0xAA)
{
/* focus on the preference file */
prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
if (prefRefNum != -1)
{
//* get the specified resource */
h = Get1Resource(perfsResType, 0);
if (h == nil)
{
/* resource did not exist, add it to resource file */
h = NewHandle(1 + bufLen);
HLock(h);
BlockMove(buffer, *h, GetHandleSize(h));
HUnlock(h);
AddResource(h, perfsResType, 0, "\p");
}
else
{
/* resoure DOES exist, change it */
hSize = GetHandleSize(h); /* get the size of the handle */
/* check for size of handle */
if (hSize != (1 + bufLen))
{
newSize = 1 + bufLen;
SetHandleSize(h, newSize);
}
HLock(h);
BlockMove(buffer, *h, GetHandleSize(h));
HUnlock(h);
ChangedResource(h);
}
ReleaseResource(h);
CloseResFile(prefRefNum);
}
}
else
{
PutAlertMessage("\pProgram cant find prefs file");
}
}
/**************************************
* set Comm Tool Box connection preference
*
****************************************/
void setCTBpref(prefsFileName)
Str255 prefsFileName;
{
short prefRefNum;
OSErr osErr, cmChooseReturnCode;
int i;
Str255 prefStr;
short procID;
Str63 toolName;
Handle h;
Ptr p;
short iErr;
Size hSize, newSize;
ConnHandle docConn;
CMBufferSizes sizes = { 0, 0, 0, 0, 0, 0, 0, 0 };
Point where = { 40, 40 };
if (isCTBavailable())
{
/* did the prefs file get opened or created OK */
if (prefFileOKFlag == 0xAA)
{
getPrefsResourceStr(prefsFileName,'pTXT', prefStr);
procID = CMGetProcID(prefStr);
if (procID != -1)
{
/* create a new connection */
docConn = CMNew(procID, cmData, sizes, 0, 0);
if (docConn != nil)
{
/* set the prefered configuration */
getPrefsResourceStr(prefsFileName,'cTXT', prefStr);
osErr = CMSetConfig(docConn, (char *)prefStr);
cmChooseReturnCode = CMChoose(&docConn, where, nil);
}
}
else
{
/* the prefered tool could not be found so I */
osErr = CRMGetIndToolName(classCM, 1, toolName);
docConn = CMNew(CMGetProcID(toolName), cmData, sizes, 0, 0);
if (docConn != nil)
{
cmChooseReturnCode = CMChoose(&docConn, where, nil);
}
}
if ((cmChooseReturnCode == chooseOKMinor) || (cmChooseReturnCode == chooseOKMajor))
{
/* change the prefs file */
if (prefFileOKFlag == 0xAA)
{
/* open the preference file */
prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsFileName, fsRdWrPerm);
if (prefRefNum != -1)
{
/* write the prefered tool name to the preference file */
HLock((Handle) docConn);
CMGetToolName((**docConn).procID, toolName);
HUnlock((Handle) docConn);
/* get the port TeXT resource */
h = Get1Resource('pTXT', 0);
if (h == nil)
{
/* resource did not exist, add it to resource file */
h = NewHandle(1 + toolName[0]);
HLock(h);
BlockMove(toolName, *h, GetHandleSize(h));
HUnlock(h);
AddResource(h, 'pTXT', 0, "");
}
else
{
/* resoure DOES exist, change it */
hSize = GetHandleSize(h); /* get the size of the handle */
/* check for size of handle */
if (hSize != (1 + toolName[0]))
{
newSize = 1 + toolName[0];
SetHandleSize(h, newSize);
}
HLock(h);
BlockMove(toolName, *h, GetHandleSize(h));
HUnlock(h);
ChangedResource(h);
}
ReleaseResource(h);
/* write the prefered configuration to the preference file */
p = CMGetConfig(docConn);
/* get the configuration TeXT resource */
h = Get1Resource('cTXT', 0);
if (h == nil)
{
/* resource did not exist, add it to resource file */
h = NewHandle(GetPtrSize(p));
HLock(h);
BlockMove(p, *h, GetHandleSize(h));
HUnlock(h);
AddResource(h, 'cTXT', 0, "");
iErr = ResError();
}
else
{
/* resoure DOES exist, change it */
hSize = GetHandleSize(h); /* get the size of the handle */
/* check for size of handle */
if (hSize != GetPtrSize(p))
{
newSize = GetPtrSize(p);
SetHandleSize(h, newSize);
}
HLock(h);
BlockMove(p, *h, GetHandleSize(h));
HUnlock(h);
ChangedResource(h);
}
ReleaseResource(h);
CloseResFile(prefRefNum);
iErr = ResError();
DisposPtr(p);
}
}
}
if (docConn != nil)
{
/* dispose of the connection */
CMDispose(docConn);
}
}
}
else
{
SysBeep(1);
SysBeep(1);
}
}
void DoCommPortSetup(prefsFileName)
Str255 prefsFileName;
{
setCTBpref(prefsFileName);
}
- -----------------------------------------------------
Mark Sproul - KB2ICI - New Jersey
sproul@sproul.com
+++++++++++++++++++++++++++
From: grobbins@Apple.COM (Grobbins)
Date: 7 Jan 93 09:42:05 GMT
Organization: Mac Experimental System Software
In article <D2150096.mv05jr@sproul.sproul.com> Sproul@sproul.sproul.com (Mark Sproul) writes:
>Below is code for finding and working with prefs file in system 7
>prefernce folder and working with said file
Modern Mac applications should use the FindFolder call to locate
the Preferences folder. For an example, see the DTS snippet prefs
(available via anonymous ftp from ftp.apple.com, in the directory
/dts/mac/sc/snippets/toolbox.) FindFolder is documented in the
Finder Interface chapter of Inside Mac VI.
Grobbins grobbins@apple.com
No disclaimers apply.
+++++++++++++++++++++++++++
From: neeri@iis.ethz.ch (Matthias Neeracher)
Date: 7 Jan 93 22:41:43 GMT
Organization: Integrated Systems Laboratory, ETH, Zurich
In article <D2150096.mv05jr@sproul.sproul.com>, Sproul@sproul.sproul.com (Mark Sproul) writes:
> Below is code for finding and working with prefs file in system 7
> prefernce folder and working with said file
Why is this code better than FindFolder() ?
> /* check for the preferences folder */
> (*infoPB).hFileInfo.ioFDirIndex = 0;
> (*infoPB).hFileInfo.ioVRefNum = prefVRefNum;
> (*infoPB).hFileInfo.ioDirID = sysfDirID;
> (*infoPB).hFileInfo.ioNamePtr = "\pPreferences";
>From SysTypes.r:
#define Language langEnglish, langFrench, langGerman, langItalian,
langDutch, langSwedish, langSpanish, langDanish, \
langPortuguese, langNorwegian, langHebrew, \
langJapanese, langArabic, langFinnish, langGreek, \
langIcelandic, langMaltese, langTurkish, \
langCroatian, langTradChinese, langUrdu, \
langHindi, langThai, langKorean, langLithuanian, \
langPolish, langHungarian, langEstonian, \
langLettish, langLappish, langFaeroese, \
langFarsi, langRussian, langSimpChinese, \
langFlemish, langIrish, langAlbanian, langRomanian, \
langCzech, langSlovak, langSlovenian, langYiddish, \
langSerbian, langMacedonian, langBulgarian, \
langUkrainian, langByelorussian, langUzbek, \
langKazakh, langAzerbaijani, langAzerbaijanAr, \
langArmenian, langGeorgian, langMoldavian, \
langKirghiz, langTajiki, langTurkmen, \
langMongolian, langMongolianCyr, langPashto, \
langKurdish, langKashmiri, langSindhi, langTibetan, \
langNepali, langSanskrit, langMarathi, langBengali, \
langAssamese, langGujarati, langPunjabi, langOriya, \
langMalayalam, langKannada, langTamil, langTelugu, \
langSinhalese, langBurmese, langKhmer, langLao, \
langVietnamese, langIndonesian, langTagalog, \
langMalayRoman, langMalayArabic, langAmharic, \
langTigrinya, langGalla, langSomali, langSwahili, \
langRuanda, langRundi, langChewa, langMalagasy, \
langEsperanto, langUnspecified = 32767 \
Are you sure that the speakers of all of these languages will be happy
with your hardcoding the name "Preferences" ?
Matthias
- -----
Matthias Neeracher neeri@iis.ethz.ch
"There once was an Age of Reason, but we've progressed beyond it."
-- Ayn Rand, _Atlas Shrugged_
+++++++++++++++++++++++++++
From: jesjones@stein.u.washington.edu (Jesse Jones)
Date: 9 Jan 1993 05:28:21 GMT
Organization: University of Washington, Seattle
There has been some discussion lately about how to handle a preferences
file, so I thought I'd post my code. It's nothing astounding, but I think
it has some advantages over the previously posted code:
1) FindFolder and the high level file manager routines are used so
the code is quit a bit easier to follow.
2) The GetPref procedure is passed a procedure parameter that is used to
return a default preference if the preference is missing from the
Prefs file.
3) Individual preferences are referenced by the ResType, resource name,
and version number. If the version numbers don't match the default
preference is returned.
--Jesse
Here is the interface for the Prefs module. It's written in SemperSoft
Modula-2. HANDLE is compatible with any pointer to pointer and Mstring is
a 256-byte string terminated with zero.
DEFINITION MODULE Prefs; (* By Jesse Jones, 1992 *)
FROM SYSTEM IMPORT HANDLE;
FROM Types IMPORT Str255, Mstring;
TYPE
PrefsProc = PROCEDURE(): HANDLE;
VAR
PrefsName: Str255; (* Name of the Preferences file. *)
PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE;
(* default is called if Prefs not present or version <> vers. *)
PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE);
(* Change or add a preference. *)
PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN;
PROCEDURE DeletePref (rType, rName: Mstring);
END Prefs.
And here is the implementation of the Prefs module.
IMPLEMENTATION MODULE Prefs;
(* Import list deleted *)
(* Version numbers are saved in PVRS resources. These resources contain a
single integer for the version number and are referenced by name, with
the name being the concatenation of the preference resource type and
the preference's name. *)
PROCEDURE GetVersion (rType, rName: Mstring): INTEGER;
VAR
data: IntHandle;
str : Str255;
vers: INTEGER;
err : OSErr;
BEGIN
str := StrToPStr(Concat(rType, rName));
data := Get1NamedResource('PVRS', str);
err := ResError();
IF (err = resNotFound) OR (data = NIL) THEN
vers := -1;
ELSIF ErrCheck(err) THEN
vers := data^^;
ELSE
vers := -1;
END;
RETURN vers;
END GetVersion;
PROCEDURE SetVersion (rType, rName: Mstring; version: INTEGER);
VAR
data : IntHandle;
str : Mstring;
err : OSErr;
dummy: BOOLEAN;
BEGIN
str := Concat(rType, rName);
data := Get1NamedResource('PVRS', StrToPStr(str));
err := ResError();
IF (err = resNotFound) OR (data = NIL) THEN
data := CreateHandle(SIZE(INTEGER));
data^^ := version;
dummy := InsertResource('PVRS', str, 0, data, TRUE);
ELSIF ErrCheck(err) THEN
data^^ := version;
dummy := ChangeResource(data);
END;
END SetVersion;
PROCEDURE OpenPrefs (VAR refNum: INTEGER; perm: SignedByte): BOOLEAN;
VAR
vRef : INTEGER;
dir : LONGINT;
err : OSErr;
dummy: BOOLEAN;
BEGIN
err := FindPrefsFolder(vRef, dir); (* find (or create) prefs folder *)
IF ErrCheck(err) THEN
refNum := HOpenResFile(vRef, dir, PrefsName, perm); (* open prefs file *)
err := ResError();
IF err = fnfErr THEN (* if prefs file is missing *)
HCreateResFile(vRef, dir, PrefsName); (* then create it *)
refNum := HOpenResFile(vRef, dir, PrefsName, perm); (* and open the new file *)
err := ResError();
dummy := ErrCheck(err);
END;
END;
RETURN err = noErr;
END OpenPrefs;
PROCEDURE GetPref (rType, rName: Mstring; vers: INTEGER; default: PrefsProc): HANDLE;
VAR
refNum: INTEGER;
data : HANDLE;
err : OSErr;
BEGIN
IF OpenPrefs(refNum, fsRdPerm) THEN
IF GetVersion(rType, rName) <> vers THEN
data := default();
ELSE
data := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
err := ResError();
IF (err = resNotFound) OR (data = NIL) THEN
data := default();
ELSIF ErrCheck(err) THEN
DetachResource(data);
HNoPurge(data);
ELSE
data := default();
END;
END;
CloseResFile(refNum);
ELSE
data := default();
END;
Assert(data <> NIL);
RETURN data;
END GetPref;
PROCEDURE PrefPresent (rType, rName: Mstring; vers: INTEGER): BOOLEAN;
VAR
refNum: INTEGER;
data : HANDLE;
found : BOOLEAN;
err : OSErr;
BEGIN
found := FALSE;
IF OpenPrefs(refNum, fsRdPerm) THEN
IF GetVersion(rType, rName) >= vers THEN
data := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
err := ResError();
IF (err <> resNotFound) AND (data <> NIL) THEN
found := ErrCheck(err);
END;
END;
CloseResFile(refNum);
END;
RETURN found;
END PrefPresent;
(* SetPref is the only routine to actually add a preference to the
Prefs file. Note that the preferences are purgable. *)
PROCEDURE SetPref (rType, rName: Mstring; vers: INTEGER; data: HANDLE);
VAR
refNum: INTEGER;
old : HANDLE;
err : OSErr;
done : BOOLEAN;
BEGIN
done := FALSE;
Assert(data <> NIL);
IF OpenPrefs(refNum, fsRdWrPerm) THEN
old := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
err := ResError();
IF (err = resNotFound) OR (old = NIL) THEN
done := InsertResource(rType, rName, 0, data, TRUE);
ELSIF ErrCheck(err) THEN
RemoveResource(old);
done := InsertResource(rType, rName, 0, data, TRUE);
END;
IF done THEN
SetVersion(rType, rName, vers);
DetachResource(data);
END;
CloseResFile(refNum);
END;
END SetPref;
PROCEDURE DeletePref (rType, rName: Mstring);
VAR
refNum: INTEGER;
old : HANDLE;
err : OSErr;
BEGIN
IF OpenPrefs(refNum, fsRdWrPerm) THEN
old := Get1NamedResource(StrToOS(rType), StrToPStr(rName));
err := ResError();
IF (err = resNotFound) OR (old = NIL) THEN
(* do nothing *)
ELSIF ErrCheck(err) THEN
RemoveResource(old);
END;
CloseResFile(refNum);
END;
END DeletePref;
BEGIN
PrefsName := "";
END Prefs.
InsertResource, ChangeResource, and RemoveResource are some handy resource
manipulation routines I wrote a while back. I'll post them in a seperate
message.
+++++++++++++++++++++++++++
From: jesjones@stein.u.washington.edu (Jesse Jones)
Date: 10 Jan 93 01:55:19 GMT
Organization: University of Washington, Seattle
Here are some handy routines for dealing with resources. Several of these
routines are used by the Prefs code I posted recently. A few of these routines
replace Apple's standard routines with something that behaves a little better.
--Jesse
PROCEDURE Unique (theType: ResType): INTEGER;
INLINE2(0A9C1H);
(* The toolbox trap, UniqueID, returns an unused positive resource ID
number. However, application resources need to be larger than 127 to
avoid conflicts with other resources used by the System. The below
procedure ensures that the ID is in the correct range. *)
PROCEDURE UniqueID (theType: ResType; system: BOOLEAN): INTEGER;
VAR
ID : INTEGER;
done: BOOLEAN;
BEGIN
REPEAT
ID := Unique(theType); (* always returns positive IDs *)
IF system THEN
done := ID < 128;
ELSE
done := ID > 127;
END;
UNTIL done;
RETURN ID;
END UniqueID;
PROCEDURE Unique1 (theType: ResType): INTEGER;
INLINE2(0A810H);
PROCEDURE Unique1ID (theType: ResType; system: BOOLEAN): INTEGER;
VAR
ID : INTEGER;
done: BOOLEAN;
BEGIN
REPEAT
ID := Unique1(theType); (* always returns positive IDs *)
IF system THEN
done := ID < 128;
ELSE
done := ID > 127;
END;
UNTIL done;
RETURN ID;
END Unique1ID;
PROCEDURE GetResourceName (rsrc: HANDLE): Mstring;
VAR
rID : INTEGER;
kind : ResType;
rName: Str255;
BEGIN
GetResInfo(rsrc, rID, kind, rName);
RETURN PStrToStr(rName);
END GetResourceName;
PROCEDURE GetResourceID (rsrc: HANDLE): INTEGER;
VAR
rID : INTEGER;
kind : ResType;
rName: Str255;
BEGIN
GetResInfo(rsrc, rID, kind, rName);
RETURN rID;
END GetResourceID;
(* resources can be specified using either name, or ID (if name is ""). *)
PROCEDURE InsertResource (type, name: Mstring; ID: INTEGER; rsrc: HANDLE; purgeable: BOOLEAN): BOOLEAN;
VAR
rName : Str255;
rType : ResType;
refNum: INTEGER;
done : BOOLEAN;
BEGIN
done := FALSE;
rType := StrToOS(type);
IF name[0] = EOS THEN
rName := "";
ELSE
rName := StrToPStr(name);
ID := Unique1ID(rType, FALSE);
END;
AddResource(rsrc, rType, ID, rName);
IF ErrCheck(ResError()) THEN
IF purgeable THEN SetResAttrs(rsrc, resPurgeable) END; (* clears resChanged bit! *)
IF ErrCheck(ResError()) THEN
ChangedResource(rsrc);
IF ErrCheck(ResError()) THEN
refNum := CurResFile();
UpdateResFile(refNum);
done := ErrCheck(ResError());
END;
END;
END;
RETURN done;
END InsertResource;
PROCEDURE ChangeResource (rsrc: HANDLE): BOOLEAN;
VAR
refNum: INTEGER;
done : BOOLEAN;
BEGIN
done := FALSE;
ChangedResource(rsrc);
IF ErrCheck(ResError()) THEN
refNum := CurResFile();
UpdateResFile(refNum);
done := ErrCheck(ResError());
END;
RETURN done;
END ChangeResource;
PROCEDURE RemoveResource (rsrc: HANDLE);
VAR
refNum: INTEGER;
done : BOOLEAN;
BEGIN
RmveResource(rsrc);
IF ErrCheck(ResError()) THEN
refNum := CurResFile();
UpdateResFile(refNum);
KillHandle(rsrc);
done := ErrCheck(ResError());
END;
END RemoveResource;
---------------------------
End of C.S.M.P. Digest
**********************