2 Copyright 2010, jimmikaelkael <jimmikaelkael@wanadoo.fr>
4 Licenced under Academic Free License version 3.0
5 Review Open PS2 Loader README & LICENSE files for further details.
11 #define SCE_STM_R 0x01
12 #define SCE_STM_W 0x02
13 #define SCE_STM_X 0x04
14 #define SCE_STM_C 0x08
15 #define SCE_STM_F 0x10
16 #define SCE_STM_D 0x20
17 #define sceMcFileAttrReadable SCE_STM_R
18 #define sceMcFileAttrWriteable SCE_STM_W
19 #define sceMcFileAttrExecutable SCE_STM_X
20 #define sceMcFileAttrDupProhibit SCE_STM_C
21 #define sceMcFileAttrFile SCE_STM_F
22 #define sceMcFileAttrSubdir SCE_STM_D
23 #define sceMcFileCreateDir 0x0040
24 #define sceMcFileAttrClosed 0x0080
25 #define sceMcFileCreateFile 0x0200
26 #define sceMcFile0400 0x0400
27 #define sceMcFileAttrPDAExec 0x0800
28 #define sceMcFileAttrPS1 0x1000
29 #define sceMcFileAttrHidden 0x2000
30 #define sceMcFileAttrExists 0x8000
32 // SONY superblock magic & version
33 static char SUPERBLOCK_MAGIC
[] = "Sony PS2 Memory Card Format ";
34 static char SUPERBLOCK_VERSION
[] = "1.2.0.0";
37 typedef struct { // size = 384
38 u8 magic
[28]; // Superblock magic, on PS2 MC : "Sony PS2 Memory Card Format "
39 u8 version
[12]; // Version number of the format used, 1.2 indicates full support for bad_block_list
40 s16 pagesize
; // size in bytes of a memory card page
41 u16 pages_per_cluster
; // number of pages in a cluster
42 u16 blocksize
; // number of pages in an erase block
44 u32 clusters_per_card
; // total size in clusters of the memory card
45 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
46 u32 alloc_end
; // The cluster after the highest allocatable cluster. Relative to alloc_offset. Not used
47 u32 rootdir_cluster
; // First cluster of the root directory. Relative to alloc_offset. Must be zero
48 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
49 u32 backup_block2
; // This block should be erased to all ones. Normally the the second last block on the card
51 u32 ifc_list
[32]; // List of indirect FAT clusters. On a standard 8M card there's only one indirect FAT cluster
52 int bad_block_list
[32]; // List of erase blocks that have errors and shouldn't be used
53 u8 cardtype
; // Memory card type. Must be 2, indicating that this is a PS2 memory card
54 u8 cardflags
; // Physical characteristics of the memory card
57 u32 FATentries_per_cluster
;
58 u32 clusters_per_block
;
63 u32 max_allocatable_clusters
;
69 typedef struct _sceMcStDateTime
{
79 typedef struct { // size = 512
83 sceMcStDateTime created
; // 8
86 sceMcStDateTime modified
; // 24
90 u8 unused3
[416]; // 96
95 static MCDevInfo devinfo
;
96 static u8 cluster_buf
[(BLOCKKB
* 1024)+16];
98 static FILE *genvmc_fh
= NULL
;
100 //-----------------------------------------------------------------------
101 static void printVer(void)
104 printf("%s version %s (Win32 Build)\n", PROGRAM_EXTNAME
, PROGRAM_VER
);
106 printf("%s version %s\n", PROGRAM_EXTNAME
, PROGRAM_VER
);
110 //-----------------------------------------------------------------------
111 static void printUsage(void)
114 printf("Usage: %s [size_in_MB] [VMC_FILENAME]\n", PROGRAM_NAME
);
115 printf("%s command-line version %s\n\n", PROGRAM_EXTNAME
, PROGRAM_VER
);
116 printf("Example: %s 8 8MB_VMC0.bin\n", PROGRAM_NAME
);
119 //--------------------------------------------------------------
120 static void long_multiply(u32 v1
, u32 v2
, u32
*HI
, u32
*LO
)
122 register long a
, b
, c
, d
;
125 a
= (v1
>> 16) & 0xffff;
127 c
= (v2
>> 16) & 0xffff;
132 y
= ((*LO
>> 16) & 0xffff) + x
;
134 *LO
= (*LO
& 0xffff) | ((y
& 0xffff) << 16);
135 *HI
= (y
>> 16) & 0xffff;
140 //--------------------------------------------------------------
141 static int mc_getmcrtime(sceMcStDateTime
*mctime
)
147 ptm
= gmtime(&rawtime
);
150 mctime
->Sec
= ((((ptm
->tm_sec
>> 4) << 2) + (ptm
->tm_sec
>> 4)) << 1) + (ptm
->tm_sec
& 0xf);
151 mctime
->Min
= ((((ptm
->tm_min
>> 4) << 2) + (ptm
->tm_min
>> 4)) << 1) + (ptm
->tm_min
& 0xf);
152 mctime
->Hour
= ((((ptm
->tm_hour
>> 4) << 2) + (ptm
->tm_hour
>> 4)) << 1) + (ptm
->tm_hour
& 0xf);
153 mctime
->Day
= ((((ptm
->tm_mday
>> 4) << 2) + (ptm
->tm_mday
>> 4)) << 1) + (ptm
->tm_mday
& 0xf);
155 mctime
->Month
= (ptm
->tm_mon
+1) & 0xf;
157 mctime
->Year
= (((((ptm
->tm_year
-100) >> 4) << 2) + ((ptm
->tm_year
-100) >> 4)) << 1) + (((ptm
->tm_year
-100) & 0xf) | 0x7d0);
162 //--------------------------------------------------------------
163 static int mc_writecluster(FILE *fd
, int cluster
, void *buf
, int dup
)
165 register int r
, size
;
166 MCDevInfo
*mcdi
= (MCDevInfo
*)&devinfo
;
168 fseek(fd
, cluster
* mcdi
->cluster_size
, SEEK_SET
);
169 size
= mcdi
->cluster_size
* dup
;
170 r
= fwrite(buf
, 1, size
, fd
);
177 //--------------------------------------------------------------
178 static int vmc_mcformat(char *filename
, int size_kb
, int blocksize
)
180 register int i
, r
, b
, ifc_index
, fat_index
;
181 register int ifc_length
, fat_length
, alloc_offset
;
182 register int ret
, j
= 0, z
= 0;
183 MCDevInfo
*mcdi
= (MCDevInfo
*)&devinfo
;
185 printf("1. creating VMC file...\n");
186 genvmc_fh
= fopen(filename
, "wb");
187 if (genvmc_fh
== NULL
)
190 // set superblock magic & version
191 memset((void *)&mcdi
->magic
, 0, sizeof (mcdi
->magic
) + sizeof (mcdi
->version
));
192 strcpy((char *)&mcdi
->magic
, SUPERBLOCK_MAGIC
);
193 strcat((char *)&mcdi
->magic
, SUPERBLOCK_VERSION
);
196 mcdi
->cluster_size
= 1024; // size in KB of clusters
197 mcdi
->blocksize
= blocksize
; // how many pages in a block of data
198 mcdi
->pages_per_cluster
= 2; // how many pages in a cluster
199 mcdi
->pagesize
= mcdi
->cluster_size
/ mcdi
->pages_per_cluster
;
200 mcdi
->clusters_per_block
= mcdi
->blocksize
/ mcdi
->pages_per_cluster
;
201 mcdi
->clusters_per_card
= (size_kb
*1024) / mcdi
->cluster_size
;
202 mcdi
->cardtype
= 0x02; // PlayStation2 card type
203 mcdi
->cardflags
= 0x2b;
205 mcdi
->FATentries_per_cluster
= mcdi
->cluster_size
/ sizeof(u32
);
207 // clear bad blocks list
209 mcdi
->bad_block_list
[i
] = -1;
211 // erase all clusters
212 printf("2. clearing all clusters...\n");
213 memset(cluster_buf
, 0xff, sizeof(cluster_buf
));
214 for (i
=0; i
<mcdi
->clusters_per_card
; i
+=16) {
215 r
= mc_writecluster(genvmc_fh
, i
, cluster_buf
, 16);
222 // calculate fat & ifc length
223 fat_length
= (((mcdi
->clusters_per_card
<< 2) - 1) / mcdi
->cluster_size
) + 1; // get length of fat in clusters
224 ifc_length
= (((fat_length
<< 2) - 1) / mcdi
->cluster_size
) + 1; // get number of needed ifc clusters
226 if (!(ifc_length
<= 32)) {
228 fat_length
= mcdi
->FATentries_per_cluster
<< 5;
233 mcdi
->ifc_list
[i
] = -1;
234 ifc_index
= mcdi
->blocksize
/ 2;
236 for (j
=0; j
<ifc_length
; j
++, i
++)
237 mcdi
->ifc_list
[j
] = i
;
239 // keep fat cluster index
242 // allocate memory for ifc clusters
243 u8
*ifc_mem
= malloc((ifc_length
* mcdi
->cluster_size
)+0XFF);
244 if (ifc_mem
== NULL
) {
248 memset(ifc_mem
, 0, ifc_length
* mcdi
->cluster_size
);
250 // build ifc clusters
251 u32
*ifc
= (u32
*)ifc_mem
;
252 for (j
=0; j
<fat_length
; j
++, i
++) {
253 // just as security...
254 if (i
>= mcdi
->clusters_per_card
) {
262 // write ifc clusters
263 printf("3. writing ifc clusters...\n");
264 for (z
=0; z
<ifc_length
; z
++) {
265 r
= mc_writecluster(genvmc_fh
, mcdi
->ifc_list
[z
], &ifc_mem
[z
* mcdi
->cluster_size
], 1);
267 // freeing ifc clusters memory
274 // freeing ifc clusters memory
281 mcdi
->backup_block1
= (mcdi
->clusters_per_card
/ mcdi
->clusters_per_block
) - 1;
282 mcdi
->backup_block2
= (mcdi
->clusters_per_card
/ mcdi
->clusters_per_block
) - 2;
284 // calculate number of allocatable clusters per card
286 long_multiply(mcdi
->clusters_per_card
, 0x10624dd3, &hi
, &lo
);
287 temp
= (hi
>> 6) - (mcdi
->clusters_per_card
>> 31);
288 mcdi
->max_allocatable_clusters
= (((((temp
<< 5) - temp
) << 2) + temp
) << 3) + 1;
291 // building/writing FAT clusters
292 printf("4. writing fat clusters...\n");
293 i
= (mcdi
->clusters_per_card
/ mcdi
->clusters_per_block
) - 2; // 2 backup blocks
294 for (z
=0; j
< (i
* mcdi
->clusters_per_block
); j
+=mcdi
->FATentries_per_cluster
) {
296 memset(cluster_buf
, 0, mcdi
->cluster_size
);
297 u32
*fc
= (u32
*)cluster_buf
;
298 int sz_u32
= (i
* mcdi
->clusters_per_block
) - j
;
299 if (sz_u32
> mcdi
->FATentries_per_cluster
)
300 sz_u32
= mcdi
->FATentries_per_cluster
;
301 for (b
=0; b
<sz_u32
; b
++)
302 fc
[b
] = 0x7fffffff; // marking free cluster
305 mcdi
->alloc_offset
= j
;
306 mcdi
->rootdir_cluster
= 0;
307 fc
[0] = 0xffffffff; // marking rootdir end
311 r
= mc_writecluster(genvmc_fh
, fat_index
++, cluster_buf
, 1);
318 // calculate alloc_end
319 mcdi
->alloc_end
= (i
* mcdi
->clusters_per_block
) - mcdi
->alloc_offset
;
321 // just a security...
322 if (z
< mcdi
->clusters_per_block
) {
330 mcdi
->rootdir_cluster2
= mcdi
->rootdir_cluster
;
332 // build root directory
333 McFsEntry
*rootdir_entry
[2];
334 sceMcStDateTime time
;
336 mc_getmcrtime(&time
);
337 rootdir_entry
[0] = (McFsEntry
*)&cluster_buf
[0];
338 rootdir_entry
[1] = (McFsEntry
*)&cluster_buf
[sizeof(McFsEntry
)];
339 memset((void *)rootdir_entry
[0], 0, sizeof(McFsEntry
));
340 memset((void *)rootdir_entry
[1], 0, sizeof(McFsEntry
));
341 rootdir_entry
[0]->mode
= sceMcFileAttrExists
| sceMcFile0400
| sceMcFileAttrSubdir
| sceMcFileAttrReadable
| sceMcFileAttrWriteable
| sceMcFileAttrExecutable
;
342 rootdir_entry
[0]->length
= 2;
343 memcpy((void *)&rootdir_entry
[0]->created
, (void *)&time
, sizeof(sceMcStDateTime
));
344 memcpy((void *)&rootdir_entry
[0]->modified
, (void *)&time
, sizeof(sceMcStDateTime
));
345 rootdir_entry
[0]->cluster
= 0;
346 rootdir_entry
[0]->dir_entry
= 0;
347 strcpy(rootdir_entry
[0]->name
, ".");
348 rootdir_entry
[1]->mode
= sceMcFileAttrExists
| sceMcFileAttrHidden
| sceMcFile0400
| sceMcFileAttrSubdir
| sceMcFileAttrWriteable
| sceMcFileAttrExecutable
;
349 rootdir_entry
[1]->length
= 2;
350 memcpy((void *)&rootdir_entry
[1]->created
, (void *)&time
, sizeof(sceMcStDateTime
));
351 memcpy((void *)&rootdir_entry
[1]->modified
, (void *)&time
, sizeof(sceMcStDateTime
));
352 rootdir_entry
[1]->cluster
= 0;
353 rootdir_entry
[1]->dir_entry
= 0;
354 strcpy(rootdir_entry
[1]->name
, "..");
356 // write root directory cluster
357 printf("5. writing root directory cluster...\n");
358 r
= mc_writecluster(genvmc_fh
, mcdi
->alloc_offset
+ mcdi
->rootdir_cluster
, cluster_buf
, 1);
364 // set superblock formatted flag
367 // finally write superblock
368 printf("6. writing superblock...\n");
369 memset(cluster_buf
, 0xff, mcdi
->cluster_size
);
370 memcpy(cluster_buf
, (void *)mcdi
, sizeof(MCDevInfo
));
371 r
= mc_writecluster(genvmc_fh
, 0, cluster_buf
, 1);
377 r
= fclose(genvmc_fh
);
386 ret
= fclose(genvmc_fh
);
393 //-----------------------------------------------------------------------
394 int main(int argc
, char **argv
, char **env
)
399 int size_MB
= strtol(argv
[1], NULL
, 10);
400 if ((size_MB
< 1) || (size_MB
> 1024)) {
401 printf("Error: VMC size_in_MB out of range...\n");
405 //if ((size_MB != 1) && (size_MB % 8)) {
406 // printf("Error: VMC size_in_MB not multiple of 8...\n");
407 // return EXIT_FAILURE;
410 int r
= vmc_mcformat(argv
[2], size_MB
* 1024, 16);
412 printf("Error: fatal error (%d) during VMC file creation...\n", r
);
416 printf("VMC file created!\n");