Jump to content











Photo
- - - - -

How to read-out FAT12 lookup-table?

grub4dos

  • Please log in to reply
43 replies to this topic

#1 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 26 January 2022 - 12:02 AM

I am working on a script 'ATTRIB16.G4B' to read-out file/ directory attributes.

With FAT16 thing are going very well, to include FAT32 I expect no real problems, but with FAT12 I need help how to read-out a 12-bits FAT lookup-table, so reading the FAT on a FAT12 file-system.

In fact I started with FAT12, but if a sub-directory is bigger than one cluster, I run into problems, so I 'did' FAT16 first.

Some pictures to introduce the project, including some echo and cat-lines to see what is actually going on.

attrib16.g4b read root.png

Finding file/ directory attributes of files in the root-directory is relatively easy, same in sub-directories if they do not exceed the size on one cluster. Also the Volume label attribute can be read-out, but only if in uppercase.

attrib16 volume label.png

First I had problems, because I wrote the label lowercase with Grub4dos Vol, not a good idea on FAT file-systems (and against the specs if I am right).

A very big directory (more than one cluster) was hard to find on my test system, but I was lucky the Windows 98SE Windows\System-directory was big enough. See next print-screen...

attrib16.g4b with lookup FAT.png

On FAT16 finding the next cluster in the FAT lookup-table was relatively easy, only relative addressing after copying with dd the whole FAT to memory was more complicated. I know no way to read hex-values directly from a file (except getting them from the textual output of cat --hex but that is rather inconvenient, also because of the Little-Endian 'thing').

This is my current script, still relatively small - without extra functionality.

My basic idea is quite simple.
1) Get needed file-system infomation [sub-routines :filesystem and :bootblockbase]
2) Finding the cluster-numbers of all sub-directories in the PATH [sub-routines :findpath + :finddirectory, with help of :catfile + :spread to get the right name-formatting]
3) Finding attributes of searched file or directory (or volume label) and converting the hex-value to binary [sub-routines :findfile and :hex2bin + :cnvhex2bin]
BTW if searched file or directory is in the root, step 2) is skipped.

!BAT
#ATTRIB16.G4B v0.1 (20220126), by deomsh.
#Function: Find FAT16 file/ directory attributes 
#Mandatory Arguments: FILE
#Optional Arguments: 
#Remarks: 
setlocal && set *
debug msg=0
#echo %*
set /A mdbase=0x20000 &; set /A basemem=%mdbase% * 0x200
set root=%@root% && root %~d1 > nul && set /a devsect=*0x82B0 &; root=%root% > nul && set root=
set /A devsize=%devsect% * 0x200
set device=%~d1
set path=%~p1
set file=%~f1
if not exist %file% && echo %file% is not an existing file, can be a directory name...
set name=%~n1
set ext=%~x1
call :filesystem "%device%" &; if not /1 %filesys%==fat16 && echo %filesys% not supported && goto :eof
echo File System=%filesys%
call :bootblockbase "%device%"
pause && clear
call :findpath "%path%" &; if %clusnum%==0 && echo %file% not found && goto :eof  
if not "%path%"=="/" && if not exist entry && echo %file% not found && goto :eof
if exist bytedone && call :findfile "%name%%%ext%" ! echo directory entry %file% not found && goto :eof
if not exist attrib && echo Attributes not found && echo && set && goto :eof ! call :hex2bin "%attrib%"
echo && echo attrib=%attrib% && echo Read-only=%bit0% System=%bit1% Hidden=%bit2% Volume label=%bit3% Directory=%bit4% Archive=%bit5%
echo && echo %0 %1 && echo
#todo#call :attributes
debug msg=3
goto :eof

:findfile
setlocal
call :catfile "%~1"
:findfileloop
set entry= && set success=
cat --skip=%bytedone% --locate=%ENTRY% --length=%dirlen% %device%%0+%devsect% > nul &; set /A entry=%?%
echo clusnum=%clusnum% bytedone=%bytedone% entry=%entry%
if exist entry && cat --hex --skip=%entry% --length=0x20 %device%%0+%devsect%
if exist entry && echo -n > (md)%mdbase%+1 && raw dd if=%device%%0+%devsect% of=(md)%mdbase%+1 bs=1 count=0x20 skip=%entry% > nul && set success=Y
if %success%==Y && set /A address=%basemem%+0xB &; read %address% &; set /A attrib=%@retval%&0xff
if not exist entry && if /i %filesys%==fat16 && if %bytedone%>=%dataarea% && if not %bytedone%>=%devsize% && if exist clusnum && call :lookupFAT "%clusnum%" &; if %clusnum%>=2 && set /A bytedone=%clusnum% - 0x2 * %clussize% + %dataarea% && set /A dirlen=%clussize% && goto :findfileloop 
endlocal && set attrib=%attrib% && set entry=%entry%
goto :eof

:findpath
setlocal
set "path=%~1"
set bytedone=%root% && set /A dirlen=%dataarea%-%root%
if "%path%"=="/" && endlocal && set bytedone=%bytedone% && set dirlen=%dirlen% && goto :eof
echo "%path%" > (md)%mdbase%+1
#cat --locate=/ (md)%mdbase%+1 > nul &; set /a numslash=%@retval% > nul
#if %numslash%>=21 && endlocal && set "message=No new path made: path too deep" && set tryagain=Y && goto :eof
cat --locate=/ --replace=\x20 (md)%mdbase%+1 > nul
cat --locate=\x22 --replace=\x20 (md)%mdbase%+1 > nul
set clusnum=0 && cat (md)%mdbase%+1 | call :finddirectory
endlocal && set clusnum=%clusnum% && set bytedone=%bytedone% && set dirlen=%dirlen% && set entry=%entry%
goto :eof

