1 /*
2  * UAE - The Un*x Amiga Emulator
3  *
4  * Support for IPF/CAPS disk images
5  *
6  * Copyright 2004-2007 Richard Drummond
7  *
8  * Based on Win32 CAPS code by Toni Wilen
9  */
10 
11 #include "sysconfig.h"
12 #include "sysdeps.h"
13 
14 #ifdef CAPS
15 
16 #include "caps.h"
17 #include "zfile.h"
18 #include "caps/capsimage.h"
19 
20 static CapsLong caps_cont[4]= {-1, -1, -1, -1};
21 static int caps_locked[4];
22 static int caps_flags = DI_LOCK_DENVAR|DI_LOCK_DENNOISE|DI_LOCK_NOISE|DI_LOCK_UPDATEFD|DI_LOCK_TYPE;
23 #define LIB_TYPE 1
24 
25 
26 #if !defined HAVE_FRAMEWORK_CAPSIMAGE
27 
28 #include "uae_dlopen.h"
29 
30 #ifdef __LIBRETRO__
31 #include "retro_files.h"
32 extern char *retro_system_directory;
33 char CAPSLIB_PATH[RETRO_PATH_MAX];
34 #endif
35 
36 #ifdef _WIN32
37 #define CAPSLIB_NAME    "capsimg.dll"
38 #define DIR_SEP_CHR     '\\'
39 #else
40 #define CAPSLIB_NAME    "capsimg.so"
41 #define DIR_SEP_CHR     '/'
42 #endif
43 
44 /*
45  * Repository for function pointers to the CAPSLib routines
46  * which gets filled when we link at run-time
47  *
48  * We don't symbolically link on the Amiga, so don't need
49  * this there
50  */
51 struct {
52   void *handle;
53   CapsLong (*CAPSInit)(void);
54   CapsLong (*CAPSExit)(void);
55   CapsLong (*CAPSAddImage)(void);
56   CapsLong (*CAPSRemImage)(CapsLong id);
57   CapsLong (*CAPSLockImage)(CapsLong id, char *name);
58   CapsLong (*CAPSLockImageMemory)(CapsLong id, CapsUByte *buffer, CapsULong length, CapsULong flag);
59   CapsLong (*CAPSUnlockImage)(CapsLong id);
60   CapsLong (*CAPSLoadImage)(CapsLong id, CapsULong flag);
61   CapsLong (*CAPSGetImageInfo)(struct CapsImageInfo *pi, CapsLong id);
62   CapsLong (*CAPSLockTrack)(struct CapsTrackInfo *pi, CapsLong id, CapsULong cylinder, CapsULong head, CapsULong flag);
63   CapsLong (*CAPSUnlockTrack)(CapsLong id, CapsULong cylinder, CapsULong head);
64   CapsLong (*CAPSUnlockAllTracks)(CapsLong id);
65   char *(*CAPSGetPlatformName)(CapsULong pid);
66   CapsLong (*CAPSGetVersionInfo)(struct CapsVersionInfo *pi, CapsULong flag);
67 } capslib;
68 
69 /*
70  * The Unix/dlopen method for loading and linking the CAPSLib plug-in
71  */
load_capslib(void)72 static int load_capslib (void)
73 {
74     /* This could be done more elegantly ;-) */
75 
76 #ifdef __LIBRETRO__
77     snprintf(CAPSLIB_PATH, RETRO_PATH_MAX, "%s%c%s", retro_system_directory, DIR_SEP_CHR, CAPSLIB_NAME);
78     if ((capslib.handle = uae_dlopen(CAPSLIB_PATH))) {
79 #else
80     if ((capslib.handle = uae_dlopen(CAPSLIB_NAME))) {
81 #endif
82     write_log (CAPSLIB_NAME " opened\n");
83 	capslib.CAPSInit            = uae_dlsym (capslib.handle, "CAPSInit");            if (capslib.CAPSInit == NULL) return 0;
84 	capslib.CAPSExit            = uae_dlsym (capslib.handle, "CAPSExit");            if (capslib.CAPSExit == NULL) return 0;
85 	capslib.CAPSAddImage        = uae_dlsym (capslib.handle, "CAPSAddImage");        if (capslib.CAPSAddImage == NULL) return 0;
86 	capslib.CAPSRemImage        = uae_dlsym (capslib.handle, "CAPSRemImage");        if (capslib.CAPSRemImage == NULL) return 0;
87 	capslib.CAPSLockImage       = uae_dlsym (capslib.handle, "CAPSLockImage");       if (capslib.CAPSLockImage == NULL) return 0;
88 	capslib.CAPSLockImageMemory = uae_dlsym (capslib.handle, "CAPSLockImageMemory"); if (capslib.CAPSLockImageMemory == NULL) return 0;
89 	capslib.CAPSUnlockImage     = uae_dlsym (capslib.handle, "CAPSUnlockImage");     if (capslib.CAPSUnlockImage == NULL) return 0;
90 	capslib.CAPSLoadImage       = uae_dlsym (capslib.handle, "CAPSLoadImage");       if (capslib.CAPSLoadImage == NULL) return 0;
91 	capslib.CAPSGetImageInfo    = uae_dlsym (capslib.handle, "CAPSGetImageInfo");    if (capslib.CAPSGetImageInfo == NULL) return 0;
92 	capslib.CAPSLockTrack       = uae_dlsym (capslib.handle, "CAPSLockTrack");       if (capslib.CAPSLockTrack == NULL) return 0;
93 	capslib.CAPSUnlockTrack     = uae_dlsym (capslib.handle, "CAPSUnlockTrack");     if (capslib.CAPSUnlockTrack == NULL) return 0;
94 	capslib.CAPSUnlockAllTracks = uae_dlsym (capslib.handle, "CAPSUnlockAllTracks"); if (capslib.CAPSUnlockAllTracks == NULL) return 0;
95 	capslib.CAPSGetPlatformName = uae_dlsym (capslib.handle, "CAPSGetPlatformName"); if (capslib.CAPSGetPlatformName == NULL) return 0;
96 	capslib.CAPSGetVersionInfo  = uae_dlsym (capslib.handle, "CAPSGetVersionInfo");  if (capslib.CAPSGetVersionInfo == NULL) return 0;
97 	if (capslib.CAPSInit() != imgeOk)
98 		write_log ("Error while opening " CAPSLIB_NAME "\n");
99 	    return 1;
100     }
101     write_log ("Unable to open " CAPSLIB_NAME "\n");
102     return 0;
103 }
104 
105 /*
106  * Some defines so that we don't care that CAPSLib
107  * isn't statically linked
108  */
109 #define CAPSInit            capslib.CAPSInit
110 #define CAPSExit            capslib.CAPSExit
111 #define CAPSAddImage        capslib.CAPSAddImage
112 #define CAPSRemImage        capslib.CAPSRemImage
113 #define CAPSLockImage       capslib.CAPSLockImage
114 #define CAPSLockImageMemory capslib.CAPSLockImageMemory
115 #define CAPSUnlockImage     capslib.CAPSUnlockImage
116 #define CAPSLoadImage       capslib.CAPSLoadImage
117 #define CAPSGetImageInfo    capslib.CAPSGetImageInfo
118 #define CAPSLockTrack       capslib.CAPSLockTrack
119 #define CAPSUnlockTrack     capslib.CAPSUnlockTrack
120 #define CAPSUnlockAllTracks capslib.CAPSUnlockAllTracks
121 #define CAPSGetPlatformName capslib.CAPSGetPlatformName
122 #define CAPSGetVersionInfo  capslib.CAPSGetVersionInfo
123 
124 #else
125 #ifdef HAVE_FRAMEWORK_CAPSIMAGE
126 
127 /*
128  * On OS X we link weakly to the CAPSImage framework. Thus we can
129  * let the dynamic linker take care of everything.
130  */
131 
132 /* We check for the existence of this symbol to tell whether the
133  * the framework could be linked or not.
134  */
135 extern CapsLong CAPSInit(void) __attribute__((weak));
136 
137 static int load_capslib (void)
138 {
139     if (CAPSInit != NULL)
140 	return 1;
141     else
142 	return 0;
143 }
144 
145 #else
146 
147 #ifdef TARGET_AMIGAOS
148 #ifdef __amigaos4__
149 #define __USE_BASETYPE__
150 #include <exec/emulation.h>
151 #include <proto/exec.h>
152 
153 static struct Device *CapsImageBase;
154 
155 /* Emulation stubs for AmigaOS4. Ideally this should be in a separate
156  * link library (until a native CAPS plug-in becomes available), but I
157  * haven't been able to get that to work.
158  *
159  * This stuff is adapated from the machine-generated output from fdtrans.
160  */
161 LONG CAPSInit (void)
162 {
163     struct Library *LibBase = (struct Library *)CapsImageBase;
164     LONG retval;
165     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
166     ULONG save_A6 = regs[14];
167 
168     retval = (LONG)EmulateTags ((APTR)LibBase,
169 				ET_Offset,      -42, /* Hex: -0x2A */
170 				ET_RegisterA6,  LibBase,
171 				TAG_DONE);
172     regs[14] = save_A6;
173     return retval;
174 }
175 
176 LONG CAPSExit (void)
177 {
178     struct Library *LibBase = (struct Library *)CapsImageBase;
179     LONG retval;
180     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
181     ULONG save_A6 = regs[14];
182 
183     retval = (LONG)EmulateTags ((APTR)LibBase,
184 				ET_Offset,      -48, /* Hex: -0x30 */
185 				ET_RegisterA6,  LibBase,
186 				TAG_DONE);
187     regs[14] = save_A6;
188     return retval;
189 }
190 
191 LONG CAPSAddImage (void)
192 {
193     struct Library *LibBase = (struct Library *)CapsImageBase;
194     LONG retval;
195     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
196     ULONG save_A6 = regs[14];
197 
198     retval = (LONG)EmulateTags ((APTR)LibBase,
199 				ET_Offset,      -54, /* Hex: -0x36 */
200 				ET_RegisterA6,  LibBase,
201 				TAG_DONE);
202     regs[14] = save_A6;
203     return retval;
204 }
205 
206 LONG CAPSRemImage (LONG id)
207 {
208     struct Library *LibBase = (struct Library*) CapsImageBase;
209     LONG retval;
210     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
211     ULONG save_A6 = regs[14];
212 
213     retval = (LONG)EmulateTags ((APTR)LibBase,
214 				ET_Offset,      -60, /* Hex: -0x3C */
215 				ET_RegisterD0,  id,
216 				ET_RegisterA6,  LibBase,
217 				TAG_DONE);
218     regs[14] = save_A6;
219     return retval;
220 }
221 
222 CapsLong CAPSLockImage (CapsLong id, char *name)
223 {
224     struct Library *LibBase = (struct Library*)CapsImageBase;
225     LONG retval;
226     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
227     ULONG save_a0 = regs[8];
228     ULONG save_A6 = regs[14];
229 
230     retval = (LONG)EmulateTags ((APTR)LibBase,
231 				ET_Offset,      -66, /* Hex: -0x42 */
232 				ET_RegisterD0,  id,
233 				ET_RegisterA0,  name,
234 				ET_RegisterA6,  LibBase,
235 				TAG_DONE);
236     regs[8]  = save_a0;
237     regs[14] = save_A6;
238     return retval;
239 }
240 
241 LONG CAPSLockImageMemory (LONG id,
242 			  UBYTE * buffer,
243 			  ULONG length,
244 			  ULONG flag)
245 {
246     struct Library *LibBase = (struct Library*)CapsImageBase;
247     LONG retval;
248     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
249     ULONG save_a0 = regs[8];
250     ULONG save_d2 = regs[2];
251     ULONG save_A6 = regs[14];
252 
253     retval = (LONG)EmulateTags ((APTR)LibBase,
254 				ET_Offset,      -72, /* Hex: -0x48 */
255 				ET_RegisterD0,  id,
256 				ET_RegisterA0,  buffer,
257 				ET_RegisterD1,  length,
258 				ET_RegisterD2,  flag,
259 				ET_RegisterA6,  LibBase,
260 				TAG_DONE);
261     regs[8]  = save_a0;
262     regs[2]  = save_d2;
263     regs[14] = save_A6;
264     return retval;
265 }
266 
267 LONG CAPSUnlockImage (LONG id)
268 {
269     struct Library *LibBase = (struct Library*)CapsImageBase;
270     LONG retval;
271     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
272     ULONG save_A6 = regs[14];
273 
274     retval = (LONG)EmulateTags ((APTR)LibBase,
275 				ET_Offset,      -78, /* Hex: -0x4E */
276 				ET_RegisterD0,  id,
277 				ET_RegisterA6,  LibBase,
278 				TAG_DONE);
279     regs[14] = save_A6;
280     return retval;
281 }
282 
283 LONG CAPSLoadImage (LONG id, ULONG flag)
284 {
285     struct Library *LibBase = (struct Library *)CapsImageBase;
286     LONG retval;
287     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
288     ULONG save_A6 = regs[14];
289 
290     retval = (LONG)EmulateTags ((APTR)LibBase,
291 				ET_Offset,      -84, /* Hex: -0x54 */
292 				ET_RegisterD0,  id,
293 				ET_RegisterD1,  flag,
294 				ET_RegisterA6,  LibBase,
295 				TAG_DONE);
296     regs[14] = save_A6;
297     return retval;
298 }
299 
300 LONG CAPSGetImageInfo (struct CapsImageInfo * pi, LONG id)
301 {
302     struct Library *LibBase = (struct Library*)CapsImageBase;
303     LONG retval;
304     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
305     ULONG save_a0 = regs[8];
306     ULONG save_A6 = regs[14];
307 
308     retval = (LONG)EmulateTags ((APTR)LibBase,
309 				ET_Offset,      -90, /* Hex: -0x5A */
310 				ET_RegisterA0,  pi,
311 				ET_RegisterD0,  id,
312 				ET_RegisterA6,  LibBase,
313 				TAG_DONE);
314     regs[8]  = save_a0;
315     regs[14] = save_A6;
316     return retval;
317 }
318 
319 LONG CAPSLockTrack (struct CapsTrackInfo * pi,
320 		    LONG id,
321 		    ULONG cylinder,
322 		    ULONG head,
323 		    ULONG flag)
324 {
325     struct Library *LibBase = (struct Library*)CapsImageBase;
326     LONG retval;
327     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
328     ULONG save_a0 = regs[8];
329     ULONG save_d2 = regs[2];
330     ULONG save_d3 = regs[3];
331     ULONG save_A6 = regs[14];
332 
333     retval = (LONG) EmulateTags((APTR)LibBase,
334 				ET_Offset,      -96, /* Hex: -0x60 */
335 				ET_RegisterA0,  pi,
336 				ET_RegisterD0,  id,
337 				ET_RegisterD1,  cylinder,
338 				ET_RegisterD2,  head,
339 				ET_RegisterD3,  flag,
340 				ET_RegisterA6,  LibBase,
341 				TAG_DONE);
342     regs[8]  = save_a0;
343     regs[2]  = save_d2;
344     regs[3]  = save_d3;
345     regs[14] = save_A6;
346     return retval;
347 }
348 
349 LONG CAPSUnlockAllTracks (LONG id)
350 {
351     struct Library *LibBase = (struct Library*)CapsImageBase;
352     LONG retval;
353     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
354     ULONG save_A6 = regs[14];
355 
356     retval = (LONG) EmulateTags ((APTR)LibBase,
357 				 ET_Offset,      -108, /* Hex: -0x6C */
358 				 ET_RegisterD0,  id,
359 				 ET_RegisterA6,  LibBase,
360 				 TAG_DONE);
361     regs[14] = save_A6;
362     return retval;
363 }
364 
365 LONG CAPSGetVersionInfo (struct CapsVersionInfo *pi, CapsULong flag)
366 {
367     struct Library *LibBase = (struct Library*)CapsImageBase;
368     LONG retval;
369     ULONG *regs   = (ULONG *)(SysBase->EmuWS);
370     ULONG save_a0 = regs[8];
371     ULONG save_A6 = regs[14];
372 
373     retval = (LONG) EmulateTags ((APTR)LibBase,
374 				 ET_Offset,      -120, /* Hex: -0x78 */
375 				 ET_RegisterA0,  pi,
376 				 ET_RegisterD0,  flag,
377 				 ET_RegisterA6,  LibBase,
378 				 TAG_DONE);
379     regs[8]  = save_a0;
380     regs[14] = save_A6;
381     return retval;
382 }
383 
384 #else
385 
386 #if 0
387 /* proto file is broken in current CAPS API */
388 #include <proto/capsimage.h>
389 #else
390 #if defined __GNUC__ && !defined __amigaos4__
391 #include <inline/capsimage.h>
392 static struct Device *CapsImageBase;
393 #endif
394 #endif
395 #endif
396 
397 #include <proto/exec.h>
398 
399 static struct MsgPort   *CAPS_MsgPort;
400 static struct IORequest *CAPS_IOReq;
401 
402 static void unload_capslib (void)
403 {
404     CloseDevice (CAPS_IOReq);
405     DeleteIORequest (CAPS_IOReq);
406     DeleteMsgPort (CAPS_MsgPort);
407 }
408 
409 static int load_capslib (void)
410 {
411     if ((CAPS_MsgPort = CreateMsgPort ())) {
412 	if ((CAPS_IOReq = CreateIORequest (CAPS_MsgPort, sizeof(struct IORequest)))) {
413 	    if (!OpenDevice(CAPS_NAME, 0, CAPS_IOReq, 0)) {
414 		CapsImageBase = CAPS_IOReq->io_Device;
415 		if (CapsImageBase->dd_Library.lib_Version >= 2) {
416 		    if (CAPSInit () == imgeOk) {
417 			atexit (unload_capslib);
418 			return 1;
419 		    }
420 		} else
421 		    write_log ("CAPS: Please install capsimage.device version 2 or newer.\n");
422 
423 		CloseDevice (CAPS_IOReq);
424 	    }
425 	    DeleteIORequest (CAPS_IOReq);
426 	}
427 	DeleteMsgPort (CAPS_MsgPort);
428     }
429     return 0;
430 }
431 
432 #else
433 
434 /*
435  * Sorry, we don't know how to load the CAPSLib plug-in
436  * on other systems yet . ..
437  */
438 static int load_capslib (void)
439 {
440     return 0;
441 }
442 
443 #endif
444 #endif
445 #endif
446 
447 /*
448  * CAPS support proper starts here
449  *
450  * This is more or less a straight copy of Toni's Win32 code
451  */
452 int caps_init (void)
453 {
454     static int init, noticed;
455     unsigned int i;
456     struct CapsVersionInfo cvi;
457 
458     if (init)
459 		return 1;
460 
461 	if (!load_capslib ()) {
462 		write_log ("Failed to load CAPS plug-in.\n");
463 		if (noticed)
464 		    return 0;
465 		gui_message ("This disk image needs the C.A.P.S. plugin "
466 			     "which is available from "
467 			     "http://www.softpres.org/download");
468 		noticed = 1;
469 		return 0;
470     }
471     init = 1;
472     cvi.type = LIB_TYPE;
473 	CAPSGetVersionInfo (&cvi, 0);
474     write_log ("CAPS: library version %d.%d\n", cvi.release, cvi.revision);
475     for (i = 0; i < 4; i++)
476 		caps_cont[i] = CAPSAddImage ();
477 
478     return 1;
479 }
480 
481 void caps_unloadimage (unsigned int drv)
482 {
483     if (!caps_locked[drv])
484 	return;
485     CAPSUnlockAllTracks (caps_cont[drv]);
486     CAPSUnlockImage (caps_cont[drv]);
487     caps_locked[drv] = 0;
488 }
489 
490 int caps_loadimage (struct zfile *zf, unsigned int drv, unsigned int *num_tracks)
491 {
492     struct CapsImageInfo ci;
493     int len, ret;
494     uae_u8 *buf;
495     char s1[100];
496     struct CapsDateTimeExt *cdt;
497 
498     if (!caps_init ())
499 	return 0;
500     caps_unloadimage (drv);
501     zfile_fseek (zf, 0, SEEK_END);
502     len = zfile_ftell (zf);
503     zfile_fseek (zf, 0, SEEK_SET);
504     buf = xmalloc (uae_u8, len);
505     if (!buf)
506 	return 0;
507     if (zfile_fread (buf, len, 1, zf) == 0)
508 	return 0;
509     ret = CAPSLockImageMemory (caps_cont[drv], buf, len, 0);
510     xfree (buf);
511     if (ret != imgeOk)
512 	return 0;
513     caps_locked[drv] = 1;
514     CAPSGetImageInfo (&ci, caps_cont[drv]);
515     *num_tracks = (ci.maxcylinder - ci.mincylinder + 1) * (ci.maxhead - ci.minhead + 1);
516     CAPSLoadImage (caps_cont[drv], caps_flags);
517     cdt = &ci.crdt;
518     sprintf (s1, "%d.%d.%d %d:%d:%d", cdt->day, cdt->month, cdt->year, cdt->hour, cdt->min, cdt->sec);
519     write_log ("CAPS: type:%d date:%s rel:%d rev:%d\n",
520 	       ci.type, s1, ci.release, ci.revision);
521     return 1;
522 }
523 
524 int caps_loadrevolution (uae_u16 *mfmbuf, unsigned int drv, unsigned int track, unsigned int *tracklength)
525 {
526     unsigned int len, i;
527     uae_u16 *mfm;
528     struct CapsTrackInfoT1 ci;
529 
530     ci.type = LIB_TYPE;
531     CAPSLockTrack ((struct CapsTrackInfo *)&ci, caps_cont[drv], track / 2, track & 1, caps_flags);
532     len = ci.tracklen;
533     *tracklength = len * 8;
534     mfm = mfmbuf;
535     for (i = 0; i < (len + 1) / 2; i++) {
536 	uae_u8 *data = ci.trackbuf + i * 2;
537 	*mfm++ = 256 * *data + *(data + 1);
538     }
539     return 1;
540 }
541 
542 int caps_loadtrack (uae_u16 *mfmbuf, uae_u16 *tracktiming, unsigned int drv, unsigned int track, unsigned int *tracklength, int *multirev, unsigned int *gapoffset)
543 {
544     unsigned int i, len, type;
545     uae_u16 *mfm;
546     struct CapsTrackInfoT1 ci;
547 
548     ci.type = LIB_TYPE;
549 	if (tracktiming)
550 	    *tracktiming = 0;
551     CAPSLockTrack ((struct CapsTrackInfo *)&ci, caps_cont[drv], track / 2, track & 1, caps_flags);
552     mfm = mfmbuf;
553     *multirev = (ci.type & CTIT_FLAG_FLAKEY) ? 1 : 0;
554     type = ci.type & CTIT_MASK_TYPE;
555     len = ci.tracklen;
556     *tracklength = len * 8;
557 	*gapoffset = ci.overlap >= 0 ? ci.overlap * 8 : -1;
558     for (i = 0; i < (len + 1) / 2; i++) {
559 		uae_u8 *data = ci.trackbuf + i * 2;
560 		*mfm++ = 256 * *data + *(data + 1);
561     }
562 	if (ci.timelen > 0 && tracktiming) {
563 		for (i = 0; i < ci.timelen; i++)
564 		    tracktiming[i] = (uae_u16)ci.timebuf[i];
565     }
566     return 1;
567 }
568 
569 #else /* #ifdef CAPS */
570 
571 /* Stop OS X linker complaining about empty link library */
572 
573 void caps_dummy (void);
574 void caps_dummy (void)
575 {
576 }
577 
578 #endif
579