Skip to content

Commit

Permalink
FAT12 parsing bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
morphx666 committed Jun 24, 2020
1 parent 04d9aee commit d1e9b8c
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 49 deletions.
130 changes: 85 additions & 45 deletions x8086NetEmu/Adapters/Disk/FileSystem/StandardDiskFormat.vb
Expand Up @@ -106,6 +106,8 @@ Public Class StandardDiskFormat
Private ReadOnly strm As IO.Stream
Private ReadOnly FATRegionStart(4 - 1) As Long

Private fatEOF As Integer

Public Sub New(s As IO.Stream)
Dim pb As GCHandle
Dim b(512 - 1) As Byte
Expand All @@ -125,6 +127,12 @@ Public Class StandardDiskFormat
Else
LoadAsHardDiskImage()
End If

Select Case mBootSectors(0).ExtendedBIOSParameterBlock.FileSystemType
Case "FAT12" : fatEOF = &HFF8
Case "FAT16" : fatEOF = &HFFF8
Case Else : mMasterBootRecord.Partitions(0).SystemId = SystemIds.EMPTY
End Select
End Sub

Private Sub LoadAsFloppyImage()
Expand Down Expand Up @@ -180,11 +188,11 @@ Public Class StandardDiskFormat
pb = GCHandle.Alloc(b, GCHandleType.Pinned)

Select Case mMasterBootRecord.Partitions(partitionNumber).SystemId
Case StandardDiskFormat.SystemIds.FAT_12
Case SystemIds.FAT_12
mBootSectors(partitionNumber) = Marshal.PtrToStructure(pb.AddrOfPinnedObject(), GetType(FAT12.BootSector))
Case StandardDiskFormat.SystemIds.FAT_16
Case SystemIds.FAT_16
mBootSectors(partitionNumber) = Marshal.PtrToStructure(pb.AddrOfPinnedObject(), GetType(FAT16.BootSector))
Case StandardDiskFormat.SystemIds.FAT_BIGDOS
Case SystemIds.FAT_BIGDOS
mBootSectors(partitionNumber) = Marshal.PtrToStructure(pb.AddrOfPinnedObject(), GetType(FAT32.BootSector))
Case SystemIds.EMPTY
pb.Free()
Expand All @@ -197,35 +205,24 @@ Public Class StandardDiskFormat
End Sub

Private Sub ReadFAT(partitionNumber As Integer)
Dim b1 As UInt16
Dim b2 As UInt16
Dim b3 As UInt16

FATRegionStart(partitionNumber) = strm.Position

ReDim mFATDataPointers(partitionNumber)(CUInt(mBootSectors(partitionNumber).BIOSParameterBlock.SectorsPerFAT) * mBootSectors(partitionNumber).BIOSParameterBlock.BytesPerSector / 2 - 1)
For j As Integer = 0 To mFATDataPointers(partitionNumber).Length - 1
Select Case mMasterBootRecord.Partitions(partitionNumber).SystemId ' mBootSectors(partitionNumber).ExtendedBIOSParameterBlock.FileSystemType
Case StandardDiskFormat.SystemIds.FAT_12 ' TODO: There has to be a simpler way to parse all 12bit addresses
Dim b1 As Byte = strm.ReadByte()
Dim b2 As Byte = strm.ReadByte()
Dim n As UInt16

If j = 0 Then
n = BitConverter.ToUInt16({b1, b2}, 0)
Else
If b1 = &HFF AndAlso b2 = &HF Then ' FIXME: FAT 12 fluggly hack #1!
n = &HFFFF
Else
If j Mod 2 = 0 Then
b2 = b2 And &HF
Else
b1 = (b1 And &HF0) >> 4
End If

n = b1 + (b2 << 12)
If n >= &HF8 Then n = &HFFFF ' FIXME: FAT 12 fluggly hack #2!
End If
End If
Select Case mMasterBootRecord.Partitions(partitionNumber).SystemId
Case StandardDiskFormat.SystemIds.FAT_12 ' Wish I had found this before: http://www.c-jump.com/CIS24/Slides/FAT/lecture.html
b1 = strm.ReadByte()
b2 = strm.ReadByte()
b3 = strm.ReadByte()