:finddirectory
if "%1"=="" && goto :eof
set /A address=%basemem%+0x1A
set ENTRY= && call :catfile "%~1"
:finddirectoryloop
set entry= && set success=
cat --skip=%bytedone% --locate=%ENTRY% --length=%dirlen% %device%%0+%devsect% > nul &; set /A entry=%?%
echo clusnum=%clusnum% bytedone=%bytedone% entry=%entry%
if exist entry && cat --hex --skip=%entry% --length=0x20 %device%%0+%devsect%
if exist entry && echo -n > (md)%mdbase%+%clussect% && raw dd if=%device%%0+%devsect% of=(md)%mdbase%+1 bs=1 count=0x20 skip=%entry% > nul && set success=Y
if %success%==Y && read %address% &; set /A clusnum=%@retval%&0xffff
if not exist entry && if not %clusnum%==0 && if /i %filesys%==fat16 && if %bytedone%>=%dataarea% && if not %bytedone%>=%devsize% && if exist clusnum && call :lookupFAT "%clusnum%" &; if %clusnum%>=2 && set /A bytedone=%clusnum% - 0x2 * %clussize% + %dataarea% && set /A dirlen=%clussize% && goto :finddirectoryloop 
set /A bytedone=%clusnum% - 0x2 * %clussize% + %dataarea% && set /A dirlen=%clussize%
echo
#echo bytedone=%bytedone% dirlen=%dirlen% entry=%entry% clusnum=%clusnum%
if not %clusnum%==0 && shift ! goto :eof
goto :finddirectory

:lookupFAT
setlocal &; set clusnum=%~1 && set FATbytes=2
set /A mdfat=%mdbase%+1 &; set /A fatmem=%mdfat% * 0x200 && set /A fatlen=%secpfat% * 0x200
call Fn.24 %fatmem% 0x00 %fatlen% 
raw dd if=%device%%0+%devsect% of=(md)%mdfat%+%secpfat% bs=1 count=%fatlen% skip=%fat1% > nul && set success=Y
if %success%==Y && echo -n in :lookupFAT FATbytes=%FATbytes% fatmem=%fatmem% && set /A address=%clusnum% * %FATbytes% + %fatmem% &; echo -n -e \x20address=%address% && read %address% &; set /A nextclus=%@retval%&0xffff &; echo -e \x20nextclus=%nextclus% && if not %nextclus%>=0xffff && set clusnum=%nextclus% ! set clusnum=0
#clusnum=%clusnum% 
echo
#fatlen=%fatlen% secpfat=%secpfat% success=%success% && pause && set clussect=%secpclus%
endlocal && set clusnum=%clusnum%
goto :eof

:catfile
setlocal && set * &; set name=%~1 && set ext=%~2
set /u NAME=%~n1 &; set /a namelen=%@retval% &; if %namelen%<=8 && call :spread "NAME" "%namelen%" &; set NAME=%NAME%%%spread% && set spread=
set /u EXT=%~x1 ;; set EXT=%EXT:~1% ;; set /a extlen=%@retval% &; if exist EXT && if %extlen%<=3 && call :spread "EXT" "%extlen%" &; set EXT=%EXT%%%spread% && set spread=
if exist EXT && set ENTRY=%NAME%%%EXT% ! set ENTRY=%NAME%\x20\x20\x20
#echo ENTRY=%ENTRY%
endlocal && set ENTRY=%ENTRY%
goto :eof

:filesystem
setlocal && set * && set mdbase=%mdbase%
set device=%~1
echo -n > (md)%mdbase%+1
errorcheck off
debug msg=1
if exist device && vol %device% > (md)%mdbase%+1
debug msg=0
if exist device && set filesys=fat12 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=fat16 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=fat32 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ntfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=exfat &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ext2fs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=iso9660 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=reiserfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=udf &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=initrdfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=minix &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=vstafs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=jfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=xfs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ufs2 &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ffs &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=fb &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=tftp &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=pxe &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=ipxe &; cat --locatei=%filesys% (md)%mdbase%+1 > nul || set filesys=
endlocal && set filesys=%filesys%
goto :eof

:hex2bin
#-#+ call :hex2bin <hexa-decimal number>
#-# Mandatory Argument: 0x before hex number = choice
#-# Remarks: max 16-bit. 
setlocal && set *
set ARG2=%~1
if not %ARG2:~0,2%==0x && set hex=0x%ARG2:~2% ! set hex=%ARG2%
if %hex%>=16 && set /A nibble0=0x%hex:~-1,1% && set /A nibble1=%hex:~0,3% ! set /A nibble0=0x%hex:~-1,1% && set /A nibble1=0x0
call :cnvhex2bin "%nibble0%" &; set bit0=%bitA% && set bit1=%bitB% && set bit2=%bitC% && set bit3=%bitD% && set bitA= && set bitB= && set bitC= && set bitD=
call :cnvhex2bin "%nibble1%" &; set bit4=%bitA% && set bit5=%bitB% && set bit6=%bitC% && set bit7=%bitD% && set bitA= && set bitB= && set bitC= && set bitD=
endlocal && set bit0=%bit0% && set bit1=%bit1% && set bit2=%bit2% && set bit3=%bit3% && set bit4=%bit4% && set bit5=%bit5% && set bit6=%bit6% && set bit7=%bit7%
goto :eof 
 
:cnvhex2bin
setlocal 
set nibble=%~1
if %nibble%==0 && set bit0=0 && set bit1=0 && set bit2=0 && set bit3=0
if %nibble%==1 && set bit0=1 && set bit1=0 && set bit2=0 && set bit3=0
if %nibble%==2 && set bit0=0 && set bit1=1 && set bit2=0 && set bit3=0
if %nibble%==3 && set bit0=1 && set bit1=1 && set bit2=0 && set bit3=0
if %nibble%==4 && set bit0=0 && set bit1=0 && set bit2=1 && set bit3=0
if %nibble%==5 && set bit0=1 && set bit1=0 && set bit2=1 && set bit3=0
if %nibble%==6 && set bit0=0 && set bit1=1 && set bit2=1 && set bit3=0
if %nibble%==7 && set bit0=1 && set bit1=1 && set bit2=1 && set bit3=0
if %nibble%==8 && set bit0=0 && set bit1=0 && set bit2=0 && set bit3=1
if %nibble%==9 && set bit0=1 && set bit1=0 && set bit2=0 && set bit3=1
if %nibble%==10 && set bit0=0 && set bit1=1 && set bit2=0 && set bit3=1
if %nibble%==11 && set bit0=1 && set bit1=1 && set bit2=0 && set bit3=1
if %nibble%==12 && set bit0=0 && set bit1=0 && set bit2=1 && set bit3=1
if %nibble%==13 && set bit0=1 && set bit1=0 && set bit2=1 && set bit3=1
if %nibble%==14 && set bit0=0 && set bit1=1 && set bit2=1 && set bit3=1
if %nibble%==15 && set bit0=1 && set bit1=1 && set bit2=1 && set bit3=1
set bitA=%bit0% && set bitB=%bit1% && set bitC=%bit2% && set bitD=%bit3%
endlocal && set bitA=%bitA% && set bitB=%bitB% && set bitC=%bitC% && set bitD=%bitD%
goto :eof

