Skip to content

Commit 956aafd

Browse files
committed
Refactor TD0 format code to use mutable classes
Convert TD0 structures (header, sector header, data header, track header, comment) to mutable classes with full property setters. Update all TD0 image handling code to use these new classes, enabling easier creation, editing, and writing of TD0 images. Add enums for data rate, drive type, and stepping. Improve flag handling, CRC logic, and debug output. Bump application version. This refactor improves maintainability and prepares for advanced TD0 editing features.
1 parent 0e66e91 commit 956aafd

File tree

13 files changed

+401
-205
lines changed

13 files changed

+401
-205
lines changed

DiskImageTool/DiskImageTool.vbproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<TargetCulture>en-US</TargetCulture>
3131
<ProductName>DiskImageTool</ProductName>
3232
<PublisherName>Digitoxin</PublisherName>
33-
<ApplicationRevision>0</ApplicationRevision>
33+
<ApplicationRevision>1</ApplicationRevision>
3434
<ApplicationVersion>2.34.0.%2a</ApplicationVersion>
3535
<UseApplicationTrust>false</UseApplicationTrust>
3636
<PublishWizardCompleted>true</PublishWizardCompleted>

DiskImageTool/ImageFormats/TD0/TD0Comment.vb

Lines changed: 78 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
Namespace ImageFormats.TD0
44
Public NotInheritable Class TD0Comment
55
Public Const HEADER_LENGTH As Integer = 10
6-
Private Const CRC_START As Integer = Offset.DataLength
76
Private Const CRC_END As Integer = Offset.Second
8-
9-
Private ReadOnly _data As Byte()
7+
Private Const CRC_START As Integer = Offset.DataLength
8+
Private _data As Byte()
109
Private ReadOnly _header As Byte()
1110

1211
Private Enum Offset As Integer
@@ -20,6 +19,11 @@ Namespace ImageFormats.TD0
2019
Second = 9
2120
End Enum
2221

22+
Public Sub New()
23+
_header = New Byte(HEADER_LENGTH - 1) {}
24+
_data = Array.Empty(Of Byte)()
25+
End Sub
26+
2327
Public Sub New(imagebuf As Byte(), offset As Integer)
2428
_header = New Byte(HEADER_LENGTH - 1) {}
2529
_data = Array.Empty(Of Byte)()
@@ -48,45 +52,46 @@ Namespace ImageFormats.TD0
4852
End If
4953
End Sub
5054

51-
Public Function GetBytes() As Byte()
52-
Dim totalLen As Integer = TotalLength
53-
Dim buf(totalLen - 1) As Byte
54-
' header
55-
Array.Copy(_header, 0, buf, 0, HEADER_LENGTH)
56-
' data
57-
Array.Copy(_data, 0, buf, HEADER_LENGTH, _data.Length)
58-
59-
Return buf
60-
End Function
61-
6255
Public ReadOnly Property DataLength As Integer
6356
Get
6457
Return ReadUInt16LE(_header, Offset.DataLength)
6558
End Get
6659
End Property
6760

68-
Public ReadOnly Property Day As Integer
61+
Public Property Day As Integer
6962
Get
7063
Return _header(Offset.Day)
7164
End Get
65+
Set(value As Integer)
66+
_header(Offset.Day) = CByte(value)
67+
End Set
7268
End Property
7369

74-
Public ReadOnly Property Hour As Integer
70+
Public Property Hour As Integer
7571
Get
7672
Return _header(Offset.Hour)
7773
End Get
74+
Set(value As Integer)
75+
_header(Offset.Hour) = CByte(value)
76+
End Set
7877
End Property
7978

80-
Public ReadOnly Property Minute As Integer
79+
Public Property Minute As Integer
8180
Get
8281
Return _header(Offset.Minute)
8382
End Get
83+
Set(value As Integer)
84+
_header(Offset.Minute) = CByte(value)
85+
End Set
8486
End Property
8587

86-
Public ReadOnly Property Month As Integer
88+
Public Property Month As Integer
8789
Get
8890
Return _header(Offset.Month0) + 1
8991
End Get
92+
Set(value As Integer)
93+
_header(Offset.Month0) = CByte(value - 1)
94+
End Set
9095
End Property
9196

9297
Public ReadOnly Property RawData As Byte()
@@ -95,10 +100,13 @@ Namespace ImageFormats.TD0
95100
End Get
96101
End Property
97102

98-
Public ReadOnly Property Second As Integer
103+
Public Property Second As Integer
99104
Get
100105
Return _header(Offset.Second)
101106
End Get
107+
Set(value As Integer)
108+
_header(Offset.Second) = CByte(value)
109+
End Set
102110
End Property
103111

104112
Public ReadOnly Property StoredCrc16 As UShort
@@ -107,14 +115,28 @@ Namespace ImageFormats.TD0
107115
End Get
108116
End Property
109117

