]> glassweightruler.freedombox.rocks Git - Ventoy.git/commitdiff
1.0.60 release v1.0.60
authorlongpanda <admin@ventoy.net>
Fri, 12 Nov 2021 11:56:05 +0000 (19:56 +0800)
committerlongpanda <admin@ventoy.net>
Fri, 12 Nov 2021 11:56:05 +0000 (19:56 +0800)
87 files changed:
.github/ISSUE_TEMPLATE/issue_template.yml
INSTALL/Ventoy2Disk.exe
INSTALL/Ventoy2Disk.sh
INSTALL/Ventoy2Disk_ARM.exe
INSTALL/Ventoy2Disk_ARM64.exe
INSTALL/Ventoy2Disk_X64.exe
INSTALL/all_in_one.sh
INSTALL/tool/VentoyWorker.sh
INSTALL/tool/aarch64/V2DServer
INSTALL/tool/aarch64/Ventoy2Disk.gtk3
INSTALL/tool/aarch64/Ventoy2Disk.qt5
INSTALL/tool/aarch64/vtoycli [moved from INSTALL/tool/aarch64/vtoyfat with 54% similarity]
INSTALL/tool/aarch64/vtoygpt [deleted file]
INSTALL/tool/i386/V2DServer
INSTALL/tool/i386/Ventoy2Disk.gtk2
INSTALL/tool/i386/Ventoy2Disk.gtk3
INSTALL/tool/i386/Ventoy2Disk.qt5
INSTALL/tool/i386/vtoycli [new file with mode: 0644]
INSTALL/tool/i386/vtoyfat [deleted file]
INSTALL/tool/i386/vtoygpt [deleted file]
INSTALL/tool/mips64el/V2DServer
INSTALL/tool/mips64el/Ventoy2Disk.gtk3
INSTALL/tool/mips64el/Ventoy2Disk.qt5
INSTALL/tool/mips64el/vtoycli [new file with mode: 0644]
INSTALL/tool/mips64el/vtoyfat [deleted file]
INSTALL/tool/mips64el/vtoygpt [deleted file]
INSTALL/tool/ventoy_lib.sh
INSTALL/tool/x86_64/V2DServer
INSTALL/tool/x86_64/Ventoy2Disk.gtk2
INSTALL/tool/x86_64/Ventoy2Disk.gtk3
INSTALL/tool/x86_64/Ventoy2Disk.qt5
INSTALL/tool/x86_64/vtoycli [new file with mode: 0644]
INSTALL/tool/x86_64/vtoyfat [deleted file]
INSTALL/tool/x86_64/vtoygpt [deleted file]
INSTALL/ventoy_pack.sh
vtoycli/build.sh [new file with mode: 0644]
vtoycli/crc32.c [new file with mode: 0644]
vtoycli/fat_io_lib/buildlib.sh [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_access.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_cache.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_defs.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_filelib.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_format.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_list.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_misc.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_opts.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_string.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_table.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_types.h [new file with mode: 0644]
vtoycli/fat_io_lib/include/fat_write.h [new file with mode: 0644]
vtoycli/fat_io_lib/lib/libfat_io_32.a [new file with mode: 0644]
vtoycli/fat_io_lib/lib/libfat_io_64.a [new file with mode: 0644]
vtoycli/fat_io_lib/lib/libfat_io_aa64.a [new file with mode: 0644]
vtoycli/fat_io_lib/lib/libfat_io_m64e.a [new file with mode: 0644]
vtoycli/fat_io_lib/release/API.txt [new file with mode: 0644]
vtoycli/fat_io_lib/release/COPYRIGHT.txt [new file with mode: 0644]
vtoycli/fat_io_lib/release/Configuration.txt [new file with mode: 0644]
vtoycli/fat_io_lib/release/History.txt [new file with mode: 0644]
vtoycli/fat_io_lib/release/License.txt [new file with mode: 0644]
vtoycli/fat_io_lib/release/Media Access API.txt [new file with mode: 0644]
vtoycli/fat_io_lib/release/example.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_access.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_access.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_cache.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_cache.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_defs.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_filelib.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_filelib.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_format.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_format.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_list.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_misc.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_misc.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_opts.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_string.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_string.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_table.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_table.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_types.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_write.c [new file with mode: 0644]
vtoycli/fat_io_lib/release/fat_write.h [new file with mode: 0644]
vtoycli/fat_io_lib/release/version.txt [new file with mode: 0644]
vtoycli/partresize.c [new file with mode: 0644]
vtoycli/vtoycli.c [new file with mode: 0644]
vtoycli/vtoycli.h [new file with mode: 0644]
vtoycli/vtoyfat.c [new file with mode: 0644]
vtoycli/vtoygpt.c [new file with mode: 0644]

index 23430c8df3f6cd84bc2bbebe97229bb3eeb89b2d..fbd3f76c0f346526d1a4e105653e5afe2b13e837 100644 (file)
@@ -21,7 +21,7 @@ body:
     attributes:
       label: Ventoy Version
       description: What version of ventoy are you running?
     attributes:
       label: Ventoy Version
       description: What version of ventoy are you running?
-      placeholder: 1.0.59
+      placeholder: 1.0.60
     validations:
       required: true
   - type: dropdown
     validations:
       required: true
   - type: dropdown
index 76c1080d72e2aa5d68777ec882e30dec32b157d3..92a77d70cedc4af10620320f35524ad54db39857 100644 (file)
Binary files a/INSTALL/Ventoy2Disk.exe and b/INSTALL/Ventoy2Disk.exe differ
index d8440d055dfb58b581c892c4335139dc4c2b4e03..2692b5ae3b05cf5e09c8c073903fb12621146439 100644 (file)
@@ -23,6 +23,8 @@ else
 fi
 export PATH="./tool/$TOOLDIR:$PATH"
 
 fi
 export PATH="./tool/$TOOLDIR:$PATH"
 
+rm -f ./log.txt
+
 
 echo ''
 echo '**********************************************'
 
 echo ''
 echo '**********************************************'
index 55fd765229f8be546e0a81e4b9c6712faf183982..d1398f64a7e2b2d04c1977870a6f6e951ec3944f 100644 (file)
Binary files a/INSTALL/Ventoy2Disk_ARM.exe and b/INSTALL/Ventoy2Disk_ARM.exe differ
index 47727bd0a376e0ae860c9b6f0bee22113fc97615..18a02ae70a69a9527c4a939b0943ff70b2247836 100644 (file)
Binary files a/INSTALL/Ventoy2Disk_ARM64.exe and b/INSTALL/Ventoy2Disk_ARM64.exe differ
index 88fd252028811525a064365c8424ebae2e28d78a..c6cf57cc447d65f7eadec77b860e09d653505e1e 100644 (file)
Binary files a/INSTALL/Ventoy2Disk_X64.exe and b/INSTALL/Ventoy2Disk_X64.exe differ
index 14163f38535dd6f69059b0861406464d2f4a53dd..827c80bac146155ac44b7ea27f97657e3f2db27f 100644 (file)
@@ -38,13 +38,10 @@ sh buildedk.sh >> $LOG 2>&1 || exit 1
 #cd $VTOY_PATH/VtoyTool
 #sh build.sh || exit 1
 
 #cd $VTOY_PATH/VtoyTool
 #sh build.sh || exit 1
 
-#cd $VTOY_PATH/vtoyfat/fat_io_lib
+#cd $VTOY_PATH/vtoycli/fat_io_lib
 #sh buildlib.sh
 
 #sh buildlib.sh
 
-#cd $VTOY_PATH/vtoyfat
-#sh build.sh || exit 1
-
-#cd $VTOY_PATH/vtoygpt
+#cd $VTOY_PATH/vtoycli
 #sh build.sh || exit 1
 
 #cd $VTOY_PATH/FUSEISO
 #sh build.sh || exit 1
 
 #cd $VTOY_PATH/FUSEISO
index 543ac69717d3fce6defd8a9c17f57bcc415772ad..22c3de0508b742b28d523ceb494edab3b5bc1f7d 100644 (file)
@@ -16,6 +16,7 @@ print_usage() {
     echo '   -s/-S       enable/disable secure boot support (default is disabled)'
     echo '   -g          use GPT partition style, default is MBR (only for install)'
     echo '   -L          Label of the 1st exfat partition (default is Ventoy)'
     echo '   -s/-S       enable/disable secure boot support (default is disabled)'
     echo '   -g          use GPT partition style, default is MBR (only for install)'
     echo '   -L          Label of the 1st exfat partition (default is Ventoy)'
+    echo '   -n          try non-destructive installation (only for install)'
     echo ''
 }
 
     echo ''
 }
 
@@ -28,6 +29,8 @@ while [ -n "$1" ]; do
     elif [ "$1" = "-I" ]; then
         MODE="install"
         FORCE="Y"
     elif [ "$1" = "-I" ]; then
         MODE="install"
         FORCE="Y"
+    elif [ "$1" = "-n" ]; then
+        NONDESTRUCTIVE="Y"
     elif [ "$1" = "-u" ]; then
         MODE="update"
     elif [ "$1" = "-l" ]; then
     elif [ "$1" = "-u" ]; then
         MODE="update"
     elif [ "$1" = "-l" ]; then
@@ -162,7 +165,7 @@ if [ -d ./tmp_mnt ]; then
 fi
 
 
 fi
 
 
-if [ "$MODE" = "install" ]; then
+if [ "$MODE" = "install" -a -z "$NONDESTRUCTIVE" ]; then
     vtdebug "install Ventoy ..."
 
     if [ -n "$VTGPT" ]; then
     vtdebug "install Ventoy ..."
 
     if [ -n "$VTGPT" ]; then
@@ -255,7 +258,10 @@ if [ "$MODE" = "install" ]; then
         exit 1
     fi
 
         exit 1
     fi
 
-    if ! dd if=/dev/zero of=$DISK bs=1 count=512 status=none conv=fsync; then
+    # check and umount
+    check_umount_disk "$DISK"
+
+    if ! dd if=/dev/zero of=$DISK bs=64 count=512 status=none conv=fsync; then
         vterr "Write data to $DISK failed, please check whether it's in use."
         exit 1
     fi
         vterr "Write data to $DISK failed, please check whether it's in use."
         exit 1
     fi
@@ -281,10 +287,14 @@ if [ "$MODE" = "install" ]; then
     PART1=$(get_disk_part_name $DISK 1)  
     PART2=$(get_disk_part_name $DISK 2)  
 
     PART1=$(get_disk_part_name $DISK 1)  
     PART2=$(get_disk_part_name $DISK 2)  
 
+    #clean part2
+    dd status=none conv=fsync if=/dev/zero of=$DISK bs=512 count=32 seek=$part2_start_sector
+
+    #format part1
+    vtinfo "Format partition 1 ..."
     mkexfatfs -n "$VTNEW_LABEL" -s $cluster_sectors ${PART1}
 
     mkexfatfs -n "$VTNEW_LABEL" -s $cluster_sectors ${PART1}
 
-    vtinfo "writing data to disk ..."
-    
+    vtinfo "writing data to disk ..."    
     dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=446
     
     if [ -n "$VTGPT" ]; then
     dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=446
     
     if [ -n "$VTGPT" ]; then
@@ -314,51 +324,181 @@ if [ "$MODE" = "install" ]; then
     sync
     
     vtinfo "esp partition processing ..."
     sync
     
     vtinfo "esp partition processing ..."