:bootblockbase
setlocal && set * && set mdbase=%mdbase%
set device=%~1
echo -n > (md)%mdbase%+1
dd if=%device%%0+1 of=(md)%mdbase%+1 > nul
set /A base=%mdbase% * 0x200
set /A address=%base%+0xB
read %address% > nul &; set /A bytepsec=%@retval%&0xffff
echo bytepsec=%bytepsec%
set /A address=%base%+0xD
read %address% > nul ;; set /A secpclus=%@retval%&0xff
echo secpclus=%secpclus%
set /A address=%base%+0xE
read %address% > nul ;; set /A reserved=%@retval%&0xffff
echo reserved=%reserved%
set /A address=%base%+0x10
read %address% > nul ;; set /A numfats=%@retval%&0xff
echo numfats=%numfats%
set /A address=%base%+0x11
read %address% > nul ;; set /A rootentr=%@retval%&0xffff
echo rootentr=%rootentr%
set /A address=%base%+0x13
read %address% > nul ;; set /A totsect=%@retval%&0xffff
echo totsect=%totsect%
set /A address=%base%+0x15
read %address% > nul ;; set /A mediabyt=%@retval%&0xff
echo mediabyt=%mediabyt%
set /A address=%base%+0x16
read %address% > nul ;; set /A secpfat=%@retval%&0xffff
echo secpfat=%secpfat%
set /A address=%base%+0x18
read %address% > nul ;; set /A secptrac=%@retval%&0xffff
echo secptrac=%secptrac%
set /A address=%base%+0x1A
read %address% > nul ;; set /A numheads=%@retval%&0xffff
echo numheads=%numheads%
set /A address=%base%+0x1C
read %address% > nul ;; set /A hiddsect=%@retval%&0xffffffff
echo hiddsect=%hiddsect%
if %totsect%==0 && set /A address=%base%+0x20
if %totsect%==0 && read %address% > nul ;; set /A totsect=%@retval%&0xffffffff
echo totsect=%totsect%
set /A address=%base%+0x24
read %address% > nul ;; set /A drivenum=%@retval%&0xff
echo drivenum=%drivenum%
set /A rootbyte=%rootentr% * 32
set /A fatbyte=%numfats% * %secpfat% * %bytepsec%
set /A rsrvbyte=%reserved% * %bytepsec%
set /A root=%fatbyte% + %rsrvbyte%
set /A dataarea=%rootbyte% + %fatbyte% + %rsrvbyte%
set /A clussize=%secpclus% * %bytepsec%
set /A fat1=%rsrvbyte%
set /A fat2=%fatbyte% / 2 + %rsrvbyte%
endlocal && set dataarea=%dataarea% && set clussize=%clussize% && set root=%root% && set fat1=%fat1% && set fat2=%fat2% && set secpfat=%secpfat% && set hiddsect=%hiddsect% && set totsect=%totsect% && set secpclus=%secpclus%
goto :eof

:spread
setlocal && set *
set "message=%~1"
set /a len=%~2 ;; if not exist len && set len=0
if %message%==NAME && set /a spaces=8-%len% && set "char=\x20"
if %message%==EXT && set /a spaces=3-%len% && set "char=\x20"
set c=1
:spreadloop
if %c%<=%spaces% && set "spread=%spread%%%char%" && set /a c=%c%+1 && goto :spreadloop
endlocal && set "spread=%spread%"
goto :eof

#:volumesize
#:NameOfFunction
setlocal && set * && set mdbase=%mdbase%
set root=%@root% && root %~1 > nul && set /a devsect=*0x82B0 &; root=%root% > nul
endlocal && set devsect=%devsect%
goto :eof

:cntvars
#-#+ call :cntvars <cntvars> <basemem> <sectors> [<subroutine>] (max 60 sectors, if needed)
#echo %~0 %*
echo -n > (md)%~2+%~3
set > (md)%~2+%~3
setlocal && set *
echo -n && call Fn.4 ;; set /A r=%@retval% > nul ;; set /a y=%r%>>8 > nul ;; set /a x=%r%&0xFF > nul
cat --locate=\x0A (md)%~2+%~3 > nul ;; set /a result=%@retval% > nul
echo -n > (md)%~2+%~3
:LoopCntVars
set ARG=%~4 &; if not "%ARG%"=="" &; if %ARG:~0,1%==: && set ARG4=%~4 ! shift && goto :LoopCntVars
if exist ARG4 && set "message=Number of variables=%result% in %ARG4%" ! set "message=Number of variables=%result%"
set "message=%message%" ;; set msglen=%@retval% > nul ;; set /a h=79-%msglen% > nul
#set ARG4=%ARG4% ;; set ARG4len=%@retval% > nul ;; set /a h=79-23-4-%ARG4len% > nul
call Fn.5 %h% 24 || echo -n $[0x0F]%message%
#if exist ARG4 && call Fn.5 %h% 24 || echo -n $[0x0F]Number of variables=%result% in %ARG4%
#if not exist ARG4 && call Fn.5 56 24 || echo -n $[0x0F]Number of variables=%result%
#if exist ARG4 && echo -n Number of variables=%result% in %ARG4%
#if not exist ARG4 && echo -n Number of variables=%result%
call Fn.5 %x% %y%
#set
endlocal
#endlocal && set result=%result%
goto :eof

Only if a searched entry is not found in the first cluster of a sub-directory, there is a call to a sub-routine to read-out the FAT-table [subroutine :lookupFAT].

