2 Copyright 2010, jimmikaelkael <jimmikaelkael@wanadoo.fr>
5 Licenced under Academic Free License version 3.0
6 Review Open PS2 Loader README & LICENSE files for further details.
12 #include "ioman_add.h"
13 #include <io_common.h>
24 #define DPRINTF(args...) printf(args)
26 #define DPRINTF(args...) do { } while(0)
29 #define MODNAME "genvmc"
30 IRX_ID(MODNAME
, 1, 1);
32 // driver ops protypes
33 int genvmc_dummy(void);
34 int genvmc_init(iop_device_t
*dev
);
35 int genvmc_deinit(iop_device_t
*dev
);
36 int genvmc_devctl(iop_file_t
*f
, const char *name
, int cmd
, void *args
, u32 arglen
, void *buf
, u32 buflen
);
38 // driver ops func tab
39 void *genvmc_ops
[27] = {
70 static iop_ext_device_t genvmc_dev
= {
72 IOP_DT_FS
| IOP_DT_FSEXT
,
75 (struct _iop_ext_device_ops
*)&genvmc_ops
90 int sceCdRC(cd_clock_t
*rtc
); // #51
92 int sceMcDetectCard(int port
, int slot
); // #05
93 int sceMcReadPage(int port
, int slot
, int page
, char *mcbuffer
); // #18
94 int sceMcGetCardType(int port
, int slot
); // #39
98 #define SCE_STM_R 0x01
99 #define SCE_STM_W 0x02
100 #define SCE_STM_X 0x04
101 #define SCE_STM_C 0x08
102 #define SCE_STM_F 0x10
103 #define SCE_STM_D 0x20
104 #define sceMcFileAttrReadable SCE_STM_R
105 #define sceMcFileAttrWriteable SCE_STM_W
106 #define sceMcFileAttrExecutable SCE_STM_X
107 #define sceMcFileAttrDupProhibit SCE_STM_C
108 #define sceMcFileAttrFile SCE_STM_F
109 #define sceMcFileAttrSubdir SCE_STM_D
110 #define sceMcFileCreateDir 0x0040
111 #define sceMcFileAttrClosed 0x0080
112 #define sceMcFileCreateFile 0x0200
113 #define sceMcFile0400 0x0400
114 #define sceMcFileAttrPDAExec 0x0800
115 #define sceMcFileAttrPS1 0x1000
116 #define sceMcFileAttrHidden 0x2000
117 #define sceMcFileAttrExists 0x8000
119 // SONY superblock magic & version
120 static char SUPERBLOCK_MAGIC
[] = "Sony PS2 Memory Card Format ";
121 static char SUPERBLOCK_VERSION
[] = "1.2.0.0";
124 typedef struct { // size = 384
125 u8 magic
[28]; // Superblock magic, on PS2 MC : "Sony PS2 Memory Card Format "
126 u8 version
[12]; // Version number of the format used, 1.2 indicates full support for bad_block_list
127 s16 pagesize
; // size in bytes of a memory card page
128 u16 pages_per_cluster
; // number of pages in a cluster
129 u16 blocksize
; // number of pages in an erase block
130 u16 unused
; // unused
131 u32 clusters_per_card
; // total size in clusters of the memory card
132 u32 alloc_offset
; // Cluster offset of the first allocatable cluster. Cluster values in the FAT and directory entries are relative to this. This is the cluster immediately after the FAT
133 u32 alloc_end
; // The cluster after the highest allocatable cluster. Relative to alloc_offset. Not used
134 u32 rootdir_cluster
; // First cluster of the root directory. Relative to alloc_offset. Must be zero
135 u32 backup_block1
; // Erase block used as a backup area during programming. Normally the the last block on the card, it may have a different value if that block was found to be bad
136 u32 backup_block2
; // This block should be erased to all ones. Normally the the second last block on the card
138 u32 ifc_list
[32]; // List of indirect FAT clusters. On a standard 8M card there's only one indirect FAT cluster
139 int bad_block_list
[32]; // List of erase blocks that have errors and shouldn't be used
140 u8 cardtype
; // Memory card type. Must be 2, indicating that this is a PS2 memory card
141 u8 cardflags
; // Physical characteristics of the memory card
144 u32 FATentries_per_cluster
;
145 u32 clusters_per_block
;
147 u32 rootdir_cluster2
;
150 u32 max_allocatable_clusters
;
156 typedef struct _sceMcStDateTime
{
166 typedef struct { // size = 512
170 sceMcStDateTime created
; // 8
173 sceMcStDateTime modified
; // 24
175 u32 unused2
[7]; // 36
177 u8 unused3
[416]; // 96
182 static MCDevInfo devinfo
__attribute__((aligned(64)));
183 static u8 cluster_buf
[(BLOCKKB
* 1024)+16] __attribute__((aligned(64)));
185 static int genvmc_io_sema
= -1;
186 static int genvmc_thread_sema
= -1;
187 static int genvmc_abort_sema
= -1;
188 static int genvmc_abort_finished_sema
= -1;
190 static int genvmc_abort
= 0;
192 static statusVMCparam_t genvmc_stats
;
194 //--------------------------------------------------------------
195 static void long_multiply(u32 v1
, u32 v2
, u32
*HI
, u32
*LO
)
197 register long a
, b
, c
, d
;
200 a
= (v1
>> 16) & 0xffff;
202 c
= (v2
>> 16) & 0xffff;
207 y
= ((*LO
>> 16) & 0xffff) + x
;
209 *LO
= (*LO
& 0xffff) | ((y
& 0xffff) << 16);
210 *HI
= (y
>> 16) & 0xffff;
215 //--------------------------------------------------------------
216 static int mc_getmcrtime(sceMcStDateTime
*time
)
218 register int retries
;
224 if (sceCdRC(&cdtime
))
226 } while (--retries
> 0);
228 if (cdtime
.stat
& 128) {
229 *((u16
*)&cdtime
.month
) = 0x7d0;
239 time
->Sec
= ((((cdtime
.second
>> 4) << 2) + (cdtime
.second
>> 4)) << 1) + (cdtime
.second
& 0xf);
240 time
->Min
= ((((cdtime
.minute
>> 4) << 2) + (cdtime
.minute
>> 4)) << 1) + (cdtime
.minute
& 0xf);
241 time
->Hour
= ((((cdtime
.hour
>> 4) << 2) + (cdtime
.hour
>> 4)) << 1) + (cdtime
.hour
& 0xf);
242 time
->Day
= ((((cdtime
.day
>> 4) << 2) + (cdtime
.day
>> 4)) << 1) + (cdtime
.day
& 0xf);
244 if ((cdtime
.month
& 0x10) != 0)
245 time
->Month
= (cdtime
.month
& 0xf) + 0xa;
247 time
->Month
= cdtime
.month
& 0xf;
249 time
->Year
= ((((cdtime
.year
>> 4) << 2) + (cdtime
.year
>> 4)) << 1) + ((cdtime
.year
& 0xf) | 0x7d0);
254 //--------------------------------------------------------------
255 static int mc_writecluster(int fd
, int cluster
, void *buf
, int dup
)
257 register int r
, size
;
258 MCDevInfo
*mcdi
= (MCDevInfo
*)&devinfo
;
260 WaitSema(genvmc_abort_sema
);
263 SignalSema(genvmc_abort_sema
);
267 lseek(fd
, cluster
* mcdi
->cluster_size
, SEEK_SET
);
268 size
= mcdi
->cluster_size
* dup
;
269 r
= write(fd
, buf
, size
);
274 SignalSema(genvmc_abort_sema
);
279 //--------------------------------------------------------------
280 // this is designed to work only with the rom0 mcman for max compatibility
281 // but that means it isn't really possible to lock properly so don't write
282 // to the card during the operation. hints came from mcdump.c by Polo35
283 static int vmc_mccopy(char *filename
, int slot
, int *progress
, char *msg
)
285 MCDevInfo
*mcdi
= (MCDevInfo
*)&devinfo
;
286 unsigned int blocks
, pagesblock
, clustersblock
, i
, j
;
289 strcpy(msg
, "Checking memcard...");
290 if ((sceMcDetectCard(slot
, 0) < -1) && (sceMcGetCardType(slot
, 0) != 2))
293 if (sceMcReadPage(slot
, 0, 0, cluster_buf
))
296 memcpy(mcdi
, cluster_buf
, sizeof(MCDevInfo
));
297 if (memcmp(mcdi
->magic
, SUPERBLOCK_MAGIC
, 28))
300 pagesblock
= (BLOCKKB
* 1024) / mcdi
->pagesize
;
301 clustersblock
= pagesblock
/ mcdi
->pages_per_cluster
;
302 blocks
= mcdi
->clusters_per_card
/ clustersblock
;
304 int genvmc_fh
= open(filename
, O_RDWR
|O_CREAT
|O_TRUNC
);
308 strcpy(msg
, "Copying memcard to VMC...");
309 for (i
= 0; i
< blocks
; i
++) {
310 *progress
= i
/ (blocks
/ 99);
311 for (j
= 0; j
< pagesblock
; j
++) {
312 if(sceMcReadPage(slot
, 0, (i
* pagesblock
) + j
, &cluster_buf
[j
* mcdi
->pagesize
])) {
317 ret
= mc_writecluster(genvmc_fh
, i
* clustersblock
, cluster_buf
, clustersblock
);
319 if (ret
== -2) // it's user abort
326 for (i
= (blocks
* clustersblock
); i
< mcdi
->clusters_per_card
; i
++) {
327 for (j
= 0; j
< mcdi
->pages_per_cluster
; j
++) {
328 if (sceMcReadPage(slot
, 0, (i
* mcdi
->pages_per_cluster
) + j
, &cluster_buf
[j
* mcdi
->pagesize
])) {
333 ret
= mc_writecluster(genvmc_fh
, i
, cluster_buf
, 1);
335 if (ret
== -2) // it's user abort
349 //--------------------------------------------------------------
350 static int vmc_mcformat(char *filename
, int size_kb
, int blocksize
, int *progress
, char *msg
)
352 register int i
, r
, b
, ifc_index
, fat_index
;
353 register int ifc_length
, fat_length
, alloc_offset
;
354 register int j
= 0, z
= 0;
356 MCDevInfo
*mcdi
= (MCDevInfo
*)&devinfo
;
358 strcpy(msg
, "Creating VMC file...");
359 int genvmc_fh
= open(filename
, O_RDWR
|O_CREAT
|O_TRUNC
);
363 // set superblock magic & version
364 memset((void *)&mcdi
->magic
, 0, sizeof (mcdi
->magic
) + sizeof (mcdi
->version
));
365 strcpy((char *)&mcdi
->magic
, SUPERBLOCK_MAGIC
);
366 strcat((char *)&mcdi
->magic
, SUPERBLOCK_VERSION
);
369 mcdi
->cluster_size
= 1024; // size in KB of clusters
370 mcdi
->blocksize
= blocksize
; // how many pages in a block of data
371 mcdi
->pages_per_cluster
= 2; // how many pages in a cluster
372 mcdi
->pagesize
= mcdi
->cluster_size
/ mcdi
->pages_per_cluster
;
373 mcdi
->clusters_per_block
= mcdi
->blocksize
/ mcdi
->pages_per_cluster
;
374 mcdi
->clusters_per_card
= (size_kb
*1024) / mcdi
->cluster_size
;
375 mcdi
->cardtype
= 0x02; // PlayStation2 card type
376 mcdi
->cardflags
= 0x2b;
378 mcdi
->FATentries_per_cluster
= mcdi
->cluster_size
/ sizeof(u32
);
380 // clear bad blocks list
382 mcdi
->bad_block_list
[i
] = -1;
384 // erase all clusters
385 strcpy(msg
, "Erasing VMC clusters...");
386 memset(cluster_buf
, 0xff, sizeof(cluster_buf
));
387 for (i
=0; i
<mcdi
->clusters_per_card
; i
+=16) {
388 *progress
= i
/ (mcdi
->clusters_per_card
/ 99);
389 r
= mc_writecluster(genvmc_fh
, i
, cluster_buf
, 16);
391 if (r
== -2) // it's user abort
399 // calculate fat & ifc length
400 fat_length
= (((mcdi
->clusters_per_card
<< 2) - 1) / mcdi
->cluster_size
) + 1; // get length of fat in clusters
401 ifc_length
= (((fat_length
<< 2) - 1) / mcdi
->cluster_size
) + 1; // get number of needed ifc clusters
403 if (!(ifc_length
<= 32)) {
405 fat_length
= mcdi
->FATentries_per_cluster
<< 5;
410 mcdi
->ifc_list
[i
] = -1;
411 ifc_index
= mcdi
->blocksize
/ 2;
413 for (j
=0; j
<ifc_length
; j
++, i
++)
414 mcdi
->ifc_list
[j
] = i
;
416 // keep fat cluster index
419 // allocate memory for ifc clusters
420 CpuSuspendIntr(&oldstate
);
421 u8
*ifc_mem
= AllocSysMemory(ALLOC_FIRST
, (ifc_length
* mcdi
->cluster_size
)+0XFF, NULL
);
422 CpuResumeIntr(oldstate
);
427 memset(ifc_mem
, 0, ifc_length
* mcdi
->cluster_size
);
429 // build ifc clusters
430 u32
*ifc
= (u32
*)ifc_mem
;
431 for (j
=0; j
<fat_length
; j
++, i
++) {
432 // just as security...
433 if (i
>= mcdi
->clusters_per_card
) {
434 CpuSuspendIntr(&oldstate
);
435 FreeSysMemory(ifc_mem
);
436 CpuResumeIntr(oldstate
);
443 // write ifc clusters
444 strcpy(msg
, "Writing ifc clusters...");
445 for (z
=0; z
<ifc_length
; z
++) {
446 r
= mc_writecluster(genvmc_fh
, mcdi
->ifc_list
[z
], &ifc_mem
[z
* mcdi
->cluster_size
], 1);
448 // freeing ifc clusters memory
449 CpuSuspendIntr(&oldstate
);
450 FreeSysMemory(ifc_mem
);
451 CpuResumeIntr(oldstate
);
452 if (r
== -2) // it's user abort
460 // freeing ifc clusters memory
461 CpuSuspendIntr(&oldstate
);
462 FreeSysMemory(ifc_mem
);
463 CpuResumeIntr(oldstate
);
469 mcdi
->backup_block1
= (mcdi
->clusters_per_card
/ mcdi
->clusters_per_block
) - 1;
470 mcdi
->backup_block2
= (mcdi
->clusters_per_card
/ mcdi
->clusters_per_block
) - 2;
472 // calculate number of allocatable clusters per card
474 long_multiply(mcdi
->clusters_per_card
, 0x10624dd3, &hi
, &lo
);
475 temp
= (hi
>> 6) - (mcdi
->clusters_per_card
>> 31);
476 mcdi
->max_allocatable_clusters
= (((((temp
<< 5) - temp
) << 2) + temp
) << 3) + 1;
479 // building/writing FAT clusters
480 strcpy(msg
, "Writing fat clusters...");
481 i
= (mcdi
->clusters_per_card
/ mcdi
->clusters_per_block
) - 2; // 2 backup blocks
482 for (z
=0; j
< (i
* mcdi
->clusters_per_block
); j
+=mcdi
->FATentries_per_cluster
) {
484 memset(cluster_buf
, 0, mcdi
->cluster_size
);
485 u32
*fc
= (u32
*)cluster_buf
;
486 int sz_u32
= (i
* mcdi
->clusters_per_block
) - j
;
487 if (sz_u32
> mcdi
->FATentries_per_cluster
)
488 sz_u32
= mcdi
->FATentries_per_cluster
;
489 for (b
=0; b
<sz_u32
; b
++)
490 fc
[b
] = 0x7fffffff; // marking free cluster
493 mcdi
->alloc_offset
= j
;
494 mcdi
->rootdir_cluster
= 0;
495 fc
[0] = 0xffffffff; // marking rootdir end
499 r
= mc_writecluster(genvmc_fh
, fat_index
++, cluster_buf
, 1);
501 if (r
== -2) // it's user abort
509 // calculate alloc_end
510 mcdi
->alloc_end
= (i
* mcdi
->clusters_per_block
) - mcdi
->alloc_offset
;
512 // just a security...
513 if (z
< mcdi
->clusters_per_block
) {
521 mcdi
->rootdir_cluster2
= mcdi
->rootdir_cluster
;
523 // build root directory
524 McFsEntry
*rootdir_entry
[2];
525 sceMcStDateTime time
;
527 mc_getmcrtime(&time
);
528 rootdir_entry
[0] = (McFsEntry
*)&cluster_buf
[0];
529 rootdir_entry
[1] = (McFsEntry
*)&cluster_buf
[sizeof(McFsEntry
)];
530 memset((void *)rootdir_entry
[0], 0, sizeof(McFsEntry
));
531 memset((void *)rootdir_entry
[1], 0, sizeof(McFsEntry
));
532 rootdir_entry
[0]->mode
= sceMcFileAttrExists
| sceMcFile0400
| sceMcFileAttrSubdir
| sceMcFileAttrReadable
| sceMcFileAttrWriteable
| sceMcFileAttrExecutable
;
533 rootdir_entry
[0]->length
= 2;
534 memcpy((void *)&rootdir_entry
[0]->created
, (void *)&time
, sizeof(sceMcStDateTime
));
535 memcpy((void *)&rootdir_entry
[0]->modified
, (void *)&time
, sizeof(sceMcStDateTime
));
536 rootdir_entry
[0]->cluster
= 0;
537 rootdir_entry
[0]->dir_entry
= 0;
538 strcpy(rootdir_entry
[0]->name
, ".");
539 rootdir_entry
[1]->mode
= sceMcFileAttrExists
| sceMcFileAttrHidden
| sceMcFile0400
| sceMcFileAttrSubdir
| sceMcFileAttrWriteable
| sceMcFileAttrExecutable
;
540 rootdir_entry
[1]->length
= 2;
541 memcpy((void *)&rootdir_entry
[1]->created
, (void *)&time
, sizeof(sceMcStDateTime
));
542 memcpy((void *)&rootdir_entry
[1]->modified
, (void *)&time
, sizeof(sceMcStDateTime
));
543 rootdir_entry
[1]->cluster
= 0;
544 rootdir_entry
[1]->dir_entry
= 0;
545 strcpy(rootdir_entry
[1]->name
, "..");
547 // write root directory cluster
548 strcpy(msg
, "Writing root directory cluster...");
549 r
= mc_writecluster(genvmc_fh
, mcdi
->alloc_offset
+ mcdi
->rootdir_cluster
, cluster_buf
, 1);
551 if (r
== -2) // it's user abort
558 // set superblock formatted flag
561 // finally write superblock
562 strcpy(msg
, "Writing superblock...");
563 memset(cluster_buf
, 0xff, mcdi
->cluster_size
);
564 memcpy(cluster_buf
, (void *)mcdi
, sizeof(MCDevInfo
));
565 r
= mc_writecluster(genvmc_fh
, 0, cluster_buf
, 1);
567 if (r
== -2) // it's user abort
587 //--------------------------------------------------------------
588 int genvmc_dummy(void)
593 //--------------------------------------------------------------
594 int genvmc_init(iop_device_t
*dev
)
596 genvmc_io_sema
= CreateMutex(IOP_MUTEX_UNLOCKED
);
597 genvmc_thread_sema
= CreateMutex(IOP_MUTEX_UNLOCKED
);
598 genvmc_abort_sema
= CreateMutex(IOP_MUTEX_UNLOCKED
);
603 //--------------------------------------------------------------
604 int genvmc_deinit(iop_device_t
*dev
)
606 DeleteSema(genvmc_io_sema
);
607 DeleteSema(genvmc_thread_sema
);
608 DeleteSema(genvmc_abort_sema
);
613 //--------------------------------------------------------------
614 static void VMC_create_thread(void *args
)
617 createVMCparam_t
*param
= (createVMCparam_t
*)args
;
619 WaitSema(genvmc_thread_sema
);
621 WaitSema(genvmc_abort_sema
);
623 SignalSema(genvmc_abort_sema
);
625 genvmc_stats
.VMC_status
= GENVMC_STAT_BUSY
;
626 genvmc_stats
.VMC_error
= 0;
627 genvmc_stats
.VMC_progress
= 0;
628 strcpy(genvmc_stats
.VMC_msg
, "Initializing...");
630 if (param
->VMC_card_slot
== -1)
631 r
= vmc_mcformat(param
->VMC_filename
, param
->VMC_size_mb
* 1024, param
->VMC_blocksize
, &genvmc_stats
.VMC_progress
, genvmc_stats
.VMC_msg
);
633 r
= vmc_mccopy(param
->VMC_filename
, param
->VMC_card_slot
, &genvmc_stats
.VMC_progress
, genvmc_stats
.VMC_msg
);
636 genvmc_stats
.VMC_status
= GENVMC_STAT_AVAIL
;
637 genvmc_stats
.VMC_error
= r
;
639 if (r
== -1000) { // user abort
640 remove(param
->VMC_filename
);
641 strcpy(genvmc_stats
.VMC_msg
, "VMC file creation aborted");
642 SignalSema(genvmc_abort_finished_sema
);
645 strcpy(genvmc_stats
.VMC_msg
, "Failed to format VMC file");
650 genvmc_stats
.VMC_status
= GENVMC_STAT_AVAIL
;
651 genvmc_stats
.VMC_error
= 0;
652 genvmc_stats
.VMC_progress
= 100;
653 strcpy(genvmc_stats
.VMC_msg
, "VMC file created");
656 SignalSema(genvmc_thread_sema
);
660 //--------------------------------------------------------------
661 static int vmc_create(createVMCparam_t
*param
)
663 DPRINTF("%s: vmc_create() filename=%s size_MB=%d blocksize=%d th_priority=0x%02x slot=%d\n", MODNAME
, \
664 param
->VMC_filename
, param
->VMC_size_mb
, param
->VMC_blocksize
, param
->VMC_thread_priority
, param
->VMC_card_slot
);
666 register int r
, thid
;
667 iop_thread_t thread_param
;
669 thread_param
.attr
= TH_C
;
670 thread_param
.option
= 0;
671 thread_param
.thread
= (void *)VMC_create_thread
;
672 thread_param
.stacksize
= 0x2000;
673 thread_param
.priority
= (param
->VMC_thread_priority
< 0x0f) ? 0x0f : param
->VMC_thread_priority
;
675 // creating VMC create thread
676 thid
= CreateThread(&thread_param
);
680 // starting VMC create thread
681 r
= StartThread(thid
, (void *)param
);
688 //--------------------------------------------------------------
689 static int vmc_abort(void)
691 WaitSema(genvmc_abort_sema
);
693 DPRINTF("%s: vmc_abort()\n", MODNAME
);
696 SignalSema(genvmc_abort_sema
);
698 genvmc_abort_finished_sema
= CreateMutex(IOP_MUTEX_LOCKED
);
699 WaitSema(genvmc_abort_finished_sema
);
700 DeleteSema(genvmc_abort_finished_sema
);
705 //--------------------------------------------------------------
706 static int vmc_status(statusVMCparam_t
*param
)
708 // copy global genvmc stats to output param
709 memcpy((void *)param
, (void *)&genvmc_stats
, sizeof(statusVMCparam_t
));
714 //--------------------------------------------------------------
715 int genvmc_devctl(iop_file_t
*f
, const char *name
, int cmd
, void *args
, u32 arglen
, void *buf
, u32 buflen
)
722 WaitSema(genvmc_io_sema
);
726 // VMC file creation request command
727 case GENVMC_DEVCTL_CREATE_VMC
:
728 r
= vmc_create((createVMCparam_t
*)args
);
733 // VMC file creation abort command
734 case GENVMC_DEVCTL_ABORT
:
740 // VMC file creation status command
741 case GENVMC_DEVCTL_STATUS
:
742 r
= vmc_status((statusVMCparam_t
*)buf
);
751 SignalSema(genvmc_io_sema
);
756 //--------------------------------------------------------------
757 int _start(int argc
, char** argv
)
759 DPRINTF("%s start!\n", MODNAME
);
763 if (AddDrv((iop_device_t
*)&genvmc_dev
) < 0)
764 return MODULE_NO_RESIDENT_END
;
766 return MODULE_RESIDENT_END
;
769 //--------------------------------------------------------------
770 // Extra import tables
772 DECLARE_IMPORT_TABLE(cdvdman
, 1, 1)
773 DECLARE_IMPORT(51, sceCdRC
)
776 DECLARE_IMPORT_TABLE(mcman
, 1, 1)
777 DECLARE_IMPORT(5, sceMcDetectCard
)
778 DECLARE_IMPORT(18, sceMcReadPage
)
779 DECLARE_IMPORT(39, sceMcGetCardType
)