+    if [ "$SECUREBOOT" != "YES" ]; then 
+        sleep 2
+        check_umount_disk "$DISK"  
+        vtoycli partresize -s $DISK $part2_start_sector
+    fi
+
+    echo ""
+    vtinfo "Install Ventoy to $DISK successfully finished."
+    echo ""
+
+elif [ "$MODE" = "install" -a -n "$NONDESTRUCTIVE" ]; then
+    vtdebug "non-destructive install Ventoy ..."
+
+    version=$(get_disk_ventoy_version $DISK)
+    if [ $? -eq 0 ]; then
+        if [ -z "$FORCE" ]; then
+            vtwarn "$DISK already contains a Ventoy with version $version."
+            vtwarn "You can not do and don not need non-destructive installation."
+            vtwarn ""
+            exit 1
+        fi
+    fi
     
     
-    sleep 1
-    check_umount_disk "$DISK"    
+    disk_sector_num=$(cat /sys/block/${DISK#/dev/}/size)
+    disk_size_gb=$(expr $disk_sector_num / 2097152)
+
+    if vtoycli partresize -t $DISK; then
+        OldStyle="GPT"
+    else
+        OldStyle="MBR"
+    fi
+
+    #Print disk info
+    echo "Disk : $DISK"
+    parted -s $DISK p 2>&1 | grep Model
+    echo "Size : $disk_size_gb GB" 
+    echo "Style: $OldStyle"    
+    echo ''
+
+    vtwarn "Attention:"
+    vtwarn "Ventoy will try non-destructive installation on $DISK if possible."
+    echo ""
+
+    read -p 'Continue? (y/n) '  Answer
+    if [ "$Answer" != "y" ]; then
+        if [ "$Answer" != "Y" ]; then
+            exit 0
+        fi
+    fi
+
+    if [ $disk_sector_num -le $VENTOY_SECTOR_NUM ]; then  
+        vterr "No enough space in disk $DISK"
+        exit 1
+    fi
+
+    PART1=$(get_disk_part_name $DISK 1)  
+    PART2=$(get_disk_part_name $DISK 2)  
+
+    #Part1 size in MB aligned with 4KB
+    PART1_SECTORS=$(cat /sys/class/block/${PART1#/dev/}/size)
+    PART1_4K=$(expr $PART1_SECTORS / 8)
+    PART1_MB=$(expr $PART1_4K / 256)
+    PART1_NEW_MB=$(expr $PART1_MB - 32)
+
+    echo "$PART1 is ${PART1_MB}MB"
+
+    #check partition layout
+    echo "check partition layout ..."
+    vtoycli partresize -c $DISK
+    vtRet=$?
+    if [ $vtRet -eq 0 ]; then
+        exit 1
+    else
+        # check and umount
+        check_umount_disk "$DISK"
+        sleep 1
+        check_umount_disk "$DISK"
     
     
-    if [ "$SECUREBOOT" != "YES" ]; then        
-        mkdir ./tmp_mnt
-        
-        vtdebug "mounting part2 ...."
-        for tt in 1 2 3 4 5; do            
-            if mount ${PART2} ./tmp_mnt > /dev/null 2>&1; then
-                vtdebug "mounting part2 success"
-                break
-            fi
+        if [ $vtRet -eq 1 ]; then
+            echo "Free space enough, start install..."
+            part2_start_sector=$(expr $PART1_SECTORS + 2048)
+        elif [ $vtRet -eq 2 ]; then
+            echo "We need to shrink partition 1 firstly ..."
             
             
-            check_umount_disk "$DISK"            
-            sleep 2
-        done
-        
-        rm -f ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
-        rm -f ./tmp_mnt/EFI/BOOT/grubx64.efi
-        rm -f ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
-        rm -f ./tmp_mnt/EFI/BOOT/grubia32.efi
-        rm -f ./tmp_mnt/EFI/BOOT/MokManager.efi
-        rm -f ./tmp_mnt/EFI/BOOT/mmia32.efi
-        rm -f ./tmp_mnt/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
-        mv ./tmp_mnt/EFI/BOOT/grubx64_real.efi  ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
-        mv ./tmp_mnt/EFI/BOOT/grubia32_real.efi  ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
-        
-        sync
-        
-        for tt in 1 2 3; do
-            if umount ./tmp_mnt; then
-                vtdebug "umount part2 success"
-                rm -rf ./tmp_mnt
-                break
+            PART1_BLKID=$(blkid $PART1)
+            blkid $PART1
+            
+            if echo $PART1_BLKID | egrep -q -i 'TYPE=ntfs|TYPE=.ntfs'; then
+                echo "Partition 1 contains NTFS filesystem"
+                
+                which ntfsresize
+                if [ $? -ne 0 ]; then
+                    echo "###[FAIL] ntfsresize not found. Please install ntfs-3g package."
+                    exit 1
+                fi
+                
+                echo "ntfsfix -b -d $PART1 ..."
+                ntfsfix -b -d $PART1
+                
+                echo "ntfsresize --size ${PART1_NEW_MB}Mi $PART1 ..."
+                ntfsresize -f --size ${PART1_NEW_MB}Mi $PART1
+                if [ $? -ne 0 ]; then
+                    echo "###[FAIL] ntfsresize failed." 
+                    exit 1
+                fi
+            elif echo $PART1_BLKID | egrep -q -i 'TYPE=ext[2-4]|TYPE=.ext[2-4]'; then
+                echo "Partition 1 contains EXT filesystem"
+                
+                which resize2fs
+                if [ $? -ne 0 ]; then
+                    echo "###[FAIL] resize2fs not found. Please install e2fsprogs package."
+                    exit 1
+                fi
+                
+                echo "e2fsck -f $PART1 ..."
+                e2fsck -f $PART1
+                
+                echo "resize2fs $PART1 ${PART1_NEW_MB}M ..."
+                resize2fs $PART1 ${PART1_NEW_MB}M
+                if [ $? -ne 0 ]; then
+                    echo "###[FAIL] resize2fs failed." 
+                    exit 1
+                fi
             else
             else
-                vtdebug "umount part2 failed, now retry..."
-                sleep 1
+                echo "###[FAIL] Unsupported filesystem in partition 1."
+                exit 1
             fi
             fi
-        done
+            
+            sync
+            PART1_NEW_END_MB=$(expr $PART1_NEW_MB + 1)
+            part2_start_sector=$(expr $PART1_NEW_END_MB \* 2048)
+        fi
     fi
 
     fi
 
-    echo ""
-    vtinfo "Install Ventoy to $DISK successfully finished."
-    echo ""
+    vtinfo "writing data to disk part2_start=$part2_start_sector ..."
+    
+    dd status=none conv=fsync if=./boot/boot.img of=$DISK bs=1 count=440
+    
+    if [ "$OldStyle" = "GPT" ]; then
+        echo -en '\x22' | dd status=none of=$DISK conv=fsync bs=1 count=1 seek=92        
+        xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2014 seek=34
+        echo -en '\x23' | dd of=$DISK conv=fsync bs=1 count=1 seek=17908 status=none
+    else
+        xzcat ./boot/core.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=2047 seek=1
+    fi
+    
+    xzcat ./ventoy/ventoy.disk.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start_sector
+    
+    #test UUID
+    testUUIDStr=$(vtoy_gen_uuid | hexdump -C)
+    vtdebug "test uuid: $testUUIDStr"
+    
+    #disk uuid
+    vtoy_gen_uuid | dd status=none conv=fsync of=${DISK} seek=384 bs=1 count=16
+    
+    vtinfo "sync data ..."
+    sync
+    
+    vtinfo "esp partition processing ..."
+    if [ "$SECUREBOOT" != "YES" ]; then
+        vtoycli partresize -s $DISK $part2_start_sector
+    fi
+
+    vtinfo "update partition table $DISK $part2_start_sector ..."
+    vtoycli partresize -p $DISK $part2_start_sector
+    if [ $? -eq 0 ]; then
+        sync
+        echo ""
+        vtinfo "Ventoy non-destructive installation on $DISK successfully finished."
+        echo ""
+    else
+        echo ""
+        vterr "Ventoy non-destructive installation on $DISK failed."
+        echo ""
+    fi
     
 else
     vtdebug "update Ventoy ..."
     
 else
     vtdebug "update Ventoy ..."
@@ -441,47 +581,13 @@ else
     check_umount_disk "$DISK"
     
     xzcat ./ventoy/ventoy.disk.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start
     check_umount_disk "$DISK"
     
     xzcat ./ventoy/ventoy.disk.img.xz | dd status=none conv=fsync of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start
-
     sync
 
     sync
 
+    vtinfo "esp partition processing ..."
     if [ "$SECUREBOOT" != "YES" ]; then
     if [ "$SECUREBOOT" != "YES" ]; then
-        mkdir ./tmp_mnt
-        
-        vtdebug "mounting part2 ...."        
-        for tt in 1 2 3 4 5; do
-            check_umount_disk "$DISK"
-
-            if mount ${PART2} ./tmp_mnt > /dev/null 2>&1; then
-                vtdebug "mounting part2 success"
-                break
-            else
-                vtdebug "mounting part2 failed, now wait and retry..."
-            fi
-            sleep 2            
-        done
-        
-        rm -f ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
-        rm -f ./tmp_mnt/EFI/BOOT/grubx64.efi
-        rm -f ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
-        rm -f ./tmp_mnt/EFI/BOOT/grubia32.efi
-        rm -f ./tmp_mnt/EFI/BOOT/MokManager.efi
-        rm -f ./tmp_mnt/EFI/BOOT/mmia32.efi
-        rm -f ./tmp_mnt/ENROLL_THIS_KEY_IN_MOKMANAGER.cer
-        mv ./tmp_mnt/EFI/BOOT/grubx64_real.efi  ./tmp_mnt/EFI/BOOT/BOOTX64.EFI
-        mv ./tmp_mnt/EFI/BOOT/grubia32_real.efi  ./tmp_mnt/EFI/BOOT/BOOTIA32.EFI
-        
-        sync
-        
-        for tt in 1 2 3; do
-            if umount ./tmp_mnt > /dev/null 2>&1; then
-                vtdebug "umount part2 success"
-                rm -rf ./tmp_mnt
-                break
-            else
-                vtdebug "umount part2 failed, now retry..."
-                sleep 1
-            fi
-        done        
+        sleep 2
+        check_umount_disk "$DISK"
+        vtoycli partresize -s $DISK $part2_start
     fi
 
     echo ""
     fi
 
     echo ""
index 26b4cbb3a10d6c1d0082efa2154bc0f9c13b3a9f..f4e4873374bb600d55cca20b395b5b570db9dd2b 100644 (file)
Binary files a/INSTALL/tool/aarch64/V2DServer and b/INSTALL/tool/aarch64/V2DServer differ
index 5692a5d7f9d5bc442277c5059396876c34ebe09e..db03db5ec092c073ed9d2bbe7e34ea149d1c5748 100644 (file)
Binary files a/INSTALL/tool/aarch64/Ventoy2Disk.gtk3 and b/INSTALL/tool/aarch64/Ventoy2Disk.gtk3 differ
index eb26488a0a45301c50dfb80d2076effe6c6c106f..4b8493966a833d1d7768c686cf1d469391247365 100644 (file)
Binary files a/INSTALL/tool/aarch64/Ventoy2Disk.qt5 and b/INSTALL/tool/aarch64/Ventoy2Disk.qt5 differ
similarity index 54%
rename from INSTALL/tool/aarch64/vtoyfat
rename to INSTALL/tool/aarch64/vtoycli
index f1ff74858d5f89ee0f6f7513b89000a53703083a..7cc072b27284d7bd7c84b599b7e31c89ce91c64a 100644 (file)
Binary files a/INSTALL/tool/aarch64/vtoyfat and b/INSTALL/tool/aarch64/vtoycli differ
diff --git a/INSTALL/tool/aarch64/vtoygpt b/INSTALL/tool/aarch64/vtoygpt
deleted file mode 100644 (file)
index 420aefc..0000000
Binary files a/INSTALL/tool/aarch64/vtoygpt and /dev/null differ
index 9e1322e0d1d34c18a217cfb915eafce6744ee625..fe154acd1060e89d40a17959de404e51352a671e 100644 (file)
Binary files a/INSTALL/tool/i386/V2DServer and b/INSTALL/tool/i386/V2DServer differ
index 9360d8b848fcb5991f8a4a4fd9f22d13d094e939..f7317c5918c88f483fe101fa351e98dfff34e317 100644 (file)
Binary files a/INSTALL/tool/i386/Ventoy2Disk.gtk2 and b/INSTALL/tool/i386/Ventoy2Disk.gtk2 differ
index 90a52077abfdaada9e4de657cd6a89e15e52c9ec..938d6808e41bd3415fbc6d17ff410c2e7f4b06df 100644 (file)
Binary files a/INSTALL/tool/i386/Ventoy2Disk.gtk3 and b/INSTALL/tool/i386/Ventoy2Disk.gtk3 differ
index 0b64ecc7c26a6009c9578ec32bac9d896305c9f0..e46494e632ec9f4c6578ca3dfc3dd04eed3d51cb 100644 (file)
Binary files a/INSTALL/tool/i386/Ventoy2Disk.qt5 and b/INSTALL/tool/i386/Ventoy2Disk.qt5 differ
diff --git a/INSTALL/tool/i386/vtoycli b/INSTALL/tool/i386/vtoycli
new file mode 100644 (file)
index 0000000..b6a9110
Binary files /dev/null and b/INSTALL/tool/i386/vtoycli differ
diff --git a/INSTALL/tool/i386/vtoyfat b/INSTALL/tool/i386/vtoyfat
deleted file mode 100644 (file)
index b372e62..0000000
Binary files a/INSTALL/tool/i386/vtoyfat and /dev/null differ
diff --git a/INSTALL/tool/i386/vtoygpt b/INSTALL/tool/i386/vtoygpt
deleted file mode 100644 (file)
index 127f080..0000000
Binary files a/INSTALL/tool/i386/vtoygpt and /dev/null differ
index 4d36cb863709efe5a541760a404a62d076db4fb3..14be9acf0e51623ceb2fd3961e50b8c928f407dc 100644 (file)
Binary files a/INSTALL/tool/mips64el/V2DServer and b/INSTALL/tool/mips64el/V2DServer differ
index 0e1d81bfb1f546d65f5a8be905c11b8d0b2973c3..540a93bac801613fd4af878c5a6d864e1d5ac7fb 100644 (file)
Binary files a/INSTALL/tool/mips64el/Ventoy2Disk.gtk3 and b/INSTALL/tool/mips64el/Ventoy2Disk.gtk3 differ
index 5e7c6a58e84cfa7866362b9ebb90223c7f0dde7c..03cc6bfb47ce767de62e83ea431d28baa350a530 100644 (file)
Binary files a/INSTALL/tool/mips64el/Ventoy2Disk.qt5 and b/INSTALL/tool/mips64el/Ventoy2Disk.qt5 differ
diff --git a/INSTALL/tool/mips64el/vtoycli b/INSTALL/tool/mips64el/vtoycli
new file mode 100644 (file)
index 0000000..c087f3d
Binary files /dev/null and b/INSTALL/tool/mips64el/vtoycli differ
diff --git a/INSTALL/tool/mips64el/vtoyfat b/INSTALL/tool/mips64el/vtoyfat
deleted file mode 100644 (file)
index 2ff2eaf..0000000
Binary files a/INSTALL/tool/mips64el/vtoyfat and /dev/null differ
diff --git a/INSTALL/tool/mips64el/vtoygpt b/INSTALL/tool/mips64el/vtoygpt
deleted file mode 100644 (file)
index 074d6f0..0000000
Binary files a/INSTALL/tool/mips64el/vtoygpt and /dev/null differ
index 709ce2c0e4b430a1dd826c4f90f7ec98f7e3b893..ef047f35b34f65a82d220c9a322fa4463e617341 100644 (file)
@@ -68,10 +68,10 @@ check_tool_work_ok() {
         return
     fi
     
         return
     fi
     
-    if vtoyfat -T; then
-        vtdebug "vtoyfat test ok ..."
+    if vtoycli fat -T; then
+        vtdebug "vtoycli fat test ok ..."
     else
     else
-        vtdebug "vtoyfat test fail ..."
+        vtdebug "vtoycli fat test fail ..."
         ventoy_false
         return
     fi
         ventoy_false
         return
     fi
@@ -183,7 +183,7 @@ check_disk_secure_boot() {
     
     PART2=$(get_disk_part_name $1 2)    
     
     
     PART2=$(get_disk_part_name $1 2)    
     
-    vtoyfat -s $PART2
+    vtoycli fat -s $PART2
 }
 
 get_disk_ventoy_version() {
 }
 
 get_disk_ventoy_version() {
@@ -195,7 +195,7 @@ get_disk_ventoy_version() {
     
     PART2=$(get_disk_part_name $1 2)    
     
     
     PART2=$(get_disk_part_name $1 2)    
     
-    ParseVer=$(vtoyfat $PART2)
+    ParseVer=$(vtoycli fat $PART2)
     if [ $? -eq 0 ]; then
         vtdebug "Ventoy version in $PART2 is $ParseVer"
         echo $ParseVer
     if [ $? -eq 0 ]; then
         vtdebug "Ventoy version in $PART2 is $ParseVer"
         echo $ParseVer
@@ -391,7 +391,7 @@ format_ventoy_disk_gpt() {
 
     sync
     
 
     sync
     
-    vtoygpt -f $DISK
+    vtoycli gpt -f $DISK
     sync
 
     udevadm trigger --name-match=$DISK >/dev/null 2>&1
     sync
 
     udevadm trigger --name-match=$DISK >/dev/null 2>&1
index 19010a41d6a7c39369cfd54cc975211b288b988b..492a0e972600476207242985ee5a343e87c0dee8 100644 (file)
Binary files a/INSTALL/tool/x86_64/V2DServer and b/INSTALL/tool/x86_64/V2DServer differ
index 717db106e99208abc890b544503891222582b7fd..7180adcecd53dd65000f314662cd372730a0a362 100644 (file)
Binary files a/INSTALL/tool/x86_64/Ventoy2Disk.gtk2 and b/INSTALL/tool/x86_64/Ventoy2Disk.gtk2 differ
index 6e70312373b1e40fb684d91d61e0ccd8ef99e242..8e4b11449f8b8143b01f92552ea0dccd76f73688 100644 (file)
Binary files a/INSTALL/tool/x86_64/Ventoy2Disk.gtk3 and b/INSTALL/tool/x86_64/Ventoy2Disk.gtk3 differ
index 8bd405b4c23327c260f373985807152a1182c5fe..3bef337bfe6b52d13bdc4b3c0496032a628e061e 100644 (file)
Binary files a/INSTALL/tool/x86_64/Ventoy2Disk.qt5 and b/INSTALL/tool/x86_64/Ventoy2Disk.qt5 differ
diff --git a/INSTALL/tool/x86_64/vtoycli b/INSTALL/tool/x86_64/vtoycli
new file mode 100644 (file)
index 0000000..edb4f2a
Binary files /dev/null and b/INSTALL/tool/x86_64/vtoycli differ
diff --git a/INSTALL/tool/x86_64/vtoyfat b/INSTALL/tool/x86_64/vtoyfat
deleted file mode 100644 (file)
index 26e020c..0000000
Binary files a/INSTALL/tool/x86_64/vtoyfat and /dev/null differ
diff --git a/INSTALL/tool/x86_64/vtoygpt b/INSTALL/tool/x86_64/vtoygpt
deleted file mode 100644 (file)
index 068aaf6..0000000
Binary files a/INSTALL/tool/x86_64/vtoygpt and /dev/null differ
index a1f10059552b1874e3382523fb7900b0d5e0e96b..00a4b2fe22a7198b4e67fa3c4cedfba58b5357e5 100644 (file)
@@ -89,9 +89,9 @@ mkdir -p $tmpmnt/tool
 # cp $OPT ./tool/x86_64/mount.exfat-fuse   $tmpmnt/tool/mount.exfat-fuse_x86_64
 # cp $OPT ./tool/aarch64/mount.exfat-fuse  $tmpmnt/tool/mount.exfat-fuse_aarch64
 # to save space
 # cp $OPT ./tool/x86_64/mount.exfat-fuse   $tmpmnt/tool/mount.exfat-fuse_x86_64
 # cp $OPT ./tool/aarch64/mount.exfat-fuse  $tmpmnt/tool/mount.exfat-fuse_aarch64
 # to save space
-cp $OPT ./tool/i386/vtoygpt     $tmpmnt/tool/mount.exfat-fuse_i386
-cp $OPT ./tool/x86_64/vtoygpt   $tmpmnt/tool/mount.exfat-fuse_x86_64
-cp $OPT ./tool/aarch64/vtoygpt  $tmpmnt/tool/mount.exfat-fuse_aarch64
+dd status=none bs=1024 count=16  if=./tool/i386/vtoycli    of=$tmpmnt/tool/mount.exfat-fuse_i386
+dd status=none bs=1024 count=16  if=./tool/x86_64/vtoycli  of=$tmpmnt/tool/mount.exfat-fuse_x86_64
+dd status=none bs=1024 count=16  if=./tool/aarch64/vtoycli of=$tmpmnt/tool/mount.exfat-fuse_aarch64
 
 
 rm -f $tmpmnt/grub/i386-pc/*.img
 
 
 rm -f $tmpmnt/grub/i386-pc/*.img
diff --git a/vtoycli/build.sh b/vtoycli/build.sh
new file mode 100644 (file)
index 0000000..a5468b5
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+rm -f vtoycli_64
+rm -f vtoycli_32
+rm -f vtoycli_aa64
+rm -f vtoycli_m64e
+
+SRCS="vtoycli.c vtoyfat.c vtoygpt.c crc32.c partresize.c"
+
+gcc -specs "/usr/local/musl/lib/musl-gcc.specs" -Os -static -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_64.a -o vtoycli_64
+
+/opt/diet32/bin/diet -Os gcc -D_FILE_OFFSET_BITS=64 -m32 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_32.a -o vtoycli_32
+
+
+#gcc -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_64.a -o vtoycli_64
+#gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_32.a -o vtoycli_32
+
+aarch64-buildroot-linux-uclibc-gcc -static -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_aa64.a -o vtoycli_aa64
+mips64el-linux-musl-gcc -mips64r2 -mabi=64 -static -O2 -D_FILE_OFFSET_BITS=64 $SRCS -Ifat_io_lib/include fat_io_lib/lib/libfat_io_m64e.a -o vtoycli_m64e
+
+
+if [ -e vtoycli_64 ] && [ -e vtoycli_32 ] && [ -e vtoycli_aa64 ] && [ -e vtoycli_m64e ]; then
+    echo -e "\n===== success $name =======\n"
+    
+    strip --strip-all vtoycli_32
+    strip --strip-all vtoycli_64
+    aarch64-buildroot-linux-uclibc-strip --strip-all vtoycli_aa64
+    mips64el-linux-musl-strip --strip-all vtoycli_m64e
+    
+    [ -d ../INSTALL/tool/i386/ ] && mv vtoycli_32 ../INSTALL/tool/i386/vtoycli
+    [ -d ../INSTALL/tool/x86_64/ ] && mv vtoycli_64 ../INSTALL/tool/x86_64/vtoycli
+    [ -d ../INSTALL/tool/aarch64/ ] && mv vtoycli_aa64 ../INSTALL/tool/aarch64/vtoycli
+    [ -d ../INSTALL/tool/mips64el/ ] && mv vtoycli_m64e ../INSTALL/tool/mips64el/vtoycli
+else
+    echo -e "\n===== failed =======\n"
+    exit 1
+fi
diff --git a/vtoycli/crc32.c b/vtoycli/crc32.c
new file mode 100644 (file)
index 0000000..5f0f777
--- /dev/null
@@ -0,0 +1,305 @@
+/******************************************************************************
+ * vtoygpt.c  ---- ventoy gpt util
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define VOID   void
+#define CHAR   char
+#define UINT64 unsigned long long
+#define UINT32 unsigned int
+#define UINT16 unsigned short
+#define CHAR16 unsigned short
+#define UINT8  unsigned char
+
+UINT32 g_crc_table[256] = {
+  0x00000000,
+  0x77073096,
+  0xEE0E612C,
+  0x990951BA,
+  0x076DC419,
+  0x706AF48F,
+  0xE963A535,
+  0x9E6495A3,
+  0x0EDB8832,
+  0x79DCB8A4,
+  0xE0D5E91E,
+  0x97D2D988,
+  0x09B64C2B,
+  0x7EB17CBD,
+  0xE7B82D07,
+  0x90BF1D91,
+  0x1DB71064,
+  0x6AB020F2,
+  0xF3B97148,
+  0x84BE41DE,
+  0x1ADAD47D,
+  0x6DDDE4EB,
+  0xF4D4B551,
+  0x83D385C7,
+  0x136C9856,
+  0x646BA8C0,
+  0xFD62F97A,
+  0x8A65C9EC,
+  0x14015C4F,
+  0x63066CD9,
+  0xFA0F3D63,
+  0x8D080DF5,
+  0x3B6E20C8,
+  0x4C69105E,
+  0xD56041E4,
+  0xA2677172,
+  0x3C03E4D1,
+  0x4B04D447,
+  0xD20D85FD,
+  0xA50AB56B,
+  0x35B5A8FA,
+  0x42B2986C,
+  0xDBBBC9D6,
+  0xACBCF940,
+  0x32D86CE3,
+  0x45DF5C75,
+  0xDCD60DCF,
+  0xABD13D59,
+  0x26D930AC,
+  0x51DE003A,
+  0xC8D75180,
+  0xBFD06116,
+  0x21B4F4B5,
+  0x56B3C423,
+  0xCFBA9599,
+  0xB8BDA50F,
+  0x2802B89E,
+  0x5F058808,
+  0xC60CD9B2,
+  0xB10BE924,
+  0x2F6F7C87,
+  0x58684C11,
+  0xC1611DAB,
+  0xB6662D3D,
+  0x76DC4190,
+  0x01DB7106,
+  0x98D220BC,
+  0xEFD5102A,
+  0x71B18589,
+  0x06B6B51F,
+  0x9FBFE4A5,
+  0xE8B8D433,
+  0x7807C9A2,
+  0x0F00F934,
+  0x9609A88E,
+  0xE10E9818,
+  0x7F6A0DBB,
+  0x086D3D2D,
+  0x91646C97,
+  0xE6635C01,
+  0x6B6B51F4,
+  0x1C6C6162,
+  0x856530D8,
+  0xF262004E,
+  0x6C0695ED,
+  0x1B01A57B,
+  0x8208F4C1,
+  0xF50FC457,
+  0x65B0D9C6,
+  0x12B7E950,
+  0x8BBEB8EA,
+  0xFCB9887C,
+  0x62DD1DDF,
+  0x15DA2D49,
+  0x8CD37CF3,
+  0xFBD44C65,
+  0x4DB26158,
+  0x3AB551CE,
+  0xA3BC0074,
+  0xD4BB30E2,
+  0x4ADFA541,
+  0x3DD895D7,
+  0xA4D1C46D,
+  0xD3D6F4FB,
+  0x4369E96A,
+  0x346ED9FC,
+  0xAD678846,
+  0xDA60B8D0,
+  0x44042D73,
+  0x33031DE5,
+  0xAA0A4C5F,
+  0xDD0D7CC9,
+  0x5005713C,
+  0x270241AA,
+  0xBE0B1010,
+  0xC90C2086,
+  0x5768B525,
+  0x206F85B3,
+  0xB966D409,
+  0xCE61E49F,
+  0x5EDEF90E,
+  0x29D9C998,
+  0xB0D09822,
+  0xC7D7A8B4,
+  0x59B33D17,
+  0x2EB40D81,
+  0xB7BD5C3B,
+  0xC0BA6CAD,
+  0xEDB88320,
+  0x9ABFB3B6,
+  0x03B6E20C,
+  0x74B1D29A,
+  0xEAD54739,
+  0x9DD277AF,
+  0x04DB2615,
+  0x73DC1683,
+  0xE3630B12,
+  0x94643B84,
+  0x0D6D6A3E,
+  0x7A6A5AA8,
+  0xE40ECF0B,
+  0x9309FF9D,
+  0x0A00AE27,
+  0x7D079EB1,
+  0xF00F9344,
+  0x8708A3D2,
+  0x1E01F268,
+  0x6906C2FE,
+  0xF762575D,
+  0x806567CB,
+  0x196C3671,
+  0x6E6B06E7,
+  0xFED41B76,
+  0x89D32BE0,
+  0x10DA7A5A,
+  0x67DD4ACC,
+  0xF9B9DF6F,
+  0x8EBEEFF9,
+  0x17B7BE43,
+  0x60B08ED5,
+  0xD6D6A3E8,
+  0xA1D1937E,
+  0x38D8C2C4,
+  0x4FDFF252,
+  0xD1BB67F1,
+  0xA6BC5767,
+  0x3FB506DD,
+  0x48B2364B,
+  0xD80D2BDA,
+  0xAF0A1B4C,
+  0x36034AF6,
+  0x41047A60,
+  0xDF60EFC3,
+  0xA867DF55,
+  0x316E8EEF,
+  0x4669BE79,
+  0xCB61B38C,
+  0xBC66831A,
+  0x256FD2A0,
+  0x5268E236,
+  0xCC0C7795,
+  0xBB0B4703,
+  0x220216B9,
+  0x5505262F,
+  0xC5BA3BBE,
+  0xB2BD0B28,
+  0x2BB45A92,
+  0x5CB36A04,
+  0xC2D7FFA7,
+  0xB5D0CF31,
+  0x2CD99E8B,
+  0x5BDEAE1D,
+  0x9B64C2B0,
+  0xEC63F226,
+  0x756AA39C,
+  0x026D930A,
+  0x9C0906A9,
+  0xEB0E363F,
+  0x72076785,
+  0x05005713,
+  0x95BF4A82,
+  0xE2B87A14,
+  0x7BB12BAE,
+  0x0CB61B38,
+  0x92D28E9B,
+  0xE5D5BE0D,
+  0x7CDCEFB7,
+  0x0BDBDF21,
+  0x86D3D2D4,
+  0xF1D4E242,
+  0x68DDB3F8,
+  0x1FDA836E,
+  0x81BE16CD,
+  0xF6B9265B,
+  0x6FB077E1,
+  0x18B74777,
+  0x88085AE6,
+  0xFF0F6A70,
+  0x66063BCA,
+  0x11010B5C,
+  0x8F659EFF,
+  0xF862AE69,
+  0x616BFFD3,
+  0x166CCF45,
+  0xA00AE278,
+  0xD70DD2EE,
+  0x4E048354,
+  0x3903B3C2,
+  0xA7672661,
+  0xD06016F7,
+  0x4969474D,
+  0x3E6E77DB,
+  0xAED16A4A,
+  0xD9D65ADC,
+  0x40DF0B66,
+  0x37D83BF0,
+  0xA9BCAE53,
+  0xDEBB9EC5,
+  0x47B2CF7F,
+  0x30B5FFE9,
+  0xBDBDF21C,
+  0xCABAC28A,
+  0x53B39330,
+  0x24B4A3A6,
+  0xBAD03605,
+  0xCDD70693,
+  0x54DE5729,
+  0x23D967BF,
+  0xB3667A2E,
+  0xC4614AB8,
+  0x5D681B02,
+  0x2A6F2B94,
+  0xB40BBE37,
+  0xC30C8EA1,
+  0x5A05DF1B,
+  0x2D02EF8D
+};
+
+UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length)
+{
+    UINT32  i;
+    UINT8  *Ptr = Buffer;
+    UINT32  Crc = 0xFFFFFFFF;
+
+    for (i = 0; i < Length; i++, Ptr++) 
+    {
+        Crc = (Crc >> 8) ^ g_crc_table[(UINT8) Crc ^ *Ptr];
+    }
+
+    return Crc ^ 0xffffffff;
+}
+
diff --git a/vtoycli/fat_io_lib/buildlib.sh b/vtoycli/fat_io_lib/buildlib.sh
new file mode 100644 (file)
index 0000000..fe6bb2f
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/sh
+
+rm -rf include
+rm -rf lib
+
+cd release
+#/opt/diet64/bin/diet -Os gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+gcc -specs "/usr/local/musl/lib/musl-gcc.specs" -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_64.a *.o
+rm -f *.o
+
+
+gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_32.a *.o
+rm -f *.o
+
+
+aarch64-linux-gnu-gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_aa64.a *.o
+rm -f *.o
+
+
+mips64el-linux-musl-gcc -mips64r2 -mabi=64 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_m64e.a *.o
+rm -f *.o
+
+cd -
+
+
+mkdir lib
+mkdir include
+
+mv release/*.a lib/
+cp -a release/*.h include/
+
diff --git a/vtoycli/fat_io_lib/include/fat_access.h b/vtoycli/fat_io_lib/include/fat_access.h
new file mode 100644 (file)
index 0000000..1752387
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK                         0
+#define FAT_INIT_MEDIA_ACCESS_ERROR         (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE        (-2)
+#define FAT_INIT_INVALID_SIGNATURE          (-3)
+#define FAT_INIT_ENDIAN_ERROR               (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE         (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE       (-6)
+#define FAT_INIT_STRUCT_PACKING             (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR          (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+    // User supplied function pointers for disk IO
+    fn_diskio_read          read_media;
+    fn_diskio_write         write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+    uint8                   sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+    uint32                  address;
+    int                     dirty;
+    uint8 *                 ptr;
+
+    // Next in chain of sector buffers
+    struct fat_buffer       *next;
+};
+
+typedef enum eFatType
+{
+    FAT_TYPE_16,
+    FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+    // Filesystem globals
+    uint8                   sectors_per_cluster;
+    uint32                  cluster_begin_lba;
+    uint32                  rootdir_first_cluster;
+    uint32                  rootdir_first_sector;
+    uint32                  rootdir_sectors;
+    uint32                  fat_begin_lba;
+    uint16                  fs_info_sector;
+    uint32                  lba_begin;
+    uint32                  fat_sectors;
+    uint32                  next_free_cluster;
+    uint16                  root_entry_count;
+    uint16                  reserved_sectors;
+    uint8                   num_of_fats;
+    tFatType                fat_type;
+
+    // Disk/Media API
+    struct disk_if          disk_io;
+
+    // [Optional] Thread Safety
+    void                    (*fl_lock)(void);
+    void                    (*fl_unlock)(void);
+
+    // Working buffer
+    struct fat_buffer        currentsector;
+
+    // FAT Buffer
+    struct fat_buffer        *fat_buffer_head;
+    struct fat_buffer        fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+    uint32                  sector;
+    uint32                  cluster;
+    uint8                   offset;
+};
+
+struct fs_dir_ent
+{
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   is_dir;
+    uint32                  cluster;
+    uint32                  size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    uint16                  access_date;
+    uint16                  write_time;
+    uint16                  write_date;
+    uint16                  create_date;
+    uint16                  create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int     fatfs_init(struct fatfs *fs);
+uint32  fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int     fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int     fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int     fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void    fatfs_show_details(struct fatfs *fs);
+uint32  fatfs_get_root_cluster(struct fatfs *fs);
+uint32  fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int     fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int     fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int     fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void    fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int     fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int     fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_cache.h b/vtoycli/fat_io_lib/include/fat_cache.h
new file mode 100644 (file)
index 0000000..348d5d3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_defs.h b/vtoycli/fat_io_lib/include/fat_defs.h
new file mode 100644 (file)
index 0000000..5fe8d6a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+//            FAT32 Offsets
+//        Name                Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT              0    // Length = 3
+#define BS_OEMNAME              3    // Length = 8
+#define BPB_BYTSPERSEC          11    // Length = 2
+#define BPB_SECPERCLUS          13    // Length = 1
+#define BPB_RSVDSECCNT          14    // Length = 2
+#define BPB_NUMFATS             16    // Length = 1
+#define BPB_ROOTENTCNT          17    // Length = 2
+#define BPB_TOTSEC16            19    // Length = 2
+#define BPB_MEDIA               21    // Length = 1
+#define    BPB_FATSZ16          22    // Length = 2
+#define BPB_SECPERTRK           24    // Length = 2
+#define BPB_NUMHEADS            26    // Length = 2
+#define BPB_HIDDSEC             28    // Length = 4
+#define BPB_TOTSEC32            32    // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM           36    // Length = 1
+#define BS_FAT_BOOTSIG          38    // Length = 1
+#define BS_FAT_VOLID            39    // Length = 4
+#define BS_FAT_VOLLAB           43    // Length = 11
+#define BS_FAT_FILSYSTYPE       54    // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32       36    // Length = 4
+#define BPB_FAT32_EXTFLAGS      40    // Length = 2
+#define BPB_FAT32_FSVER         42    // Length = 2
+#define BPB_FAT32_ROOTCLUS      44    // Length = 4
+#define BPB_FAT32_FSINFO        48    // Length = 2
+#define BPB_FAT32_BKBOOTSEC     50    // Length = 2
+#define BS_FAT32_DRVNUM         64    // Length = 1
+#define BS_FAT32_BOOTSIG        66    // Length = 1
+#define BS_FAT32_VOLID          67    // Length = 4
+#define BS_FAT32_VOLLAB         71    // Length = 11
+#define BS_FAT32_FILSYSTYPE     82    // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12          1
+#define FAT_TYPE_FAT16          2
+#define FAT_TYPE_FAT32          3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION              510
+#define SIGNATURE_VALUE                 0xAA55
+#define PARTITION1_TYPECODE_LOCATION    450
+#define FAT32_TYPECODE1                 0x0B
+#define FAT32_TYPECODE2                 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION   454
+#define PARTITION1_SIZE_LOCATION        458
+
+#define FAT_DIR_ENTRY_SIZE              32
+#define FAT_SFN_SIZE_FULL               11
+#define FAT_SFN_SIZE_PARTIAL            8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY             0x01
+#define FILE_ATTR_HIDDEN                0x02
+#define FILE_ATTR_SYSTEM                0x04
+#define FILE_ATTR_SYSHID                0x06
+#define FILE_ATTR_VOLUME_ID             0x08
+#define FILE_ATTR_DIRECTORY             0x10
+#define FILE_ATTR_ARCHIVE               0x20
+#define FILE_ATTR_LFN_TEXT              0x0F
+#define FILE_HEADER_BLANK               0x00
+#define FILE_HEADER_DELETED             0xE5
+#define FILE_TYPE_DIR                   0x10
+#define FILE_TYPE_FILE                  0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT            11
+#define FAT_TIME_HOURS_MASK             0x1F
+#define FAT_TIME_MINUTES_SHIFT          5
+#define FAT_TIME_MINUTES_MASK           0x3F
+#define FAT_TIME_SECONDS_SHIFT          0
+#define FAT_TIME_SECONDS_MASK           0x1F
+#define FAT_TIME_SECONDS_SCALE          2
+#define FAT_DATE_YEAR_SHIFT             9
+#define FAT_DATE_YEAR_MASK              0x7F
+#define FAT_DATE_MONTH_SHIFT            5
+#define FAT_DATE_MONTH_MASK             0xF
+#define FAT_DATE_DAY_SHIFT              0
+#define FAT_DATE_DAY_MASK               0x1F
+#define FAT_DATE_YEAR_OFFSET            1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER              0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER           0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+    uint8 Name[11];
+    uint8 Attr;
+    uint8 NTRes;
+    uint8 CrtTimeTenth;
+    uint8 CrtTime[2];
+    uint8 CrtDate[2];
+    uint8 LstAccDate[2];
+    uint16 FstClusHI;
+    uint8 WrtTime[2];
+    uint8 WrtDate[2];
+    uint16 FstClusLO;
+    uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_filelib.h b/vtoycli/fat_io_lib/include/fat_filelib.h
new file mode 100644 (file)
index 0000000..a40a28f
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+    #define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+    #define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+    #define SEEK_SET    0
+#endif
+
+#ifndef EOF
+    #define EOF         (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+    uint32 ClusterIdx;
+    uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+    uint32                  parentcluster;
+    uint32                  startcluster;
+    uint32                  bytenum;
+    uint32                  filelength;
+    int                     filelength_changed;
+    char                    path[FATFS_MAX_LONG_FILENAME];
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32                  cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+    uint32                  cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+    // Cluster Lookup
+    struct cluster_lookup   last_fat_lookup;
+
+    // Read/Write sector buffer
+    uint8                   file_data_sector[FAT_SECTOR_SIZE];
+    uint32                  file_data_address;
+    int                     file_data_dirty;
+
+    // File fopen flags
+    uint8                   flags;
+#define FILE_READ           (1 << 0)
+#define FILE_WRITE          (1 << 1)
+#define FILE_APPEND         (1 << 2)
+#define FILE_BINARY         (1 << 3)
+#define FILE_ERASE          (1 << 4)
+#define FILE_CREATE         (1 << 5)
+
+    struct fat_node         list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void                fl_init(void);
+void                fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int                 fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void                fl_shutdown(void);
+
+// Standard API
+void*               fl_fopen(const char *path, const char *modifiers);
+void                fl_fclose(void *file);
+int                 fl_fflush(void *file);
+int                 fl_fgetc(void *file);
+char *              fl_fgets(char *s, int n, void *f);
+int                 fl_fputc(int c, void *file);
+int                 fl_fputs(const char * str, void *file);
+int                 fl_fwrite(const void * data, int size, int count, void *file );
+int                 fl_fread(void * data, int size, int count, void *file );
+int                 fl_fseek(void *file , long offset , int origin );
+int                 fl_fgetpos(void *file , uint32 * position);
+long                fl_ftell(void *f);
+int                 fl_feof(void *f);
+int                 fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status    FL_DIR;
+typedef struct fs_dir_ent            fl_dirent;
+
+FL_DIR*             fl_opendir(const char* path, FL_DIR *dir);
+int                 fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int                 fl_closedir(FL_DIR* dir);
+
+// Extensions
+void                fl_listdirectory(const char *path);
+int                 fl_createdirectory(const char *path);
+int                 fl_is_dir(const char *path);
+
+int                 fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs*       fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE            FL_FILE
+
+#define fopen(a,b)      fl_fopen(a, b)
+#define fclose(a)       fl_fclose(a)
+#define fflush(a)       fl_fflush(a)
+#define fgetc(a)        fl_fgetc(a)
+#define fgets(a,b,c)    fl_fgets(a, b, c)
+#define fputc(a,b)      fl_fputc(a, b)
+#define fputs(a,b)      fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d)  fl_fread(a, b, c, d)
+#define fseek(a,b,c)    fl_fseek(a, b, c)
+#define fgetpos(a,b)    fl_fgetpos(a, b)
+#define ftell(a)        fl_ftell(a)
+#define feof(a)         fl_feof(a)
+#define remove(a)       fl_remove(a)
+#define mkdir(a)        fl_createdirectory(a)
+#define rmdir(a)        0
+
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_format.h b/vtoycli/fat_io_lib/include/fat_format.h
new file mode 100644 (file)
index 0000000..a8a6bba
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_list.h b/vtoycli/fat_io_lib/include/fat_list.h
new file mode 100644 (file)
index 0000000..bd386ef
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+    #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+    #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+    struct fat_node    *previous;
+    struct fat_node    *next;
+};
+
+struct fat_list
+{
+    struct fat_node    *head;
+    struct fat_node    *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m)     p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p)         (p)->next
+#define fat_list_prev(l, p)         (p)->previous
+#define fat_list_first(l)           (l)->head
+#define fat_list_last(l)            (l)->tail
+#define fat_list_for_each(l, p)     for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if(!node->previous)
+        list->head = node->next;
+    else
+        node->previous->next = node->next;
+
+    if(!node->next)
+        list->tail = node->previous;
+    else
+        node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node;
+    new_node->next = node->next;
+    if (!node->next)
+        list->tail = new_node;
+    else
+        node->next->previous = new_node;
+    node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node->previous;
+    new_node->next = node;
+    if (!node->previous)
+        list->head = new_node;
+    else
+        node->previous->next = new_node;
+    node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->head)
+    {
+        list->head = node;
+        list->tail = node;
+        node->previous = 0;
+        node->next = 0;
+    }
+    else
+        fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->tail)
+        fat_list_insert_first(list, node);
+     else
+        fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+    struct fat_node * node;
+
+    FAT_ASSERT(list);
+
+    node = fat_list_first(list);
+    if (node)
+        fat_list_remove(list, node);
+
+    return node;
+}
+
+#endif
+
diff --git a/vtoycli/fat_io_lib/include/fat_misc.h b/vtoycli/fat_io_lib/include/fat_misc.h
new file mode 100644 (file)
index 0000000..0c02634
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES    20
+#define MAX_LFN_ENTRY_LENGTH        13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location)    ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location)    ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); \
+                                                  buffer[location+2] = (uint8)((value>>16)&0xFF); \
+                                                  buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+    // Long File Name Structure (max 260 LFN length)
+    uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+    uint8 Null;
+#endif
+    uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void    fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char*   fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int     fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int     fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int     fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int     fatfs_entry_is_file(struct fat_dir_entry *entry);
+int     fatfs_lfn_entries_required(char *filename);
+void    fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void    fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int     fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int     fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void    fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void    fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16  fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16  fatfs_convert_to_fat_date(int day, int month, int year);
+void    fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_opts.h b/vtoycli/fat_io_lib/include/fat_opts.h
new file mode 100644 (file)
index 0000000..ac4dc86
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+    #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+    #define FATFS_IS_LITTLE_ENDIAN          1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+    #define FATFS_MAX_LONG_FILENAME         260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+    #define FATFS_MAX_OPEN_FILES            2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS              1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS                     1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES         128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+    #define FATFS_INC_WRITE_SUPPORT         1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+    #define FATFS_INC_LFN_SUPPORT           1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+    #define FATFS_DIR_LIST_SUPPORT          1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+    #define FATFS_INC_TIME_DATE_SUPPORT     0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+    #define FATFS_INC_FORMAT_SUPPORT        1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE                     512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+    // Don't include stdio, but there is a printf function available
+    #ifdef FAT_PRINTF_NOINC_STDIO
+        extern int printf(const char* ctrl1, ... );
+        #define FAT_PRINTF(a)               printf a
+    // Include stdio to use printf
+    #else
+        #include <stdio.h>
+        #define FAT_PRINTF(a)               printf a
+    #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+    #include <time.h>
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_string.h b/vtoycli/fat_io_lib/include/fat_string.h
new file mode 100644 (file)
index 0000000..90ca8e0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_table.h b/vtoycli/fat_io_lib/include/fat_table.h
new file mode 100644 (file)
index 0000000..ead75f3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_fat_init(struct fatfs *fs);
+int     fatfs_fat_purge(struct fatfs *fs);
+uint32  fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void    fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int     fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int     fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int     fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int     fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32  fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_types.h b/vtoycli/fat_io_lib/include/fat_types.h
new file mode 100644 (file)
index 0000000..5e2cca8
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+    #if __SIZEOF_LONG__ == 8
+        #define FATFS_DEF_UINT32_AS_INT
+    #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+
+    // If compiling on a 64-bit machine, use int as 32-bits
+    #ifdef FATFS_DEF_UINT32_AS_INT
+        typedef unsigned int uint32;
+    // Else for 32-bit machines & embedded systems, use long...
+    #else
+        typedef unsigned long uint32;
+    #endif
+#endif
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+    #define FAT_HTONS(n) (n)
+    #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+    #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+    #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+                    ((((uint32)(n) & 0xFF00)) << 8) | \
+                    ((((uint32)(n) & 0xFF0000)) >> 8) | \
+                    ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED           __attribute__ ((packed))
+#else
+    // Other compilers may require other methods of packing structures
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/include/fat_write.h b/vtoycli/fat_io_lib/include/fat_write.h
new file mode 100644 (file)
index 0000000..5558a86
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/vtoycli/fat_io_lib/lib/libfat_io_32.a b/vtoycli/fat_io_lib/lib/libfat_io_32.a
new file mode 100644 (file)
index 0000000..cd58131
Binary files /dev/null and b/vtoycli/fat_io_lib/lib/libfat_io_32.a differ
diff --git a/vtoycli/fat_io_lib/lib/libfat_io_64.a b/vtoycli/fat_io_lib/lib/libfat_io_64.a
new file mode 100644 (file)
index 0000000..9c06eb5
Binary files /dev/null and b/vtoycli/fat_io_lib/lib/libfat_io_64.a differ
diff --git a/vtoycli/fat_io_lib/lib/libfat_io_aa64.a b/vtoycli/fat_io_lib/lib/libfat_io_aa64.a
new file mode 100644 (file)
index 0000000..8c1179d
Binary files /dev/null and b/vtoycli/fat_io_lib/lib/libfat_io_aa64.a differ
diff --git a/vtoycli/fat_io_lib/lib/libfat_io_m64e.a b/vtoycli/fat_io_lib/lib/libfat_io_m64e.a
new file mode 100644 (file)
index 0000000..eacfdfe
Binary files /dev/null and b/vtoycli/fat_io_lib/lib/libfat_io_m64e.a differ
diff --git a/vtoycli/fat_io_lib/release/API.txt b/vtoycli/fat_io_lib/release/API.txt
new file mode 100644 (file)
index 0000000..61fc164
--- /dev/null
@@ -0,0 +1,22 @@
+File IO Lib API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+void fl_init(void)\r
+\r
+  Called to initialize FAT IO library.\r
+  This should be called prior to any other functions.\r
+\r
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))\r
+\r
+  [Optional] File system thread safety locking functions.\r
+  For thread safe operation, you should provide lock() and unlock() functions.\r
+  Note that locking primitive used must support recursive locking, i.e lock() called within an already \91locked\92 region.\r
+\r
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+  This function is used to attach system specific disk/media access functions.  \r
+  This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).\r
+\r
+void fl_shutdown(void)\r
+\r
+  Shutdown the FAT IO library. This purges any un-saved data back to disk.\r
diff --git a/vtoycli/fat_io_lib/release/COPYRIGHT.txt b/vtoycli/fat_io_lib/release/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..4335f7f
--- /dev/null
@@ -0,0 +1,345 @@
+                   GNU GENERAL PUBLIC LICENSE\r
+                      Version 2, June 1991\r
+\r
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.\r
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ Everyone is permitted to copy and distribute verbatim copies\r
+ of this license document, but changing it is not allowed.\r
+\r
+                           Preamble\r
+\r
+  The licenses for most software are designed to take away your\r
+freedom to share and change it.  By contrast, the GNU General Public\r
+License is intended to guarantee your freedom to share and change free\r
+software--to make sure the software is free for all its users.  This\r
+General Public License applies to most of the Free Software\r
+Foundation's software and to any other program whose authors commit to\r
+using it.  (Some other Free Software Foundation software is covered by\r
+the GNU Library General Public License instead.)  You can apply it to\r
+your programs, too.\r
+\r
+  When we speak of free software, we are referring to freedom, not\r
+price.  Our General Public Licenses are designed to make sure that you\r
+have the freedom to distribute copies of free software (and charge for\r
+this service if you wish), that you receive source code or can get it\r
+if you want it, that you can change the software or use pieces of it\r
+in new free programs; and that you know you can do these things.\r
+\r
+  To protect your rights, we need to make restrictions that forbid\r
+anyone to deny you these rights or to ask you to surrender the rights.\r
+These restrictions translate to certain responsibilities for you if you\r
+distribute copies of the software, or if you modify it.\r
+\r
+  For example, if you distribute copies of such a program, whether\r
+gratis or for a fee, you must give the recipients all the rights that\r
+you have.  You must make sure that they, too, receive or can get the\r
+source code.  And you must show them these terms so they know their\r
+rights.\r
+\r
+  We protect your rights with two steps: (1) copyright the software, and\r
+(2) offer you this license which gives you legal permission to copy,\r
+distribute and/or modify the software.\r
+\r
+  Also, for each author's protection and ours, we want to make certain\r
+that everyone understands that there is no warranty for this free\r
+software.  If the software is modified by someone else and passed on, we\r
+want its recipients to know that what they have is not the original, so\r
+that any problems introduced by others will not reflect on the original\r
+authors' reputations.\r
+\r
+  Finally, any free program is threatened constantly by software\r
+patents.  We wish to avoid the danger that redistributors of a free\r
+program will individually obtain patent licenses, in effect making the\r
+program proprietary.  To prevent this, we have made it clear that any\r
+patent must be licensed for everyone's free use or not licensed at all.\r
+\r
+  The precise terms and conditions for copying, distribution and\r
+modification follow.\r
+\r
+\r
+                   GNU GENERAL PUBLIC LICENSE\r
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\r
+\r
+  0. This License applies to any program or other work which contains\r
+a notice placed by the copyright holder saying it may be distributed\r
+under the terms of this General Public License.  The "Program", below,\r
+refers to any such program or work, and a "work based on the Program"\r
+means either the Program or any derivative work under copyright law:\r
+that is to say, a work containing the Program or a portion of it,\r
+either verbatim or with modifications and/or translated into another\r
+language.  (Hereinafter, translation is included without limitation in\r
+the term "modification".)  Each licensee is addressed as "you".\r
+\r
+Activities other than copying, distribution and modification are not\r
+covered by this License; they are outside its scope.  The act of\r
+running the Program is not restricted, and the output from the Program\r
+is covered only if its contents constitute a work based on the\r
+Program (independent of having been made by running the Program).\r
+Whether that is true depends on what the Program does.\r
+\r
+  1. You may copy and distribute verbatim copies of the Program's\r
+source code as you receive it, in any medium, provided that you\r
+conspicuously and appropriately publish on each copy an appropriate\r
+copyright notice and disclaimer of warranty; keep intact all the\r
+notices that refer to this License and to the absence of any warranty;\r
+and give any other recipients of the Program a copy of this License\r
+along with the Program.\r
+\r
+You may charge a fee for the physical act of transferring a copy, and\r
+you may at your option offer warranty protection in exchange for a fee.\r
+\r
+  2. You may modify your copy or copies of the Program or any portion\r
+of it, thus forming a work based on the Program, and copy and\r
+distribute such modifications or work under the terms of Section 1\r
+above, provided that you also meet all of these conditions:\r
+\r
+    a) You must cause the modified files to carry prominent notices\r
+    stating that you changed the files and the date of any change.\r
+\r
+    b) You must cause any work that you distribute or publish, that in\r
+    whole or in part contains or is derived from the Program or any\r
+    part thereof, to be licensed as a whole at no charge to all third\r
+    parties under the terms of this License.\r
+\r
+    c) If the modified program normally reads commands interactively\r
+    when run, you must cause it, when started running for such\r
+    interactive use in the most ordinary way, to print or display an\r
+    announcement including an appropriate copyright notice and a\r
+    notice that there is no warranty (or else, saying that you provide\r
+    a warranty) and that users may redistribute the program under\r
+    these conditions, and telling the user how to view a copy of this\r
+    License.  (Exception: if the Program itself is interactive but\r
+    does not normally print such an announcement, your work based on\r
+    the Program is not required to print an announcement.)\r
+\r
+\r
+These requirements apply to the modified work as a whole.  If\r
+identifiable sections of that work are not derived from the Program,\r
+and can be reasonably considered independent and separate works in\r
+themselves, then this License, and its terms, do not apply to those\r
+sections when you distribute them as separate works.  But when you\r
+distribute the same sections as part of a whole which is a work based\r
+on the Program, the distribution of the whole must be on the terms of\r
+this License, whose permissions for other licensees extend to the\r
+entire whole, and thus to each and every part regardless of who wrote it.\r
+\r
+Thus, it is not the intent of this section to claim rights or contest\r
+your rights to work written entirely by you; rather, the intent is to\r
+exercise the right to control the distribution of derivative or\r
+collective works based on the Program.\r
+\r
+In addition, mere aggregation of another work not based on the Program\r
+with the Program (or with a work based on the Program) on a volume of\r
+a storage or distribution medium does not bring the other work under\r
+the scope of this License.\r
+\r
+  3. You may copy and distribute the Program (or a work based on it,\r
+under Section 2) in object code or executable form under the terms of\r
+Sections 1 and 2 above provided that you also do one of the following:\r
+\r
+    a) Accompany it with the complete corresponding machine-readable\r
+    source code, which must be distributed under the terms of Sections\r
+    1 and 2 above on a medium customarily used for software interchange; or,\r
+\r
+    b) Accompany it with a written offer, valid for at least three\r
+    years, to give any third party, for a charge no more than your\r
+    cost of physically performing source distribution, a complete\r
+    machine-readable copy of the corresponding source code, to be\r
+    distributed under the terms of Sections 1 and 2 above on a medium\r
+    customarily used for software interchange; or,\r
+\r
+    c) Accompany it with the information you received as to the offer\r
+    to distribute corresponding source code.  (This alternative is\r
+    allowed only for noncommercial distribution and only if you\r
+    received the program in object code or executable form with such\r
+    an offer, in accord with Subsection b above.)\r
+\r
+The source code for a work means the preferred form of the work for\r
+making modifications to it.  For an executable work, complete source\r
+code means all the source code for all modules it contains, plus any\r
+associated interface definition files, plus the scripts used to\r
+control compilation and installation of the executable.  However, as a\r
+special exception, the source code distributed need not include\r
+anything that is normally distributed (in either source or binary\r
+form) with the major components (compiler, kernel, and so on) of the\r
+operating system on which the executable runs, unless that component\r
+itself accompanies the executable.\r
+\r
+If distribution of executable or object code is made by offering\r
+access to copy from a designated place, then offering equivalent\r
+access to copy the source code from the same place counts as\r
+distribution of the source code, even though third parties are not\r
+compelled to copy the source along with the object code.\r
+\r
+\r
+  4. You may not copy, modify, sublicense, or distribute the Program\r
+except as expressly provided under this License.  Any attempt\r
+otherwise to copy, modify, sublicense or distribute the Program is\r
+void, and will automatically terminate your rights under this License.\r
+However, parties who have received copies, or rights, from you under\r
+this License will not have their licenses terminated so long as such\r
+parties remain in full compliance.\r
+\r
+  5. You are not required to accept this License, since you have not\r
+signed it.  However, nothing else grants you permission to modify or\r
+distribute the Program or its derivative works.  These actions are\r
+prohibited by law if you do not accept this License.  Therefore, by\r
+modifying or distributing the Program (or any work based on the\r
+Program), you indicate your acceptance of this License to do so, and\r
+all its terms and conditions for copying, distributing or modifying\r
+the Program or works based on it.\r
+\r
+  6. Each time you redistribute the Program (or any work based on the\r
+Program), the recipient automatically receives a license from the\r
+original licensor to copy, distribute or modify the Program subject to\r
+these terms and conditions.  You may not impose any further\r
+restrictions on the recipients' exercise of the rights granted herein.\r
+You are not responsible for enforcing compliance by third parties to\r
+this License.\r
+\r
+  7. If, as a consequence of a court judgment or allegation of patent\r
+infringement or for any other reason (not limited to patent issues),\r
+conditions are imposed on you (whether by court order, agreement or\r
+otherwise) that contradict the conditions of this License, they do not\r
+excuse you from the conditions of this License.  If you cannot\r
+distribute so as to satisfy simultaneously your obligations under this\r
+License and any other pertinent obligations, then as a consequence you\r
+may not distribute the Program at all.  For example, if a patent\r
+license would not permit royalty-free redistribution of the Program by\r
+all those who receive copies directly or indirectly through you, then\r
+the only way you could satisfy both it and this License would be to\r
+refrain entirely from distribution of the Program.\r
+\r
+If any portion of this section is held invalid or unenforceable under\r
+any particular circumstance, the balance of the section is intended to\r
+apply and the section as a whole is intended to apply in other\r
+circumstances.\r
+\r
+It is not the purpose of this section to induce you to infringe any\r
+patents or other property right claims or to contest validity of any\r
+such claims; this section has the sole purpose of protecting the\r
+integrity of the free software distribution system, which is\r
+implemented by public license practices.  Many people have made\r
+generous contributions to the wide range of software distributed\r
+through that system in reliance on consistent application of that\r
+system; it is up to the author/donor to decide if he or she is willing\r
+to distribute software through any other system and a licensee cannot\r
+impose that choice.\r
+\r
+This section is intended to make thoroughly clear what is believed to\r
+be a consequence of the rest of this License.\r
+\r
+\r
+  8. If the distribution and/or use of the Program is restricted in\r
+certain countries either by patents or by copyrighted interfaces, the\r
+original copyright holder who places the Program under this License\r
+may add an explicit geographical distribution limitation excluding\r
+those countries, so that distribution is permitted only in or among\r
+countries not thus excluded.  In such case, this License incorporates\r
+the limitation as if written in the body of this License.\r
+\r
+  9. The Free Software Foundation may publish revised and/or new versions\r
+of the General Public License from time to time.  Such new versions will\r
+be similar in spirit to the present version, but may differ in detail to\r
+address new problems or concerns.\r
+\r
+Each version is given a distinguishing version number.  If the Program\r
+specifies a version number of this License which applies to it and "any\r
+later version", you have the option of following the terms and conditions\r
+either of that version or of any later version published by the Free\r
+Software Foundation.  If the Program does not specify a version number of\r
+this License, you may choose any version ever published by the Free Software\r
+Foundation.\r
+\r
+  10. If you wish to incorporate parts of the Program into other free\r
+programs whose distribution conditions are different, write to the author\r
+to ask for permission.  For software which is copyrighted by the Free\r
+Software Foundation, write to the Free Software Foundation; we sometimes\r
+make exceptions for this.  Our decision will be guided by the two goals\r
+of preserving the free status of all derivatives of our free software and\r
+of promoting the sharing and reuse of software generally.\r
+\r
+                           NO WARRANTY\r
+\r
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY\r
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN\r
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES\r
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED\r
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS\r
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE\r
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,\r
+REPAIR OR CORRECTION.\r
+\r
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\r
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR\r
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\r
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING\r
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED\r
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY\r
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER\r
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE\r
+POSSIBILITY OF SUCH DAMAGES.\r
+\r
+                    END OF TERMS AND CONDITIONS\r
+\r
+\r
+           How to Apply These Terms to Your New Programs\r
+\r
+  If you develop a new program, and you want it to be of the greatest\r
+possible use to the public, the best way to achieve this is to make it\r
+free software which everyone can redistribute and change under these terms.\r
+\r
+  To do so, attach the following notices to the program.  It is safest\r
+to attach them to the start of each source file to most effectively\r
+convey the exclusion of warranty; and each file should have at least\r
+the "copyright" line and a pointer to where the full notice is found.\r
+\r
+    <one line to give the program's name and a brief idea of what it does.>\r
+    Copyright (C) <year>  <name of author>\r
+\r
+    This program is free software; you can redistribute it and/or modify\r
+    it under the terms of the GNU General Public License as published by\r
+    the Free Software Foundation; either version 2 of the License, or\r
+    (at your option) any later version.\r
+\r
+    This program is distributed in the hope that it will be useful,\r
+    but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+    GNU General Public License for more details.\r
+\r
+    You should have received a copy of the GNU General Public License\r
+    along with this program; if not, write to the Free Software\r
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+\r
+\r
+Also add information on how to contact you by electronic and paper mail.\r
+\r
+If the program is interactive, make it output a short notice like this\r
+when it starts in an interactive mode:\r
+\r
+    Gnomovision version 69, Copyright (C) year name of author\r
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\r
+    This is free software, and you are welcome to redistribute it\r
+    under certain conditions; type `show c' for details.\r
+\r
+The hypothetical commands `show w' and `show c' should show the appropriate\r
+parts of the General Public License.  Of course, the commands you use may\r
+be called something other than `show w' and `show c'; they could even be\r
+mouse-clicks or menu items--whatever suits your program.\r
+\r
+You should also get your employer (if you work as a programmer) or your\r
+school, if any, to sign a "copyright disclaimer" for the program, if\r
+necessary.  Here is a sample; alter the names:\r
+\r
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program\r
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.\r
+\r
+  <signature of Ty Coon>, 1 April 1989\r
+  Ty Coon, President of Vice\r
+\r
+This General Public License does not permit incorporating your program into\r
+proprietary programs.  If your program is a subroutine library, you may\r
+consider it more useful to permit linking proprietary applications with the\r
+library.  If this is what you want to do, use the GNU Library General\r
+Public License instead of this License.\r
diff --git a/vtoycli/fat_io_lib/release/Configuration.txt b/vtoycli/fat_io_lib/release/Configuration.txt
new file mode 100644 (file)
index 0000000..9ec576e
--- /dev/null
@@ -0,0 +1,53 @@
+File IO Lib Options\r
+-=-=-=-=-=-=-=-=-=-\r
+\r
+See defines in fat_opts.h:\r
+\r
+FATFS_IS_LITTLE_ENDIAN         [1/0]\r
+  Which endian is your system? Set to 1 for little endian, 0 for big endian.\r
+\r
+FATFS_MAX_LONG_FILENAME        [260]\r
+  By default, 260 characters (max LFN length). Increase this to support greater path depths.\r
+\r
+FATFS_MAX_OPEN_FILES   \r
+  The more files you wish to have concurrently open, the greater this number should be.\r
+  This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).\r
+\r
+FAT_BUFFER_SECTORS\r
+  Minimum is 1, more increases performance.\r
+  This defines how many FAT sectors can be buffered per FAT_BUFFER entry.\r
+\r
+FAT_BUFFERS\r
+  Minimum is 1, more increases performance.\r
+  This defines how many FAT buffer entries are available.\r
+  Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE\r
+\r
+FATFS_INC_WRITE_SUPPORT\r
+  Support file write functionality.\r
+\r
+FAT_SECTOR_SIZE\r
+  Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).\r
+\r
+FAT_PRINTF\r
+  A define that allows the File IO library to print to console/stdout. \r
+  Provide your own printf function if printf not available.\r
+\r
+FAT_CLUSTER_CACHE_ENTRIES\r
+  Size of cluster chain cache (can be undefined if not required).\r
+  Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2\r
+  Improves access speed considerably.\r
+\r
+FATFS_INC_LFN_SUPPORT  [1/0]\r
+  Enable/Disable support for long filenames.\r
+\r
+FATFS_DIR_LIST_SUPPORT         [1/0]\r
+  Include support for directory listing.\r
+\r
+FATFS_INC_TIME_DATE_SUPPORT  [1/0]\r
+  Use time/date functions provided by time.h to update creation & modification timestamps.\r
+\r
+FATFS_INC_FORMAT_SUPPORT\r
+  Include support for formatting disks (FAT16 only).\r
+\r
+FAT_PRINTF_NOINC_STDIO\r
+  Disable use of printf & inclusion of stdio.h\r
diff --git a/vtoycli/fat_io_lib/release/History.txt b/vtoycli/fat_io_lib/release/History.txt
new file mode 100644 (file)
index 0000000..58958f4
--- /dev/null
@@ -0,0 +1,24 @@
+Revision History\r
+-=-=-=-=-=-=-=-=-\r
+v2.6.11 - Fix compilation with GCC on 64-bit machines\r
+v2.6.10 - Added support for FAT32 format.\r
+V2.6.9  - Added support for time & date handling.\r
+V2.6.8  - Fixed error with FSINFO sector write.\r
+V2.6.7  - Added fgets().\r
+          Fixed C warnings, removed dependancy on some string.h functions.\r
+V2.6.6  \96 Massive read + write performance  improvements.\r
+V2.6.5  \96 Bug fixes for big endian systems.\r
+V2.6.4  \96 Further bug fixes and performance improvements for write operations.\r
+V2.6.3  \96 Peformance improvements, FAT16 formatting support. Various bug fixes.\r
+V2.6    - Basic support for FAT16 added (18-04-10).\r
+V2.5    - Code cleaned up. Many bugs fixed. Thread safety functions added.\r
+V2.x    - Write support added as well as better stdio like API.\r
+V1.0    - Rewrite of all code to enable multiple files to be opened and provides a \r
+          better file API.\r
+         Also better string matching, and generally better C code than origonal \r
+          version.\r
+V0.1c   - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping \r
+          the drive reads from addressing a sector that is out of range.\r
+V0.1b   - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32 \r
+          access.\r
+V0.1a   - First release (27/12/03); fopen(), fgetc() unbuffered reads.\r
diff --git a/vtoycli/fat_io_lib/release/License.txt b/vtoycli/fat_io_lib/release/License.txt
new file mode 100644 (file)
index 0000000..c7fb0cc
--- /dev/null
@@ -0,0 +1,10 @@
+FAT File IO Library License\r
+-=-=-=-=-=-=-=-=-=-=-=-=-=-\r
+\r
+This versions license: GPL\r
+\r
+If you include GPL software in your project, you must release the source code of that project too.\r
+\r
+If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.\r
+\r
+Email: admin@ultra-embedded.com\r
diff --git a/vtoycli/fat_io_lib/release/Media Access API.txt b/vtoycli/fat_io_lib/release/Media Access API.txt
new file mode 100644 (file)
index 0000000..45eede0
--- /dev/null
@@ -0,0 +1,40 @@
+Media Access API\r
+-=-=-=-=-=-=-=-=-\r
+\r
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+       Sector: 32-bit sector number\r
+       Buffer: Target buffer to read n sectors of data into.\r
+       Sector_count: Number of sectors to read.\r
+\r
+Return: \r
+       int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+   Application/target specific disk/media read function.\r
+   Sector number (sectors are usually 512 byte pages) to read.\r
+\r
+Media Write API\r
+\r
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)\r
+\r
+Params:\r
+       Sector: 32-bit sector number\r
+       Buffer: Target buffer to write n sectors of data from.\r
+       Sector_count: Number of sectors to write.\r
+\r
+Return: \r
+       int, 1 = success, 0 = failure.\r
+\r
+Description:\r
+   Application/target specific disk/media write function.\r
+   Sector number (sectors are usually 512 byte pages) to write to.\r
+\r
+File IO Library Linkage\r
+   Use the following API to attach the media IO functions to the File IO library.\r
+\r
+   int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)\r
+\r
+\r
+\r
diff --git a/vtoycli/fat_io_lib/release/example.c b/vtoycli/fat_io_lib/release/example.c
new file mode 100644 (file)
index 0000000..5d30e5b
--- /dev/null
@@ -0,0 +1,87 @@
+#include <stdio.h>\r
+#include "fat_filelib.h"\r
+\r
+int media_init()\r
+{\r
+    // ...\r
+    return 1;\r
+}\r
+\r
+int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+    unsigned long i;\r
+\r
+    for (i=0;i<sector_count;i++)\r
+    {\r
+        // ...\r
+        // Add platform specific sector (512 bytes) read code here\r
+        //..\r
+\r
+        sector ++;\r
+        buffer += 512;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)\r
+{\r
+    unsigned long i;\r
+\r
+    for (i=0;i<sector_count;i++)\r
+    {\r
+        // ...\r
+        // Add platform specific sector (512 bytes) write code here\r
+        //..\r
+\r
+        sector ++;\r
+        buffer += 512;\r
+    }\r
+\r
+    return 1;\r
+}\r
+\r
+void main()\r
+{\r
+    FL_FILE *file;\r
+\r
+    // Initialise media\r
+    media_init();\r
+\r
+    // Initialise File IO Library\r
+    fl_init();\r
+\r
+    // Attach media access functions to library\r
+    if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)\r
+    {\r
+        printf("ERROR: Media attach failed\n");\r
+        return; \r
+    }\r
+\r
+    // List root directory\r
+    fl_listdirectory("/");\r
+\r
+    // Create File\r
+    file = fl_fopen("/file.bin", "w");\r
+    if (file)\r
+    {\r
+        // Write some data\r
+        unsigned char data[] = { 1, 2, 3, 4 };\r
+        if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))\r
+            printf("ERROR: Write file failed\n");\r
+    }\r
+    else\r
+        printf("ERROR: Create file failed\n");\r
+\r
+    // Close file\r
+    fl_fclose(file);\r
+\r
+    // Delete File\r
+    if (fl_remove("/file.bin") < 0)\r
+        printf("ERROR: Delete file failed\n");\r
+\r
+    // List root directory\r
+    fl_listdirectory("/");\r
+\r
+    fl_shutdown();\r
+}\r
diff --git a/vtoycli/fat_io_lib/release/fat_access.c b/vtoycli/fat_io_lib/release/fat_access.c
new file mode 100644 (file)
index 0000000..f55bd59
--- /dev/null
@@ -0,0 +1,904 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs)
+{
+    uint8 num_of_fats;
+    uint16 reserved_sectors;
+    uint32 FATSz;
+    uint32 root_dir_sectors;
+    uint32 total_sectors;
+    uint32 data_sectors;
+    uint32 count_of_clusters;
+    uint8 valid_partition = 0;
+
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have a read function (write function is optional)
+    if (!fs->disk_io.read_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // MBR: Sector 0 on the disk
+    // NOTE: Some removeable media does not have this.
+
+    // Load MBR (LBA 0) into the 512 byte buffer
+    if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make Sure 0x55 and 0xAA are at end of sector
+    // (this should be the case regardless of the MBR or boot sector)
+    if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Now check again using the access function to prove endian conversion function
+    if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+        return FAT_INIT_ENDIAN_ERROR;
+
+    // Verify packed structures
+    if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+        return FAT_INIT_STRUCT_PACKING;
+
+    // Check the partition type code
+    switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+    {
+        case 0x0B:
+        case 0x06:
+        case 0x0C:
+        case 0x0E:
+        case 0x0F:
+        case 0x05:
+            valid_partition = 1;
+        break;
+        case 0x00:
+            valid_partition = 0;
+            break;
+        default:
+            if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+                valid_partition = 1;
+        break;
+    }
+
+    // Read LBA Begin for the file system
+    if (valid_partition)
+        fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+    // Else possibly MBR less disk
+    else
+        fs->lba_begin = 0;\r
+\r
+    // Load Volume 1 table into sector buffer
+    // (We may already have this in the buffer if MBR less drive!)
+    if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make sure there are 512 bytes per cluster
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+        return FAT_INIT_INVALID_SECTOR_SIZE;\r
+
+    // Load Parameters of FAT partition
+    fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+    reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+    num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+    fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    // For FAT32 (which this may be)
+    fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+    fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Calculate the root dir sectors
+    root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+        total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+    else
+        total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+    data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+    // Find out which version of FAT this is...
+    if (fs->sectors_per_cluster != 0)
+    {
+        count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+        if(count_of_clusters < 4085)
+            // Volume is FAT12
+            return FAT_INIT_WRONG_FILESYS_TYPE;
+        else if(count_of_clusters < 65525)
+        {
+            // Clear this FAT32 specific param
+            fs->rootdir_first_cluster = 0;
+
+            // Volume is FAT16
+            fs->fat_type = FAT_TYPE_16;
+            return FAT_INIT_OK;
+        }
+        else
+        {
+            // Volume is FAT32
+            fs->fat_type = FAT_TYPE_32;
+            return FAT_INIT_OK;
+        }
+    }
+    else
+        return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+    else
+        return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.read_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.write_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+    uint32 sector_to_read = 0;
+    uint32 cluster_to_read = 0;
+    uint32 cluster_chain = 0;
+    uint32 i;
+    uint32 lba;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+    {
+        if (offset < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+        else
+            return 0;
+    }
+    // FAT16/32 Other
+    else
+    {
+        // Set start of cluster chain to initial value
+        cluster_chain = start_cluster;
+
+        // Find parameters
+        cluster_to_read = offset / fs->sectors_per_cluster;
+        sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+        // Follow chain to find cluster to read
+        for (i=0; i<cluster_to_read; i++)
+            cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
+
+        // If end of cluster chain then return false
+        if (cluster_chain == FAT32_LAST_CLUSTER)
+            return 0;
+
+        // Calculate sector address
+        lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
+    }
+
+    // User provided target array
+    if (target)
+        return fs->disk_io.read_media(lba, target, 1);
+    // Else read sector if not already loaded
+    else if (lba != fs->currentsector.address)
+    {
+        fs->currentsector.address = lba;
+        return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+    }
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16, there are a limited amount of sectors in root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate read address
+            fs->currentsector.address = lba;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate read address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16 we cannot extend the root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = lba;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate write address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+    FAT_PRINTF(("FAT details:\r\n"));
+    FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+    FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+    FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+    FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+    FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+    // NOTE: On FAT16 this will be 0 which has a special meaning...
+    return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    struct fat_dir_entry *directoryEntry;
+
+    fatfs_lfn_cache_init(&lfn, 1);
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(long_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(short_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                        return 1;
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+    time_t time_now;
+    struct tm * time_info;
+    uint16 fat_time;
+    uint16 fat_date;
+
+    // Get system time
+    time(&time_now);
+
+    // Convert to local time
+    time_info = localtime(&time_now);
+
+    // Convert time to FAT format
+    fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+    // Convert date to FAT format
+    fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+    // Update requested fields
+    if (create)
+    {
+        directoryEntry->CrtTime[1] = fat_time >> 8;
+        directoryEntry->CrtTime[0] = fat_time >> 0;
+        directoryEntry->CrtDate[1] = fat_date >> 8;
+        directoryEntry->CrtDate[0] = fat_date >> 0;
+    }
+
+    if (modify)
+    {
+        directoryEntry->WrtTime[1] = fat_time >> 8;
+        directoryEntry->WrtTime[0] = fat_time >> 0;
+        directoryEntry->WrtDate[1] = fat_date >> 8;
+        directoryEntry->WrtDate[0] = fat_date >> 0;
+    }
+
+    if (access)
+    {
+        directoryEntry->LstAccDate[1] = fat_time >> 8;
+        directoryEntry->LstAccDate[0] = fat_time >> 0;
+        directoryEntry->LstAccDate[1] = fat_date >> 8;
+        directoryEntry->LstAccDate[0] = fat_date >> 0;
+    }
+
+    return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        // Mark as deleted
+                        directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+    dirls->cluster = StartCluster;
+    dirls->sector = 0;
+    dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+    uint8 i,item;
+    uint16 recordoffset;
+    struct fat_dir_entry *directoryEntry;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    int result = 0;
+
+    // Initialise LFN cache first
+    fatfs_lfn_cache_init(&lfn, 0);
+
+    while (1)
+    {
+        // If data read OK
+        if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
+        {
+            // Maximum of 16 directory entries
+            for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Increase directory offset
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if ( fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    // Get text
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+                    strncpy(entry->filename, long_filename, FATFS_MAX_LONG_FILENAME-1);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if ( fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+            }// end of for
+
+            // If reached end of the dir move onto next sector
+            dirls->sector++;
+            dirls->offset = 0;
+        }
+        else
+            break;
+    }
+
+    return result;
+}
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_access.h b/vtoycli/fat_io_lib/release/fat_access.h
new file mode 100644 (file)
index 0000000..1752387
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK                         0
+#define FAT_INIT_MEDIA_ACCESS_ERROR         (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE        (-2)
+#define FAT_INIT_INVALID_SIGNATURE          (-3)
+#define FAT_INIT_ENDIAN_ERROR               (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE         (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE       (-6)
+#define FAT_INIT_STRUCT_PACKING             (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR          (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+    // User supplied function pointers for disk IO
+    fn_diskio_read          read_media;
+    fn_diskio_write         write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+    uint8                   sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+    uint32                  address;
+    int                     dirty;
+    uint8 *                 ptr;
+
+    // Next in chain of sector buffers
+    struct fat_buffer       *next;
+};
+
+typedef enum eFatType
+{
+    FAT_TYPE_16,
+    FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+    // Filesystem globals
+    uint8                   sectors_per_cluster;
+    uint32                  cluster_begin_lba;
+    uint32                  rootdir_first_cluster;
+    uint32                  rootdir_first_sector;
+    uint32                  rootdir_sectors;
+    uint32                  fat_begin_lba;
+    uint16                  fs_info_sector;
+    uint32                  lba_begin;
+    uint32                  fat_sectors;
+    uint32                  next_free_cluster;
+    uint16                  root_entry_count;
+    uint16                  reserved_sectors;
+    uint8                   num_of_fats;
+    tFatType                fat_type;
+
+    // Disk/Media API
+    struct disk_if          disk_io;
+
+    // [Optional] Thread Safety
+    void                    (*fl_lock)(void);
+    void                    (*fl_unlock)(void);
+
+    // Working buffer
+    struct fat_buffer        currentsector;
+
+    // FAT Buffer
+    struct fat_buffer        *fat_buffer_head;
+    struct fat_buffer        fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+    uint32                  sector;
+    uint32                  cluster;
+    uint8                   offset;
+};
+
+struct fs_dir_ent
+{
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   is_dir;
+    uint32                  cluster;
+    uint32                  size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    uint16                  access_date;
+    uint16                  write_time;
+    uint16                  write_date;
+    uint16                  create_date;
+    uint16                  create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int     fatfs_init(struct fatfs *fs);
+uint32  fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int     fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int     fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int     fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void    fatfs_show_details(struct fatfs *fs);
+uint32  fatfs_get_root_cluster(struct fatfs *fs);
+uint32  fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int     fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int     fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int     fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void    fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int     fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int     fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_cache.c b/vtoycli/fat_io_lib/release/fat_cache.c
new file mode 100644 (file)
index 0000000..de77e6a
--- /dev/null
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    int i;
+
+    for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
+    {
+        file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+        file->cluster_cache_data[i] = 0;
+    }
+#endif
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+    {
+        *pNextCluster = file->cluster_cache_data[slot];
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+        file->cluster_cache_data[slot] = nextCluster;
+    else
+    {
+        file->cluster_cache_idx[slot] = clusterIdx;
+        file->cluster_cache_data[slot] = nextCluster;
+    }
+#endif
+
+    return 1;
+}
diff --git a/vtoycli/fat_io_lib/release/fat_cache.h b/vtoycli/fat_io_lib/release/fat_cache.h
new file mode 100644 (file)
index 0000000..348d5d3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_defs.h b/vtoycli/fat_io_lib/release/fat_defs.h
new file mode 100644 (file)
index 0000000..5fe8d6a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+//            FAT32 Offsets
+//        Name                Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT              0    // Length = 3
+#define BS_OEMNAME              3    // Length = 8
+#define BPB_BYTSPERSEC          11    // Length = 2
+#define BPB_SECPERCLUS          13    // Length = 1
+#define BPB_RSVDSECCNT          14    // Length = 2
+#define BPB_NUMFATS             16    // Length = 1
+#define BPB_ROOTENTCNT          17    // Length = 2
+#define BPB_TOTSEC16            19    // Length = 2
+#define BPB_MEDIA               21    // Length = 1
+#define    BPB_FATSZ16          22    // Length = 2
+#define BPB_SECPERTRK           24    // Length = 2
+#define BPB_NUMHEADS            26    // Length = 2
+#define BPB_HIDDSEC             28    // Length = 4
+#define BPB_TOTSEC32            32    // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM           36    // Length = 1
+#define BS_FAT_BOOTSIG          38    // Length = 1
+#define BS_FAT_VOLID            39    // Length = 4
+#define BS_FAT_VOLLAB           43    // Length = 11
+#define BS_FAT_FILSYSTYPE       54    // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32       36    // Length = 4
+#define BPB_FAT32_EXTFLAGS      40    // Length = 2
+#define BPB_FAT32_FSVER         42    // Length = 2
+#define BPB_FAT32_ROOTCLUS      44    // Length = 4
+#define BPB_FAT32_FSINFO        48    // Length = 2
+#define BPB_FAT32_BKBOOTSEC     50    // Length = 2
+#define BS_FAT32_DRVNUM         64    // Length = 1
+#define BS_FAT32_BOOTSIG        66    // Length = 1
+#define BS_FAT32_VOLID          67    // Length = 4
+#define BS_FAT32_VOLLAB         71    // Length = 11
+#define BS_FAT32_FILSYSTYPE     82    // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12          1
+#define FAT_TYPE_FAT16          2
+#define FAT_TYPE_FAT32          3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION              510
+#define SIGNATURE_VALUE                 0xAA55
+#define PARTITION1_TYPECODE_LOCATION    450
+#define FAT32_TYPECODE1                 0x0B
+#define FAT32_TYPECODE2                 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION   454
+#define PARTITION1_SIZE_LOCATION        458
+
+#define FAT_DIR_ENTRY_SIZE              32
+#define FAT_SFN_SIZE_FULL               11
+#define FAT_SFN_SIZE_PARTIAL            8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY             0x01
+#define FILE_ATTR_HIDDEN                0x02
+#define FILE_ATTR_SYSTEM                0x04
+#define FILE_ATTR_SYSHID                0x06
+#define FILE_ATTR_VOLUME_ID             0x08
+#define FILE_ATTR_DIRECTORY             0x10
+#define FILE_ATTR_ARCHIVE               0x20
+#define FILE_ATTR_LFN_TEXT              0x0F
+#define FILE_HEADER_BLANK               0x00
+#define FILE_HEADER_DELETED             0xE5
+#define FILE_TYPE_DIR                   0x10
+#define FILE_TYPE_FILE                  0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT            11
+#define FAT_TIME_HOURS_MASK             0x1F
+#define FAT_TIME_MINUTES_SHIFT          5
+#define FAT_TIME_MINUTES_MASK           0x3F
+#define FAT_TIME_SECONDS_SHIFT          0
+#define FAT_TIME_SECONDS_MASK           0x1F
+#define FAT_TIME_SECONDS_SCALE          2
+#define FAT_DATE_YEAR_SHIFT             9
+#define FAT_DATE_YEAR_MASK              0x7F
+#define FAT_DATE_MONTH_SHIFT            5
+#define FAT_DATE_MONTH_MASK             0xF
+#define FAT_DATE_DAY_SHIFT              0
+#define FAT_DATE_DAY_MASK               0x1F
+#define FAT_DATE_YEAR_OFFSET            1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER              0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER           0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+    uint8 Name[11];
+    uint8 Attr;
+    uint8 NTRes;
+    uint8 CrtTimeTenth;
+    uint8 CrtTime[2];
+    uint8 CrtDate[2];
+    uint8 LstAccDate[2];
+    uint16 FstClusHI;
+    uint8 WrtTime[2];
+    uint8 WrtDate[2];
+    uint16 FstClusLO;
+    uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_filelib.c b/vtoycli/fat_io_lib/release/fat_filelib.c
new file mode 100644 (file)
index 0000000..2c4a236
--- /dev/null
@@ -0,0 +1,1603 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+
+//-----------------------------------------------------------------------------
+// Locals
+//-----------------------------------------------------------------------------
+static FL_FILE            _files[FATFS_MAX_OPEN_FILES];
+static int                _filelib_init = 0;
+static int                _filelib_valid = 0;
+static struct fatfs       _fs;
+static struct fat_list    _open_file_list;
+static struct fat_list    _free_file_list;
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT()     { if (_filelib_init==0) fl_init(); }
+
+#define FL_LOCK(a)          do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a)        do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void                _fl_init();
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(void)
+{
+    // Allocate free file
+    struct fat_node *node = fat_list_pop_head(&_free_file_list);
+
+    // Add to open list
+    if (node)
+        fat_list_insert_last(&_open_file_list, node);
+
+    return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(FL_FILE* file)
+{
+    struct fat_node *node;
+
+    // Compare open files
+    fat_list_for_each(&_open_file_list, node)
+    {
+        FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+        // If not the current file
+        if (openFile != file)
+        {
+            // Compare path and name
+            if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+                return 1;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(FL_FILE* file)
+{
+    // Remove from open list
+    fat_list_remove(&_open_file_list, &file->list_node);
+
+    // Add to free list
+    fat_list_insert_last(&_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+//                                Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(char *path, uint32 *pathCluster)
+{
+    int levels;
+    int sublevel;
+    char currentfolder[FATFS_MAX_LONG_FILENAME];
+    struct fat_dir_entry sfEntry;
+    uint32 startcluster;
+
+    // Set starting cluster to root cluster
+    startcluster = fatfs_get_root_cluster(&_fs);
+
+    // Find number of levels
+    levels = fatfs_total_path_levels(path);
+
+    // Cycle through each level and get the start sector
+    for (sublevel=0;sublevel<(levels+1);sublevel++)
+    {
+        if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+            return 0;
+
+        // Find clusteraddress for folder (currentfolder)
+        if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
+        {
+            // Check entry is folder
+            if (fatfs_entry_is_dir(&sfEntry))
+                startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            else
+                return 0;
+        }
+        else
+            return 0;
+    }
+
+    *pathCluster = startcluster;
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+    int i;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return 0;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the folder (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Erase new directory cluster
+    memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+    for (i=0;i<_fs.sectors_per_cluster;i++)
+    {
+        if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_fat_purge(&_fs);
+
+    _free_file(file);
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(const char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0]==0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Using dir cluster address search for filename
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
+        // Make sure entry is file not dir!
+        if (fatfs_entry_is_file(&sfEntry))
+        {
+            // Initialise file details
+            memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+            file->filelength = FAT_HTONL(sfEntry.FileSize);
+            file->bytenum = 0;
+            file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            file->file_data_address = 0xFFFFFFFF;
+            file->file_data_dirty = 0;
+            file->filelength_changed = 0;
+
+            // Quick lookup for next link in the chain
+            file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+            file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+            fatfs_cache_init(&_fs, file);
+
+            fatfs_fat_purge(&_fs);
+
+            return file;
+        }
+
+    _free_file(file);
+    return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(const char *filename)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+
+    // No write access?
+    if (!_fs.disk_io.write_media)
+        return NULL;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the file (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_cache_init(&_fs, file);
+
+    fatfs_fat_purge(&_fs);
+
+    return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+    uint32 Sector = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 i;
+    uint32 lba;
+
+    // Find cluster index within file & sector with cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors read to the number remaining in this cluster
+    if ((Sector + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - Sector;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            Cluster = nextCluster;
+        }
+
+        // Record current cluster lookup details (if valid)
+        if (Cluster != FAT32_LAST_CLUSTER)
+        {
+            file->last_fat_lookup.CurrentCluster = Cluster;
+            file->last_fat_lookup.ClusterIdx = ClusterIdx;
+        }
+    }
+
+    // If end of cluster chain then return false
+    if (Cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Calculate sector address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
+
+    // Read sector of file
+    if (fatfs_sector_read(&_fs, lba, buffer, count))
+        return count;
+    else
+        return 0;
+}
+
+//-----------------------------------------------------------------------------
+//                                External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(void)
+{
+    int i;
+
+    fat_list_init(&_free_file_list);
+    fat_list_init(&_open_file_list);
+
+    // Add all file objects to free list
+    for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
+        fat_list_insert_last(&_free_file_list, &_files[i].list_node);
+
+    _filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+{
+    _fs.fl_lock = lock;
+    _fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    _fs.disk_io.read_media = rd;
+    _fs.disk_io.write_media = wr;
+
+    // Initialise FAT parameters
+    if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
+    {
+        FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+        return res;
+    }
+
+    _filelib_valid = 1;
+    return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(void)
+{
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    fatfs_fat_purge(&_fs);
+    FL_UNLOCK(&_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(const char *path, const char *mode)
+{
+    int i;
+    FL_FILE* file;
+    uint8 flags = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!_filelib_valid)
+        return NULL;
+
+    if (!path || !mode)
+        return NULL;
+
+    // Supported Modes:
+    // "r" Open a file for reading.
+    //        The file must exist.
+    // "w" Create an empty file for writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a" Append to a file.
+    //        Writing operations append data at the end of the file.
+    //        The file is created if it does not exist.
+    // "r+" Open a file for update both reading and writing.
+    //        The file must exist.
+    // "w+" Create an empty file for both reading and writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a+" Open a file for reading and appending.
+    //        All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+    //        You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+    //        will move it back to the end of file.
+    //        The file is created if it does not exist.
+
+    for (i=0;i<(int)strlen(mode);i++)
+    {
+        switch (mode[i])
+        {
+        case 'r':
+        case 'R':
+            flags |= FILE_READ;
+            break;
+        case 'w':
+        case 'W':
+            flags |= FILE_WRITE;
+            flags |= FILE_ERASE;
+            flags |= FILE_CREATE;
+            break;
+        case 'a':
+        case 'A':
+            flags |= FILE_WRITE;
+            flags |= FILE_APPEND;
+            flags |= FILE_CREATE;
+            break;
+        case '+':
+            if (flags & FILE_READ)
+                flags |= FILE_WRITE;
+            else if (flags & FILE_WRITE)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_ERASE;
+                flags |= FILE_CREATE;
+            }
+            else if (flags & FILE_APPEND)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_WRITE;
+                flags |= FILE_APPEND;
+                flags |= FILE_CREATE;
+            }
+            break;
+        case 'b':
+        case 'B':
+            flags |= FILE_BINARY;
+            break;
+        }
+    }
+
+    file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+    // No write support!
+    flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+    // No write access - remove write/modify flags
+    if (!_fs.disk_io.write_media)
+        flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+    FL_LOCK(&_fs);
+
+    // Read
+    if (flags & FILE_READ)
+        file = _open_file(path);
+
+    // Create New
+#if FATFS_INC_WRITE_SUPPORT
+    if (!file && (flags & FILE_CREATE))
+        file = _create_file(path);
+#endif
+
+    // Write Existing (and not open due to read or create)
+    if (!(flags & FILE_READ))
+        if ((flags & FILE_CREATE) && !file)
+            if (flags & (FILE_WRITE | FILE_APPEND))
+                file = _open_file(path);
+
+    if (file)
+        file->flags = flags;
+
+    FL_UNLOCK(&_fs);
+    return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+    uint32 SectorNumber = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 LastCluster = FAT32_LAST_CLUSTER;
+    uint32 i;
+    uint32 lba;
+    uint32 TotalWriteCount = count;
+
+    // Find values for Cluster index & sector within cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors written to the number remaining in this cluster
+    if ((SectorNumber + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - SectorNumber;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            LastCluster = Cluster;
+            Cluster = nextCluster;
+
+            // Dont keep following a dead end
+            if (Cluster == FAT32_LAST_CLUSTER)
+                break;
+        }
+
+        // If we have reached the end of the chain, allocate more!
+        if (Cluster == FAT32_LAST_CLUSTER)
+        {
+            // Add some more cluster(s) to the last good cluster chain
+            if (!fatfs_add_free_space(&_fs, &LastCluster,  (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
+                return 0;
+
+            Cluster = LastCluster;
+        }
+
+        // Record current cluster lookup details
+        file->last_fat_lookup.CurrentCluster = Cluster;
+        file->last_fat_lookup.ClusterIdx = ClusterIdx;
+    }
+
+    // Calculate write address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
+
+    if (fatfs_sector_write(&_fs, lba, buf, count))
+        return count;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // If some write data still in buffer
+        if (file->file_data_dirty)
+        {
+            // Write back current sector before loading next
+            if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
+                file->file_data_dirty = 0;
+        }
+
+        FL_UNLOCK(&_fs);
+    }
+#endif
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // Flush un-written data to file
+        fl_fflush(f);
+
+        // File size changed?
+        if (file->filelength_changed)
+        {
+#if FATFS_INC_WRITE_SUPPORT
+            // Update filesize in directory
+            fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+            file->filelength_changed = 0;
+        }
+
+        file->bytenum = 0;
+        file->filelength = 0;
+        file->startcluster = 0;
+        file->file_data_address = 0xFFFFFFFF;
+        file->file_data_dirty = 0;
+        file->filelength_changed = 0;
+
+        // Free file handle
+        _free_file(file);
+
+        fatfs_fat_purge(&_fs);
+
+        FL_UNLOCK(&_fs);
+    }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(void *f)
+{
+    int res;
+    uint8 data = 0;
+
+    res = fl_fread(&data, 1, 1, f);
+    if (res == 1)
+        return (int)data;
+    else
+        return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(char *s, int n, void *f)
+{
+    int idx = 0;
+
+    // Space for null terminator?
+    if (n > 0)
+    {
+        // While space (+space for null terminator)
+        while (idx < (n-1))
+        {
+            int ch = fl_fgetc(f);
+
+            // EOF / Error?
+            if (ch < 0)
+                break;
+
+            // Store character read from stream
+            s[idx++] = (char)ch;
+
+            // End of line?
+            if (ch == '\n')
+                break;
+        }
+
+        if (idx > 0)
+            s[idx] = '\0';
+    }
+
+    return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(void * buffer, int size, int length, void *f )
+{
+    uint32 sector;
+    uint32 offset;
+    int copyCount;
+    int count = size * length;
+    int bytesRead = 0;
+
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (buffer==NULL || file==NULL)
+        return -1;
+
+    // No read permissions
+    if (!(file->flags & FILE_READ))
+        return -1;
+
+    // Nothing to be done
+    if (!count)
+        return 0;
+
+    // Check if read starts past end of file
+    if (file->bytenum >= file->filelength)
+        return -1;
+
+    // Limit to file size
+    if ( (file->bytenum + count) > file->filelength )
+        count = file->filelength - file->bytenum;
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesRead < count)
+    {
+        // Read whole sector, read from media directly into target buffer
+        if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+        {
+            // Read as many sectors as possible into target buffer
+            uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+            if (sectorsRead)
+            {
+                // We have upto one sector to copy
+                copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+                // Move onto next sector and reset copy offset
+                sector+= sectorsRead;
+                offset = 0;
+            }
+            else
+                break;
+        }
+        else
+        {
+            // Do we need to re-read the sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // Get LBA of sector offset within file
+                if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                    // Read failed - out of range (probably)
+                    break;
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (count - bytesRead))
+                copyCount = (count - bytesRead);
+
+            // Copy to application buffer
+            memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+
+        // Increase total read count
+        bytesRead += copyCount;
+
+        // Increment file pointer
+        file->bytenum += copyCount;
+    }
+
+    return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek( void *f, long offset, int origin )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res = -1;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    if (origin == SEEK_END && offset != 0)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Invalidate file buffer
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+
+    if (origin == SEEK_SET)
+    {
+        file->bytenum = (uint32)offset;
+
+        if (file->bytenum > file->filelength)
+            file->bytenum = file->filelength;
+
+        res = 0;
+    }
+    else if (origin == SEEK_CUR)
+    {
+        // Positive shift
+        if (offset >= 0)
+        {
+            file->bytenum += offset;
+
+            if (file->bytenum > file->filelength)
+                file->bytenum = file->filelength;
+        }
+        // Negative shift
+        else
+        {
+            // Make shift positive
+            offset = -offset;
+
+            // Limit to negative shift to start of file
+            if ((uint32)offset > file->bytenum)
+                file->bytenum = 0;
+            else
+                file->bytenum-= offset;
+        }
+
+        res = 0;
+    }
+    else if (origin == SEEK_END)
+    {
+        file->bytenum = file->filelength;
+        res = 0;
+    }
+    else
+        res = -1;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(void *f , uint32 * position)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Get position
+    *position = file->bytenum;
+
+    FL_UNLOCK(&_fs);
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(void *f)
+{
+    uint32 pos = 0;
+
+    fl_fgetpos(f, &pos);
+
+    return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    if (file->bytenum == file->filelength)
+        res = EOF;
+    else
+        res = 0;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(int c, void *f)
+{
+    uint8 data = (uint8)c;
+    int res;
+
+    res = fl_fwrite(&data, 1, 1, f);
+    if (res == 1)
+        return c;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(const void * data, int size, int count, void *f )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    uint32 sector;
+    uint32 offset;
+    uint32 length = (size*count);
+    uint8 *buffer = (uint8 *)data;
+    uint32 bytesWritten = 0;
+    uint32 copyCount;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // No write permissions
+    if (!(file->flags & FILE_WRITE))
+    {
+        FL_UNLOCK(&_fs);
+        return -1;
+    }
+
+    // Append writes to end of file
+    if (file->flags & FILE_APPEND)
+        file->bytenum = file->filelength;
+    // Else write to current position
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesWritten < length)
+    {
+        // Whole sector or more to be written?
+        if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+        {
+            uint32 sectorsWrote;
+
+            // Buffered sector, flush back to disk
+            if (file->file_data_address != 0xFFFFFFFF)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                file->file_data_address = 0xFFFFFFFF;
+                file->file_data_dirty = 0;
+            }
+
+            // Write as many sectors as possible
+            sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+            copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector+= sectorsWrote;
+            offset = 0;
+
+            if (!sectorsWrote)
+                break;
+        }
+        else
+        {
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (length - bytesWritten))
+                copyCount = (length - bytesWritten);
+
+            // Do we need to read a new sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // If we plan to overwrite the whole sector, we don't need to read it first!
+                if (copyCount != FAT_SECTOR_SIZE)
+                {
+                    // NOTE: This does not have succeed; if last sector of file
+                    // reached, no valid data will be read in, but write will
+                    // allocate some more space for new data.
+
+                    // Get LBA of sector offset within file
+                    if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                        memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+                }
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // Copy from application buffer into sector buffer
+            memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+            // Mark buffer as dirty
+            file->file_data_dirty = 1;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+    }
+
+    // Write increased extent of the file?
+    if (file->bytenum > file->filelength)
+    {
+        // Increase file size to new point
+        file->filelength = file->bytenum;
+
+        // We are changing the file length and this
+        // will need to be writen back at some point
+        file->filelength_changed = 1;
+    }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    // If time & date support is enabled, always force directory entry to be
+    // written in-order to update file modify / access time & date.
+    file->filelength_changed = 1;
+#endif
+
+    FL_UNLOCK(&_fs);
+
+    return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(const char * str, void *f)
+{
+    int len = (int)strlen(str);
+    int res = fl_fwrite(str, 1, len, f);
+
+    if (res == len)
+        return len;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove( const char * filename )
+{
+    FL_FILE* file;
+    int res = -1;
+
+    FL_LOCK(&_fs);
+
+    // Use read_file as this will check if the file is already open!
+    file = fl_fopen((char*)filename, "r");
+    if (file)
+    {
+        // Delete allocated space
+        if (fatfs_free_cluster_chain(&_fs, file->startcluster))
+        {
+            // Remove directory entries
+            if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
+            {
+                // Close the file handle (this should not write anything to the file
+                // as we have not changed the file since opening it!)
+                fl_fclose(file);
+
+                res = 0;
+            }
+        }
+    }
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(const char *path)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    res =_create_directory((char*)path);
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(const char *path)
+{
+    FL_DIR dirstat;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+    if (fl_opendir(path, &dirstat))
+    {
+        struct fs_dir_ent dirent;
+
+        while (fl_readdir(&dirstat, &dirent) == 0)
+        {
+#if FATFS_INC_TIME_DATE_SUPPORT
+            int d,m,y,h,mn,s;
+            fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+            fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+            FAT_PRINTF(("%02d/%02d/%04d  %02d:%02d      ", d,mn,y,h,m));
+#endif
+
+            if (dirent.is_dir)
+            {
+                FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
+            }
+            else
+            {
+                FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+            }
+        }
+
+        fl_closedir(&dirstat);
+    }
+
+    FL_UNLOCK(&_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
+{
+    int levels;
+    int res = 1;
+    uint32 cluster = FAT32_INVALID_CLUSTER;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    levels = fatfs_total_path_levels((char*)path) + 1;
+
+    // If path is in the root dir
+    if (levels == 0)
+        cluster = fatfs_get_root_cluster(&_fs);
+    // Find parent directory start cluster
+    else
+        res = _open_directory((char*)path, &cluster);
+
+    if (res)
+        fatfs_list_directory_start(&_fs, dir, cluster);
+
+    FL_UNLOCK(&_fs);
+
+    return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
+{
+    int res = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    res = fatfs_list_directory_next(&_fs, dirls, entry);
+
+    FL_UNLOCK(&_fs);
+
+    return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(FL_DIR* dir)
+{
+    // Not used
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(const char *path)
+{
+    int res = 0;
+    FL_DIR dir;
+
+    if (fl_opendir(path, &dir))
+    {
+        res = 1;
+        fl_closedir(&dir);
+    }
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(uint32 volume_sectors, const char *name)
+{
+    return fatfs_format(&_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void)
+{
+    return &_fs;
+}
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_filelib.h b/vtoycli/fat_io_lib/release/fat_filelib.h
new file mode 100644 (file)
index 0000000..a40a28f
--- /dev/null
@@ -0,0 +1,146 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+    #define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+    #define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+    #define SEEK_SET    0
+#endif
+
+#ifndef EOF
+    #define EOF         (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+    uint32 ClusterIdx;
+    uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+    uint32                  parentcluster;
+    uint32                  startcluster;
+    uint32                  bytenum;
+    uint32                  filelength;
+    int                     filelength_changed;
+    char                    path[FATFS_MAX_LONG_FILENAME];
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32                  cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+    uint32                  cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+    // Cluster Lookup
+    struct cluster_lookup   last_fat_lookup;
+
+    // Read/Write sector buffer
+    uint8                   file_data_sector[FAT_SECTOR_SIZE];
+    uint32                  file_data_address;
+    int                     file_data_dirty;
+
+    // File fopen flags
+    uint8                   flags;
+#define FILE_READ           (1 << 0)
+#define FILE_WRITE          (1 << 1)
+#define FILE_APPEND         (1 << 2)
+#define FILE_BINARY         (1 << 3)
+#define FILE_ERASE          (1 << 4)
+#define FILE_CREATE         (1 << 5)
+
+    struct fat_node         list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void                fl_init(void);
+void                fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int                 fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void                fl_shutdown(void);
+
+// Standard API
+void*               fl_fopen(const char *path, const char *modifiers);
+void                fl_fclose(void *file);
+int                 fl_fflush(void *file);
+int                 fl_fgetc(void *file);
+char *              fl_fgets(char *s, int n, void *f);
+int                 fl_fputc(int c, void *file);
+int                 fl_fputs(const char * str, void *file);
+int                 fl_fwrite(const void * data, int size, int count, void *file );
+int                 fl_fread(void * data, int size, int count, void *file );
+int                 fl_fseek(void *file , long offset , int origin );
+int                 fl_fgetpos(void *file , uint32 * position);
+long                fl_ftell(void *f);
+int                 fl_feof(void *f);
+int                 fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status    FL_DIR;
+typedef struct fs_dir_ent            fl_dirent;
+
+FL_DIR*             fl_opendir(const char* path, FL_DIR *dir);
+int                 fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int                 fl_closedir(FL_DIR* dir);
+
+// Extensions
+void                fl_listdirectory(const char *path);
+int                 fl_createdirectory(const char *path);
+int                 fl_is_dir(const char *path);
+
+int                 fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs*       fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE            FL_FILE
+
+#define fopen(a,b)      fl_fopen(a, b)
+#define fclose(a)       fl_fclose(a)
+#define fflush(a)       fl_fflush(a)
+#define fgetc(a)        fl_fgetc(a)
+#define fgets(a,b,c)    fl_fgets(a, b, c)
+#define fputc(a,b)      fl_fputc(a, b)
+#define fputs(a,b)      fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d)  fl_fread(a, b, c, d)
+#define fseek(a,b,c)    fl_fseek(a, b, c)
+#define fgetpos(a,b)    fl_fgetpos(a, b)
+#define ftell(a)        fl_ftell(a)
+#define feof(a)         fl_feof(a)
+#define remove(a)       fl_remove(a)
+#define mkdir(a)        fl_createdirectory(a)
+#define rmdir(a)        0
+
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_format.c b/vtoycli/fat_io_lib/release/fat_format.c
new file mode 100644 (file)
index 0000000..d067f37
--- /dev/null
@@ -0,0 +1,532 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+    uint32  sectors;
+    uint8   sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+    { 32680, 2},    // 16MB - 1K
+    { 262144, 4},   // 128MB - 2K
+    { 524288, 8},   // 256MB - 4K
+    { 1048576, 16}, // 512MB - 8K
+    { 2097152, 32}, // 1GB - 16K
+    { 4194304, 64}, // 2GB - 32K
+    { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+    { 0 , 0 }       // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+    { 532480, 1},     // 260MB - 512b
+    { 16777216, 8},   // 8GB - 4K
+    { 33554432, 16},  // 16GB - 8K
+    { 67108864, 32},  // 32GB - 16K
+    { 0xFFFFFFFF, 64},// >32GB - 32K
+    { 0 , 0 }         // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+    int i;
+
+    if (!is_fat32)
+    {
+        for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table16[i].sectors)
+                return _cluster_size_table16[i].sectors_per_cluster;
+    }
+    else
+    {
+        for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table32[i].sectors)
+                return _cluster_size_table32[i].sectors_per_cluster;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
+{
+    int i;
+
+    // Zero sector first
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    for (i=0;i<count;i++)
+        if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+    uint32 total_clusters;
+    int i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // OEM Name & Jump Code
+    fs->currentsector.sector[0] = 0xEB;
+    fs->currentsector.sector[1] = 0x3C;
+    fs->currentsector.sector[2] = 0x90;
+    fs->currentsector.sector[3] = 0x4D;
+    fs->currentsector.sector[4] = 0x53;
+    fs->currentsector.sector[5] = 0x44;
+    fs->currentsector.sector[6] = 0x4F;
+    fs->currentsector.sector[7] = 0x53;
+    fs->currentsector.sector[8] = 0x35;
+    fs->currentsector.sector[9] = 0x2E;
+    fs->currentsector.sector[10] = 0x30;
+
+    // Bytes per sector
+    fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+    fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+    // Get sectors per cluster size for the disk
+    fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+    if (!fs->sectors_per_cluster)
+        return 0; // Invalid disk size
+
+    // Sectors per cluster
+    fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+    // Reserved Sectors
+    if (!is_fat32)
+        fs->reserved_sectors = 8;
+    else
+        fs->reserved_sectors = 32;
+    fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+    fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+    // Number of FATS
+    fs->num_of_fats = 2;
+    fs->currentsector.sector[16] = fs->num_of_fats;
+
+    // Max entries in root dir (FAT16 only)
+    if (!is_fat32)
+    {
+        fs->root_entry_count = 512;
+        fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+        fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+    }
+    else
+    {
+        fs->root_entry_count = 0;
+        fs->currentsector.sector[17] = 0;
+        fs->currentsector.sector[18] = 0;
+    }
+
+    // [FAT16] Total sectors (use FAT32 count instead)
+    fs->currentsector.sector[19] = 0x00;
+    fs->currentsector.sector[20] = 0x00;
+
+    // Media type
+    fs->currentsector.sector[21] = 0xF8;
+
+
+    // FAT16 BS Details
+    if (!is_fat32)
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+        fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+        fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+        // Sectors per track
+        fs->currentsector.sector[24] = 0x00;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads
+        fs->currentsector.sector[26] = 0x00;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x20;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        // Drive number
+        fs->currentsector.sector[36] = 0x00;
+
+        // Reserved
+        fs->currentsector.sector[37] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[38] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[39] = 0x12;
+        fs->currentsector.sector[40] = 0x34;
+        fs->currentsector.sector[41] = 0x56;
+        fs->currentsector.sector[42] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+43] = name[i];
+            else
+                fs->currentsector.sector[i+43] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[54] = 'F';
+        fs->currentsector.sector[55] = 'A';
+        fs->currentsector.sector[56] = 'T';
+        fs->currentsector.sector[57] = '1';
+        fs->currentsector.sector[58] = '6';
+        fs->currentsector.sector[59] = ' ';
+        fs->currentsector.sector[60] = ' ';
+        fs->currentsector.sector[61] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+    // FAT32 BS Details
+    else
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        fs->currentsector.sector[22] = 0;
+        fs->currentsector.sector[23] = 0;
+
+        // Sectors per track (default)
+        fs->currentsector.sector[24] = 0x3F;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads (default)
+        fs->currentsector.sector[26] = 0xFF;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x00;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+        // BPB_FATSz32
+        fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+        fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+        fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+        fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+        // BPB_ExtFlags
+        fs->currentsector.sector[40] = 0;
+        fs->currentsector.sector[41] = 0;
+
+        // BPB_FSVer
+        fs->currentsector.sector[42] = 0;
+        fs->currentsector.sector[43] = 0;
+
+        // BPB_RootClus
+        fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+        fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+        fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+        fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+        // BPB_FSInfo
+        fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+        fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+        // BPB_BkBootSec
+        fs->currentsector.sector[50] = 6;
+        fs->currentsector.sector[51] = 0;
+
+        // Drive number
+        fs->currentsector.sector[64] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[66] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[67] = 0x12;
+        fs->currentsector.sector[68] = 0x34;
+        fs->currentsector.sector[69] = 0x56;
+        fs->currentsector.sector[70] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+71] = name[i];
+            else
+                fs->currentsector.sector[i+71] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[82] = 'F';
+        fs->currentsector.sector[83] = 'A';
+        fs->currentsector.sector[84] = 'T';
+        fs->currentsector.sector[85] = '3';
+        fs->currentsector.sector[86] = '2';
+        fs->currentsector.sector[87] = ' ';
+        fs->currentsector.sector[88] = ' ';
+        fs->currentsector.sector[89] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+
+    if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
+{
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // FSI_LeadSig
+    fs->currentsector.sector[0] = 0x52;
+    fs->currentsector.sector[1] = 0x52;
+    fs->currentsector.sector[2] = 0x61;
+    fs->currentsector.sector[3] = 0x41;
+
+    // FSI_StrucSig
+    fs->currentsector.sector[484] = 0x72;
+    fs->currentsector.sector[485] = 0x72;
+    fs->currentsector.sector[486] = 0x41;
+    fs->currentsector.sector[487] = 0x61;
+
+    // FSI_Free_Count
+    fs->currentsector.sector[488] = 0xFF;
+    fs->currentsector.sector[489] = 0xFF;
+    fs->currentsector.sector[490] = 0xFF;
+    fs->currentsector.sector[491] = 0xFF;
+
+    // FSI_Nxt_Free
+    fs->currentsector.sector[492] = 0xFF;
+    fs->currentsector.sector[493] = 0xFF;
+    fs->currentsector.sector[494] = 0xFF;
+    fs->currentsector.sector[495] = 0xFF;
+
+    // Signature
+    fs->currentsector.sector[510] = 0x55;
+    fs->currentsector.sector[511] = 0xAA;
+
+    if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
+{
+    uint32 i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // Initialise default allocate / reserved clusters
+    if (!is_fat32)
+    {
+        SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+        SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+    }
+    else
+    {
+        SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+        SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+        SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+    }
+
+    if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+        return 0;
+
+    // Zero remaining FAT sectors
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+    for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
+        if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT16
+    fs->fat_type = FAT_TYPE_16;
+
+    // Not valid for FAT16
+    fs->fs_info_sector = 0;
+    fs->rootdir_first_cluster = 0;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
+        return 0;
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 0))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT32
+    fs->fat_type = FAT_TYPE_32;
+
+    // Basic defaults for normal FAT32 partitions
+    fs->fs_info_sector = 1;
+    fs->rootdir_first_cluster = 2;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
+        return 0;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FSInfo sector
+    if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
+        return 0;
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 1))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    // 2GB - 32K limit for safe behaviour for FAT16
+    if (volume_sectors <= 4194304)
+        return fatfs_format_fat16(fs, volume_sectors, name);
+    else
+        return fatfs_format_fat32(fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
diff --git a/vtoycli/fat_io_lib/release/fat_format.h b/vtoycli/fat_io_lib/release/fat_format.h
new file mode 100644 (file)
index 0000000..a8a6bba
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_list.h b/vtoycli/fat_io_lib/release/fat_list.h
new file mode 100644 (file)
index 0000000..bd386ef
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+    #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+    #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+    struct fat_node    *previous;
+    struct fat_node    *next;
+};
+
+struct fat_list
+{
+    struct fat_node    *head;
+    struct fat_node    *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m)     p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p)         (p)->next
+#define fat_list_prev(l, p)         (p)->previous
+#define fat_list_first(l)           (l)->head
+#define fat_list_last(l)            (l)->tail
+#define fat_list_for_each(l, p)     for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if(!node->previous)
+        list->head = node->next;
+    else
+        node->previous->next = node->next;
+
+    if(!node->next)
+        list->tail = node->previous;
+    else
+        node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node;
+    new_node->next = node->next;
+    if (!node->next)
+        list->tail = new_node;
+    else
+        node->next->previous = new_node;
+    node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node->previous;
+    new_node->next = node;
+    if (!node->previous)
+        list->head = new_node;
+    else
+        node->previous->next = new_node;
+    node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->head)
+    {
+        list->head = node;
+        list->tail = node;
+        node->previous = 0;
+        node->next = 0;
+    }
+    else
+        fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->tail)
+        fat_list_insert_first(list, node);
+     else
+        fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+    struct fat_node * node;
+
+    FAT_ASSERT(list);
+
+    node = fat_list_first(list);
+    if (node)
+        fat_list_remove(list, node);
+
+    return node;
+}
+
+#endif
+
diff --git a/vtoycli/fat_io_lib/release/fat_misc.c b/vtoycli/fat_io_lib/release/fat_misc.c
new file mode 100644 (file)
index 0000000..cbf6f08
--- /dev/null
@@ -0,0 +1,505 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <stdlib.h>
+#include <string.h>
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+    int i = 0;
+
+    lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Zero out buffer also
+    if (wipeTable)
+        for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
+            memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+    uint8 LFNIndex, i;
+    LFNIndex = entryBuffer[0] & 0x1F;
+
+    // Limit file name to cache size!
+    if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+        return ;
+
+    // This is an error condition
+    if (LFNIndex == 0)
+        return ;
+
+    if (lfn->no_of_strings == 0)
+        lfn->no_of_strings = LFNIndex;
+
+    lfn->String[LFNIndex-1][0] = entryBuffer[1];
+    lfn->String[LFNIndex-1][1] = entryBuffer[3];
+    lfn->String[LFNIndex-1][2] = entryBuffer[5];
+    lfn->String[LFNIndex-1][3] = entryBuffer[7];
+    lfn->String[LFNIndex-1][4] = entryBuffer[9];
+    lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+    lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+    lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+    lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+    lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+    lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+    lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+    lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+    for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
+        if (lfn->String[LFNIndex-1][i]==0xFF)
+            lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+    // Null terminate long filename
+    if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+        lfn->Null = '\0';
+    else if (lfn->no_of_strings)
+        lfn->String[lfn->no_of_strings][0] = '\0';
+    else
+        lfn->String[0][0] = '\0';
+
+    return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+    if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+    if ( (entry->Name[0]==FILE_HEADER_BLANK)  ||
+         (entry->Name[0]==FILE_HEADER_DELETED)||
+         (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+         (entry->Attr & FILE_ATTR_SYSHID) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+         (lfn->no_of_strings) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) )
+        return 1;
+    else
+        return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_DIR)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_FILE)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+    int length = (int)strlen(filename);
+
+    if (length)
+        return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+    int i;
+    int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+    // 13 characters entries
+    int length = (int)strlen(filename);
+    int entriesRequired = fatfs_lfn_entries_required(filename);
+
+    // Filename offset
+    int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+    // Initialise to zeros
+    memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+    // LFN entry number
+    buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+    // LFN flag
+    buffer[11] = 0x0F;
+
+    // Checksum of short filename
+    buffer[13] = sfnChk;
+
+    // Copy to buffer
+    for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
+    {
+        if ( (start+i) < length )
+            buffer[nameIndexes[i]] = filename[start+i];
+        else if ( (start+i) == length )
+            buffer[nameIndexes[i]] = 0x00;
+        else
+        {
+            buffer[nameIndexes[i]] = 0xFF;
+            buffer[nameIndexes[i]+1] = 0xFF;
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_sfn_create_entry: Create the short filename directory entry
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
+{
+    int i;
+
+    // Copy short filename
+    for (i=0;i<FAT_SFN_SIZE_FULL;i++)
+        entry->Name[i] = shortfilename[i];
+
+    // Unless we have a RTC we might as well set these to 1980
+    entry->CrtTimeTenth = 0x00;
+    entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+    entry->CrtDate[1] = 0x00;
+    entry->CrtDate[0] = 0x20;
+    entry->LstAccDate[1] = 0x00;
+    entry->LstAccDate[0] = 0x20;
+    entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+    entry->WrtDate[1] = 0x00;
+    entry->WrtDate[0] = 0x20;
+
+    if (!dir)
+        entry->Attr = FILE_TYPE_FILE;
+    else
+        entry->Attr = FILE_TYPE_DIR;
+
+    entry->NTRes = 0x00;
+
+    entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+    entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+    entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+    int i;
+    int dotPos = -1;
+    char ext[3];
+    int pos;
+    int len = (int)strlen(filename);
+
+    // Invalid to start with .
+    if (filename[0]=='.')
+        return 0;
+
+    memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+    memset(ext, ' ', 3);
+
+    // Find dot seperator
+    for (i = 0; i< len; i++)
+    {
+        if (filename[i]=='.')
+            dotPos = i;
+    }
+
+    // Extract extensions
+    if (dotPos!=-1)
+    {
+        // Copy first three chars of extension
+        for (i = (dotPos+1); i < (dotPos+1+3); i++)
+            if (i<len)
+                ext[i-(dotPos+1)] = filename[i];
+
+        // Shorten the length to the dot position
+        len = dotPos;
+    }
+
+    // Add filename part
+    pos = 0;
+    for (i=0;i<len;i++)
+    {
+        if ( (filename[i]!=' ') && (filename[i]!='.') )
+        {
+            if (filename[i] >= 'a' && filename[i] <= 'z')
+                sfn_output[pos++] = filename[i] - 'a' + 'A';
+            else
+                sfn_output[pos++] = filename[i];
+        }
+
+        // Fill upto 8 characters
+        if (pos==FAT_SFN_SIZE_PARTIAL)
+            break;
+    }
+
+    // Add extension part
+    for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
+    {
+        if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+        else
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+    char* cp;
+    char outbuf[12];
+    const char digits[] = "0123456789ABCDEF";
+
+    // Build string backwards
+    cp = outbuf;
+    do
+    {
+        *cp++ = digits[(int)(num % 10)];
+    }
+    while ((num /= 10) > 0);
+
+    *cp-- = 0;
+
+    // Copy in forwards
+    while (cp >= outbuf)
+        *s++ = *cp--;
+
+    *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+    int tail_chars;
+    char tail_str[12];
+
+    if (tailNum > 99999)
+        return 0;
+
+    // Convert to number
+    memset(tail_str, 0x00, sizeof(tail_str));
+    tail_str[0] = '~';
+    fatfs_itoa(tailNum, tail_str+1);
+
+    // Copy in base filename
+    memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+    // Overwrite with tail
+    tail_chars = (int)strlen(tail_str);
+    memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+    return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+    *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+    *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+    *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+    *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+    *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+    *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+    *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+    *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+    uint16 fat_time = 0;
+
+    // Most FAT times are to a resolution of 2 seconds
+    seconds /= FAT_TIME_SECONDS_SCALE;
+
+    fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+    fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+    fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+    return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+    uint16 fat_date = 0;
+
+    // FAT dates are relative to 1980
+    if (year >= FAT_DATE_YEAR_OFFSET)
+        year -= FAT_DATE_YEAR_OFFSET;
+
+    fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+    fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+    fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+    return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+    int i;
+    int j;
+
+    FAT_PRINTF(("Sector %d:\n", sector));
+
+    for (i=0;i<FAT_SECTOR_SIZE;i++)
+    {
+        if (!((i) % 16))
+        {
+            FAT_PRINTF(("  %04d: ", i));
+        }
+
+        FAT_PRINTF(("%02x", data[i]));
+        if (!((i+1) % 4))
+        {
+            FAT_PRINTF((" "));
+        }
+
+        if (!((i+1) % 16))
+        {
+            FAT_PRINTF(("   "));
+            for (j=0;j<16;j++)
+            {
+                char ch = data[i-15+j];
+
+                // Is printable?
+                if (ch > 31 && ch < 127)
+                {
+                    FAT_PRINTF(("%c", ch));
+                }
+                else
+                {
+                    FAT_PRINTF(("."));
+                }
+            }
+
+            FAT_PRINTF(("\n"));
+        }
+    }
+}
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_misc.h b/vtoycli/fat_io_lib/release/fat_misc.h
new file mode 100644 (file)
index 0000000..0c02634
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES    20
+#define MAX_LFN_ENTRY_LENGTH        13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location)    ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location)    ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); \
+                                                  buffer[location+2] = (uint8)((value>>16)&0xFF); \
+                                                  buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+    // Long File Name Structure (max 260 LFN length)
+    uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+    uint8 Null;
+#endif
+    uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void    fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char*   fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int     fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int     fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int     fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int     fatfs_entry_is_file(struct fat_dir_entry *entry);
+int     fatfs_lfn_entries_required(char *filename);
+void    fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void    fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int     fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int     fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void    fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void    fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16  fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16  fatfs_convert_to_fat_date(int day, int month, int year);
+void    fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_opts.h b/vtoycli/fat_io_lib/release/fat_opts.h
new file mode 100644 (file)
index 0000000..ac4dc86
--- /dev/null
@@ -0,0 +1,90 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+    #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+    #define FATFS_IS_LITTLE_ENDIAN          1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+    #define FATFS_MAX_LONG_FILENAME         260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+    #define FATFS_MAX_OPEN_FILES            2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS              1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS                     1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES         128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+    #define FATFS_INC_WRITE_SUPPORT         1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+    #define FATFS_INC_LFN_SUPPORT           1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+    #define FATFS_DIR_LIST_SUPPORT          1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+    #define FATFS_INC_TIME_DATE_SUPPORT     0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+    #define FATFS_INC_FORMAT_SUPPORT        1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE                     512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+    // Don't include stdio, but there is a printf function available
+    #ifdef FAT_PRINTF_NOINC_STDIO
+        extern int printf(const char* ctrl1, ... );
+        #define FAT_PRINTF(a)               printf a
+    // Include stdio to use printf
+    #else
+        #include <stdio.h>
+        #define FAT_PRINTF(a)               printf a
+    #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+    #include <time.h>
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_string.c b/vtoycli/fat_io_lib/release/fat_string.c
new file mode 100644 (file)
index 0000000..f7206ce
--- /dev/null
@@ -0,0 +1,514 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include <assert.h>
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+//        c:\folder\file.zip
+//        /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+    int levels = 0;
+    char expectedchar;
+
+    if (!path)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Count levels in path string
+    while (*path)
+    {
+        // Fast forward through actual subdir text to next slash
+        for (; *path; )
+        {
+            // If slash detected escape from for loop
+            if (*path == expectedchar) { path++; break; }
+            path++;
+        }
+
+        // Increase number of subdirs founds
+        levels++;
+    }
+
+    // Subtract the file itself
+    return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+    int i;
+    int pathlen=0;
+    int levels=0;
+    int copypnt=0;
+    char expectedchar;
+
+    if (!path || max_len <= 0)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Get string length of path
+    pathlen = (int)strlen (path);
+
+    // Loop through the number of times as characters in 'path'
+    for (i = 0; i<pathlen; i++)
+    {
+        // If a '\' is found then increase level
+        if (*path == expectedchar) levels++;
+
+        // If correct level and the character is not a '\' or '/' then copy text to 'output'
+        if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
+            output[copypnt++] = *path;
+
+        // Increment through path string
+        path++;
+    }
+
+    // Null Terminate
+    output[copypnt] = '\0';
+
+    // If a string was copied return 0 else return 1
+    if (output[0] != '\0')
+        return 0;    // OK
+    else
+        return -1;    // Error
+}
+//-----------------------------------------------------------------------------
+// fatfs_split_path: Full path contains the passed in string.
+// Returned is the path string and file Name string
+// E.g. C:\folder\file.zip -> path = C:\folder  filename = file.zip
+// E.g. C:\file.zip -> path = [blank]  filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+    int strindex;
+
+    // Count the levels to the filepath
+    int levels = fatfs_total_path_levels(full_path);
+    if (levels == -1)
+        return -1;
+
+    // Get filename part of string
+    if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+        return -1;
+
+    // If root file
+    if (levels == 0)
+        path[0] = '\0';
+    else
+    {
+        strindex = (int)strlen(full_path) - (int)strlen(filename);
+        if (strindex > max_path)
+            strindex = max_path;
+
+        memcpy(path, full_path, strindex);
+        path[strindex-1] = '\0';
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+    int diff;
+    char a,b;
+
+    while (n--)
+    {
+        a = *s1;
+        b = *s2;
+
+        // Make lower case if uppercase
+        if ((a>='A') && (a<='Z'))
+            a+= 32;
+        if ((b>='A') && (b<='Z'))
+            b+= 32;
+
+        diff = a - b;
+
+        // If different
+        if (diff)
+            return diff;
+
+        // If run out of strings
+        if ( (*s1 == 0) || (*s2 == 0) )
+            break;
+
+        s1++;
+        s2++;
+    }
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+    int dotPos = -1;
+    char *strSrc = str;
+
+    // Find last '.' in string (if at all)
+    while (*strSrc)
+    {
+        if (*strSrc=='.')
+            dotPos = (int)(strSrc-str);
+
+        strSrc++;
+    }
+
+    return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+    int length = strLen;
+    char *strSrc = str+strLen-1;
+
+    // Find last non white space
+    while (strLen != 0)
+    {
+        if (*strSrc == ' ')
+            length = (int)(strSrc - str);
+        else
+            break;
+
+        strSrc--;
+        strLen--;
+    }
+
+    return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+    char *ext1 = NULL;
+    char *ext2 = NULL;
+    int ext1Pos, ext2Pos;
+    int file1Len, file2Len;
+
+    // Get both files extension
+    ext1Pos = FileString_GetExtension(strA);
+    ext2Pos = FileString_GetExtension(strB);
+
+    // NOTE: Extension position can be different for matching
+    // filename if trailing space are present before it!
+    // Check that if one has an extension, so does the other
+    if ((ext1Pos==-1) && (ext2Pos!=-1))
+        return 0;
+    if ((ext2Pos==-1) && (ext1Pos!=-1))
+        return 0;
+
+    // If they both have extensions, compare them
+    if (ext1Pos!=-1)
+    {
+        // Set pointer to start of extension
+        ext1 = strA+ext1Pos+1;
+        ext2 = strB+ext2Pos+1;
+
+        // Verify that the file extension lengths match!
+        if (strlen(ext1) != strlen(ext2))
+            return 0;
+
+        // If they dont match
+        if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+            return 0;
+
+        // Filelength is upto extensions
+        file1Len = ext1Pos;
+        file2Len = ext2Pos;
+    }
+    // No extensions
+    else
+    {
+        // Filelength is actual filelength
+        file1Len = (int)strlen(strA);
+        file2Len = (int)strlen(strB);
+    }
+
+    // Find length without trailing spaces (before ext)
+    file1Len = FileString_TrimLength(strA, file1Len);
+    file2Len = FileString_TrimLength(strB, file2Len);
+
+    // Check the file lengths match
+    if (file1Len!=file2Len)
+        return 0;
+
+    // Compare main part of filenames
+    if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+        return 0;
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+    if (path)
+    {
+        while (*path)
+        {
+            // Last character?
+            if (!(*(path+1)))
+            {
+                if (*path == '\\' || *path == '/')
+                    return 1;
+            }
+
+            path++;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+    int len = 0;
+    while (*in && len <= 11)
+    {
+        char a = *in++;
+
+        if (a == ' ')
+            continue;
+        // Make lower case if uppercase
+        else if ((a>='A') && (a<='Z'))
+            a+= 32;
+
+        *out++ = a;
+        len++;
+    }
+
+    *out = '\0';
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+    int len = 0;
+
+    // Get files extension offset
+    int ext_pos = FileString_GetExtension(filename);
+
+    if (ext_pos > 0 && out && maxlen)
+    {
+        filename += ext_pos + 1;
+
+        while (*filename && len < (maxlen-1))
+        {
+            char a = *filename++;
+
+            // Make lowercase if uppercase
+            if ((a>='A') && (a<='Z'))
+                a+= 32;
+
+            *out++ = a;
+            len++;
+        }
+
+        *out = '\0';
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+    int len = 0;
+    char last = 0;
+    char seperator = '/';
+
+    if (path && filename && out && maxlen > 0)
+    {
+        while (*path && len < (maxlen-2))
+        {
+            last = *path++;
+            if (last == '\\')
+                seperator = '\\';
+            *out++ = last;
+            len++;
+        }
+
+        // Add a seperator if trailing one not found
+        if (last != '\\' && last != '/')
+            *out++ = seperator;
+
+        while (*filename && len < (maxlen-1))
+        {
+            *out++ = *filename++;
+            len++;
+        }
+
+        *out = '\0';
+
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+    char output[255];
+    char output2[255];
+
+    assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+    assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+    assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+    assert(fatfs_total_path_levels("C:\\") == -1);
+    assert(fatfs_total_path_levels("") == -1);
+    assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+    assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "folder") == 0);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "dev") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "etc") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "C:\\folder") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(output[0] == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "/dev/etc") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+    assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+    assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+    assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("C:\\file.zip   ", strlen("C:\\file.zip   ")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("   ", strlen("   ")) == 0);
+
+    assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file2.ext") == 0);
+
+    assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+    assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+    assert(fatfs_string_ends_with_slash("/path") == 0);
+    assert(fatfs_string_ends_with_slash("/path/a") == 0);
+    assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+    assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+    assert(strcmp(output, "ext") != 0);
+
+    assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+    assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+    assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_string.h b/vtoycli/fat_io_lib/release/fat_string.h
new file mode 100644 (file)
index 0000000..90ca8e0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_table.c b/vtoycli/fat_io_lib/release/fat_table.c
new file mode 100644 (file)
index 0000000..8d05d3b
--- /dev/null
@@ -0,0 +1,478 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+    #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+//                            FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location)        ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location)        ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+    int i;
+
+    // FAT buffer chain head
+    fs->fat_buffer_head = NULL;
+
+    for (i=0;i<FAT_BUFFERS;i++)
+    {
+        // Initialise buffers to invalid
+        fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
+        fs->fat_buffers[i].dirty = 0;
+        memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+        fs->fat_buffers[i].ptr = NULL;
+
+        // Add to head of queue
+        fs->fat_buffers[i].next = fs->fat_buffer_head;
+        fs->fat_buffer_head = &fs->fat_buffers[i];
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
+{
+    if (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+        {
+            if (fs->disk_io.write_media)
+            {
+                uint32 sectors = FAT_BUFFER_SECTORS;
+                uint32 offset = pcur->address - fs->fat_begin_lba;
+
+                // Limit to sectors used for the FAT
+                if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+                    sectors = FAT_BUFFER_SECTORS;
+                else
+                    sectors = fs->fat_sectors - offset;
+
+                if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
+                    return 0;
+            }
+
+            pcur->dirty = 0;
+        }
+
+        return 1;
+    }
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
+{
+    struct fat_buffer *last = NULL;
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Sector within this buffer?
+        if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+            break;
+
+        // End of list?
+        if (pcur->next == NULL)
+        {
+            // Remove buffer from list
+            if (last)
+                last->next = NULL;
+            // We the first and last buffer in the chain?
+            else
+                fs->fat_buffer_head = NULL;
+        }
+
+        last = pcur;
+        pcur = pcur->next;
+    }
+
+    // We found the sector already in FAT buffer chain
+    if (pcur)
+    {
+        pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+        return pcur;
+    }
+
+    // Else, we removed the last item from the list
+    pcur = last;
+
+    // Add to start of sector buffer list (now newest sector)
+    pcur->next = fs->fat_buffer_head;
+    fs->fat_buffer_head = pcur;
+
+    // Writeback sector if changed
+    if (pcur->dirty)
+        if (!fatfs_fat_writeback(fs, pcur))
+            return 0;
+
+    // Address is now new sector
+    pcur->address = sector;
+
+    // Read next sector
+    if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+    {
+        // Read failed, invalidate buffer address
+        pcur->address = FAT32_INVALID_CLUSTER;
+        return NULL;
+    }
+
+    pcur->ptr = pcur->sector;
+    return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fatfs *fs)
+{
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+            if (!fatfs_fat_writeback(fs, pcur))
+                return 0;
+
+        pcur = pcur->next;
+    }
+
+    return 1;
+}
+
+//-----------------------------------------------------------------------------
+//                        General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    struct fat_buffer *pbuf;
+
+    // Why is '..' labelled with cluster 0 when it should be 2 ??
+    if (current_cluster == 0)
+        current_cluster = 2;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = current_cluster / 256;
+    else
+        fat_sector_offset = current_cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return (FAT32_LAST_CLUSTER);
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+        // If end of chain found
+        if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+        // Mask out MS 4 bits (its 28bit addressing)
+        nextcluster = nextcluster & 0x0FFFFFFF;
+
+        // If end of chain found
+        if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+
+    // Else return next cluster
+    return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        ;
+    else
+    {
+        // Load sector to change it
+        struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
+        if (!pbuf)
+            return ;
+
+        // Change
+        FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+        fs->next_free_cluster = newValue;
+
+        // Write back FSINFO sector to disk
+        if (fs->disk_io.write_media)
+            fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
+
+        // Invalidate cache entry
+        pbuf->address = FAT32_INVALID_CLUSTER;
+        pbuf->dirty = 0;
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    uint32 current_cluster = start_cluster;
+    struct fat_buffer *pbuf;
+
+    do
+    {
+        // Find which sector of FAT table to read
+        if (fs->fat_type == FAT_TYPE_16)
+            fat_sector_offset = current_cluster / 256;
+        else
+            fat_sector_offset = current_cluster / 128;
+
+        if ( fat_sector_offset < fs->fat_sectors)
+        {
+            // Read FAT sector into buffer
+            pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+            if (!pbuf)
+                return 0;
+
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+            }
+            else
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+                // Mask out MS 4 bits (its 28bit addressing)
+                nextcluster = nextcluster & 0x0FFFFFFF;
+            }
+
+            if (nextcluster !=0 )
+                current_cluster++;
+        }
+        else
+            // Otherwise, run out of FAT sectors to check...
+            return 0;
+    }
+    while (nextcluster != 0x0);
+
+    // Found blank entry
+    *free_cluster = current_cluster;
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+    struct fat_buffer *pbuf;
+    uint32 fat_sector_offset, position;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = cluster / 256;
+    else
+        fat_sector_offset = cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return 0;
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 16 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 256)) * 2;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 128)) * 4;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
+{
+    uint32 last_cluster;
+    uint32 next_cluster = start_cluster;
+
+    // Loop until end of chain
+    while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+
+        // Clear last link
+        fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+    uint32 last_cluster = FAT32_LAST_CLUSTER;
+    uint32 next_cluster = start_cluster;
+
+    if (start_cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Loop until end of chain
+    while ( next_cluster != FAT32_LAST_CLUSTER )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+        if (!next_cluster)
+            return 0;
+    }
+
+    // Add link in for new cluster
+    fatfs_fat_set_cluster(fs, last_cluster, newEntry);
+
+    // Mark new cluster as end of chain
+    fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fatfs *fs)
+{
+    uint32 i,j;
+    uint32 count = 0;
+    struct fat_buffer *pbuf;
+
+    for (i = 0; i < fs->fat_sectors; i++)
+    {
+        // Read FAT sector into buffer
+        pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
+        if (!pbuf)
+            break;
+
+        for (j = 0; j < FAT_SECTOR_SIZE; )
+        {
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 2;
+            }
+            else
+            {
+                if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 4;
+            }
+        }
+    }
+
+    return count;
+}
diff --git a/vtoycli/fat_io_lib/release/fat_table.h b/vtoycli/fat_io_lib/release/fat_table.h
new file mode 100644 (file)
index 0000000..ead75f3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_fat_init(struct fatfs *fs);
+int     fatfs_fat_purge(struct fatfs *fs);
+uint32  fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void    fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int     fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int     fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int     fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int     fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32  fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_types.h b/vtoycli/fat_io_lib/release/fat_types.h
new file mode 100644 (file)
index 0000000..5e2cca8
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+    #if __SIZEOF_LONG__ == 8
+        #define FATFS_DEF_UINT32_AS_INT
+    #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+
+    // If compiling on a 64-bit machine, use int as 32-bits
+    #ifdef FATFS_DEF_UINT32_AS_INT
+        typedef unsigned int uint32;
+    // Else for 32-bit machines & embedded systems, use long...
+    #else
+        typedef unsigned long uint32;
+    #endif
+#endif
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+    #define FAT_HTONS(n) (n)
+    #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+    #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+    #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+                    ((((uint32)(n) & 0xFF00)) << 8) | \
+                    ((((uint32)(n) & 0xFF0000)) >> 8) | \
+                    ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED           __attribute__ ((packed))
+#else
+    // Other compilers may require other methods of packing structures
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_write.c b/vtoycli/fat_io_lib/release/fat_write.c
new file mode 100644 (file)
index 0000000..0718cb1
--- /dev/null
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+//                            FAT16/32 File IO Library\r
+//                                    V2.6\r
+//                              Ultra-Embedded.com\r
+//                            Copyright 2003 - 2012\r
+//\r
+//                         Email: admin@ultra-embedded.com\r
+//\r
+//                                License: GPL\r
+//   If you would like a version with a more permissive license for use in\r
+//   closed source commercial applications please contact me for details.\r
+//-----------------------------------------------------------------------------\r
+//\r
+// This file is part of FAT File IO Library.\r
+//\r
+// FAT File IO Library is free software; you can redistribute it and/or modify\r
+// it under the terms of the GNU General Public License as published by\r
+// the Free Software Foundation; either version 2 of the License, or\r
+// (at your option) any later version.\r
+//\r
+// FAT File IO Library is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+// GNU General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with FAT File IO Library; if not, write to the Free Software\r
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+//-----------------------------------------------------------------------------\r
+//-----------------------------------------------------------------------------\r
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+    uint32 i;
+    uint32 nextcluster;
+    uint32 start = *startCluster;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    for (i=0;i<clusters;i++)
+    {
+        // Start looking for free clusters from the beginning
+        if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+        {
+            // Point last to this
+            fatfs_fat_set_cluster(fs, start, nextcluster);
+
+            // Point this to end of file
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+
+            // Adjust argument reference
+            start = nextcluster;
+            if (i == 0)
+                *startCluster = nextcluster;
+        }
+        else
+            return 0;
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+    uint32 clusterSize;
+    uint32 clusterCount;
+    uint32 nextcluster;
+
+    if (size==0)
+        return 0;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    // Work out size and clusters
+    clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+    clusterCount = (size / clusterSize);
+
+    // If any left over
+    if (size-(clusterSize*clusterCount))
+        clusterCount++;
+
+    // Allocated first link in the chain if a new file
+    if (newFile)
+    {
+        if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+            return 0;
+
+        // If this is all that is needed then all done
+        if (clusterCount==1)
+        {
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+            *startCluster = nextcluster;
+            return 1;
+        }
+    }
+    // Allocate from end of current chain (startCluster is end of chain)
+    else
+        nextcluster = *startCluster;
+
+    if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+    struct fat_dir_entry *directoryEntry;
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    int possible_spaces = 0;
+    int start_recorded = 0;
+
+    // No entries required?
+    if (entryCount == 0)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+                // LFN Entry
+                if (fatfs_entry_lfn_text(directoryEntry))
+                {
+                    // First entry?
+                    if (possible_spaces == 0)
+                    {
+                        // Store start
+                        *pSector = x-1;
+                        *pOffset = item;
+                        start_recorded = 1;
+                    }
+
+                    // Increment the count in-case the file turns
+                    // out to be deleted...
+                    possible_spaces++;
+                }
+                // SFN Entry
+                else
+                {
+                    // Has file been deleted?
+                    if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+
+                        // Else continue counting until we find a valid entry!
+                    }
+                    // Is the file entry empty?
+                    else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        // Increment the blank entries count
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+                    }
+                    // File entry is valid
+                    else
+                    {
+                        // Reset all flags
+                        possible_spaces = 0;
+                        start_recorded = 0;
+                    }
+                }
+            } // End of for
+        } // End of if
+        // Run out of free space in the directory, allocate some more
+        else
+        {
+            uint32 newCluster;
+
+            // Get a new cluster for directory
+            if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
+                return 0;
+
+            // Add cluster to end of directory tree
+            if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
+                return 0;
+
+            // Erase new directory cluster
+            memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+            for (i=0;i<fs->sectors_per_cluster;i++)
+            {
+                if (!fatfs_write_sector(fs, newCluster, i, 0))
+                    return 0;
+            }
+
+            // If non of the name fitted on previous sectors
+            if (!start_recorded)
+            {
+                // Store start
+                *pSector = (x-1);
+                *pOffset = 0;
+                start_recorded = 1;
+            }
+
+            return 1;
+        }
+    } // End of while loop
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    uint32 x=0;
+    int entryCount;
+    struct fat_dir_entry shortEntry;
+    int dirtySector = 0;
+
+    uint32 dirSector = 0;
+    uint8 dirOffset = 0;
+    int foundEnd = 0;
+
+    uint8 checksum;
+    uint8 *pSname;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+    // How many LFN entries are required?
+    // NOTE: We always request one LFN even if it would fit in a SFN!
+    entryCount = fatfs_lfn_entries_required(filename);
+    if (!entryCount)
+        return 0;
+#else
+    entryCount = 0;
+#endif
+
+    // Find space in the directory for this filename (or allocate some more)
+    // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+    if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+        return 0;
+
+    // Generate checksum of short filename
+    pSname = (uint8*)shortfilename;
+    checksum = 0;
+    for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+    // Start from current sector where space was found!
+    x = dirSector;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // If the start position for the entry has been found
+                if (foundEnd==0)
+                    if ( (dirSector==(x-1)) && (dirOffset==item) )
+                        foundEnd = 1;
+
+                // Start adding filename
+                if (foundEnd)
+                {
+                    if (entryCount==0)
+                    {
+                        // Short filename
+                        fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update create, access & modify time & date
+                        fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+                        memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+                        // Writeback
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+#if FATFS_INC_LFN_SUPPORT
+                    else
+                    {
+                        entryCount--;
+
+                        // Copy entry to directory buffer
+                        fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+                        dirtySector = 1;
+                    }
+#endif
+                }
+            } // End of if
+
+            // Write back to disk before loading another sector
+            if (dirtySector)
+            {
+                if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
+                    return 0;
+
+                dirtySector = 0;
+            }
+        }
+        else
+            return 0;
+    } // End of while loop
+
+    return 0;
+}
+#endif
diff --git a/vtoycli/fat_io_lib/release/fat_write.h b/vtoycli/fat_io_lib/release/fat_write.h
new file mode 100644 (file)
index 0000000..5558a86
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/vtoycli/fat_io_lib/release/version.txt b/vtoycli/fat_io_lib/release/version.txt
new file mode 100644 (file)
index 0000000..5a00a98
--- /dev/null
@@ -0,0 +1 @@
+2.6.11\r
diff --git a/vtoycli/partresize.c b/vtoycli/partresize.c
new file mode 100644 (file)
index 0000000..4452205
--- /dev/null
@@ -0,0 +1,682 @@
+/******************************************************************************
+ * partresize.c  ---- ventoy part resize util
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fat_filelib.h>
+#include "vtoycli.h"
+
+static int g_disk_fd = 0;
+static UINT64 g_disk_offset = 0;
+static GUID g_ZeroGuid = {0};
+static GUID g_WindowsDataPartGuid = { 0xebd0a0a2, 0xb9e5, 0x4433, { 0x87, 0xc0, 0x68, 0xb6, 0xb7, 0x26, 0x99, 0xc7 } };
+
+static int vtoy_disk_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+    UINT64 offset = sector * 512ULL;
+    
+    lseek(g_disk_fd, g_disk_offset + offset, SEEK_SET);
+    read(g_disk_fd, buffer, sector_count * 512);
+    
+    return 1;
+}
+
+static int vtoy_disk_write(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+    UINT64 offset = sector * 512ULL;
+    
+    lseek(g_disk_fd, g_disk_offset + offset, SEEK_SET);
+    write(g_disk_fd, buffer, sector_count * 512);
+    
+    return 1;
+}
+
+
+static int gpt_check(const char *disk)
+{
+    int fd = -1;
+    int rc = 1;
+    VTOY_GPT_INFO *pGPT = NULL;
+
+    fd = open(disk, O_RDONLY);
+    if (fd < 0)
+    {
+        printf("Failed to open %s\n", disk);
+        goto out;
+    }
+
+    pGPT = malloc(sizeof(VTOY_GPT_INFO));
+    if (NULL == pGPT)
+    {
+        goto out;
+    }
+    memset(pGPT, 0, sizeof(VTOY_GPT_INFO));
+    
+    read(fd, pGPT, sizeof(VTOY_GPT_INFO));
+
+    if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE && memcmp(pGPT->Head.Signature, "EFI PART", 8) == 0)
+       {
+        rc = 0;
+       }
+
+out:
+    check_close(fd);        
+    check_free(pGPT);
+    return rc;
+}
+
+static int part_check(const char *disk)
+{
+    int i;
+    int fd = -1;
+    int rc = 0;
+    int Index = 0;
+    int Count = 0;
+    int PartStyle = 0;
+    UINT64 Part1Start;
+    UINT64 Part1End;
+    UINT64 NextPartStart;
+    UINT64 DiskSizeInBytes;
+    VTOY_GPT_INFO *pGPT = NULL;
+
+    DiskSizeInBytes = get_disk_size_in_byte(disk);
+    if (DiskSizeInBytes == 0)
+    {
+        printf("Failed to get disk size of %s\n", disk);
+        goto out;
+    }
+    
+    fd = open(disk, O_RDONLY);
+    if (fd < 0)
+    {
+        printf("Failed to open %s\n", disk);
+        goto out;
+    }
+
+    pGPT = malloc(sizeof(VTOY_GPT_INFO));
+    if (NULL == pGPT)
+    {
+        goto out;
+    }
+    memset(pGPT, 0, sizeof(VTOY_GPT_INFO));
+    
+    read(fd, pGPT, sizeof(VTOY_GPT_INFO));
+
+    if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE && memcmp(pGPT->Head.Signature, "EFI PART", 8) == 0)
+       {
+        PartStyle = 1;
+       }
+       else
+       {
+        PartStyle = 0;
+       }
+
+    if (PartStyle == 0)
+    {
+               PART_TABLE *PartTbl = pGPT->MBR.PartTbl;
+
+        for (Count = 0, i = 0; i < 4; i++)
+        {
+            if (PartTbl[i].SectorCount > 0)
+            {
+                               printf("MBR Part%d SectorStart:%u SectorCount:%u\n", i + 1, PartTbl[i].StartSectorId, PartTbl[i].SectorCount);
+                Count++;
+            }
+        }
+
+               //We must have a free partition table for VTOYEFI partition
+               if (Count >= 4)
+               {
+                       printf("###[FAIL] 4 MBR partition tables are all used.\n");
+                       goto out;
+               }
+
+               if (PartTbl[0].SectorCount > 0)
+               {
+                       Part1Start = PartTbl[0].StartSectorId;
+                       Part1End = PartTbl[0].SectorCount + Part1Start;
+               }
+               else
+               {
+                       printf("###[FAIL] MBR Partition 1 is invalid\n");
+                       goto out;
+               }
+
+               Index = -1;
+               NextPartStart = DiskSizeInBytes / 512ULL;
+               for (i = 1; i < 4; i++)
+               {
+                       if (PartTbl[i].SectorCount > 0 && NextPartStart > PartTbl[i].StartSectorId)
+                       {
+                               Index = i;
+                               NextPartStart = PartTbl[i].StartSectorId;
+                       }
+               }
+
+        NextPartStart *= 512ULL;
+               printf("DiskSize:%llu NextPartStart:%llu(LBA:%llu) Index:%d\n", 
+            DiskSizeInBytes, NextPartStart, NextPartStart / 512ULL, Index);
+    }
+    else
+    {
+               VTOY_GPT_PART_TBL *PartTbl = pGPT->PartTbl;
+
+        for (Count = 0, i = 0; i < 128; i++)
+        {
+            if (memcmp(&(PartTbl[i].PartGuid), &g_ZeroGuid, sizeof(GUID)))
+            {
+                               printf("GPT Part%d StartLBA:%llu LastLBA:%llu\n", i + 1, PartTbl[i].StartLBA, PartTbl[i].LastLBA);
+                Count++;
+            }
+        }
+
+               if (Count >= 128)
+               {
+                       printf("###[FAIL] 128 GPT partition tables are all used.\n");
+                       goto out;
+               }
+
+               if (memcmp(&(PartTbl[0].PartGuid), &g_ZeroGuid, sizeof(GUID)))
+               {
+                       Part1Start = PartTbl[0].StartLBA;
+                       Part1End = PartTbl[0].LastLBA + 1;
+               }
+               else
+               {
+                       printf("###[FAIL] GPT Partition 1 is invalid\n");
+                       goto out;
+               }
+
+               Index = -1;
+               NextPartStart = (pGPT->Head.PartAreaEndLBA + 1);
+               for (i = 1; i < 128; i++)
+               {
+                       if (memcmp(&(PartTbl[i].PartGuid), &g_ZeroGuid, sizeof(GUID)) && NextPartStart > PartTbl[i].StartLBA)
+                       {
+                               Index = i;
+                               NextPartStart = PartTbl[i].StartLBA;
+                       }
+               }
+
+               NextPartStart *= 512ULL;
+               printf("DiskSize:%llu NextPartStart:%llu(LBA:%llu) Index:%d\n",
+            DiskSizeInBytes, NextPartStart, NextPartStart / 512ULL, Index);
+    }
+
+       printf("Valid partition table (%s): Valid partition count:%d\n", (PartStyle == 0) ? "MBR" : "GPT", Count);
+
+       //Partition 1 MUST start at 1MB
+       Part1Start *= 512ULL;
+       Part1End *= 512ULL;
+
+       printf("Partition 1 start at: %llu %lluKB, end:%llu, NextPartStart:%llu\n", 
+               Part1Start, Part1Start / 1024, Part1End, NextPartStart);
+    if (Part1Start != SIZE_1MB)
+    {
+        printf("###[FAIL] Partition 1 is not start at 1MB\n");
+        goto out;
+    }
+
+
+       //If we have free space after partition 1
+       if (NextPartStart - Part1End >= VENTOY_EFI_PART_SIZE)
+       {
+               printf("Free space after partition 1 (%llu) is enough for VTOYEFI part\n", NextPartStart - Part1End);
+               rc = 1;
+       }
+       else if (NextPartStart == Part1End)
+       {
+               printf("There is no free space after partition 1\n");
+        rc = 2;
+       }
+       else
+       {
+               printf("The free space after partition 1 is not enough\n");
+        rc = 2;
+       }
+
+out:
+    check_close(fd);        
+    check_free(pGPT);
+    return rc;
+}
+
+static int secureboot_proc(char *disk, UINT64 part2start)
+{
+       int rc = 0;
+       int size;
+    int fd = -1;
+       char *filebuf = NULL;
+       void *file = NULL;
+
+    fd = open(disk, O_RDWR);
+    if (fd < 0)
+    {
+        printf("Failed to open %s\n", disk);
+        return 1;
+    }
+
+    g_disk_fd = fd;
+    g_disk_offset = part2start * 512ULL;
+
+       fl_init();
+
+       if (0 == fl_attach_media(vtoy_disk_read, vtoy_disk_write))
+       {
+               file = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+               printf("Open ventoy efi file %p\n", file);
+               if (file)
+               {
+                       fl_fseek(file, 0, SEEK_END);
+                       size = (int)fl_ftell(file);
+                       fl_fseek(file, 0, SEEK_SET);
+
+                       printf("ventoy x64 efi file size %d ...\n", size);
+
+                       filebuf = (char *)malloc(size);
+                       if (filebuf)
+                       {
+                               fl_fread(filebuf, 1, size, file);
+                       }
+
+                       fl_fclose(file);
+
+                       fl_remove("/EFI/BOOT/BOOTX64.EFI");
+                       fl_remove("/EFI/BOOT/grubx64.efi");
+                       fl_remove("/EFI/BOOT/grubx64_real.efi");
+                       fl_remove("/EFI/BOOT/MokManager.efi");
+            fl_remove("/ENROLL_THIS_KEY_IN_MOKMANAGER.cer");
+
+                       file = fl_fopen("/EFI/BOOT/BOOTX64.EFI", "wb");
+                       printf("Open bootx64 efi file %p\n", file);
+                       if (file)
+                       {
+                               if (filebuf)
+                               {
+                                       fl_fwrite(filebuf, 1, size, file);
+                               }
+                               
+                               fl_fflush(file);
+                               fl_fclose(file);
+                       }
+
+                       if (filebuf)
+                       {
+                               free(filebuf);
+                       }
+               }
+
+        file = fl_fopen("/EFI/BOOT/grubia32_real.efi", "rb");
+        printf("Open ventoy ia32 efi file %p\n", file);
+        if (file)
+        {
+            fl_fseek(file, 0, SEEK_END);
+            size = (int)fl_ftell(file);
+            fl_fseek(file, 0, SEEK_SET);
+
+            printf("ventoy efi file size %d ...\n", size);
+
+            filebuf = (char *)malloc(size);
+            if (filebuf)
+            {
+                fl_fread(filebuf, 1, size, file);
+            }
+
+            fl_fclose(file);
+
+            fl_remove("/EFI/BOOT/BOOTIA32.EFI");
+            fl_remove("/EFI/BOOT/grubia32.efi");
+            fl_remove("/EFI/BOOT/grubia32_real.efi");
+            fl_remove("/EFI/BOOT/mmia32.efi");            
+
+            file = fl_fopen("/EFI/BOOT/BOOTIA32.EFI", "wb");
+            printf("Open bootia32 efi file %p\n", file);
+            if (file)
+            {
+                if (filebuf)
+                {
+                    fl_fwrite(filebuf, 1, size, file);
+                }
+
+                fl_fflush(file);
+                fl_fclose(file);
+            }
+
+            if (filebuf)
+            {
+                free(filebuf);
+            }
+        }
+
+       }
+       else
+       {
+               rc = 1;
+       }
+
+       fl_shutdown();
+    fsync(fd);
+
+       return rc;
+}
+
+static int VentoyFillMBRLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
+{
+    UINT8 Head;
+    UINT8 Sector;
+    UINT8 nSector = 63;
+    UINT8 nHead = 8;    
+    UINT32 Cylinder;
+    UINT32 EndSectorId;
+
+    while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
+    {
+        nHead = (UINT8)nHead * 2;
+    }
+
+    if (nHead == 0)
+    {
+        nHead = 255;
+    }
+
+    Cylinder = StartSectorId / nSector / nHead;
+    Head = StartSectorId / nSector % nHead;
+    Sector = StartSectorId % nSector + 1;
+
+    Table->StartHead = Head;
+    Table->StartSector = Sector;
+    Table->StartCylinder = Cylinder;
+
+    EndSectorId = StartSectorId + SectorCount - 1;
+    Cylinder = EndSectorId / nSector / nHead;
+    Head = EndSectorId / nSector % nHead;
+    Sector = EndSectorId % nSector + 1;
+
+    Table->EndHead = Head;
+    Table->EndSector = Sector;
+    Table->EndCylinder = Cylinder;
+
+    Table->StartSectorId = StartSectorId;
+    Table->SectorCount = SectorCount;
+
+    return 0;
+}
+
+
+static int WriteDataToPhyDisk(int fd, UINT64 offset, void *buffer, int len)
+{
+    ssize_t wrlen;
+    off_t newseek;
+        
+    newseek = lseek(fd, offset, SEEK_SET);
+    if (newseek != offset)
+    {
+        printf("Failed to lseek %llu %lld %d\n", offset, (long long)newseek, errno);
+        return 0;
+    }
+    
+    wrlen = write(fd, buffer, len);
+    if ((int)wrlen != len)
+    {
+        printf("Failed to write %d %d %d\n", len, (int)wrlen, errno);
+        return 0;
+    }
+    
+    return 1;
+}
+
+static int VentoyFillBackupGptHead(VTOY_GPT_INFO *pInfo, VTOY_GPT_HDR *pHead)
+{
+    UINT64 LBA;
+    UINT64 BackupLBA;
+
+    memcpy(pHead, &pInfo->Head, sizeof(VTOY_GPT_HDR));
+
+    LBA = pHead->EfiStartLBA;
+    BackupLBA = pHead->EfiBackupLBA;
+    
+    pHead->EfiStartLBA = BackupLBA;
+    pHead->EfiBackupLBA = LBA;
+    pHead->PartTblStartLBA = BackupLBA + 1 - 33;
+
+    pHead->Crc = 0;
+    pHead->Crc = VtoyCrc32(pHead, pHead->Length);
+
+    return 0;
+}
+
+static int update_part_table(char *disk, UINT64 part2start)
+{
+    int i;
+    int j;
+    int fd = -1;
+    int rc = 1;
+    int PartStyle = 0;
+    ssize_t len = 0;
+    UINT64 DiskSizeInBytes;
+    VTOY_GPT_INFO *pGPT = NULL;
+    VTOY_GPT_HDR *pBack = NULL;
+
+    DiskSizeInBytes = get_disk_size_in_byte(disk);
+    if (DiskSizeInBytes == 0)
+    {
+        printf("Failed to get disk size of %s\n", disk);
+        goto out;
+    }
+    
+    fd = open(disk, O_RDWR);
+    if (fd < 0)
+    {
+        printf("Failed to open %s\n", disk);
+        goto out;
+    }
+
+    pGPT = malloc(sizeof(VTOY_GPT_INFO) + sizeof(VTOY_GPT_HDR));
+    if (NULL == pGPT)
+    {
+        goto out;
+    }
+    memset(pGPT, 0, sizeof(VTOY_GPT_INFO) + sizeof(VTOY_GPT_HDR));
+
+    pBack = (VTOY_GPT_HDR *)(pGPT + 1);
+    
+    len = read(fd, pGPT, sizeof(VTOY_GPT_INFO));
+    if (len != (ssize_t)sizeof(VTOY_GPT_INFO))
+    {
+        printf("Failed to read partition table %d err:%d\n", (int)len, errno);
+        goto out;
+    }
+
+    if (pGPT->MBR.PartTbl[0].FsFlag == 0xEE && memcmp(pGPT->Head.Signature, "EFI PART", 8) == 0)
+       {
+        PartStyle = 1;
+       }
+       else
+       {
+        PartStyle = 0;
+       }
+
+    if (PartStyle == 0)
+    {
+               PART_TABLE *PartTbl = pGPT->MBR.PartTbl;
+
+        for (i = 1; i < 4; i++)
+        {
+            if (PartTbl[i].SectorCount == 0)
+            {
+                               break;
+            }
+        }
+
+               if (i >= 4)
+               {
+                       printf("###[FAIL] Can not find a free MBR partition table.\n");
+                       goto out;
+               }
+
+        for (j = i - 1; j > 0; j--)
+               {
+                       printf("Move MBR partition table %d --> %d\n", j + 1, j + 2);
+                       memcpy(PartTbl + (j + 1), PartTbl + j, sizeof(PART_TABLE));
+               }
+
+        memset(PartTbl + 1, 0, sizeof(PART_TABLE));
+        VentoyFillMBRLocation(DiskSizeInBytes, (UINT32)part2start, VENTOY_EFI_PART_SIZE / 512, PartTbl + 1);
+               PartTbl[1].Active = 0x00;
+               PartTbl[1].FsFlag = 0xEF; // EFI System Partition
+
+        PartTbl[0].Active = 0x80; // bootable
+        PartTbl[0].SectorCount = (UINT32)part2start - 2048;
+        
+        if (!WriteDataToPhyDisk(fd, 0, &(pGPT->MBR), 512))
+               {
+                       printf("MBR write MBR failed\n");
+                       goto out;
+               }
+
+        fsync(fd);
+        printf("MBR update partition table success.\n");
+        rc = 0;
+    }
+    else
+    {
+               VTOY_GPT_PART_TBL *PartTbl = pGPT->PartTbl;
+
+        for (i = 1; i < 128; i++)
+        {
+            if (memcmp(&(PartTbl[i].PartGuid), &g_ZeroGuid, sizeof(GUID)) == 0)
+            {
+                               break;
+            }
+        }
+
+               if (i >= 128)
+               {
+                       printf("###[FAIL] Can not find a free GPT partition table.\n");
+                       goto out;
+               }
+
+               for (j = i - 1; j > 0; j--)
+               {
+                       printf("Move GPT partition table %d --> %d\n", j + 1, j + 2);
+                       memcpy(PartTbl + (j + 1), PartTbl + j, sizeof(VTOY_GPT_PART_TBL));
+               }
+
+        // to fix windows issue
+        memset(PartTbl + 1, 0, sizeof(VTOY_GPT_PART_TBL));
+               memcpy(&(PartTbl[1].PartType), &g_WindowsDataPartGuid, sizeof(GUID));
+               ventoy_gen_preudo_uuid(&(PartTbl[1].PartGuid));
+
+        PartTbl[0].LastLBA = part2start - 1;
+
+        PartTbl[1].StartLBA = PartTbl[0].LastLBA + 1;
+               PartTbl[1].LastLBA = PartTbl[1].StartLBA + VENTOY_EFI_PART_SIZE / 512 - 1;
+               PartTbl[1].Attr = 0xC000000000000001ULL;
+        PartTbl[1].Name[0] = 'V';
+        PartTbl[1].Name[1] = 'T';
+        PartTbl[1].Name[2] = 'O';
+        PartTbl[1].Name[3] = 'Y';
+        PartTbl[1].Name[4] = 'E';
+        PartTbl[1].Name[5] = 'F';
+        PartTbl[1].Name[6] = 'I';
+        PartTbl[1].Name[7] = 0;
+
+               //Update CRC
+               pGPT->Head.PartTblCrc = VtoyCrc32(pGPT->PartTbl, sizeof(pGPT->PartTbl));
+               pGPT->Head.Crc = 0;
+               pGPT->Head.Crc = VtoyCrc32(&(pGPT->Head), pGPT->Head.Length);
+
+               printf("pGPT->Head.EfiStartLBA=%llu\n", pGPT->Head.EfiStartLBA);
+               printf("pGPT->Head.EfiBackupLBA=%llu\n", pGPT->Head.EfiBackupLBA);
+
+               VentoyFillBackupGptHead(pGPT, pBack);
+               if (!WriteDataToPhyDisk(fd, pGPT->Head.EfiBackupLBA * 512, pBack, 512))
+               {
+                       printf("GPT write backup head failed\n");
+                       goto out;
+               }
+
+               if (!WriteDataToPhyDisk(fd, (pGPT->Head.EfiBackupLBA - 32) * 512, pGPT->PartTbl, 512 * 32))
+               {
+                       printf("GPT write backup partition table failed\n");
+                       goto out;
+               }
+
+               if (!WriteDataToPhyDisk(fd, 0, pGPT, 512 * 34))
+               {
+                       printf("GPT write MBR & Main partition table failed\n");
+                       goto out;
+               }
+
+        fsync(fd);
+        printf("GPT update partition table success.\n");
+        rc = 0;
+    }
+
+out:
+    check_close(fd);        
+    check_free(pGPT);
+    return rc;
+}
+
+int partresize_main(int argc, char **argv)
+{
+    UINT64 sector;
+    
+    if (argc != 3 && argc != 4)
+    {
+        printf("usage: partresize -c/-f /dev/sdb\n");
+        return 1;
+    }
+
+    if (strcmp(argv[1], "-c") == 0)
+    {
+        return part_check(argv[2]);
+    }
+    else if (strcmp(argv[1], "-s") == 0)
+    {
+        sector = strtoull(argv[3], NULL, 10);
+        return secureboot_proc(argv[2], sector);
+    }
+    else if (strcmp(argv[1], "-p") == 0)
+    {
+        sector = strtoull(argv[3], NULL, 10);    
+        return update_part_table(argv[2], sector);
+    }
+    else if (strcmp(argv[1], "-t") == 0)
+    {
+        return gpt_check(argv[2]);
+    }
+    else
+    {
+        return 1;
+    }
+}
+
diff --git a/vtoycli/vtoycli.c b/vtoycli/vtoycli.c
new file mode 100644 (file)
index 0000000..3a92917
--- /dev/null
@@ -0,0 +1,120 @@
+/******************************************************************************
+ * vtoycli.c
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "vtoycli.h"
+
+void ventoy_gen_preudo_uuid(void *uuid)
+{
+    int i;
+    int fd;
+
+    fd = open("/dev/urandom", O_RDONLY);
+    if (fd < 0)
+    {
+        srand(time(NULL));
+        for (i = 0; i < 8; i++)
+        {
+            *((uint16_t *)uuid + i) = (uint16_t)(rand() & 0xFFFF);
+        }
+    }
+    else
+    {
+        read(fd, uuid, 16);
+        close(fd);
+    }
+}
+
+UINT64 get_disk_size_in_byte(const char *disk)
+{
+    int fd;
+    int rc;
+    const char *pos = disk;
+    unsigned long long size = 0;
+    char diskpath[256] = {0};
+    char sizebuf[64] = {0};
+
+    if (strncmp(disk, "/dev/", 5) == 0)
+    {
+        pos = disk + 5;
+    }
+
+    // Try 1: get size from sysfs
+    snprintf(diskpath, sizeof(diskpath) - 1, "/sys/block/%s/size", pos);
+    if (access(diskpath, F_OK) >= 0)
+    {
+        fd = open(diskpath, O_RDONLY);
+        if (fd >= 0)
+        {
+            read(fd, sizebuf, sizeof(sizebuf));
+            size = strtoull(sizebuf, NULL, 10);
+            close(fd);
+            return (size * 512);
+        }
+    }
+    else
+    {
+        printf("%s not exist \n", diskpath);
+    }
+
+    printf("disk %s size %llu bytes\n", disk, (unsigned long long)size);
+    return size;
+}
+
+
+int main(int argc, char **argv)
+{
+    if (argc < 2)
+    {
+        return 1;
+    }
+    else if (strcmp(argv[1], "fat") == 0)
+    {
+        return vtoyfat_main(argc - 1, argv + 1);
+    }
+    else if (strcmp(argv[1], "gpt") == 0)
+    {
+        return vtoygpt_main(argc - 1, argv + 1);
+    }
+    else if (strcmp(argv[1], "partresize") == 0)
+    {
+        return partresize_main(argc - 1, argv + 1);
+    }
+    else
+    {
+        return 1;
+    }
+}
+
diff --git a/vtoycli/vtoycli.h b/vtoycli/vtoycli.h
new file mode 100644 (file)
index 0000000..1a6667f
--- /dev/null
@@ -0,0 +1,138 @@
+/******************************************************************************
+ * vtoycli.h
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VTOYCLI_H__
+#define __VTOYCLI_H__
+
+#define VENTOY_EFI_PART_ATTR   0xC000000000000001ULL
+
+#define SIZE_1MB   (1024 * 1024)
+#define VENTOY_EFI_PART_SIZE   (32 * SIZE_1MB)
+
+#define check_free(p) if (p) free(p)
+#define check_close(fd) if (fd >= 0) close(fd)
+
+#define VOID   void
+#define CHAR   char
+#define UINT64 unsigned long long
+#define UINT32 unsigned int
+#define UINT16 unsigned short
+#define CHAR16 unsigned short
+#define UINT8  unsigned char
+
+UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length);
+
+#define COMPILE_ASSERT(expr)  extern char __compile_assert[(expr) ? 1 : -1]
+
+#pragma pack(1)
+
+typedef struct PART_TABLE
+{
+    UINT8  Active;
+
+    UINT8  StartHead;
+    UINT16 StartSector : 6;
+    UINT16 StartCylinder : 10;
+
+    UINT8  FsFlag;
+
+    UINT8  EndHead;
+    UINT16 EndSector : 6;
+    UINT16 EndCylinder : 10;
+
+    UINT32 StartSectorId;
+    UINT32 SectorCount;
+}PART_TABLE;
+
+typedef struct MBR_HEAD
+{
+    UINT8 BootCode[446];
+    PART_TABLE PartTbl[4];
+    UINT8 Byte55;
+    UINT8 ByteAA;
+}MBR_HEAD;
+
+typedef struct GUID
+{
+    UINT32   data1;
+    UINT16   data2;
+    UINT16   data3;
+    UINT8    data4[8];
+}GUID;
+
+typedef struct VTOY_GPT_HDR
+{
+    CHAR   Signature[8]; /* EFI PART */
+    UINT8  Version[4];
+    UINT32 Length;
+    UINT32 Crc;
+    UINT8  Reserved1[4];
+    UINT64 EfiStartLBA;
+    UINT64 EfiBackupLBA;
+    UINT64 PartAreaStartLBA;
+    UINT64 PartAreaEndLBA;
+    GUID   DiskGuid;
+    UINT64 PartTblStartLBA;
+    UINT32 PartTblTotNum;
+    UINT32 PartTblEntryLen;
+    UINT32 PartTblCrc;
+    UINT8  Reserved2[420];
+}VTOY_GPT_HDR;
+
+COMPILE_ASSERT(sizeof(VTOY_GPT_HDR) == 512);
+
+typedef struct VTOY_GPT_PART_TBL
+{
+    GUID   PartType;
+    GUID   PartGuid;
+    UINT64 StartLBA;
+    UINT64 LastLBA;
+    UINT64 Attr;
+    CHAR16 Name[36];
+}VTOY_GPT_PART_TBL;
+COMPILE_ASSERT(sizeof(VTOY_GPT_PART_TBL) == 128);
+
+typedef struct VTOY_GPT_INFO
+{
+    MBR_HEAD MBR;
+    VTOY_GPT_HDR Head;
+    VTOY_GPT_PART_TBL PartTbl[128];
+}VTOY_GPT_INFO;
+
+typedef struct VTOY_BK_GPT_INFO
+{
+    VTOY_GPT_PART_TBL PartTbl[128];
+    VTOY_GPT_HDR Head;
+}VTOY_BK_GPT_INFO;
+
+COMPILE_ASSERT(sizeof(VTOY_GPT_INFO) == 512 * 34);
+COMPILE_ASSERT(sizeof(VTOY_BK_GPT_INFO) == 512 * 33);
+
+#pragma pack()
+
+UINT32 VtoyCrc32(VOID *Buffer, UINT32 Length);
+int vtoygpt_main(int argc, char **argv);
+int vtoyfat_main(int argc, char **argv);
+int partresize_main(int argc, char **argv);
+void ventoy_gen_preudo_uuid(void *uuid);
+UINT64 get_disk_size_in_byte(const char *disk);
+    
+#endif /* __VTOYCLI_H__ */
+
diff --git a/vtoycli/vtoyfat.c b/vtoycli/vtoyfat.c
new file mode 100644 (file)
index 0000000..8f3225f
--- /dev/null
@@ -0,0 +1,159 @@
+/******************************************************************************
+ * vtoyfat.c  ---- Parse fat file system
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/stat.h> 
+#include <fcntl.h>
+
+#include <fat_filelib.h>
+
+static int g_disk_fd = 0;
+
+static int vtoy_disk_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+    lseek(g_disk_fd, sector * 512, SEEK_SET);
+    read(g_disk_fd, buffer, sector_count * 512);
+    
+    return 1;
+}
+
+static int check_secure_boot(void)
+{
+    void *flfile = NULL;
+    
+    flfile = fl_fopen("/EFI/BOOT/grubx64_real.efi", "rb");
+    if (flfile)
+    {
+        fl_fclose(flfile);
+        return 0;
+    }
+    
+    return 1;
+}
+
+static int get_ventoy_version(void)
+{
+    int rc = 1;
+    int size = 0;
+    char *buf = NULL;
+    char *pos = NULL;
+    char *end = NULL;
+    void *flfile = NULL;
+
+    flfile = fl_fopen("/grub/grub.cfg", "rb");
+    if (flfile)
+    {
+        fl_fseek(flfile, 0, SEEK_END);
+        size = (int)fl_ftell(flfile);
+
+        fl_fseek(flfile, 0, SEEK_SET);
+
+        buf = malloc(size + 1);
+        if (buf)
+        {
+            fl_fread(buf, 1, size, flfile);
+            buf[size] = 0;
+
+            pos = strstr(buf, "VENTOY_VERSION=");
+            if (pos)
+            {
+                pos += strlen("VENTOY_VERSION=");
+                if (*pos == '"')
+                {
+                    pos++;
+                }
+
+                end = pos;
+                while (*end != 0 && *end != '"' && *end != '\r' && *end != '\n')
+                {
+                    end++;
+                }
+
+                *end = 0;
+
+                printf("%s\n", pos);
+                rc = 0;
+            }
+            free(buf);
+        }
+
+        fl_fclose(flfile);
+    }
+
+    return rc;
+}
+
+int vtoyfat_main(int argc, char **argv)
+{
+    int op = 0;
+    int rc = 1;
+    char *disk;
+
+    if (argc != 2 && argc != 3)
+    {   
+        printf("Usage: vtoyfat /dev/sdbs \n");
+        printf("Usage: vtoyfat -s /dev/sdbs \n");
+        return 1;
+    }
+    
+    if (argv[1][0] == '-' && argv[1][1] == 'T')
+    {
+        return 0;
+    }
+    
+    disk = argv[1];
+    if (argv[1][0] == '-' && argv[1][1] == 's')
+    {
+        op = 1;
+        disk = argv[2];
+    } 
+    
+    g_disk_fd = open(disk, O_RDONLY);
+    if (g_disk_fd < 0)
+    {
+        printf("Failed to open %s\n", disk);
+        return 1;
+    }
+
+    fl_init();
+
+    if (0 == fl_attach_media(vtoy_disk_read, NULL))
+    {
+        if (op == 0)
+        {
+            rc = get_ventoy_version();
+        }
+        else
+        {
+            rc = check_secure_boot();
+        }        
+    }
+
+    fl_shutdown();
+
+    close(g_disk_fd);
+
+    return rc;
+}
+
diff --git a/vtoycli/vtoygpt.c b/vtoycli/vtoygpt.c
new file mode 100644 (file)
index 0000000..1d9d262
--- /dev/null
@@ -0,0 +1,220 @@
+/******************************************************************************
+ * vtoygpt.c  ---- ventoy gpt util
+ *
+ * Copyright (c) 2021, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 of the
+ * License, or (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <stdio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include "vtoycli.h"
+
+void DumpGuid(const char *prefix, GUID *guid)
+{
+    printf("%s: %08x-%04x-%04x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x\n",
+        prefix,
+        guid->data1, guid->data2, guid->data3,
+        guid->data4[0], guid->data4[1], guid->data4[2], guid->data4[3],
+        guid->data4[4], guid->data4[5], guid->data4[6], guid->data4[7]
+        );
+}
+
+void DumpHead(VTOY_GPT_HDR *pHead)
+{
+    UINT32 CrcRead;
+    UINT32 CrcCalc;
+    
+    printf("Signature:<%s>\n", pHead->Signature);
+    printf("Version:<%02x %02x %02x %02x>\n", pHead->Version[0], pHead->Version[1], pHead->Version[2], pHead->Version[3]);
+    printf("Length:%u\n", pHead->Length);
+    printf("Crc:0x%08x\n", pHead->Crc);
+    printf("EfiStartLBA:%lu\n", pHead->EfiStartLBA);
+    printf("EfiBackupLBA:%lu\n", pHead->EfiBackupLBA);
+    printf("PartAreaStartLBA:%lu\n", pHead->PartAreaStartLBA);
+    printf("PartAreaEndLBA:%lu\n", pHead->PartAreaEndLBA);
+    DumpGuid("DiskGuid", &pHead->DiskGuid);
+    
+    printf("PartTblStartLBA:%lu\n", pHead->PartTblStartLBA);
+    printf("PartTblTotNum:%u\n", pHead->PartTblTotNum);
+    printf("PartTblEntryLen:%u\n", pHead->PartTblEntryLen);
+    printf("PartTblCrc:0x%08x\n", pHead->PartTblCrc);
+
+    CrcRead = pHead->Crc;
+    pHead->Crc = 0;
+    CrcCalc = VtoyCrc32(pHead, pHead->Length);
+
+    if (CrcCalc != CrcRead)
+    {
+        printf("Head CRC Check Failed\n");
+    }
+    else
+    {
+        printf("Head CRC Check SUCCESS [%x] [%x]\n", CrcCalc, CrcRead);
+    }
+
+    CrcRead = pHead->PartTblCrc;
+    CrcCalc = VtoyCrc32(pHead + 1, pHead->PartTblEntryLen * pHead->PartTblTotNum);
+    if (CrcCalc != CrcRead)
+    {
+        printf("Part Table CRC Check Failed\n");
+    }
+    else
+    {
+        printf("Part Table CRC Check SUCCESS [%x] [%x]\n", CrcCalc, CrcRead);
+    }
+}
+
+void DumpPartTable(VTOY_GPT_PART_TBL *Tbl)
+{
+    int i;
+    
+    DumpGuid("PartType", &Tbl->PartType);
+    DumpGuid("PartGuid", &Tbl->PartGuid);
+    printf("StartLBA:%lu\n", Tbl->StartLBA);
+    printf("LastLBA:%lu\n", Tbl->LastLBA);
+    printf("Attr:0x%lx\n", Tbl->Attr);
+    printf("Name:");
+
+    for (i = 0; i < 36 && Tbl->Name[i]; i++)
+    {
+        printf("%c", (CHAR)(Tbl->Name[i]));
+    }
+    printf("\n");    
+}
+
+void DumpMBR(MBR_HEAD *pMBR)
+{
+    int i;
+    
+    for (i = 0; i < 4; i++)
+    {
+        printf("=========== Partition Table %d ============\n", i + 1);
+        printf("PartTbl.Active = 0x%x\n", pMBR->PartTbl[i].Active);
+        printf("PartTbl.FsFlag = 0x%x\n", pMBR->PartTbl[i].FsFlag);
+        printf("PartTbl.StartSectorId = %u\n", pMBR->PartTbl[i].StartSectorId);
+        printf("PartTbl.SectorCount = %u\n", pMBR->PartTbl[i].SectorCount);
+        printf("PartTbl.StartHead = %u\n", pMBR->PartTbl[i].StartHead);
+        printf("PartTbl.StartSector = %u\n", pMBR->PartTbl[i].StartSector);
+        printf("PartTbl.StartCylinder = %u\n", pMBR->PartTbl[i].StartCylinder);
+        printf("PartTbl.EndHead = %u\n", pMBR->PartTbl[i].EndHead);
+        printf("PartTbl.EndSector = %u\n", pMBR->PartTbl[i].EndSector);
+        printf("PartTbl.EndCylinder = %u\n", pMBR->PartTbl[i].EndCylinder);
+    }
+}
+
+int DumpGptInfo(VTOY_GPT_INFO *pGptInfo)
+{
+    int i;
+
+    DumpMBR(&pGptInfo->MBR);
+    DumpHead(&pGptInfo->Head);
+
+    for (i = 0; i < 128; i++)
+    {
+        if (pGptInfo->PartTbl[i].StartLBA == 0)
+        {
+            break;
+        }
+    
+        printf("=====Part %d=====\n", i);
+        DumpPartTable(pGptInfo->PartTbl + i);
+    }
+
+    return 0;
+}
+
+int vtoygpt_main(int argc, char **argv)
+{
+    int i;
+    int fd;
+    UINT64 DiskSize;
+    CHAR16 *Name = NULL;
+    VTOY_GPT_INFO *pMainGptInfo = NULL;
+    VTOY_BK_GPT_INFO *pBackGptInfo = NULL;
+
+    if (argc != 3)
+    {
+        printf("usage: vtoygpt -f /dev/sdb\n");
+        return 1;
+    }
+
+    fd = open(argv[2], O_RDWR);
+    if (fd < 0)
+    {
+        printf("Failed to open %s\n", argv[2]);
+        return 1;
+    }
+
+    pMainGptInfo = malloc(sizeof(VTOY_GPT_INFO));
+    pBackGptInfo = malloc(sizeof(VTOY_BK_GPT_INFO));
+    if (NULL == pMainGptInfo || NULL == pBackGptInfo)
+    {
+        close(fd);
+        return 1;
+    }
+
+    read(fd, pMainGptInfo, sizeof(VTOY_GPT_INFO));
+    
+    if (argv[1][0] == '-' && argv[1][1] == 'd')
+    {
+        DumpGptInfo(pMainGptInfo);
+    }
+    else
+    {
+        DiskSize = lseek(fd, 0, SEEK_END);
+        lseek(fd, DiskSize - 33 * 512, SEEK_SET);
+        read(fd, pBackGptInfo, sizeof(VTOY_BK_GPT_INFO));
+
+        Name = pMainGptInfo->PartTbl[1].Name;
+        if (Name[0] == 'V' && Name[1] == 'T' && Name[2] == 'O' && Name[3] == 'Y')
+        {
+            pMainGptInfo->PartTbl[1].Attr = VENTOY_EFI_PART_ATTR;
+            pMainGptInfo->Head.PartTblCrc = VtoyCrc32(pMainGptInfo->PartTbl, sizeof(pMainGptInfo->PartTbl));
+            pMainGptInfo->Head.Crc = 0;
+            pMainGptInfo->Head.Crc = VtoyCrc32(&pMainGptInfo->Head, pMainGptInfo->Head.Length);
+
+            pBackGptInfo->PartTbl[1].Attr = VENTOY_EFI_PART_ATTR;
+            pBackGptInfo->Head.PartTblCrc = VtoyCrc32(pBackGptInfo->PartTbl, sizeof(pBackGptInfo->PartTbl));
+            pBackGptInfo->Head.Crc = 0;
+            pBackGptInfo->Head.Crc = VtoyCrc32(&pBackGptInfo->Head, pBackGptInfo->Head.Length);
+
+            lseek(fd, 512, SEEK_SET);
+            write(fd, (UINT8 *)pMainGptInfo + 512, sizeof(VTOY_GPT_INFO) - 512);
+
+            lseek(fd, DiskSize - 33 * 512, SEEK_SET);
+            write(fd, pBackGptInfo, sizeof(VTOY_BK_GPT_INFO));
+
+            fsync(fd);
+        }
+    }
+
+    free(pMainGptInfo);
+    free(pBackGptInfo);
+    close(fd);
+
+    return 0;
+}
+