:lookupFAT
setlocal &; set clusnum=%~1 && set FATbytes=2
set /A mdfat=%mdbase%+1 &; set /A fatmem=%mdfat% * 0x200 && set /A fatlen=%secpfat% * 0x200
call Fn.24 %fatmem% 0x00 %fatlen% 
raw dd if=%device%%0+%devsect% of=(md)%mdfat%+%secpfat% bs=1 count=%fatlen% skip=%fat1% > nul && set success=Y
if %success%==Y && set /A address=%clusnum% * %FATbytes% + %fatmem% &; read %address% &; set /A nextclus=%@retval%&0xffff &; if not %nextclus%>=0xffff && set clusnum=%nextclus% ! set clusnum=0
endlocal && set clusnum=%clusnum%
goto :eof

This is the whole sub-routine which seems to work on FAT16 (here without echoos):Functions:
1) Copy entire (first) FAT to memory with dd
2) Setting the right memory address to read-out the FAT-entry that corresponds with the last cluster-number where searching was not succesfull, and then read the hex-value with Grub4dos read
3) Evaluating if this cluster should be searched and continue. Or if it was already the last cluster, the file/ directory is set to 'not found'

But how to manage this on FAT12, with 12-bits values instead of 16-bits is not clear to me. :unsure:

How can I change the bold-written essentail parts of my central script-line to read-out FAT12?

if %success%==Y && set /A address=%clusnum% * %FATbytes% + %fatmem% &; read %address% &; set /A nextclus=%@retval%&0xffff &; if not %nextclus%>=0xffff && set clusnum=%nextclus% ! set clusnum=0

Any help would be greatly appreciated!



#2 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 26 January 2022 - 03:27 PM

If I get this right  :unsure: the issue revolves around:

set /A address=%clusnum% * %FATbytes%

which right now actually resolves to:

set /A address=%clusnum% * 2

and that you cannot have as

set /A address=%clusnum% * 1.5

:dubbio:

 

:duff:

Wonko



#3 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 26 January 2022 - 10:47 PM

Thanks for specifying the problem, I think I found a solution in between.

 

First a print-screen, using the FAT12 lookup-table succesfully:

 

attrib12.g4b with lookupFAT.png

 

BTW before I set the file to Read-only, just for the test.

 

I found out with help of a website about FAT (http://www.c-jump.co...AT/lecture.html), how two clusters-numbers 'share' three bytes in the FAT12 lookup-table. I used FATbyte=1.5 in the following way, with help of the way calc is dividing, so no reminder, (the reminder of calc is only used to determine if the last found cluster-number is even or odd).

Instead of one line I need two lines for the separate cases (first line is 'processed' in case of even-numbered cluster numbers, second line if odd):

 

Line 1

if %success%==Y && calc %clusnum% % 2 |; set /A address=%clusnum% / 2 + %clusnum% + %fatmem% &; read %address% &; set /A nextclus=%@retval%&0xffff &; set nextclus=%nextclus:~2% &; set nextclus=0x%nextclus:~-3,3% &; if %nextclus%>=0xfff && set clusnum=0 ! set clusnum=%nextclus% && set succes=done

 

Line 2

if %success%==Y && calc %clusnum% % 2 &; set /A address=%clusnum% / 2 + %clusnum% + %fatmem% &; read %address% &; set /A nextclus=%@retval%&0xffff &; set nextclus=%nextclus:~2% &; set nextclus=0x%nextclus:~0,-1% &; if %nextclus%>=0xfff && set clusnum=0 ! set clusnum=%nextclus% && set succes=done

 

So the (relative) address of the first byte of a FAT12 three-bytes-unit can now (hopefully) always be derived from the number of even-numbered clusters, and the (relative) address of the second byte from the number of odd-numbered clusters (third byte is only used to read-out next cluster-number, 'coming' from an odd numbered cluster - and is not 'counted').

 

To find the number of the next FAT12-cluster, I used a mixed calc/ text-approach. I do not now how else. :wacko:

 

BTW first nibble of (shared) second byte is the low nibble of the number of the next cluster, 'coming' from an odd-numbered cluster. Second nibble of second byte is the high nibble of the number of the next cluster, 'coming' from an even-numbered cluster.

 

In case of a contiguous file as the first Directory-entry, below a print-screen of the the first lines of the FAT12 loop-table:

 

FAT12 lookup-table next cluster number(s) of a contigous file.jpg

 

So cluster 002 on disk 'points' to FAT12 byte3 (3=2*1.5 for sure) where next cluster-number 003 is read-out. This cluster-number 'points' to next byte4 (4=3*1.5 !!!) and next cluster-number 004 is read-out - all in the same three bytes { 03 [4]0 [00] }.

Cluster 004 'points' to byte6 (6=4*1.5) where following cluster-number 005 can be read-out: { 05 [0]0 [06] } And ad infinitum (=4,096 on FAT12, if I am right).

 

I hope I made no errors, please correct me if I am wrong. Forcing a FAT12-lookup in case of Directory entries is a heavy job on FAT12, and will be more likely with cluster-sizes of one or two sectors, instead eight on the Floppy-image on my test-system. :(

 

 

 

 

 

 

 



#4 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 27 January 2022 - 09:40 AM

I believe it is a little less than 4096 as some values are reserved, possibly 4080.

 

I don't know if it would be easier/faster to create a "fake/virtual" FAT16-like FAT table in memory :unsure:, i.e. translating the 1.5 to 2 bytes entries at the start and then read this table in memory instead of the one on disk.

 

:duff:

Wonko



#5 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 27 January 2022 - 07:13 PM

Thanks :)

 

Can you tell me on which location on disk I can found these 'reserved clusters', or how to calculate them?

So far I understood now, is that Cluster numbering starts at 2, which seems logical because the FAT starts with a FAT-version specific sort of header, (three bytes on FAT12).

 

 

About your idea to 'rewrite' the FAT12-table: how to proceed with the shared byte in the middle of each three bytes?

First nibble belongs to the third byte (lowest four bits), second nibble to the first byte (highest four bits).

 

BTW I do not read-out the FAT-on-disk, but the dd-copy on a memory location. But reading Directory-entries in clusters is directly from disk.



#6 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 28 January 2022 - 10:17 AM

It's not "reserved sectors", it is "reserved values".

Valid numbers are 0x002 to 0xFEF, i.e. 4079-2+1=4078 (I said earlier 4080 as I forgot the 0x000 and 0x001 that are as well reserved), the complete list should be this one:

http://www.brokentho...ces/OSDev6.html

 

 

To better understand this lets look at the possible values:
Value marks free cluster : 0x00
Value marks Reserved cluster : 0x01
This cluster is in use--the value represents next cluster : 0x002 through 0xFEF
Reserved values : 0xFF0 through 0xFF6
Value marks bad clusters : 0xFF7
Value marks this cluster as the last in the file : 0xFF8 through 0xFFF
A FAT is just an array of these values--thats all. When we find the starting sector form the Root Directory, we can look through the FAT to find which clusters to load. How? We simply check the value. If the value is between 0x02 and 0xfef, this value represents the next cluster to load for the file.

 

I odn' tknow if it would be easier/faster to work with the binary operators, or textually, but in the former case, using the notation here:

http://www.c-jump.co...#F01_0210_fat12

 

You read 4 bytes (nn means here *whatever*) on disk:

yz Zx XY nn

 

and you obtain hex value 0xnnXYZxyz

from which you get the two values

0xnnXYZxyz&0x00fff000 -> 0xXYZ

0xnnXYZxyz&0x00000fff -> 0xxyz

that you can write as 0x0XYZ0xyz

resulting on disk (in memory in this case) as

yz 0x YZ 0X

which (if I got the bytes order correct) should be "FAT16" entries.

 

:duff:

Wonko



#7 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 29 January 2022 - 12:11 AM

Thanks a lot for your elaboration and for the details from www.brokenthorn.com  :)

 

