| 1 |
/* |
| 2 |
Copyright Eric Helgeson 2023-2024. |
| 3 |
*/ |
| 4 |
|
| 5 |
#include "main.h" |
| 6 |
#include "file_ext.h" |
| 7 |
#include <Script.h> |
| 8 |
|
| 9 |
// Globals |
| 10 |
OSType gType; |
| 11 |
OSType gCreator; |
| 12 |
long gHasAppleEvents; |
| 13 |
|
| 14 |
// Change the modification date on the parent folder so the |
| 15 |
// Finder notices a change. |
| 16 |
OSErr TouchFolder(short vRefNum, long parID) |
| 17 |
{ |
| 18 |
CInfoPBRec rec; |
| 19 |
Str63 name; |
| 20 |
short err; |
| 21 |
|
| 22 |
rec.hFileInfo.ioNamePtr = name; |
| 23 |
name[0]=0; |
| 24 |
rec.hFileInfo.ioVRefNum = vRefNum; |
| 25 |
rec.hFileInfo.ioDirID = parID; |
| 26 |
rec.hFileInfo.ioFDirIndex = -1; |
| 27 |
rec.hFileInfo.ioFVersNum = 0; |
| 28 |
err = PBGetCatInfoSync(&rec); |
| 29 |
if(err) |
| 30 |
return err; |
| 31 |
GetDateTime(&rec.dirInfo.ioDrMdDat); |
| 32 |
|
| 33 |
rec.hFileInfo.ioVRefNum = vRefNum; |
| 34 |
rec.hFileInfo.ioDirID = parID; |
| 35 |
rec.hFileInfo.ioFDirIndex = -1; |
| 36 |
rec.hFileInfo.ioFVersNum = 0; |
| 37 |
rec.hFileInfo.ioNamePtr[0] = 0; |
| 38 |
err = PBSetCatInfoSync(&rec); |
| 39 |
return err; |
| 40 |
} |
| 41 |
|
| 42 |
pascal OSErr DoOpenDoc(AppleEvent *event, AppleEvent *reply, long handlerRefcon) |
| 43 |
{ |
| 44 |
FSSpec fss; |
| 45 |
AEDescList docList; |
| 46 |
OSErr err = noErr; |
| 47 |
long index, itemsInList; |
| 48 |
Size actualSize; |
| 49 |
AEKeyword keywd; |
| 50 |
DescType returnedType; |
| 51 |
short fRefNum = 0; |
| 52 |
|
| 53 |
err = AEGetParamDesc(event, keyDirectObject, typeAEList, &docList); |
| 54 |
if(err != noErr) return err; |
| 55 |
err = AECountItems(&docList, &itemsInList); |
| 56 |
if(err != noErr) return err; |
| 57 |
|
| 58 |
for(index = 1; index <= itemsInList; index++) |
| 59 |
{ |
| 60 |
err = AEGetNthPtr(&docList, index, typeFSS, &keywd, &returnedType, (Ptr)&fss, sizeof(fss), &actualSize); |
| 61 |
if(err) return err; |
| 62 |
|
| 63 |
err = FSpOpenDF(&fss, fsRdPerm, &fRefNum); |
| 64 |
if(err) return err; |
| 65 |
|
| 66 |
err = openFile(fss.name, fRefNum, fss.vRefNum, fss.parID); |
| 67 |
if(err) |
| 68 |
return err; |
| 69 |
else |
| 70 |
gHandledByDnD = true; |
| 71 |
} |
| 72 |
AEDisposeDesc(&docList); |
| 73 |
return noErr; |
| 74 |
} |
| 75 |
|
| 76 |
void InstallEventHandlers() |
| 77 |
{ |
| 78 |
EventRecord event; |
| 79 |
OSErr err = 0; |
| 80 |
short i = 0; |
| 81 |
|
| 82 |
if(Gestalt(gestaltAppleEventsAttr, &gHasAppleEvents) == noErr) |
| 83 |
{ |
| 84 |
AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, &DoOpenDoc, 0L, false); |
| 85 |
} else { |
| 86 |
return; |
| 87 |
} |
| 88 |
do { |
| 89 |
WaitNextEvent(highLevelEventMask, &event, 30, 0); |
| 90 |
i = i+1; |
| 91 |
} while(event.what != kHighLevelEvent && i < 5); |
| 92 |
if(event.what == kHighLevelEvent) |
| 93 |
AEProcessAppleEvent(&event); |
| 94 |
} |
| 95 |
|
| 96 |
OSErr MyGetWDInfo(short wdRefNum, short *vRefNum, long *dirID, long *procID) |
| 97 |
{ |
| 98 |
OSErr result; |
| 99 |
WDPBRec wdPB; |
| 100 |
wdPB.ioVRefNum = wdRefNum; |
| 101 |
wdPB.ioWDIndex = 0; |
| 102 |
wdPB.ioNamePtr = nil; |
| 103 |
result = PBGetWDInfoSync(&wdPB); |
| 104 |
*vRefNum = wdPB.ioWDVRefNum; |
| 105 |
*dirID = wdPB.ioWDDirID; |
| 106 |
*procID = wdPB.ioWDProcID; |
| 107 |
return result; |
| 108 |
} |
| 109 |
|
| 110 |
void OpenFileDialog() |
| 111 |
{ |
| 112 |
long dirID = 0, procID = 0; |
| 113 |
short volRefNum = 0; |
| 114 |
SFReply tr = {0}; |
| 115 |
short fRefNum = 0; |
| 116 |
|
| 117 |
Point where; |
| 118 |
where.h = 100; |
| 119 |
where.v = 50; |
| 120 |
|
| 121 |
do { |
| 122 |
SFGetFile(where, nil, nil, -1, nil, nil, &tr); |
| 123 |
if(tr.good) |
| 124 |
{ |
| 125 |
MyGetWDInfo(tr.vRefNum, &volRefNum, &dirID, &procID); |
| 126 |
HOpen(tr.vRefNum, dirID, tr.fName, fsRdPerm, &fRefNum); |
| 127 |
openFile(tr.fName, fRefNum, volRefNum, dirID); |
| 128 |
} |
| 129 |
} while(tr.good); |
| 130 |
} |
| 131 |
|
| 132 |
OSErr openFile(unsigned char *fName, short fRefNum, short vRefNum, long dirID) |
| 133 |
{ |
| 134 |
OSErr err = noErr; |
| 135 |
Str255 errString = "\p"; |
| 136 |
long count = BUF_SIZE; |
| 137 |
FInfo fi = {0}; |
| 138 |
Boolean found = false; |
| 139 |
char ext[5] = {0}; |
| 140 |
short i = 0, j = 0; |
| 141 |
|
| 142 |
err = FSRead(fRefNum, &count, gBuf); |
| 143 |
// eofErr == partial read, probably small file, ok to continue. |
| 144 |
if(err && err != eofErr) return err; |
| 145 |
// Check for magic in first 1024 bytes |
| 146 |
if(isBinHex4()) |
| 147 |
found = true; |
| 148 |
else if(isSit5()) |
| 149 |
found = true; |
| 150 |
else if(isSit15()) |
| 151 |
found = true; |
| 152 |
else if(isBinSit()) |
| 153 |
found = true; |
| 154 |
else if(isDsk4()) |
| 155 |
found = true; |
| 156 |
else if(isZip()) |
| 157 |
found = true; |
| 158 |
else if(isMar()) |
| 159 |
found = true; |
| 160 |
else if(isCpt()) |
| 161 |
found = true; |
| 162 |
|
| 163 |
// Checks @ 1024 |
| 164 |
if(!found && count >= 2048) |
| 165 |
{ |
| 166 |
SetFPos(fRefNum, fsFromStart, 1024); |
| 167 |
FSRead(fRefNum, &count, gBuf); |
| 168 |
|
| 169 |
if(isDsk_1024()) |
| 170 |
found = true; |
| 171 |
} |
| 172 |
|
| 173 |
if(!found) |
| 174 |
{ |
| 175 |
for(i = fName[0]; i > 0; i--) |
| 176 |
if(fName[i] == '.') |
| 177 |
break; |
| 178 |
if(i != fName[0] && (fName[0] - i) <= 5) // There is an extension |
| 179 |
{ |
| 180 |
i = i + 1; // increment to avoid '.' |
| 181 |
// Parse file extension |
| 182 |
for(; i <= fName[0]; i++) |
| 183 |
ext[j++] = fName[i]; |
| 184 |
LowerText(ext, StringLen(ext)); |
| 185 |
found = CheckFileExt(ext, &gType, &gCreator); |
| 186 |
} |
| 187 |
} |
| 188 |
|
| 189 |
if(found) |
| 190 |
{ |
| 191 |
if(gCreator != 0 && gType != 0) |
| 192 |
{ |
| 193 |
err = HGetFInfo(vRefNum, dirID, fName, &fi); |
| 194 |
if(err) return err; |
| 195 |
fi.fdType = gType; |
| 196 |
fi.fdCreator = gCreator; |
| 197 |
err = HSetFInfo(vRefNum, dirID, fName, &fi); |
| 198 |
|
| 199 |
if(err) |
| 200 |
{ |
| 201 |
NumToString(err, errString); |
| 202 |
ParamText("\pCould't set type/creator for ", fName, "\p err: ", errString); |
| 203 |
StopAlert(128, nil); |
| 204 |
} |
| 205 |
} |
| 206 |
// else unkown type/creator |
| 207 |
} else { |
| 208 |
ParamText("\pCould't determine type/creator for ", fName, "\p", "\p"); |
| 209 |
StopAlert(128, nil); |
| 210 |
} |
| 211 |
|
| 212 |
TouchFolder(vRefNum, dirID); |
| 213 |
return FSClose(fRefNum); |
| 214 |
} |
| 215 |
|
| 216 |
void main() |
| 217 |
{ |
| 218 |
MaxApplZone(); |
| 219 |
InitGraf(&qd.thePort); |
| 220 |
InitFonts(); |
| 221 |
InitWindows(); |
| 222 |
InitMenus(); |
| 223 |
TEInit(); |
| 224 |
InitDialogs(nil); |
| 225 |
InitCursor(); |
| 226 |
|
| 227 |
FlushEvents(everyEvent, 0); |
| 228 |
InstallEventHandlers(); |
| 229 |
if(!gHandledByDnD) |
| 230 |
OpenFileDialog(); |
| 231 |
return; |
| 232 |
} |
| 233 |
// TODO: Move to own file. |
| 234 |
|
| 235 |
Boolean isBinHex4() |
| 236 |
{ |
| 237 |
// FixMe: Magic can be any line in the first 8k of the file. |
| 238 |
return magicCheck("BinHex 4.0", 10, 34, 'BINA', 'SITx'); |
| 239 |
} |
| 240 |
|
| 241 |
Boolean isSit15() |
| 242 |
{ |
| 243 |
short stuffitVersion = 0; |
| 244 |
if(magicCheck("SIT!", 4, 0, 'SIT!', 'SIT!')) |
| 245 |
{ |
| 246 |
gType = 0; |
| 247 |
gCreator = 0; |
| 248 |
// 0x01 for 1.5.x, 0x02 for 1.6-4.5 |
| 249 |
stuffitVersion = gBuf[14]; |
| 250 |
return (stuffitVersion == 0x01 || stuffitVersion == 0x02) && magicCheck("rLau", 4, 10, 'SIT!', 'SIT!'); |
| 251 |
} |
| 252 |
return false; |
| 253 |
} |
| 254 |
|
| 255 |
Boolean isSit5() |
| 256 |
{ |
| 257 |
short stuffitVersion = gBuf[82]; // 0x05 |
| 258 |
return (stuffitVersion == 0x05 && magicCheck("StuffIt (c)1997", 15, 0, 'SITD', 'SIT!')); |
| 259 |
} |
| 260 |
|
| 261 |
Boolean isBinSit() |
| 262 |
{ |
| 263 |
short stuffitVersion = 0; |
| 264 |
if(magicCheck("SIT!", 4, 0 + 128, 'SIT!', 'SIT!')) |
| 265 |
{ |
| 266 |
gType = 0; |
| 267 |
gCreator = 0; |
| 268 |
// 0x01 for 1.5.x, 0x02 for 1.6-4.5 |
| 269 |
stuffitVersion = gBuf[14 + 128]; |
| 270 |
return (stuffitVersion == 0x01 || stuffitVersion == 0x02) && magicCheck("rLau", 4, 10 + 128, 'BINA', 'SITx'); |
| 271 |
} |
| 272 |
return false; |
| 273 |
} |
| 274 |
|
| 275 |
Boolean isDsk4() |
| 276 |
{ |
| 277 |
char magic[] = "\1\0"; |
| 278 |
return magicCheck(magic, 2, 52, 'dImg', 'dCpy'); |
| 279 |
} |
| 280 |
// Disk Copy 6 |
| 281 |
Boolean isDsk_1024() |
| 282 |
{ |
| 283 |
return magicCheck("BD", 2, 0, 'DDim', 'ddsk'); |
| 284 |
} |
| 285 |
|
| 286 |
// Very loose check, do last. |
| 287 |
Boolean isCpt() |
| 288 |
{ |
| 289 |
char magic[] = "\1\1"; |
| 290 |
return magicCheck(magic, 2, 0, 'PACT', 'CPCT'); |
| 291 |
} |
| 292 |
|
| 293 |
Boolean isMar() |
| 294 |
{ |
| 295 |
return magicCheck("MAR", 3, 0, 'MARf', 'MARc'); |
| 296 |
} |
| 297 |
|
| 298 |
Boolean isZip() |
| 299 |
{ |
| 300 |
return magicCheck("PK", 2, 0, 'ZIP ', 'IZip'); |
| 301 |
} |
| 302 |
|
| 303 |
short StringLen(const char *str) |
| 304 |
{ |
| 305 |
const char *s; |
| 306 |
for (s = str; *s; ++s); |
| 307 |
return (s-str); |
| 308 |
} |
| 309 |
|
| 310 |
Boolean StringCompare(const char *lhs, const char *rhs) |
| 311 |
{ |
| 312 |
short i = 0; |
| 313 |
short len = StringLen(lhs); |
| 314 |
if(len != StringLen(rhs)) return false; |
| 315 |
|
| 316 |
for(i = 0; i < len; i++) |
| 317 |
if(lhs[i] != rhs[i]) |
| 318 |
return false; |
| 319 |
return true; |
| 320 |
} |
| 321 |
|
| 322 |
Boolean magicCheck(char *magic, short len, short offset, OSType type, OSType creator) |
| 323 |
{ |
| 324 |
short i; |
| 325 |
for(i = 0; i < len; i++) |
| 326 |
if(gBuf[offset+i] != magic[i]) |
| 327 |
return false; |
| 328 |
gType = type; |
| 329 |
gCreator = creator; |
| 330 |
return true; |
| 331 |
} |