110-
Public ReadOnly Property Text As String
118+
Public Property Text As String
111119
Get
112120
If _data Is Nothing OrElse _data.Length = 0 Then
113121
Return ""
114122
End If
115123
' NUL-terminated lines, convert NUL to CRLF
116124
Return Encoding.ASCII.GetString(_data).Replace(ChrW(0), vbCrLf).Trim()
117125
End Get
126+
Set(value As String)
127+
If String.IsNullOrEmpty(value) Then
128+
_data = Array.Empty(Of Byte)()
129+
Else
130+
' Normalize line endings, then convert CR/LF to NULs
131+
Dim normalized As String = value.Replace(vbCrLf, vbLf).Replace(vbCr, vbLf)
132+
133+
Dim asciiBytes As Byte() = Encoding.ASCII.GetBytes(normalized.Replace(vbLf, ChrW(0)))
134+
135+
_data = asciiBytes
136+
End If
137+
138+
WriteUInt16LE(_header, Offset.DataLength, _data.Length)
139+
End Set
118140
End Property
119141

120142
Public ReadOnly Property TotalLength As Integer
@@ -123,17 +145,16 @@ Namespace ImageFormats.TD0
123145
End Get
124146
End Property
125147

126-
Public ReadOnly Property Year As Integer
148+
Public Property Year As Integer
127149
Get
128150
' year since 1900
129151
Return 1900 + _header(Offset.Year)
130152
End Get
153+
Set(value As Integer)
154+
_header(Offset.Year) = CByte(value - 1900)
155+
End Set
131156
End Property
132157

133-
Public Function CrcValid() As Boolean
134-
Return StoredCrc16 = ComputedCrc16()
135-
End Function
136-
137158
Public Function ComputedCrc16() As UShort
138159
Dim crc As UShort = 0US
139160

@@ -149,6 +170,21 @@ Namespace ImageFormats.TD0
149170
Return crc
150171
End Function
151172

173+
Public Function CrcValid() As Boolean
174+
Return StoredCrc16 = ComputedCrc16()
175+
End Function
176+
177+
Public Function GetBytes() As Byte()
178+
Dim totalLen As Integer = TotalLength
179+
Dim buf(totalLen - 1) As Byte
180+
' header
181+
Array.Copy(_header, 0, buf, 0, HEADER_LENGTH)
182+
' data
183+
Array.Copy(_data, 0, buf, HEADER_LENGTH, _data.Length)
184+
185+
Return buf
186+
End Function
187+
152188
Public Function GetTimestamp() As DateTime?
153189
Try
154190
' TD0 stores month as 0–11
@@ -174,5 +210,22 @@ Namespace ImageFormats.TD0
174210
Return Nothing
175211
End Try
176212
End Function
213+
214+
Public Sub RefreshStoredCrc16()
215+
WriteUInt16LE(_header, Offset.Crc16, ComputedCrc16())
216+
End Sub
217+
218+
Public Sub SetTimestamp(ts As DateTime)
219+
If ts.Year < 1900 OrElse ts.Year > 2155 Then
220+
Throw New ArgumentOutOfRangeException(NameOf(ts), ts, "TD0 timestamp year must be between 1900 and 2155.")
221+
End If
222+
223+
Year = ts.Year
224+
Month = ts.Month
225+
Day = ts.Day
226+
Hour = ts.Hour
227+
Minute = ts.Minute
228+
Second = ts.Second
229+
End Sub
177230
End Class
178231
End Namespace

DiskImageTool/ImageFormats/TD0/TD0Enums.vb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@
1616
Rle = 2
1717
End Enum
1818

19+
Public Enum TD0DataRate As Byte
20+
Rate250Kbps = 0
21+
Rate300Kbps = 1
22+
Rate500Kbps = 2
23+
End Enum
24+
25+
Public Enum TD0DriveType As Byte
26+
Drive5_25_96TPI = 0
27+
Drive360K = 1
28+
Drive1200K = 2
29+
Drive720K = 3
30+
Drive1440K = 4
31+
Drive8_Inch = 5
32+
Drive3_5_Unknown = 6
33+
End Enum
34+
35+
Public Enum TD0Stepping As Byte
36+
SteppingSingle = 0
37+
SteppingDouble = 1
38+
SteppingEvenOnly = 2
39+
End Enum
40+
1941
Friend Module TD0Helpers
2042
Friend Const TD0_MAX_BUFSZ As Integer = 1024 * 1024 * 4 ' 4 MiB
2143
Friend Function ReadUInt16LE(buf As Byte(), offset As Integer) As Integer
@@ -31,5 +53,13 @@
3153
buf(offset) = CByte(value And &HFF)
3254
buf(offset + 1) = CByte((value >> 8) And &HFF)
3355
End Sub
56+
57+
Friend Function SetFlagByte(original As Byte, flag As Byte, enabled As Boolean) As Byte
58+
If enabled Then
59+
Return CByte(original Or flag)
60+
Else
61+
Return CByte(original And Not flag)
62+
End If
63+
End Function
3464
End Module
3565
End Namespace