About rewriting FAT12 in a FAT16 format: to me it seems your byte order is correct.

It would be an interesting project to convert FAT12 to FAT16 with help of your approach!

 

But to make your conversion of the whole FAT12-table EVERY time attrib12.g4b is used before a read-out?

I think for now I will stick to my two lines for even and odd cluster-numbers.

 

BTW I got rid of the parts where I needed a textual-approach:

Even cluster numbers: read %address% &; set /A nextclus=%@retval%&0x0fff (leading zero in 0fff can be left out)

Odd cluster numbers: read %address% &; set /A nextclus=%@retval%&0xffff>>4

 

Everything seems to work in case of both even AND odd cluster-numbers, according to following test on a floppy-image with 2 sectors per cluster. To find DIR.064-cluster two read-outs of the FAT12-lookup-table needed (see next print-screen):

 

ATTRIBFT lookup FAT12-tabel successfull, both EVEN and ODD cluster-numbers II.jpg

 

BTW ATTRIBFT is already the generalized version with FAT16 and FAT32 included, so slightly different from attrib12.g4b above. I only have to add reading different dates and times, to get full file-information (although: are date and time file-attributes too, in the strict sense? :unsure: )



#8 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 29 January 2022 - 08:37 AM

Yep, your two lines approach is just fine, if it works, it doesn't make any sense to translate the whole table.

 

About attributes, the word is so 1980's :w00t: :ph34r:, the new (since some 30 years) trend is AFAIK to use the portmanteau "metadata" to generically include anything about a file, including date(s)/time(s) and attributes (and permissions).

 

:duff:

Wonko



#9 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 09 February 2022 - 12:41 AM

I have a question about the Long File Name specification, still in behalf of my ATTRIB-script

 

I found very helpful info on https://web.archive....~brainy/lfn.htm but there is something I do not understand.

 

According to the site linked, Directory-entries of a Long File Name (LFN) are splitted in 'batches' of 32 bytes, each with max 13 chars of the file name. So far no problems to use this in my script. My problem is how to interpret the value of the ordinal field at offset 0.

 

If there are max 13 chars in the LFN, the ordinal is the first AND the last, 'emperically' 41h. Can be seen on the first print-screen at address 00034AC0 (echos & cat --hex I used to 'debug' my script). Interesting is LFN 'Program Files' fits one 32-byte 'batch'.

 

ATTRIBFT first Directory Entry of LFN (hd2,0)-Program Files-Dependency Walker I.jpg

 

So far no problems.

 

With the second Directory Entry there are two 'batches' needed to held the LFN 'Dependency Walker', first ordinal is 01h, second and last 42h at address 004A3DE0

 

ATTRIBFT OKE of LFN (hd2,0)-Program Files-Dependency Walker III.jpg

 

I think this is working in case of longer file names up to Fh x 13, so 195 chars, with 4Fh in the last ordinal field. So far untested.

 

But how can I reach the final goal of 255 (or 254?) chars, what should be the value of the last ordinal, and how can I compute this value? :wacko:

 

The site mentions following spec - I do not understand.

 

"(...) since any long filename can be up to 255 bytes long and each entry can hold up to 13 characters, there can only be up to 20 entries of LFN per file. That means it only needs 5 bits (0th bit to 4th bit) of the ordinal field. And with the 6th bit used to mark the last entry (...)"

 

If I am right 20 is binary 10100, so how will the corresponding last ordinal value be? :blink:



#10 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 10 February 2022 - 07:30 AM

Solved, just misread 6th bit!

So I have to add 2 exp 6 = 40h to the value of the last ordinal. Highest value 14h + 40h = 54h

Worked.

ATTRIBFT DONE LFN abcdefg. of 254 chars on (fd0) V.jpg

But my solution to search the whole Long File Name is not very fast. Better would be reading the first part and then calculating the checksum of the short file name 32 bytes 'later' and comparing with the given value at offset 13 (written in each LFN entry).

But so far I do not understand the calculation. Should be CAh for the SFN in the print-screen. So some 8-bit checksum ?

#11 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 10 February 2022 - 09:22 AM

Yes, see here:

https://www.win.tue..../fat/fat-1.html

at point 1.4

 

 

 

Old programs might change directories in ways that can separate the special entries from the ordinary one. To guard against that the special entries have in byte 13 a checksum of the short name:

int i; unsigned char sum = 0;

for (i = 0; i < 11; i++) {
sum = (sum >> 1) + ((sum & 1) << 7); /* rotate */
sum += name[i]; /* add next name byte */
}

:duff:

Wonko



#12 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 10 February 2022 - 10:20 PM

Thanks a lot :)

 

Sadly I am not so good with such programming-notation, AND: how to implement it as grub4dos-script lines...

 

