mgh: fix for default HDD DMA mode, that wasn't correctly set
[open-ps2-loader.git] / pc / genvmc / src / genvmc.c
blob6ba9cd15e2571e8df441bf175f5db57a06dc63ed
1 /*
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.
6 */
8 #include "genvmc.h"
10 // mc file attributes
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";
36 // superblock struct
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
43 u16 unused; // unused
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
50 u8 unused2[8];
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
55 u16 unused3;
56 u32 cluster_size;
57 u32 FATentries_per_cluster;
58 u32 clusters_per_block;
59 int cardform;
60 u32 rootdir_cluster2;
61 u32 unknown1;
62 u32 unknown2;
63 u32 max_allocatable_clusters;
64 u32 unknown3;
65 u32 unknown4;
66 int unknown5;
67 } MCDevInfo;
69 typedef struct _sceMcStDateTime {
70 u8 Resv2;
71 u8 Sec;
72 u8 Min;
73 u8 Hour;
74 u8 Day;
75 u8 Month;
76 u16 Year;
77 } sceMcStDateTime;
79 typedef struct { // size = 512
80 u16 mode; // 0
81 u16 unused; // 2
82 u32 length; // 4
83 sceMcStDateTime created; // 8
84 u32 cluster; // 16
85 u32 dir_entry; // 20
86 sceMcStDateTime modified; // 24
87 u32 attr; // 32
88 u32 unused2[7]; // 36
89 char name[32]; // 64
90 u8 unused3[416]; // 96
91 } McFsEntry;
93 #define BLOCKKB 16
95 static MCDevInfo devinfo;
96 static u8 cluster_buf[(BLOCKKB * 1024)+16];
98 static FILE *genvmc_fh = NULL;
100 //-----------------------------------------------------------------------
101 static void printVer(void)
103 #ifdef _WIN32
104 printf("%s version %s (Win32 Build)\n", PROGRAM_EXTNAME, PROGRAM_VER);
105 #else
106 printf("%s version %s\n", PROGRAM_EXTNAME, PROGRAM_VER);
107 #endif
110 //-----------------------------------------------------------------------
111 static void printUsage(void)
113 printVer();
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;
123 register long x, y;
125 a = (v1 >> 16) & 0xffff;
126 b = v1 & 0xffff;
127 c = (v2 >> 16) & 0xffff;
128 d = v2 & 0xffff;
130 *LO = b * d;
131 x = a * d + c * b;
132 y = ((*LO >> 16) & 0xffff) + x;
134 *LO = (*LO & 0xffff) | ((y & 0xffff) << 16);
135 *HI = (y >> 16) & 0xffff;
137 *HI += a * c;
140 //--------------------------------------------------------------
141 static int mc_getmcrtime(sceMcStDateTime *mctime)
143 time_t rawtime;
144 struct tm *ptm;
146 time(&rawtime);
147 ptm = gmtime(&rawtime);
149 mctime->Resv2 = 0;
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);
159 return 0;
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);
171 if (r != size)
172 return -1;
174 return 0;
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)
188 return -101;
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);
195 // set mc specs
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;
204 mcdi->cardform = -1;
205 mcdi->FATentries_per_cluster = mcdi->cluster_size / sizeof(u32);
207 // clear bad blocks list
208 for (i=0; i<32; i++)
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);
216 if (r < 0) {
217 r = -102;
218 goto err_out;
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)) {
227 ifc_length = 32;
228 fat_length = mcdi->FATentries_per_cluster << 5;
231 // clear ifc list
232 for (i=0; i<32; i++)
233 mcdi->ifc_list[i] = -1;
234 ifc_index = mcdi->blocksize / 2;
235 i = ifc_index;
236 for (j=0; j<ifc_length; j++, i++)
237 mcdi->ifc_list[j] = i;
239 // keep fat cluster index
240 fat_index = i;
242 // allocate memory for ifc clusters
243 u8 *ifc_mem = malloc((ifc_length * mcdi->cluster_size)+0XFF);
244 if (ifc_mem == NULL) {
245 r = -103;
246 goto err_out;
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) {
255 free(ifc_mem);
256 r = -104;
257 goto err_out;
259 ifc[j] = i;
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);
266 if (r < 0) {
267 // freeing ifc clusters memory
268 free(ifc_mem);
269 r = -105;
270 goto err_out;
274 // freeing ifc clusters memory
275 free(ifc_mem);
277 // set alloc offset
278 alloc_offset = i;
280 // set backup blocks
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
285 u32 hi, lo, temp;
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;
289 j = alloc_offset;
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
304 if (z == 0) {
305 mcdi->alloc_offset = j;
306 mcdi->rootdir_cluster = 0;
307 fc[0] = 0xffffffff; // marking rootdir end
309 z+=sz_u32;
311 r = mc_writecluster(genvmc_fh, fat_index++, cluster_buf, 1);
312 if (r < 0) {
313 r = -107;
314 goto err_out;
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) {
323 r = -108;
324 goto err_out;
327 mcdi->unknown1 = 0;
328 mcdi->unknown2 = 0;
329 mcdi->unknown5 = -1;
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);
359 if (r < 0) {
360 r = -109;
361 goto err_out;
364 // set superblock formatted flag
365 mcdi->cardform = 1;
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);
372 if (r < 0) {
373 r = -110;
374 goto err_out;
377 r = fclose(genvmc_fh);
378 if (r < 0)
379 return -111;
380 genvmc_fh = NULL;
382 return 0;
385 err_out:
386 ret = fclose(genvmc_fh);
387 if (!(ret < 0))
388 genvmc_fh = NULL;
390 return r;
393 //-----------------------------------------------------------------------
394 int main(int argc, char **argv, char **env)
396 if (argc != 3)
397 printUsage();
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");
402 return EXIT_FAILURE;
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);
411 if (r != 0) {
412 printf("Error: fatal error (%d) during VMC file creation...\n", r);
413 return EXIT_FAILURE;
416 printf("VMC file created!\n");
418 return EXIT_SUCCESS;