DiskImageTool/ImageFormats/TD0/TD0FloppyImage.vb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ Namespace ImageFormats.TD0
5959
For Each Track In _Image.Tracks
6060
TrackData = SetTrack(Track.Cylinder, Track.Head)
6161
If Track.Sectors.Count > 0 AndAlso Track.Sectors(0) IsNot Nothing Then
62-
TrackData.SectorSize = CUInt(Math.Max(0, Track.Sectors(0).GetSizeBytes()))
62+
TrackData.SectorSize = CUInt(Math.Max(0, Track.Sectors(0).Header.GetSectorSizeBytes))
6363
End If
6464
TrackData.Encoding = GetEncoding(Track.IsFM)
6565
TrackData.FirstSectorId = Track.FirstSectorId
@@ -77,7 +77,7 @@ Namespace ImageFormats.TD0
7777
Private Function CalculateHash(hashAlg As HashAlgorithm) As String
7878
For Each trk In _Image.Tracks
7979
For Each s In trk.Sectors
80-
If s IsNot Nothing AndAlso Not s.Unavailable AndAlso Not s.ChecksumError AndAlso s.Data IsNot Nothing Then
80+
If s IsNot Nothing AndAlso Not s.Header.NoData AndAlso Not s.Header.HasCrcError AndAlso s.Data IsNot Nothing Then
8181
hashAlg.TransformBlock(s.Data, 0, s.Data.Length, Nothing, 0)
8282
End If
8383
Next
@@ -95,15 +95,15 @@ Namespace ImageFormats.TD0
9595
Return False
9696
End If
9797

98-
If Sector.SectorId < 1 Or Sector.SectorId > MaxSectors Then
98+
If Sector.Header.SectorId < 1 Or Sector.Header.SectorId > MaxSectors Then
9999
Return False
100100
End If
101101

102-
If Sector.Unavailable Or Sector.Deleted Or Sector.ChecksumError Then
102+
If Sector.Header.NoData Or Sector.Header.IsDeletedDataMark Or Sector.Header.HasCrcError Or Sector.Data Is Nothing Then
103103
Return False
104104
End If
105105

106-
If Sector.Cylinder <> Track.Cylinder Or Sector.Head <> Track.Head Then
106+
If Sector.Header.Cylinder <> Track.Cylinder Or Sector.Header.Head <> Track.Head Then
107107
Return False
108108
End If
109109

@@ -119,17 +119,17 @@ Namespace ImageFormats.TD0
119119
For Each Sector In Track.Sectors
120120
IsStandard = IsStandardSector(Track, Sector, SECTOR_COUNT)
121121
If IsStandard Then
122-
BitstreamSector = GetSector(Track.Cylinder, Track.Head, Sector.SectorId)
122+
BitstreamSector = GetSector(Track.Cylinder, Track.Head, Sector.Header.SectorId)
123123
If BitstreamSector Is Nothing Then
124124
SectorSize = Sector.Data.Length
125125
If SectorSize > 0 And SectorSize < 512 Then
126126
Buffer = New Byte(511) {}
127127
Array.Copy(Sector.Data, 0, Buffer, 0, SectorSize)
128128
BitstreamSector = New BitstreamSector(Buffer, Buffer.Length, False)
129-
SetSector(Track.Cylinder, Track.Head, Sector.SectorId, BitstreamSector)
129+
SetSector(Track.Cylinder, Track.Head, Sector.Header.SectorId, BitstreamSector)
130130
ElseIf SectorSize = 512 Then
131131
BitstreamSector = New BitstreamSector(Sector.Data, Sector.Data.Length, IsStandard)
132-
SetSector(Track.Cylinder, Track.Head, Sector.SectorId, BitstreamSector)
132+
SetSector(Track.Cylinder, Track.Head, Sector.Header.SectorId, BitstreamSector)
133133
End If
134134
Else
135135
BitstreamSector.IsStandard = False
@@ -148,7 +148,7 @@ Namespace ImageFormats.TD0
148148
IsStandard = IsStandardSector(Track, Sector, 4)
149149
If IsStandard And Sector.Data.Length = 1024 Then
150150
For i = 0 To 1
151-
NewSectorId = (Sector.SectorId - 1) * 2 + 1 + i
151+
NewSectorId = (Sector.Header.SectorId - 1) * 2 + 1 + i
152152
BitstreamSector = GetSector(Track.Cylinder, Track.Head, NewSectorId)
153153
If BitstreamSector Is Nothing Then
154154
Buffer = New Byte(511) {}

0 commit comments

Comments
 (0)