This evening I found following solution, using only a few lines, different for even and for odd numbers (as earlier in this thread).

 

But after one initial success (checksum of ABCDEF~1 = CAh) all my other attempts failed. Solution appears to be to take the lowest 8 bits only after each addition with the hex-value of next char. So far successfully for the 'classic' MS LFN-directiories.

 

See print-screens in case of PROGRA~1 (checksum should be 20h)

 

SFN Program Files crc=20h.jpg Calc crc SFN Program Files crc=20h round down to 8 bits with &0xff.jpg

 

So this possible 'skeleton' appears to be quite simplistic:

set sum=%char1%
calc %sum% % 2 && set /A rotate=%sum%-1/2+128 ;; calc %sum% % 2 || set /A rotate=%sum%/2
set /A sum=%rotate%+%char2%
set /A sum=%sum%&0xff
calc %sum% % 2 && set /A rotate=%sum%-1/2+128
calc %sum% % 2 || set /A rotate=%sum%/2
set /A sum=%rotate%+%char3%
set /A sum=%sum%&0xff
calc %sum% % 2 && set /A rotate=%sum%-1/2+128
calc %sum% % 2 || set /A rotate=%sum%/2
set /A sum=%rotate%+%char4%


etc. up to last addition with %char11%

But maybe there is an easier solution with calc bit-shifting etc?



#13 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 11 February 2022 - 10:52 AM

Something *like* this?

It seemingly works, but of course needs to be tested/adapted.

!BAT
set sum=
call :checksum  00 41 42 43 44 45 46 7E 31 20 20 20

set sum=
call :checksum  00 50 52 4F 47 52 41 7E 31 20 20 20

goto :EOF

:checksum
if "%2"=="" goto :outofloop
set /A first=%sum%>>1 > nul
set /A second=%sum%&1<<7 > nul
shift
set /A sum=%first%+%second%+0x%1&0xFF > nul
goto :checksum

:outofloop
set sum



:duff:

Wonko



#14 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 11 February 2022 - 11:14 PM

This is very good, Thanks so much :baby:

 

I tested your sub-routine in ATTRIBFT.G4B with other Long File Names (just created on a floppy image to run in Vbox) Everything seems to be fine: see first print-screen. Also path consisting of Long File Names shows the right checksums: see second print-screen

 

AttribFT Sub-routine ChecksumSFN of Wonko Microsoft Shared =D8 Start Menu =D1 My documents =D7 V.jpg AttribFT LFN-PATH Sub-routine ChecksumSFN of Wonko Microsoft Shared =D8 Start Menu =D1 My documents =D7 V.jpg

 

