A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 530 lines 17 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * 11 * FAT32 formatting functions. Based on: 12 * 13 * Fat32 formatter version 1.03 14 * (c) Tom Thornhill 2005 15 * This software is covered by the GPL. 16 * By using this tool, you agree to absolve Ridgecrop of an liabilities for 17 * lost data. 18 * Please backup any data you value before using this tool. 19 * 20 * 21 * Modified June 2007 by Dave Chapman for use in ipodpatcher 22 * 23 * 24 * This program is free software; you can redistribute it and/or 25 * modify it under the terms of the GNU General Public License 26 * as published by the Free Software Foundation; either version 2 27 * of the License, or (at your option) any later version. 28 * 29 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 30 * KIND, either express or implied. 31 * 32 ****************************************************************************/ 33 34#include <stdio.h> 35#include <stdlib.h> 36#include <string.h> 37#include <stdbool.h> 38#include <stdint.h> 39#include <inttypes.h> 40 41#include "ipodio.h" 42 43static inline uint16_t swap16(uint16_t value) 44{ 45 return (value >> 8) | (value << 8); 46} 47 48static inline uint32_t swap32(uint32_t value) 49{ 50 uint32_t hi = swap16(value >> 16); 51 uint32_t lo = swap16(value & 0xffff); 52 return (lo << 16) | hi; 53} 54 55/* The following functions are not the most efficient, but are 56 self-contained and don't require needing to know endianness of CPU 57 at compile-time. 58 59 Note that htole16/htole32 exist on some platforms, so for 60 simplicity we use different names. 61 62*/ 63 64static uint16_t rb_htole16(uint16_t x) 65{ 66 uint16_t test = 0x1234; 67 unsigned char* p = (unsigned char*)&test; 68 69 if (p[0]==0x12) { 70 /* Big-endian */ 71 return swap16(x); 72 } else { 73 return x; 74 } 75} 76 77static uint32_t rb_htole32(uint32_t x) 78{ 79 uint32_t test = 0x12345678; 80 unsigned char* p = (unsigned char*)&test; 81 82 if (p[0]==0x12) { 83 /* Big-endian */ 84 return swap32(x); 85 } else { 86 return x; 87 } 88} 89 90 91/* TODO: Pass these as parameters to the various create_ functions */ 92 93/* can be zero for default or 1,2,4,8,16,32 or 64 */ 94static int sectors_per_cluster = 0; 95 96/* Recommended values */ 97static uint32_t ReservedSectCount = 32; 98static uint32_t NumFATs = 2; 99static uint32_t BackupBootSect = 6; 100static uint32_t VolumeId=0; /* calculated before format */ 101 102/* Calculated later */ 103static uint32_t FatSize=0; 104static uint32_t BytesPerSect=0; 105static uint32_t SectorsPerCluster=0; 106static uint32_t TotalSectors=0; 107static uint32_t SystemAreaSize=0; 108static uint32_t UserAreaSize=0; 109static uint8_t VolId[12] = "NO NAME "; 110 111 112struct FAT_BOOTSECTOR32 113{ 114 /* Common fields. */ 115 uint8_t sJmpBoot[3]; 116 char sOEMName[8]; 117 uint16_t wBytsPerSec; 118 uint8_t bSecPerClus; 119 uint16_t wRsvdSecCnt; 120 uint8_t bNumFATs; 121 uint16_t wRootEntCnt; 122 uint16_t wTotSec16; /* if zero, use dTotSec32 instead */ 123 uint8_t bMedia; 124 uint16_t wFATSz16; 125 uint16_t wSecPerTrk; 126 uint16_t wNumHeads; 127 uint32_t dHiddSec; 128 uint32_t dTotSec32; 129 130 /* Fat 32/16 only */ 131 uint32_t dFATSz32; 132 uint16_t wExtFlags; 133 uint16_t wFSVer; 134 uint32_t dRootClus; 135 uint16_t wFSInfo; 136 uint16_t wBkBootSec; 137 uint8_t Reserved[12]; 138 uint8_t bDrvNum; 139 uint8_t Reserved1; 140 uint8_t bBootSig; /* == 0x29 if next three fields are ok */ 141 uint32_t dBS_VolID; 142 uint8_t sVolLab[11]; 143 uint8_t sBS_FilSysType[8]; 144} __attribute__((packed)); 145 146struct FAT_FSINFO { 147 uint32_t dLeadSig; // 0x41615252 148 uint8_t sReserved1[480]; // zeros 149 uint32_t dStrucSig; // 0x61417272 150 uint32_t dFree_Count; // 0xFFFFFFFF 151 uint32_t dNxt_Free; // 0xFFFFFFFF 152 uint8_t sReserved2[12]; // zeros 153 uint32_t dTrailSig; // 0xAA550000 154} __attribute__((packed)); 155 156 157/* Write "count" zero sectors, starting at sector "sector" */ 158static int zero_sectors(struct ipod_t* ipod, uint64_t sector, int count) 159{ 160 int n; 161 162 if (ipod_seek(ipod, sector * ipod->sector_size) < 0) { 163 fprintf(stderr,"[ERR] Seek failed\n"); 164 return -1; 165 } 166 167 memset(ipod->sectorbuf, 0, 128 * ipod->sector_size); 168 169 /* Write 128 sectors at a time */ 170 while (count) { 171 if (count >= 128) 172 n = 128; 173 else 174 n = count; 175 176 if (ipod_write(ipod,n * ipod->sector_size) < 0) { 177 perror("[ERR] Write failed in zero_sectors\n"); 178 return -1; 179 } 180 181 count -= n; 182 } 183 184 return 0; 185} 186 187 188/* 18928.2 CALCULATING THE VOLUME SERIAL NUMBER 190 191For example, say a disk was formatted on 26 Dec 95 at 9:55 PM and 41.94 192seconds. DOS takes the date and time just before it writes it to the 193disk. 194 195Low order word is calculated: Volume Serial Number is: 196 Month & Day 12/26 0c1ah 197 Sec & Hundrenths 41:94 295eh 3578:1d02 198 ----- 199 3578h 200 201High order word is calculated: 202 Hours & Minutes 21:55 1537h 203 Year 1995 07cbh 204 ----- 205 1d02h 206*/ 207static uint32_t get_volume_id ( ) 208{ 209 /* TODO */ 210#if 0 211 SYSTEMTIME s; 212 uint32_t d; 213 uint16_t lo,hi,tmp; 214 215 GetLocalTime( &s ); 216 217 lo = s.wDay + ( s.wMonth << 8 ); 218 tmp = (s.wMilliseconds/10) + (s.wSecond << 8 ); 219 lo += tmp; 220 221 hi = s.wMinute + ( s.wHour << 8 ); 222 hi += s.wYear; 223 224 d = lo + (hi << 16); 225 return(d); 226#endif 227 return(0); 228} 229 230/* 231This is the Microsoft calculation from FATGEN 232 233 uint32_t RootDirSectors = 0; 234 uint32_t TmpVal1, TmpVal2, FATSz; 235 236 TmpVal1 = DskSize - ( ReservedSecCnt + RootDirSectors); 237 TmpVal2 = (256 * SecPerClus) + NumFATs; 238 TmpVal2 = TmpVal2 / 2; 239 FATSz = (TmpVal1 + (TmpVal2 - 1)) / TmpVal2; 240 241 return( FatSz ); 242*/ 243 244 245static uint32_t get_fat_size_sectors(uint32_t DskSize, uint32_t ReservedSecCnt, 246 uint32_t SecPerClus, uint32_t NumFATs, 247 uint32_t BytesPerSect) 248{ 249 uint64_t Numerator, Denominator; 250 uint64_t FatElementSize = 4; 251 uint64_t FatSz; 252 253 /* This is based on 254 http://hjem.get2net.dk/rune_moeller_barnkob/filesystems/fat.html 255 I've made the obvious changes for FAT32 256 */ 257 258 Numerator = FatElementSize * ( DskSize - ReservedSecCnt ); 259 Denominator = ( SecPerClus * BytesPerSect ) + ( FatElementSize * NumFATs ); 260 FatSz = Numerator / Denominator; 261 262 /* round up */ 263 FatSz += 1; 264 265 return((uint32_t)FatSz); 266} 267 268static uint8_t get_spc(uint32_t ClusterSizeKB, uint32_t BytesPerSect) 269{ 270 uint32_t spc = ( ClusterSizeKB * 1024 ) / BytesPerSect; 271 return( (uint8_t) spc ); 272} 273 274static uint8_t get_sectors_per_cluster(uint32_t DiskSizeSectors, 275 uint32_t BytesPerSect) 276{ 277 uint8_t ret = 0x01; /* 1 sector per cluster */ 278 uint64_t DiskSizeBytes = (uint64_t)DiskSizeSectors * (uint64_t)BytesPerSect; 279 int64_t DiskSizeMB = DiskSizeBytes / ( 1024*1024 ); 280 281 /* 512 MB to 8,191 MB 4 KB */ 282 if ( DiskSizeMB > 512 ) 283 ret = get_spc( 4, BytesPerSect ); /* ret = 0x8; */ 284 285 /* 8,192 MB to 16,383 MB 8 KB */ 286 if ( DiskSizeMB > 8192 ) 287 ret = get_spc( 8, BytesPerSect ); /* ret = 0x10; */ 288 289 /* 16,384 MB to 32,767 MB 16 KB */ 290 if ( DiskSizeMB > 16384 ) 291 ret = get_spc( 16, BytesPerSect ); /* ret = 0x20; */ 292 293 /* Larger than 32,768 MB 32 KB */ 294 if ( DiskSizeMB > 32768 ) 295 ret = get_spc( 32, BytesPerSect ); /* ret = 0x40; */ 296 297 return( ret ); 298 299} 300 301static void create_boot_sector(unsigned char* buf, 302 struct ipod_t* ipod, int partition) 303{ 304 struct FAT_BOOTSECTOR32* pFAT32BootSect = (struct FAT_BOOTSECTOR32*)buf; 305 306 /* fill out the boot sector and fs info */ 307 pFAT32BootSect->sJmpBoot[0]=0xEB; 308 pFAT32BootSect->sJmpBoot[1]=0x5A; 309 pFAT32BootSect->sJmpBoot[2]=0x90; 310 memcpy(pFAT32BootSect->sOEMName, "MSWIN4.1", 8 ); 311 pFAT32BootSect->wBytsPerSec = rb_htole16(BytesPerSect); 312 pFAT32BootSect->bSecPerClus = SectorsPerCluster ; 313 pFAT32BootSect->wRsvdSecCnt = rb_htole16(ReservedSectCount); 314 pFAT32BootSect->bNumFATs = NumFATs; 315 pFAT32BootSect->wRootEntCnt = rb_htole16(0); 316 pFAT32BootSect->wTotSec16 = rb_htole16(0); 317 pFAT32BootSect->bMedia = 0xF8; 318 pFAT32BootSect->wFATSz16 = rb_htole16(0); 319 pFAT32BootSect->wSecPerTrk = rb_htole16(ipod->sectors_per_track); 320 pFAT32BootSect->wNumHeads = rb_htole16(ipod->num_heads); 321 pFAT32BootSect->dHiddSec = rb_htole16(ipod->pinfo[partition].start); 322 pFAT32BootSect->dTotSec32 = rb_htole32(TotalSectors); 323 pFAT32BootSect->dFATSz32 = rb_htole32(FatSize); 324 pFAT32BootSect->wExtFlags = rb_htole16(0); 325 pFAT32BootSect->wFSVer = rb_htole16(0); 326 pFAT32BootSect->dRootClus = rb_htole32(2); 327 pFAT32BootSect->wFSInfo = rb_htole16(1); 328 pFAT32BootSect->wBkBootSec = rb_htole16(BackupBootSect); 329 pFAT32BootSect->bDrvNum = 0x80; 330 pFAT32BootSect->Reserved1 = 0; 331 pFAT32BootSect->bBootSig = 0x29; 332 pFAT32BootSect->dBS_VolID = rb_htole32(VolumeId); 333 memcpy(pFAT32BootSect->sVolLab, VolId, 11); 334 memcpy(pFAT32BootSect->sBS_FilSysType, "FAT32 ", 8 ); 335 336 buf[510] = 0x55; 337 buf[511] = 0xaa; 338} 339 340static void create_fsinfo(unsigned char* buf) 341{ 342 struct FAT_FSINFO* pFAT32FsInfo = (struct FAT_FSINFO*)buf; 343 344 /* FSInfo sect */ 345 pFAT32FsInfo->dLeadSig = rb_htole32(0x41615252); 346 pFAT32FsInfo->dStrucSig = rb_htole32(0x61417272); 347 pFAT32FsInfo->dFree_Count = rb_htole32((uint32_t) -1); 348 pFAT32FsInfo->dNxt_Free = rb_htole32((uint32_t) -1); 349 pFAT32FsInfo->dTrailSig = rb_htole32(0xaa550000); 350 pFAT32FsInfo->dFree_Count = rb_htole32((UserAreaSize/SectorsPerCluster)-1); 351 352 /* clusters 0-1 reserved, we used cluster 2 for the root dir */ 353 pFAT32FsInfo->dNxt_Free = rb_htole32(3); 354} 355 356static void create_firstfatsector(unsigned char* buf) 357{ 358 uint32_t* p = (uint32_t*)buf; /* We know the buffer is aligned */ 359 360 /* First FAT Sector */ 361 p[0] = rb_htole32(0x0ffffff8); /* Reserved cluster 1 media id in low byte */ 362 p[1] = rb_htole32(0x0fffffff); /* Reserved cluster 2 EOC */ 363 p[2] = rb_htole32(0x0fffffff); /* end of cluster chain for root dir */ 364} 365 366int format_partition(struct ipod_t* ipod, int partition) 367{ 368 uint32_t i; 369 uint64_t qTotalSectors=0; 370 uint64_t FatNeeded; 371 372 VolumeId = get_volume_id( ); 373 374 /* Only support hard disks at the moment */ 375 if ( ipod->sector_size != 512 ) 376 { 377 fprintf(stderr,"[ERR] Only disks with 512 bytes per sector are supported.\n"); 378 return -1; 379 } 380 BytesPerSect = ipod->sector_size; 381 382 /* Checks on Disk Size */ 383 qTotalSectors = ipod->pinfo[partition].size; 384 385 /* low end limit - 65536 sectors */ 386 if ( qTotalSectors < 65536 ) 387 { 388 /* I suspect that most FAT32 implementations would mount this 389 volume just fine, but the spec says that we shouldn't do 390 this, so we won't */ 391 392 fprintf(stderr,"[ERR] This drive is too small for FAT32 - there must be at least 64K clusters\n" ); 393 return -1; 394 } 395 396 if ( qTotalSectors >= 0xffffffff ) 397 { 398 /* This is a more fundamental limitation on FAT32 - the total 399 sector count in the root dir is 32bit. With a bit of 400 creativity, FAT32 could be extended to handle at least 2^28 401 clusters There would need to be an extra field in the 402 FSInfo sector, and the old sector count could be set to 403 0xffffffff. This is non standard though, the Windows FAT 404 driver FASTFAT.SYS won't understand this. Perhaps a future 405 version of FAT32 and FASTFAT will handle this. */ 406 407 fprintf(stderr,"[ERR] This drive is too big for FAT32 - max 2TB supported\n"); 408 } 409 410 if ( sectors_per_cluster ) { 411 SectorsPerCluster = sectors_per_cluster; 412 } else { 413 SectorsPerCluster = get_sectors_per_cluster(ipod->pinfo[partition].size, 414 BytesPerSect ); 415 } 416 417 TotalSectors = (uint32_t) qTotalSectors; 418 419 FatSize = get_fat_size_sectors(TotalSectors, ReservedSectCount, 420 SectorsPerCluster, NumFATs, BytesPerSect ); 421 422 UserAreaSize = TotalSectors - ReservedSectCount - (NumFATs*FatSize); 423 424 /* First zero out ReservedSect + FatSize * NumFats + SectorsPerCluster */ 425 SystemAreaSize = (ReservedSectCount+(NumFATs*FatSize) + SectorsPerCluster); 426 427 /* Work out the Cluster count */ 428 FatNeeded = UserAreaSize/SectorsPerCluster; 429 430 /* check for a cluster count of >2^28, since the upper 4 bits of 431 the cluster values in the FAT are reserved. */ 432 if (FatNeeded > 0x0FFFFFFF) { 433 fprintf(stderr,"[ERR] This drive has more than 2^28 clusters, try to specify a larger cluster size\n" ); 434 return -1; 435 } 436 437 /* Sanity check, make sure the fat is big enough. 438 Convert the cluster count into a Fat sector count, and check 439 the fat size value we calculated earlier is OK. */ 440 441 FatNeeded *=4; 442 FatNeeded += (BytesPerSect-1); 443 FatNeeded /= BytesPerSect; 444 445 if ( FatNeeded > FatSize ) { 446 fprintf(stderr,"[ERR] Drive too big to format\n"); 447 return -1; 448 } 449 450 /* 451 Write boot sector, fats 452 Sector 0 Boot Sector 453 Sector 1 FSInfo 454 Sector 2 More boot code - we write zeros here 455 Sector 3 unused 456 Sector 4 unused 457 Sector 5 unused 458 Sector 6 Backup boot sector 459 Sector 7 Backup FSInfo sector 460 Sector 8 Backup 'more boot code' 461 zero'd sectors upto ReservedSectCount 462 FAT1 ReservedSectCount to ReservedSectCount + FatSize 463 ... 464 FATn ReservedSectCount to ReservedSectCount + FatSize 465 RootDir - allocated to cluster2 466 */ 467 468 fprintf(stderr,"[INFO] Heads - %d, sectors/track = %d\n",ipod->num_heads,ipod->sectors_per_track); 469 fprintf(stderr,"[INFO] Size : %" PRIu64 "GB %u sectors\n", 470 ((uint64_t)ipod->pinfo[partition].size * (uint64_t)ipod->sector_size) / (1000*1000*1000), TotalSectors ); 471 fprintf(stderr,"[INFO] %d Bytes Per Sector, Cluster size %d bytes\n", BytesPerSect, SectorsPerCluster*BytesPerSect ); 472 fprintf(stderr,"[INFO] Volume ID is %x:%x\n", VolumeId>>16, VolumeId&0xffff ); 473 fprintf(stderr,"[INFO] %d Reserved Sectors, %d Sectors per FAT, %d fats\n", ReservedSectCount, FatSize, NumFATs ); 474 fprintf (stderr,"[INFO] %d Total clusters\n", UserAreaSize/SectorsPerCluster ); 475 476 fprintf(stderr,"[INFO] Formatting partition %d:...\n",partition); 477 478 /* Once zero_sectors has run, any data on the drive is basically lost... */ 479 fprintf(stderr,"[INFO] Clearing out %d sectors for Reserved sectors, fats and root cluster...\n", SystemAreaSize ); 480 481 zero_sectors(ipod, ipod->pinfo[partition].start, SystemAreaSize); 482 483 fprintf(stderr,"[INFO] Initialising reserved sectors and FATs...\n" ); 484 485 /* Create the boot sector structure */ 486 create_boot_sector(ipod->sectorbuf, ipod, partition); 487 create_fsinfo(ipod->sectorbuf + 512); 488 489 /* Write boot sector and fsinfo at start of partition */ 490 if (ipod_seek(ipod, ipod->pinfo[partition].start * ipod->sector_size) < 0) { 491 fprintf(stderr,"[ERR] Seek failed\n"); 492 return -1; 493 } 494 if (ipod_write(ipod,512 * 2) < 0) { 495 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); 496 return -1; 497 } 498 499 /* Write backup copy of boot sector and fsinfo */ 500 if (ipod_seek(ipod, (ipod->pinfo[partition].start + BackupBootSect) * ipod->sector_size) < 0) { 501 fprintf(stderr,"[ERR] Seek failed\n"); 502 return -1; 503 } 504 if (ipod_write(ipod,512 * 2) < 0) { 505 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); 506 return -1; 507 } 508 509 /* Create the first FAT sector */ 510 create_firstfatsector(ipod->sectorbuf); 511 512 /* Write the first fat sector in the right places */ 513 for ( i=0; i<NumFATs; i++ ) { 514 int SectorStart = ReservedSectCount + (i * FatSize ); 515 516 if (ipod_seek(ipod, (ipod->pinfo[partition].start + SectorStart) * ipod->sector_size) < 0) { 517 fprintf(stderr,"[ERR] Seek failed\n"); 518 return -1; 519 } 520 521 if (ipod_write(ipod,512) < 0) { 522 perror("[ERR] Write failed (first copy of bootsect/fsinfo)\n"); 523 return -1; 524 } 525 } 526 527 fprintf(stderr,"[INFO] Format successful\n"); 528 529 return 0; 530}