Download
nulleric
/Fix-a-Fork
/main.c
(View History)
eric Release 1.0.1-a | Latest amendment: 36 on 2024-04-05 |
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 | } |