* fix for theme font handling (when switching back to the default theme, the language...
[open-ps2-loader/simon.git] / src / hdd.c
blobd959d91773513396bb1928d0132d891a74d77e17
1 #include "include/usbld.h"
2 #include "include/ioman.h"
3 #include "include/hddsupport.h"
5 //#define TEST_WRITES
7 typedef struct // size = 1024
9 u32 checksum; // HDL uses 0xdeadfeed magic here
10 u32 magic;
11 char gamename[160];
12 u8 hdl_compat_flags;
13 u8 ops2l_compat_flags;
14 u8 dma_type;
15 u8 dma_mode;
16 char startup[60];
17 u32 layer1_start;
18 u32 discType;
19 int num_partitions;
20 struct {
21 u32 part_offset; // in MB
22 u32 data_start; // in sectors
23 u32 part_size; // in KB
24 } part_specs[65];
25 } hdl_apa_header;
28 // DEVCTL commands
30 #define APA_DEVCTL_MAX_SECTORS 0x00004801 // max partition size(in sectors)
31 #define APA_DEVCTL_TOTAL_SECTORS 0x00004802
32 #define APA_DEVCTL_IDLE 0x00004803
33 #define APA_DEVCTL_FLUSH_CACHE 0x00004804
34 #define APA_DEVCTL_SWAP_TMP 0x00004805
35 #define APA_DEVCTL_DEV9_SHUTDOWN 0x00004806
36 #define APA_DEVCTL_STATUS 0x00004807
37 #define APA_DEVCTL_FORMAT 0x00004808
38 #define APA_DEVCTL_SMART_STAT 0x00004809
39 #define APA_DEVCTL_GETTIME 0x00006832
40 #define APA_DEVCTL_SET_OSDMBR 0x00006833// arg = hddSetOsdMBR_t
41 #define APA_DEVCTL_GET_SECTOR_ERROR 0x00006834
42 #define APA_DEVCTL_GET_ERROR_PART_NAME 0x00006835// bufp = namebuffer[0x20]
43 #define APA_DEVCTL_ATA_READ 0x00006836// arg = hddAtaTransfer_t
44 #define APA_DEVCTL_ATA_WRITE 0x00006837// arg = hddAtaTransfer_t
45 #define APA_DEVCTL_SCE_IDENTIFY_DRIVE 0x00006838// bufp = buffer for atadSceIdentifyDrive
46 #define APA_DEVCTL_IS_48BIT 0x00006840
47 #define APA_DEVCTL_SET_TRANSFER_MODE 0x00006841
49 static apa_partition_table_t *ptable = NULL;
51 // chunks_map
52 #define MAP_AVAIL '.'
53 #define MAP_MAIN 'M'
54 #define MAP_SUB 's'
55 #define MAP_COLL 'x'
56 #define MAP_ALLOC '*'
58 static u8 hdd_buf[16384];
60 //-------------------------------------------------------------------------
61 int hddCheck(void)
63 register int ret;
65 fileXioInit();
66 ret = fileXioDevctl("hdd0:", APA_DEVCTL_STATUS, NULL, 0, NULL, 0);
68 if ((ret >= 3) || (ret < 0))
69 return -1;
71 return ret;
74 //-------------------------------------------------------------------------
75 u32 hddGetTotalSectors(void)
77 return fileXioDevctl("hdd0:", APA_DEVCTL_TOTAL_SECTORS, NULL, 0, NULL, 0);
80 //-------------------------------------------------------------------------
81 int hddIs48bit(void)
83 return fileXioDevctl("hdd0:", APA_DEVCTL_IS_48BIT, NULL, 0, NULL, 0);
86 //-------------------------------------------------------------------------
87 int hddSetTransferMode(int type, int mode)
89 u8 args[16];
91 *(u32 *)&args[0] = type;
92 *(u32 *)&args[4] = mode;
94 return fileXioDevctl("hdd0:", APA_DEVCTL_SET_TRANSFER_MODE, args, 8, NULL, 0);
97 //-------------------------------------------------------------------------
98 int hddSetIdleTimeout(int timeout)
100 // From hdparm man:
101 // A value of zero means "timeouts are disabled": the
102 // device will not automatically enter standby mode. Values from 1
103 // to 240 specify multiples of 5 seconds, yielding timeouts from 5
104 // seconds to 20 minutes. Values from 241 to 251 specify from 1 to
105 // 11 units of 30 minutes, yielding timeouts from 30 minutes to 5.5
106 // hours. A value of 252 signifies a timeout of 21 minutes. A
107 // value of 253 sets a vendor-defined timeout period between 8 and
108 // 12 hours, and the value 254 is reserved. 255 is interpreted as
109 // 21 minutes plus 15 seconds. Note that some older drives may
110 // have very different interpretations of these values.
112 u8 args[16];
114 *(u32 *)&args[0] = timeout & 0xff;
116 return fileXioDevctl("hdd0:", APA_DEVCTL_IDLE, args, 4, NULL, 0);
119 //-------------------------------------------------------------------------
120 static int hddReadSectors(u32 lba, u32 nsectors, void *buf, int bufsize)
122 u8 args[16];
124 *(u32 *)&args[0] = lba;
125 *(u32 *)&args[4] = nsectors;
127 if (fileXioDevctl("hdd0:", APA_DEVCTL_ATA_READ, args, 8, buf, bufsize) != 0)
128 return -1;
130 return 0;
133 //-------------------------------------------------------------------------
134 static int hddWriteSectors(u32 lba, u32 nsectors, void *buf)
136 int argsz;
137 u8 *args = (u8 *)hdd_buf;
139 *(u32 *)&args[0] = lba;
140 *(u32 *)&args[4] = nsectors;
141 memcpy(&args[8], buf, nsectors << 9);
143 argsz = 8 + (nsectors << 9);
145 if (fileXioDevctl("hdd0:", APA_DEVCTL_ATA_WRITE, args, argsz, NULL, 0) != 0)
146 return -1;
148 return 0;
151 //-------------------------------------------------------------------------
152 static int hddFlushCache(void)
154 return fileXioDevctl("hdd0:", APA_DEVCTL_FLUSH_CACHE, NULL, 0, NULL, 0);
157 //-------------------------------------------------------------------------
158 int hddGetFormat(void)
160 return fileXioDevctl("hdd0:", APA_DEVCTL_FORMAT, NULL, 0, NULL, 0);
163 //-------------------------------------------------------------------------
164 static int apaCheckSum(apa_header *header)
166 u32 *ptr=(u32 *)header;
167 u32 sum=0;
168 int i;
170 for(i=1; i < 256; i++)
171 sum+=ptr[i];
172 return sum;
175 //-------------------------------------------------------------------------
176 static int apaAddPartition(apa_partition_table_t *table, apa_header *header, int existing)
178 register u32 nbytes;
180 if (table->part_count == table->part_alloc_) {
182 // grow buffer of 16 parts
183 nbytes = (table->part_alloc_ + 16) * sizeof(apa_partition_t);
184 apa_partition_t *parts = malloc(nbytes);
185 if(parts != NULL) {
187 memset(parts, 0, nbytes);
189 // copy existing parts
190 if (table->parts != NULL)
191 memcpy(parts, table->parts, table->part_count * sizeof(apa_partition_t));
193 // free old parts mem
194 free(table->parts);
195 table->parts = parts;
196 table->part_alloc_ += 16;
198 else
199 return -2;
202 // copy the part to its buffer
203 memcpy(&table->parts[table->part_count].header, header, sizeof(apa_header));
204 table->parts[table->part_count].existing = existing;
205 table->parts[table->part_count].modified = !existing;
206 table->parts[table->part_count].linked = 1;
207 table->part_count++;
209 return 0;
212 //-------------------------------------------------------------------------
213 static void apaFreePartitionTable(apa_partition_table_t *table)
215 register int i;
217 if (table != NULL) {
218 for (i=0; i<table->part_count; i++) {
219 if (table->parts)
220 free(table->parts);
223 if (table->chunks_map)
224 free(table->chunks_map);
228 //-------------------------------------------------------------------------
229 static int apaSetPartitionTableStats(apa_partition_table_t *table)
231 register int i;
232 char *chunks_map;
234 table->total_chunks = table->device_size_in_mb / 128;
235 chunks_map = malloc(table->total_chunks);
236 if (chunks_map == NULL)
237 return -1;
239 *chunks_map = MAP_AVAIL;
240 for (i=0; i<table->total_chunks; i++)
241 chunks_map[i] = MAP_AVAIL;
243 // build occupided/available space map
244 table->allocated_chunks = 0;
245 table->free_chunks = table->total_chunks;
246 for (i=0; i<table->part_count; i++) {
247 apa_header *part_hdr = (apa_header *)&table->parts[i].header;
248 int part_num = part_hdr->start / ((128 * 1024 * 1024) / 512);
249 int num_parts = part_hdr->length / ((128 * 1024 * 1024) / 512);
251 // "alloc" num_parts starting at part_num
252 while (num_parts) {
253 if (chunks_map[part_num] == MAP_AVAIL)
254 chunks_map[part_num] = (part_hdr->main == 0 ? MAP_MAIN : MAP_SUB);
255 else
256 chunks_map[part_num] = MAP_COLL; // collision
257 part_num++;
258 num_parts--;
259 table->allocated_chunks++;
260 table->free_chunks--;
264 if (table->chunks_map)
265 free(table->chunks_map);
267 table->chunks_map = (u8 *)chunks_map;
269 return 0;
272 //-------------------------------------------------------------------------
273 static int apaReadPartitionTable(apa_partition_table_t **table)
275 register u32 nsectors, lba;
276 register int ret;
278 // get number of sectors on device
279 nsectors = hddGetTotalSectors();
281 // if not already done allocate space for the partition table
282 if (*table == NULL) {
283 *table = malloc(sizeof(apa_partition_table_t));
284 if (*table == NULL) {
285 return -2;
288 else // if partition table was already allocated, Free all
289 apaFreePartitionTable(*table);
291 // clear the partition table
292 memset(*table, 0, sizeof(apa_partition_table_t));
294 // read the partition table
295 lba = 0;
296 do {
297 apa_header apaHeader;
298 // read partition header
299 ret = hddReadSectors(lba, sizeof(apa_header) >> 9, &apaHeader, sizeof(apa_header));
300 if (ret == 0) {
301 // check checksum & header magic
302 if ((apaCheckSum(&apaHeader) == apaHeader.checksum) && (apaHeader.magic == APA_MAGIC)) {
303 if (apaHeader.start < nsectors) {
304 //if ((apaHeader.flags == 0) && (apaHeader.type == 0x1337))
305 ret = apaAddPartition(*table, &apaHeader, 1);
306 if (ret == 0)
307 lba = apaHeader.next;
309 else {
310 ret = 7; // data behind end-of-HDD
311 break;
314 else ret = 1;
317 if ((*table)->part_count > 10000)
318 ret = 7;
320 } while (ret == 0 && lba != 0);
322 if (ret == 0) {
323 (*table)->device_size_in_mb = nsectors >> 11;
324 ret = apaSetPartitionTableStats(*table);
326 else {
327 apaFreePartitionTable(*table);
328 ret = 20000 + (*table)->part_count;
331 return ret;
334 //-------------------------------------------------------------------------
335 static int apaCheckLinkedList(apa_partition_table_t *table, int flag)
338 register int i;
340 for (i=0; i<table->part_count; i++) {
341 apa_partition_t *prev = (apa_partition_t *)&table->parts[(i > 0 ? i - 1 : table->part_count - 1)];
342 apa_partition_t *curr = (apa_partition_t *)&table->parts[i];
343 apa_partition_t *next = (apa_partition_t *)&table->parts[(i + 1 < table->part_count ? i + 1 : 0)];
345 if (curr->header.prev != prev->header.start) {
346 if (!flag)
347 return -1;
348 else {
349 curr->modified = 1;
350 curr->header.prev = prev->header.start;
353 if (curr->header.next != next->header.start) {
354 if (!flag)
355 return -1;
356 else {
357 curr->modified = 1;
358 curr->header.next = next->header.start;
361 if ((flag) && (curr->modified))
362 curr->header.checksum = apaCheckSum(&curr->header);
365 return 0;
368 //-------------------------------------------------------------------------
369 static int apaCheckPartitionTable(apa_partition_table_t *table)
371 register int i, j, k;
373 u32 total_sectors = table->device_size_in_mb * 1024 * 2;
375 for (i=0; i<table->part_count; i++) {
376 apa_header *part_hdr = &table->parts[i].header;
378 // check checksum
379 if (part_hdr->checksum != apaCheckSum(part_hdr))
380 return -1;
382 // check for data behind end of HDD
383 if (!((part_hdr->start < total_sectors) && (part_hdr->start + part_hdr->length <= total_sectors)))
384 return -2;
386 // check partition length is multiple of 128 MB
387 if ((part_hdr->length % ((128 * 1024 * 1024) / 512)) != 0)
388 return -3;
390 // check partition start is multiple of partion length
391 if ((part_hdr->start % part_hdr->length) != 0)
392 return -4;
394 // check all subs partitions
395 if ((part_hdr->main == 0) && (part_hdr->flags == 0) && (part_hdr->start != 0)) {
396 int count = 0;
398 for (j=0; j<table->part_count; j++) {
399 apa_header *part2_hdr = &table->parts[j].header;
400 if (part2_hdr->main == part_hdr->start) { // sub-partition of current main partition
401 int found;
402 if (part2_hdr->flags != APA_FLAG_SUB)
403 return -5;
405 found = 0;
406 for (k=0; k<part_hdr->nsub; k++) {
407 if (part_hdr->subs[k].start == part2_hdr->start) { // in list
408 if (part_hdr->subs[k].length != part2_hdr->length)
409 return -6;
410 found = 1;
411 break;
414 if (!found)
415 return -7; // not found in the list
417 count++;
420 if (count != part_hdr->nsub)
421 return -8; // wrong number of sub-partitions
425 // verify double-linked list
426 if (apaCheckLinkedList(table, 0) < 0)
427 return -9; // bad links
429 LOG("HDD CheckPartitionTable OK!\n");
431 return 0;
434 //-------------------------------------------------------------------------
435 static int apaWritePartitionTable(apa_partition_table_t *table)
437 register int ret, i;
439 ret = apaCheckPartitionTable(table);
440 if (ret < 0)
441 return ret;
443 for (i=0; i<table->part_count; i++) {
445 if (table->parts[i].modified) {
446 apa_header *part_hdr = &table->parts[i].header;
447 LOG("HDD Writing 2 sectors at sector 0x%X\n", part_hdr->start);
448 #ifndef TEST_WRITES
449 ret = hddWriteSectors(part_hdr->start, 2, (void *)part_hdr);
450 if (ret < 0)
451 return -10;
452 #endif
456 return 0;
459 //-------------------------------------------------------------------------
460 static int apaFindPartition(apa_partition_table_t *table, char *partname)
462 register int ret, i;
464 ret = -1;
466 for (i=0; i<table->part_count; i++) {
468 apa_header *part_hdr = &table->parts[i].header;
470 // if part found, return its index
471 if ((part_hdr->main == 0) && (!strcmp(part_hdr->id, partname))) {
472 ret = i;
473 break;
477 return ret;
480 //-------------------------------------------------------------------------
481 static int apaDeletePartition(apa_partition_table_t *table, char *partname)
483 register int i, part_index;
484 register int count = 1;
485 u32 pending_deletes[APA_MAXSUB];
487 LOG("HDD DeletePartition %s\n", partname);
489 // retrieve part index
490 part_index = apaFindPartition(table, partname);
491 if (part_index < 0)
492 return -1;
494 apa_header *part_hdr = &table->parts[part_index].header;
496 // Do not delete system partitions
497 if (part_hdr->type == 1)
498 return -2;
500 // preserve a list of starting sectors of partitions to be deleted
501 pending_deletes[0] = part_hdr->start;
502 LOG("HDD Found part at %d \n", part_hdr->start / 262144);
503 for (i=0; i<part_hdr->nsub; i++) {
504 LOG("HDD Found subpart at %d \n", part_hdr->subs[i].start / 262144);
505 pending_deletes[count++] = part_hdr->subs[i].start;
508 LOG("HDD Number of subpartitions=%d count=%d\n", part_hdr->nsub, count);
510 // remove partitions from the double-linked list
511 i = 0;
512 while (i<table->part_count) {
513 int j;
514 int found = 0;
516 for (j=0; j<count; j++) {
517 if (table->parts[i].header.start == pending_deletes[j]) {
518 found = 1;
519 break;
523 if (found) {
524 // remove this partition
525 int part_num = table->parts[i].header.start / 262144; // 262144 sectors == 128M
526 int num_parts = table->parts[i].header.length / 262144;
528 LOG("HDD Partition found! num_parts=%d part_num=%d\n", num_parts, part_num);
530 memmove((void *)&table->parts[i], (void *)&table->parts[i+1], sizeof(apa_partition_t) * (table->part_count-i-1));
531 table->part_count--;
533 // "free" num_parts starting at part_num
534 while (num_parts) {
535 table->chunks_map[part_num] = MAP_AVAIL;
536 part_num++;
537 num_parts--;
538 table->allocated_chunks--;
539 table->free_chunks++;
542 else
543 i++;
546 apaCheckLinkedList(table, 1);
548 return 0;
551 //-------------------------------------------------------------------------
552 static int hddGetHDLGameInfo(apa_header *header, hdl_game_info_t *ginfo)
554 u32 i, size;
555 const u32 offset = 0x101000;
556 char buf[1024];
557 register int ret;
559 u32 start_sector = header->start + offset / 512;
561 ret = hddReadSectors(start_sector, 2, buf, 2 * 512);
562 if (ret == 0) {
564 hdl_apa_header *hdl_header = (hdl_apa_header *)buf;
566 // calculate total size
567 size = header->length;
569 for (i = 0; i < header->nsub; i++)
570 size += header->subs[i].length;
572 memcpy(ginfo->partition_name, header->id, APA_IDMAX);
573 ginfo->partition_name[APA_IDMAX] = 0;
574 strncpy(ginfo->name, hdl_header->gamename, HDL_GAME_NAME_MAX);
575 strncpy(ginfo->startup, hdl_header->startup, 12);
576 ginfo->hdl_compat_flags = hdl_header->hdl_compat_flags;
577 ginfo->ops2l_compat_flags = hdl_header->ops2l_compat_flags;
578 ginfo->dma_type = hdl_header->dma_type;
579 ginfo->dma_mode = hdl_header->dma_mode;
580 ginfo->layer_break = hdl_header->layer1_start;
581 ginfo->disctype = hdl_header->discType;
582 ginfo->start_sector = start_sector;
583 ginfo->total_size_in_kb = size / 2;
585 else ret = -1;
587 return ret;
590 //-------------------------------------------------------------------------
591 int hddGetHDLGamelist(hdl_games_list_t **game_list)
593 register int ret;
595 ret = apaReadPartitionTable(&ptable);
596 if (ret == 0) {
598 u32 i, count = 0;
599 void *tmp;
601 if (*game_list != NULL) {
602 for (i = 0; i < (*game_list)->count; i++) {
603 if ((*game_list)->games != NULL)
604 free((*game_list)->games);
608 for (i = 0; i < ptable->part_count; i++)
609 count += ((ptable->parts[i].header.flags == 0) && (ptable->parts[i].header.type == 0x1337));
611 tmp = malloc(sizeof(hdl_game_info_t) * count);
612 if (tmp != NULL) {
614 memset(tmp, 0, sizeof(hdl_game_info_t) * count);
615 *game_list = malloc(sizeof(hdl_games_list_t));
616 if (*game_list != NULL) {
618 u32 index = 0;
619 memset(*game_list, 0, sizeof(hdl_games_list_t));
620 (*game_list)->count = count;
621 (*game_list)->games = tmp;
622 (*game_list)->total_chunks = ptable->total_chunks;
623 (*game_list)->free_chunks = ptable->free_chunks;
625 for (i = 0; ((ret == 0) && (i < ptable->part_count)); i++) {
626 apa_header *header = &ptable->parts[i].header;
627 if ((header->flags == 0) && (header->type == 0x1337))
628 ret = hddGetHDLGameInfo(header, (*game_list)->games + index++);
631 if (ret != 0)
632 free(*game_list);
633 } else
634 ret = -2;
636 if (ret != 0)
637 free(tmp);
639 } else ret = -2;
642 return ret;
645 //-------------------------------------------------------------------------
646 int hddFreeHDLGamelist(hdl_games_list_t *game_list)
648 register int i;
650 apaFreePartitionTable(ptable);
652 if (game_list != NULL) {
653 for (i = 0; i < game_list->count; i++) {
654 if (game_list->games != NULL)
655 free(game_list->games);
658 free(game_list);
661 return 0;
664 //-------------------------------------------------------------------------
665 int hddSetHDLGameInfo(hdl_game_info_t *ginfo)
667 const u32 offset = 0x101000;
668 char buf[1024];
669 int part_index;
671 if (ptable == NULL)
672 return -1;
674 // retrieve part index
675 part_index = apaFindPartition(ptable, ginfo->partition_name);
676 if (part_index < 0)
677 return -1;
679 apa_header *header = &ptable->parts[part_index].header;
680 if (header == NULL)
681 return -2;
683 u32 start_sector = header->start + offset / 512;
685 if (hddReadSectors(start_sector, 2, buf, 2 * 512) != 0)
686 return -3;
688 hdl_apa_header *hdl_header = (hdl_apa_header *)buf;
690 // checking deadfeed magic & PS2 game magic
691 if (hdl_header->checksum != 0xdeadfeed)
692 return -4;
694 // just change game name and compat flags !!!
695 strncpy(hdl_header->gamename, ginfo->name, 160);
696 hdl_header->gamename[159] = '\0';
697 //hdl_header->hdl_compat_flags = ginfo->hdl_compat_flags;
698 hdl_header->ops2l_compat_flags = ginfo->ops2l_compat_flags;
699 hdl_header->dma_type = ginfo->dma_type;
700 hdl_header->dma_mode = ginfo->dma_mode;
702 if (hddWriteSectors(start_sector, 2, buf) != 0)
703 return -5;
705 hddFlushCache();
707 return 0;
710 //-------------------------------------------------------------------------
711 int hddDeleteHDLGame(hdl_game_info_t *ginfo)
713 register int ret;
715 LOG("HDD Delete game: '%s'\n", ginfo->name);
717 if (ptable == NULL)
718 return -1;
720 ret = apaDeletePartition(ptable, ginfo->partition_name);
721 if (ret < 0)
722 return -2;
724 ret = apaWritePartitionTable(ptable);
725 if (ret < 0)
726 return -3;
728 hddFlushCache();
730 LOG("HDD Game: '%s' deleted!\n", ginfo->name);
732 return 0;