Intitially I had some problems to adapt my call to the sub-routine of your standards (with leading 00's). See below for a (nice) picture of my line-editor after changing my own (working) sub-routine with yours.

 

FATTEXT Sub-routine ChecksumSFN of Wonko before my own I.jpg

 

BTW shown date&time + attributes is the default read-out of ATTRIBFT.G4B. Values of last modification (MS-DOS). ACCDATE and Creation date&time on disk are other possibilities of my script, modification of attributes by writing a new attribute-byte to disk too.

 

BTW2 In Windows only Long File Names are showed in Properties (Explorer), same in my beloved Total Commander. :(

 

ATTRIBFT.G4B will become a Loosely Linked Library in the future - I am in need getting file-attributes/properties for another project that is near the limit of 2000 script-lines allready.

Maybe a function like: 'GetSFN' or even 'GetSFNchecksum' would be a nice addition. :unsure:



#15 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 12 February 2022 - 09:05 AM

I think in Windows the (only?) way to have short filenames is "dir /x", which BTW is useful for "queer" and "abnormal" directory names, only as a side-side note:

https://msfn.org/boa...&comment=842843

 

:duff:

Wonko



#16 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 12 February 2022 - 04:15 PM

Thanks, didn't know the DIR /x cmd-option.

 

Looks good:

 

CMD Win10 Dir -X for ATTRIBFT.img.v11_Tablet.img.jpg

 

I played a bit with removing the checksum from Long File Name entries to explore Windows (10) FAT-drivers' behavior.

 

In case of LFN 'zyxwvutsrqponmlkjihgfedcba' WITHOUT the -two- checksum-bytes, Explorer found/displayed the SFN 'ZYXWVU~1' only. After rewriting the checksum-bytes back (82h in this case), Explorer was happily showing the full LFN again.

 

EXPLORER on REPAIRED LFN without ChecksumSFN gives SFN only on (fd0)-attribft.img.v011_tablet.img.jpg CAT REPAIR checksum =82 in LFN on (fd0)-attribft.img.v011_tablet.img.jpg EXPLORER on REPAIRED LFN WITH ChecksumSFN gives SFN only on (fd0)-attribft.img.v011_tablet.img.jpg

 

To me it seems comparing the calculated Short File Name checksum with byte 13 in each LFN-entry is unevitable. Which is sad: will take much more time to read out?



#17 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 21 February 2022 - 11:22 PM

About attributes, the word is so 1980's :w00t: :ph34r:, the new (since some 30 years) trend is AFAIK to use the portmanteau "metadata" to generically include anything about a file, including date(s)/time(s) and attributes (and permissions).

 

:duff:

Wonko

 

I finally decided to extent the meaning of attributes (or metadata) to Long File Names. :)

 

So far I could find (in internet sources and while watching the results of Windows 10 fat-driver on my 360KB home-made floppy-image), Long File Names entries are always written DIRECTLY before the corresponding Short File Name entry, with the SFN-checksum as verifying 'intermediate'. Although Long File Names can 'cross' cluster-borders, they are still in one 'row' if reading the clusters in the right order.

 

But not everything is clear to me. For instance, directories written with Explorer are giving after first four 'normal' Short File Names two different ones with abbreviations I do not understand clearly. To me it seems these two are not according to the windows 95 spec's.

 

To illustrate this, see the four print-screens. On the first results of fat dir and ls regarding the Short and Long File Names (ls filters for names starting with 'v', fat dir can't - so neglect all NEWLON~).

 

Second print-screen shows deleting the last Long File Name attribute using ATTRIBFT.LLL with the function dellfn to delete all directories starting with 'v' (one-by-one) Don't mind the 'debug'-output.

 

The Short File Names that remain are seen on third print-screen (can be safely deleted afterwards with fat del or re-used to make new Long file Name attributes to the Short File Name directories).

 

Afterwards I deleted the Short File Names too with Explorer and made six new ones (output of dir /x can bee seen in last print-screen). Same pattern: first four 'normal' than two abbreviations close to the earlier ones (even with slightly different numbering).

 

TEST ATTRIBFT DELLFN '(fd0)-NEWLON~3-very long folder name to test behavior of Windows 10 fat driver' up to 6 gives different coded SFN~s after VERYLO~4 I.jpg TEST ATTRIBFT DELLFN '(fd0)-NEWLON~3-very long folder name to test behavior of Windows 10 fat driver 6' SFN VE537F~1 left afterwards II.jpg TEST ATTRIBFT after DELLFN '(fd0)-NEWLON~3-very long folder name to test behavior of Windows 10 fat driver' both fat dir & ls gives 6 different coded SFN~s after VERYLO~4 III.jpg STUDY ATTRIBFT DELLFN '(fd0)-NEWLON~3-very long folder name to test behavior of Windows 10 fat driver' up to 6 gives different coded SFN~s after VERYLO~4 II.jpg

 

Or is Windows NT making different Short File Name abbreviations in case of those almost equal Long File Names?

 

And if: how, and why first four 'normal' ones? :blink:
 



#18 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 22 February 2022 - 10:23 AM

The behaviour has been observed (and it is not limited to FAT but extends to NTFS) but AFAIK there is not documentation on how the short file names beyond the first few ones are generated, and this may cause issues in some copy operations in Windows, with additionally differences between NT and DOS/W9x/Me JFYI:

 

https://guyrleech.wo...t-names-primer/

 

https://en.wikipedia...ki/8.3_filename

 

The algorithm/hashing has been reverse engineered, but - given that it is correct - may well vary on different MS OS's:

 

https://web.archive..../09/filenames/ 

 

:duff:

Wonko

 

.



#19 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 23 February 2022 - 12:40 AM

Thanks for the story of the undocumented hash, I vaguely remember.

 

But the hash seems to be documented in between. With Google I found https://tomgalvin.uk...6/09/filenames/

 

There is a link to following c-program:

 

Screenshot_8dot3-checksum.c.png

 

I made a quick script: LFNHASH.G4B

!BAT
#-#+ LFNHASH.G4B v.01 (20220222), by deomsh
#-# Thomas Galvin's '8dot3-checksum.c' rewritten to grub4dos script

::/* A checksum calculator for Windows 8.3 filenames.
:: * The relevant post can be found at http://usn.pw/blog/cs/2015/06/09/filenames/
:: * Written by Thomas Galvin. */

debug msg=0
#if /i %~2==echo && echo "c:\SPN>ShortPathName "a.txt3" AEE90~1.TXT c:\SPN>Checksum "a.txt3" ee90"
#if /i %~2==echo && echo "c:\SPN>ShortPathName a.txt7 AB720~1.TXT c:\SPN>Checksum a.txt7 b720"

#-# ASCII: 'a = 61' '. = 2E' 't = 74' 'x = 78' '3 = 33'
#-# ASCIi: '7 = 37'

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

if "%~1"=="" && pager on && cat --skip=5 /%0 && goto :eof
pause --wait=0 %~1 > (md)0x300+1
cat --locate=\x0A --number=1 (md)0x300+1 > nul &; set /a n=%?%
set /A address=0x300 * 0x200

#OKE#set char1=0x61 && set char2=0x2E && set char3=0x74 && set char4=0x78 && set char5=0x74 && set char6=0x37

::uint16_t chksum(wchar_t * name)
::{
::	int i;
::	uint16_t checksum = 0;
::	for (i = 0; name[i]; i++) {
::		checksum = checksum * 0x25 + name[i];
::	}

set checksum=0 && set c=1
:readcharchecksumloop
read %address% > nul &; set /A char=%@retval%&0xff
if exist char && call set /A checksum=0x25*%checksum%+%char%&0xffff
set /A address=%address%+0x1 && set /a c=%c%+1 && set char=
if %c%<=%n% && goto :readcharchecksumloop

::	int32_t temp = checksum * 314159269;

calc 314159269*%checksum% &; set /A temp1=%@retval%
#SAME RESULT#calc 314159269*%checksum% &; set /A temp1=%@retval%&0xffffffff
#BAD?#set /A temp1=314159269*%checksum%&0xffffffff
if /i %~2==echo && echo 1 temp1=%temp1%

::	if (temp < 0) temp = -temp;

if %temp1%<=0 && set /A temp1=-1*%temp1%
if /i %~2==echo && echo 2 temp1=%temp1%

::	temp -= ((uint64_t)((int64_t)temp * 1152921497) >> 60) * 1000000007;

#-# 'minus=' in 'temp -=' ???
## assuming uint64 value will not be needed (value <= 8 bytes), OR if calc output is positive, same result as ((uint64)(int64 ...) ?

set /A temp2=1152921497*%temp1%&0xffffffffffffffff>>60
if /i %~2==echo && echo 3 temp2=%temp2%
set /A temp3=1000000007*%temp2%
if /i %~2==echo && echo 4 temp3=%temp3%

## but if 'C -= A is equivalent to C = C - A' 

set /A temp=%temp1%-%temp3%
if /i %~2==echo && echo 5 temp=%temp%

::	checksum = temp;

set checksum=%temp%
if /i %~2==echo && echo checksum=%checksum%

::	// reverse nibble order
::	checksum =
::		((checksum & 0xf000) >> 12) |

set /A nibble0=%checksum%&0xf000>>12
if /i %~2==echo && echo nibble0=%nibble0%

::		((checksum & 0x0f00) >> 4) |

set /A nibble1=%checksum%&0x0f00>>4
if /i %~2==echo && echo nibble1=%nibble1%

::		((checksum & 0x00f0) << 4) |

set /A nibble2=%checksum%&0x00f0<<4
if /i %~2==echo && echo nibble2=%nibble2%

::		((checksum & 0x000f) << 12);

set /A nibble3=%checksum%&0x000f<<12
if /i %~2==echo && echo nibble3=%nibble3%

::	return checksum;
::}

set /A checksum=%nibble3%+%nibble2%+%nibble1%+%nibble0%
set checksum=0000%checksum:~2%
set checksum=%checksum:~-4,4%
echo Final checksum=%checksum%

::int main(int argc, char * argv[]) {
::	int length = strlen(argv[1]); 
::	wchar_t * wide_filename = malloc(sizeof(wchar_t) * (length + 1));
::	mbstowcs(wide_filename, argv[1], length);
::	printf("%x\n", chksum(wide_filename));
::	free(wide_filename);
::	return 0;
::}

debug msg=3

Seems to work, see following print-screens

 

LFNHASH test names I.jpg LFNHASH verify test names II.jpg

BTW last two hashes can be seen in my post before (last print-screen).

 

Edit: two corrections in the script. 1) after cat /%0 && goto :eof (not vital). 2) In first test ASCII's: dot is 2E, not 20 (not used anymore in thee script)