mFATDataPointers(partitionNumber)(j + 0) = (b2 And &HF) << 8 Or b1
mFATDataPointers(partitionNumber)(j + 1) = b3 << 4 Or b2 >> 4

mFATDataPointers(partitionNumber)(j) = n
If j Mod 2 = 0 Then strm.Position -= 1
j += 1
Case StandardDiskFormat.SystemIds.FAT_16
mFATDataPointers(partitionNumber)(j) = BitConverter.ToUInt16({strm.ReadByte(), strm.ReadByte()}, 0)
Case StandardDiskFormat.SystemIds.FAT_BIGDOS
Expand All @@ -249,7 +246,7 @@ Public Class StandardDiskFormat
Dim dirEntryCount As Integer = -1
Dim de As Object = Nothing

While clusterIndex < &HFFF8
While clusterIndex < fatEOF
If clusterIndex = -1 Then
strm.Position = FATRegionStart(partitionNumber) + mBootSectors(partitionNumber).BIOSParameterBlock.NumberOfFATCopies * mFATDataPointers(partitionNumber).Length * 2
Else
Expand Down Expand Up @@ -306,14 +303,13 @@ Public Class StandardDiskFormat
Dim clusterIndex As UInt16 = de.StartingClusterValue
Dim bytesRead As Long

While clusterIndex < &HFFF8 AndAlso bytesRead < de.FileSize
While clusterIndex < fatEOF AndAlso bytesRead < de.FileSize
strm.Position = ClusterIndexToSector(partitionNumber, clusterIndex)

Do
b(bytesRead) = strm.ReadByte()
bytesRead += 1

'If bytesRead >= de.FileSize AndAlso (bytesRead Mod bytesInCluster) = 0 Then
If (bytesRead Mod bytesInCluster) = 0 Then
clusterIndex = mFATDataPointers(partitionNumber)(clusterIndex)
Exit Do
Expand All @@ -325,13 +321,13 @@ Public Class StandardDiskFormat
Return b
End Function

Public Sub WriteFile(partitionNumber As Integer, de As Object, file As IO.FileInfo)
Public Sub WriteFile(partitionNumber As Integer, parentFolder As Object, file As IO.FileInfo)
Dim bytesInCluster As UInt16 = mBootSectors(partitionNumber).BIOSParameterBlock.SectorsPerCluster * mBootSectors(partitionNumber).BIOSParameterBlock.BytesPerSector
Dim clustersInFile As UInt32
Dim pb As GCHandle
Dim b(32 - 1) As Byte
Dim bytesWritten As UInt32
Dim clusterIndex As Integer = If(de?.StartingCluster = 0, -1, de.StartingCluster)
Dim clusterIndex As Integer = If(parentFolder?.StartingCluster = 0, -1, parentFolder.StartingCluster)
Dim foundEmptyDirectoryEntry As Boolean = False

Dim FindEmptyCluster = Function()
Expand All @@ -345,7 +341,7 @@ Public Class StandardDiskFormat
End Function

' Find empty directory entry
While clusterIndex < &HFFF8
While clusterIndex < fatEOF
If clusterIndex = -1 Then
strm.Position = FATRegionStart(partitionNumber) + mBootSectors(partitionNumber).BIOSParameterBlock.NumberOfFATCopies * mFATDataPointers(partitionNumber).Length * 2
Else
Expand Down Expand Up @@ -426,13 +422,8 @@ Public Class StandardDiskFormat
End While

mFATDataPointers(partitionNumber)(clusterIndex) = &HFFFF
UpdateFATTables(partitionNumber)

' Write new FAT table
For i As Integer = 0 To mBootSectors(partitionNumber).BIOSParameterBlock.NumberOfFATCopies - 1
WriteNewFATTable(partitionNumber, FATRegionStart(partitionNumber) + i * mFATDataPointers(partitionNumber).Length * 2)
Next

strm.Flush()
src.Close()
End Using
Else
Expand All @@ -441,7 +432,60 @@ Public Class StandardDiskFormat
End If
End Sub

Private Sub WriteNewFATTable(partitionNumber As Integer, fatTableStart As Long)
Public Sub DeleteFile(partitionNumber As Integer, parentFolder As Object, de As Object)
Dim pb As GCHandle
Dim b(32 - 1) As Byte
Dim bytesInCluster As UInt32 = mBootSectors(partitionNumber).BIOSParameterBlock.SectorsPerCluster * mBootSectors(partitionNumber).BIOSParameterBlock.BytesPerSector
Dim clustersInFile As UInt32 = Math.Ceiling(de.FileSize / bytesInCluster)
Dim clusterIndex As Integer = de.StartingClusterValue
Dim tmpFile As Object = Nothing

While clusterIndex < fatEOF
Dim nextClusterIndex = mFATDataPointers(partitionNumber)(clusterIndex)
mFATDataPointers(partitionNumber)(clusterIndex) = 0
clusterIndex = nextClusterIndex
End While

clusterIndex = If(parentFolder?.StartingCluster = 0, -1, parentFolder.StartingCluster)
While clusterIndex < fatEOF
If clusterIndex = -1 Then
strm.Position = FATRegionStart(partitionNumber) + mBootSectors(partitionNumber).BIOSParameterBlock.NumberOfFATCopies * mFATDataPointers(partitionNumber).Length * 2
Else
strm.Position = ClusterIndexToSector(partitionNumber, clusterIndex)
End If

Do
strm.Read(b, 0, b.Length)
pb = GCHandle.Alloc(b, GCHandleType.Pinned)
Select Case mMasterBootRecord.Partitions(partitionNumber).SystemId
Case SystemIds.FAT_12, SystemIds.FAT_16
tmpFile = Marshal.PtrToStructure(pb.AddrOfPinnedObject(), GetType(FAT12.DirectoryEntry))
Case SystemIds.FAT_BIGDOS
tmpFile = Marshal.PtrToStructure(pb.AddrOfPinnedObject(), GetType(FAT32.DirectoryEntry))
End Select
pb.Free()

If tmpFile.FullFileName = de.FullFileName Then
strm.Position -= b.Length
strm.WriteByte(0)

UpdateFATTables(partitionNumber)
Exit Sub
End If
Loop

If clusterIndex = -1 Then Exit While
End While
End Sub

Private Sub UpdateFATTables(partitionNumber As Integer)
For i As Integer = 0 To mBootSectors(partitionNumber).BIOSParameterBlock.NumberOfFATCopies - 1
UpdateFATTable(partitionNumber, FATRegionStart(partitionNumber) + i * mFATDataPointers(partitionNumber).Length * 2)
Next
strm.Flush()
End Sub

Private Sub UpdateFATTable(partitionNumber As Integer, fatTableStart As Long)
Dim b() As Byte
Dim nibbles(6 - 1) As UInt16
Dim f As Boolean = True
Expand All @@ -451,7 +495,7 @@ Public Class StandardDiskFormat
For j As Integer = 0 To mFATDataPointers(partitionNumber).Length - 1
Select Case mMasterBootRecord.Partitions(partitionNumber).SystemId
Case StandardDiskFormat.SystemIds.FAT_12
b = BitConverter.GetBytes(mFATDataPointers(partitionNumber)(j) And &HFFFF)
b = BitConverter.GetBytes(mFATDataPointers(partitionNumber)(j))

If f Then
nibbles(0) = (b(0) And &HF0) >> 4
Expand All @@ -465,10 +509,6 @@ Public Class StandardDiskFormat
' Save
b(0) = nibbles(0) << 4 Or nibbles(1)
b(1) = nibbles(5) << 4 Or nibbles(3)
If b(0) = 239 Then ' FIXME: FAT 12 fluggly hack #3!
b(0) = 255
b(1) = If(b(1) = 0, 15, 255)
End If
strm.Write(b, 0, 2)