#20 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 23 February 2022 - 11:02 AM

Yep, the page you found is the new site for the same page I linked to via Wayback Machine, and though interesting and useful, it doesn't actually "cover" different versions of DOS and Windows (let alone other relatively common OS's that can write to FAT, the multi-zillion Linux distros, FreeDOS, OS/2, ReactOS, etc.).

 

So the (nice BTW) reverse engineering produced a hashing algorithm that is surely correct in some (most?) cases but - not being a documented feature or a specification - might well be completely off in some other cases.

 

:duff:

Wonko



#21 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 23 February 2022 - 02:44 PM

Hmm... :unsure:

 

When I was reading your post I saw first two links only, and started reading directly. Maybe I missed the last part of your post. :ph34r:

 

I still have questions about the 'temp'-part of my quick script to rewrite the c-lines.

 

1) is int32_t the same number-format as %@retval% and uint32_t same as %@retval%&0xffffffff ? They gave different results.

 

2) what is the function of ((uint64_)((int32_t) etc. )? 

I asked my oldest programmer son, he said that in this case some 'overrun' would occur.

But I observed calc happily calculating, maybe because it is 128-bit?



#22 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 23 February 2022 - 04:51 PM

No idea, sorry. :(

 

In grub4dos (in recent versions) some commands return/use 64 bit (8 bytes) numbers/values and some 32 bit (4 bytes) ones, so &0xFFFFFFFF is probably needed to make sure-sure that  the value is 32 bit, from your script it seems to come out that the @retval is already 32 bit, but I cannot see at first sight why what you report as "bad" is actually "bad", i.e. why calc and SET /A seemingly give different results, as they should be the same :dubbio:

 

 

calc 314159269*%checksum% &; set /A temp1=%@retval%
#SAME RESULT#calc 314159269*%checksum% &; set /A temp1=%@retval%&0xffffffff
#BAD?#set /A temp1=314159269*%checksum%&0xffffffff

 

:duff:

Wonko



#23 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 23 February 2022 - 08:12 PM

I have the habit to heap my lines during scripting, newer, -better- lines before -bad/ not so good- older lines.

 

Results of lines

 

#SAME RESULT#calc 314159269*%checksum% &; set /A temp1=%@retval%&0xffffffff
#BAD?#set /A temp1=314159269*%checksum%&0xffffffff

 

are the same.

 

But actual line

 

calc 314159269*%checksum% &; set /A temp1=%@retval%

 

gives differents results, and is needed to get the 'right' Long File Name Hash (right in -at least- the MS Win10 sense).

 

I started my investigation with Thomas Galvin's examples: a.txt3 and a.txt7

In these cases the last 32 bits of the multiplication of the first checksum with the pi-based first number are below 0x80000000 and results are the same (last two calculations in the first print-screen with debug msg=3 for thes lines).

 

But in case of Long Folder Name 5 results are not the same anymore and gives only %@retval% WITHOUT 0xffffffff the right first temp (first three calculations in first print-screen).

 

To be sure, I uploaded results of DIR /X again (second print-screen).

 

 

LFNHASH int32_t or uint32_t vs %@retval% or %@retval%&0xffffffff I.jpg DIR -X LFN_hash II.jpg

 

BTW first 'good' line is run with argument 'int32' and second/ third line with uint32A/ uint32B.

 

I think calc makes always same calculation, but (negative) output of %@retval% in first line makes the difference.

 

what do you think? :unsure:



#24 Wonko the Sane

Wonko the Sane

    The Finder

  • Advanced user
  • 16066 posts
  • Location:The Outside of the Asylum (gate is closed)
  •  
    Italy

Posted 24 February 2022 - 07:40 AM

Yes, so if I get it right it is not a calc vs. set /A issue, but rather the @retval that returns a different value, the 0x80000000 + values are interpreted as signed (like it happens in DOS/Windows batch), it makes sense, as the grub4dos batch is modeled after the Windows NT one.

 

:duff:

Wonko



#25 deomsh

deomsh

    Frequent Member

  • Advanced user
  • 196 posts
  •  
    Netherlands

Posted 24 February 2022 - 12:14 PM

Great! :)

 

Does this imply %@retval% returns the (signed) int32 value while %@retval%&0xffffffff returns the (unsigned) uint32 value?

 

This is quite important in case of some of my projects. Uptil now I had to use the textual output of cat --length=0 FILE to determine a >=2GB filesize (0x80000000+).

 

I did following small experiment on a 8GB vhd in Vbox, can be seen on the print-screen.

 

1) Create a 3GB file with fatmini (fat has a bug on 4GB+ disks - max free space is 4GB, otherwise can be wrong - I reported earlier, still unsolved).

2) Read out filesize: textual, and with %@retval% signed/ unsigned.

3) As an illustration output of FATLSDIR.G4B (still unpublished) in ls mode and in fat dir mode (in this case both modes of FATLSDIR.G4B are using textual cat-output, only textual date/ time output of fat dir is used because filesize is rounded down to KB by fat dir).

 

STUDY signed and unsigned 32-bits output of %@retval% I.jpg







Also tagged with one or more of these keywords: grub4dos

0 user(s) are reading this topic

0 members, 0 guests, 0 anonymous users