b(0) = nibbles(2) << 4 Or nibbles(4)
Expand Down
2 changes: 2 additions & 0 deletions x8086NetEmu/x8086.vb
Expand Up @@ -432,6 +432,8 @@ Public Class X8086
instrucionsCounter += 1

opCode = RAM8(mRegisters.CS, mRegisters.IP)

'If mRegisters.IP = &H100 Then DebugMode = True
End Sub

Private Sub Execute_DEBUG()
Expand Down
41 changes: 37 additions & 4 deletions x8086NetEmuWinForms/Tools/FormDiskExplorer.vb
Expand Up @@ -8,6 +8,10 @@ Public Class FormDiskExplorer
Private isLeftMouseDown As Boolean
Private mouseDownLocation As Point

' Alpha features
Private showDropWarning As Boolean = True
Private showDeleteWarning As Boolean = True

Public Sub Initialize(fileName As String)
sdf = New StandardDiskFormat(New IO.FileStream(fileName, IO.FileMode.Open, IO.FileAccess.ReadWrite, IO.FileShare.ReadWrite))

Expand Down Expand Up @@ -332,23 +336,22 @@ Public Class FormDiskExplorer
End If
End Sub

Private showWarning As Boolean = True
Private Sub ListViewFileSystem_DragDrop(sender As Object, e As DragEventArgs) Handles ListViewFileSystem.DragDrop
If e.Effect = DragDropEffects.Copy Then
Dim node As TreeNode = TreeViewDirs.SelectedNode
Dim de As Object = node.Tag ' Parent folder
Dim files() As String = CType(e.Data.GetData("FileDrop"), String())

If showWarning Then
If showDropWarning Then
If MsgBox("This feature is still under heavy development and using it may corrupt your disk images." + Environment.NewLine +
"Are you sure you want to use it anyway?", MsgBoxStyle.YesNo Or MsgBoxStyle.Question) = MsgBoxResult.Yes Then
showWarning = False
showDropWarning = False
Else
Exit Sub
End If
End If

For i = 0 To files.Length - 1
For i = 0 To files.Length - 1
If Not IO.File.Exists(files(i)) Then
MessageBox.Show("Dropping directories is not yet supported", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
Exit Sub
Expand All @@ -372,6 +375,36 @@ Public Class FormDiskExplorer
End If
End Sub

Private Sub ListViewFileSystem_KeyDown(sender As Object, e As KeyEventArgs) Handles ListViewFileSystem.KeyDown
If ListViewFileSystem.SelectedItems.Count = 0 Then Exit Sub

If e.KeyCode = Keys.Delete Then
If showDeleteWarning Then
If MsgBox("This feature is still under heavy development and using it may corrupt your disk images." + Environment.NewLine +
"Are you sure you want to use it anyway?", MsgBoxStyle.YesNo Or MsgBoxStyle.Question) = MsgBoxResult.Yes Then
showDeleteWarning = False
Else
Exit Sub
End If
End If
End If

Dim de As Object
Dim itemsToDelete As New List(Of Object)
For Each item As ListViewItem In ListViewFileSystem.SelectedItems
de = CType(item.Tag, Object())(1)
If (de.Attribute And FAT12.EntryAttributes.Directory) = FAT12.EntryAttributes.Directory Then
MsgBox("Deleting dicretories is not yet supported")
Exit Sub
End If
itemsToDelete.Add(de)
Next

Dim node As TreeNode = TreeViewDirs.SelectedNode
de = node.Tag ' Parent folder
itemsToDelete.ForEach(Sub(itd) sdf.DeleteFile(selectedParitionIndex, de, itd))
End Sub

'Private Sub ListViewFileSystem_MouseMove(sender As Object, e As MouseEventArgs) Handles ListViewFileSystem.MouseMove
' If isMouseDown Then
' Dim filesCount As Integer = ListViewFileSystem.SelectedItems.Count
Expand Down

0 comments on commit d1e9b8c

Please sign in to comment.