Skip to content

Commit 69ac104

Browse files
committed
Added a new experimental feature to which generates a patch that can be run under MS-DOS to apply image modifications to the actual physical disk.
1 parent 0a43742 commit 69ac104

File tree

14 files changed

+476
-146
lines changed

14 files changed

+476
-146
lines changed

DiskImageTool/DebugScript.vb

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
Imports System.IO
2+
Imports System.IO.Compression
3+
Imports System.Text
4+
5+
Module DebugScript
6+
Public Structure DebugSegment
7+
Dim Offset As UInteger
8+
Dim Data() As Byte
9+
End Structure
10+
11+
Private Function ConvertUintegerToByteArray(Value As UInteger) As Byte()
12+
Return BitConverter.GetBytes(Value)
13+
End Function
14+
Private Function ConvertUShortToByteArray(Value As UShort) As Byte()
15+
Return BitConverter.GetBytes(Value)
16+
End Function
17+
Private Function ConvertByteToByteArray(Value As Byte) As Byte()
18+
Dim Data(0) As Byte
19+
Data(0) = Value
20+
Return Data
21+
End Function
22+
23+
Private Function DebugDump(Address As UInteger, Length As UInteger) As String
24+
Dim Response As String
25+
26+
Response = "D " & Address.ToString("X4") & " " & (Address + Length - 1).ToString("X4")
27+
28+
Return Response
29+
End Function
30+
31+
Private Function DebugEnter(Address As UInteger, Data() As Byte) As String
32+
Dim Response As String
33+
34+
Response = "E " & Address.ToString("X4") & " " & BitConverter.ToString(Data).Replace("-", " ")
35+
36+
Return Response
37+
End Function
38+
39+
Private Function DebugLoad(Address As UInteger, Drive As Byte, FirstSector As UInteger, Number As UShort) As String
40+
Dim Response As String
41+
42+
Response = "L " & Address.ToString("X4") & " " & Drive & " " & FirstSector.ToString("X") & " " & Number.ToString("X")
43+
44+
Return Response
45+
End Function
46+
47+
Private Function DebugWrite(Address As UInteger, Drive As Byte, FirstSector As UInteger, Number As UShort) As String
48+
Dim Response As String
49+
50+
Response = "W " & Address.ToString("X4") & " " & Drive & " " & FirstSector.ToString("X") & " " & Number.ToString("X")
51+
52+
Return Response
53+
End Function
54+
55+
Private Function GetSegment(Offset As UInteger, Value As Object) As DebugSegment
56+
Dim Segment As DebugSegment
57+
58+
Segment.Offset = Offset
59+
If TypeOf Value Is System.UInt16 Then
60+
Segment.Data = ConvertUShortToByteArray(Value)
61+
ElseIf TypeOf Value Is System.UInt32 Then
62+
Segment.Data = ConvertUintegerToByteArray(Value)
63+
ElseIf TypeOf Value Is System.Byte Then
64+
Segment.Data = ConvertByteToByteArray(Value)
65+
Else
66+
Segment.Data = Value
67+
End If
68+
69+
Return Segment
70+
End Function
71+
72+
Public Sub GenerateDebugPackage(Disk As DiskImage.Disk, ImageData As LoadedImageData)
73+
Dim TempPath As String = Path.GetTempPath() & Guid.NewGuid().ToString()
74+
Dim ContentPath As String = Path.Combine(TempPath, "CONTENT")
75+
Dim ToolsPath As String = Path.Combine(ContentPath, "TOOLS")
76+
Dim DataPath As String = Path.Combine(ContentPath, "DATA")
77+
Dim ZipPath As String = Path.Combine(TempPath, "PATCH.ZIP")
78+
79+
Directory.CreateDirectory(DataPath)
80+
Directory.CreateDirectory(ToolsPath)
81+
82+
GenerateDebugScripts(DataPath, Disk, ImageData.SessionModifications)
83+
GenerateDirectoryDump(DataPath, Disk, ImageData.CachedRootDir)
84+
85+
WriteResourceToFile("CHOICE.COM", Path.Combine(ToolsPath, "CHOICE.COM"))
86+
WriteResourceToFile("CRC32.COM", Path.Combine(ToolsPath, "CRC32.COM"))
87+
WriteResourceToFile("PATCH.BAT", Path.Combine(ContentPath, "PATCH.BAT"))
88+
89+
ZipContent(ZipPath, ContentPath)
90+
91+
Dim Dialog = New SaveFileDialog With {
92+
.FileName = Path.GetFileName(ZipPath),
93+
.Filter = "Zip Archive (*.zip)|*.zip"
94+
}
95+
If Dialog.ShowDialog = DialogResult.OK Then
96+
File.Copy(ZipPath, Dialog.FileName, True)
97+
End If
98+
99+
Directory.Delete(TempPath, True)
100+
End Sub
101+
102+
Public Sub GenerateDirectoryDump(DataPath As String, Disk As DiskImage.Disk, CachedRootDir() As Byte)
103+
Dim FileName As String
104+
105+
Dim SectorStart = Disk.BootSector.RootDirectoryRegionStart
106+
Dim SectorEnd = Disk.BootSector.DataRegionStart
107+
Dim Length = (SectorEnd - SectorStart) * Disk.BootSector.BytesPerSector
108+
Dim Offset As UInteger = 256
109+
110+
For Index = 0 To 1
111+
FileName = Path.Combine(DataPath, "DIRDUMP" & Index & ".TXT")
112+
Dim SB = New StringBuilder()
113+
114+
SB.AppendLine("N ROOTDIR.TMP")
115+
SB.AppendLine(DebugLoad(Offset, Index, SectorStart, SectorEnd - SectorStart))
116+
SB.AppendLine("RCX")
117+
SB.AppendLine(Length.ToString("X4"))
118+
SB.AppendLine("RBX")
119+
SB.AppendLine("0")
120+
SB.AppendLine("W " & Offset.ToString("X4"))
121+
SB.AppendLine("Q")
122+
123+
File.WriteAllText(FileName, SB.ToString)
124+
Next Index
125+
126+
FileName = Path.Combine(DataPath, "ROOTDIR.CRC")
127+
File.WriteAllText(FileName, "ROOTDIR.TMP " & Crc32.ComputeChecksum(Disk.Directory.GetContent()).ToString("X8"))
128+
End Sub
129+
130+
Public Sub GenerateDebugScripts(DataPath As String, Disk As DiskImage.Disk, Modifications As Hashtable)
131+
Dim ScriptModifications As New Hashtable
132+
Dim SectorDict As New Dictionary(Of UInteger, List(Of DebugSegment))
133+
Dim SegmentList As List(Of DebugSegment)
134+
Dim ByteLenPerLine As Byte = 16
135+
136+
Dim DriveAFile As String = Path.Combine(DataPath, "DRIVE0.TXT")
137+
Dim DriveBFile As String = Path.Combine(DataPath, "DRIVE1.TXT")
138+
139+
Dim SB_A = New StringBuilder()
140+
Dim SB_B = New StringBuilder()
141+
142+
For Each Offset As UInteger In Modifications.Keys
143+
ScriptModifications.Item(Offset) = Modifications.Item(Offset)
144+
Next
145+
For Each Offset As UInteger In Disk.Modifications.Keys
146+
ScriptModifications.Item(Offset) = Disk.Modifications.Item(Offset)
147+
Next
148+
149+
Dim OffsetArray(ScriptModifications.Keys.Count - 1) As UInteger
150+
ScriptModifications.Keys.CopyTo(OffsetArray, 0)
151+
Array.Sort(OffsetArray)
152+
153+
For Each Offset As UInteger In OffsetArray
154+
Dim Sector As UInteger = Disk.OffsetToSector(Offset)
155+
156+
If SectorDict.ContainsKey(Sector) Then
157+
SegmentList = SectorDict.Item(Sector)
158+
Else
159+
SegmentList = New List(Of DebugSegment)
160+
SectorDict.Add(Sector, SegmentList)
161+
End If
162+
163+
Dim SectorOffset As UInteger = Disk.SectorToOffset(Sector)
164+
Dim Value = ScriptModifications.Item(Offset)
165+
SegmentList.Add(GetSegment(Offset - SectorOffset, Value))
166+
Next
167+
168+
For Each Sector In SectorDict.Keys
169+
SB_A.AppendLine(DebugLoad(0, 0, Sector, 1))
170+
SB_B.AppendLine(DebugLoad(0, 1, Sector, 1))
171+
SegmentList = SectorDict.Item(Sector)
172+
For Each Segment In SegmentList
173+
For I = 0 To Math.Ceiling(Segment.Data.Length / ByteLenPerLine) - 1
174+
Dim SourceIndex As Integer = I * ByteLenPerLine
175+
Dim SegmentOffset As Integer = Segment.Offset + SourceIndex
176+
Dim Length As Integer = Math.Min(ByteLenPerLine, Segment.Data.Length - SourceIndex)
177+
Dim Block(Length - 1) As Byte
178+
Array.Copy(Segment.Data, SourceIndex, Block, 0, Length)
179+
SB_A.AppendLine(DebugEnter(SegmentOffset, Block))
180+
SB_B.AppendLine(DebugEnter(SegmentOffset, Block))
181+
Next
182+
Next
183+
SB_A.AppendLine(DebugWrite(0, 0, Sector, 1))
184+
SB_B.AppendLine(DebugWrite(0, 1, Sector, 1))
185+
Next
186+
SB_A.AppendLine("Q")
187+
SB_B.AppendLine("Q")
188+
189+
File.WriteAllText(DriveAFile, SB_A.ToString)
190+
File.WriteAllText(DriveBFile, SB_B.ToString)
191+
End Sub
192+
193+
Private Sub WriteResourceToFile(ResourceName As String, FileName As String)
194+
Dim AppName = System.Diagnostics.Process.GetCurrentProcess().ProcessName
195+
Using Resource = Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream(AppName & "." & ResourceName)
196+
Using File = New FileStream(FileName, FileMode.Create, FileAccess.Write)
197+
Resource.CopyTo(File)
198+
End Using
199+
End Using
200+
End Sub
201+
202+
Private Sub ZipContent(ZipPath As String, ContentPath As String)
203+
Using ZipStream As Stream = New FileStream(ZipPath, FileMode.Create, FileAccess.Write)
204+
Using Archive As New ZipArchive(ZipStream, ZipArchiveMode.Create)
205+
For Each FilePath In Directory.GetFiles(ContentPath, "*.*", SearchOption.AllDirectories)
206+
Dim RelativePath = FilePath.Replace(ContentPath & "\", String.Empty)
207+
Using FileStream As Stream = New FileStream(FilePath, FileMode.Open, FileAccess.Read)
208+
Using FileStreamInZip As Stream = Archive.CreateEntry(RelativePath).Open()
209+
FileStream.CopyTo(FileStreamInZip)
210+
End Using
211+
End Using
212+
Next
213+
End Using
214+
End Using
215+
End Sub
216+
End Module
1.71 KB
Binary file not shown.
784 Bytes
Binary file not shown.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
@echo off
2+
echo Which drive contains the disk to be patched?
3+
TOOLS\CHOICE /C:AB
4+
IF errorlevel 2 GOTO DRIVE_B
5+
IF errorlevel 1 GOTO DRIVE_A
6+
GOTO EXIT
7+
8+
:DRIVE_A
9+
ECHO Reading Root Directory on Drive A
10+
ECHO.
11+
DEBUG < DATA\DIRDUMP0.TXT > nul
12+
TOOLS\CRC32 < DATA\ROOTDIR.CRC > nul
13+
if errorlevel 1 GOTO WRONGA
14+
ECHO Patching Disk in Drive A
15+
ECHO.
16+
DEBUG < DATA\DRIVE0.TXT > nul
17+
ECHO Patch applied successfully
18+
ECHO.
19+
GOTO EXIT
20+
21+
:DRIVE_B
22+
ECHO Reading Root Directory on Drive B
23+
ECHO.
24+
DEBUG < DATA\DIRDUMP1.TXT > nul
25+
TOOLS\CRC32 < DATA\ROOTDIR.CRC > nul
26+
IF errorlevel 1 GOTO WRONGB
27+
ECHO Patching Disk in Drive B
28+
ECHO.
29+
DEBUG < DATA\DRIVE1.TXT > nul
30+
ECHO Pach applied successfully
31+
ECHO.
32+
GOTO EXIT
33+
34+
:WRONGA
35+
ECHO Error: The wrong disk appears to be in Drive A
36+
ECHO.
37+
GOTO EXIT
38+
39+
:WRONGB
40+
ECHO Error: The wrong disk appears to be in Drive B
41+
ECHO.
42+
GOTO EXIT
43+
44+
:EXIT
45+
IF EXIST ROOTDIR.TMP DEL ROOTDIR.TMP

DiskImageTool/DiskImage/Directory.vb

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,40 @@
22
Public Class Directory
33
Private ReadOnly _FatClusterList As List(Of UShort)
44
Private ReadOnly _Parent As Disk
5+
Private ReadOnly _Length As UInteger
56

6-
Sub New(Parent As Disk, Optional FatClusterList As List(Of UShort) = Nothing)
7+
Sub New(Parent As Disk, Optional FatClusterList As List(Of UShort) = Nothing, Optional Length As UInteger = 0)
78
_Parent = Parent
89
_FatClusterList = FatClusterList
10+
_Length = Length
911
End Sub
1012

13+
Public Function GetContent() As Byte()
14+
If _FatClusterList Is Nothing Then
15+
Return GetContentRoot()
16+
Else
17+
Return GetContentSubDirectory()
18+
End If
19+
End Function
20+
21+
Private Function GetContentRoot() As Byte()
22+
Dim SectorStart = _Parent.BootSector.RootDirectoryRegionStart
23+
Dim SectorEnd = _Parent.BootSector.DataRegionStart
24+
Dim Length = (SectorEnd - SectorStart) * _Parent.BootSector.BytesPerSector
25+
Dim Offset = _Parent.SectorToOffset(SectorStart)
26+
27+
Return _Parent.GetBytes(Offset, Length)
28+
End Function
29+
30+
Private Function GetContentSubDirectory() As Byte()
31+
Dim Content = _Parent.GetDataFromFATClusterList(_FatClusterList)
32+
If Content.Length <> _Length Then
33+
Array.Resize(Of Byte)(Content, _Length)
34+
End If
35+
36+
Return Content
37+
End Function
38+
1139
Private Function GetDataBlockRoot() As DataBlock
1240
Dim SectorStart = _Parent.BootSector.RootDirectoryRegionStart
1341
Dim SectorEnd = _Parent.BootSector.DataRegionStart

DiskImageTool/DiskImage/DirectoryEntry.vb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,18 @@ Namespace DiskImage
241241
Return FATClusterList
242242
End Function
243243

244+
Public Function GetFileExtension() As String
245+
Return Encoding.UTF8.GetString(Extension).Trim
246+
End Function
247+
244248
Public Function GetFileName() As String
245-
Dim File = Encoding.UTF8.GetString(FileName).Trim
246-
Dim Ext = Encoding.UTF8.GetString(Extension).Trim
249+
Return Encoding.UTF8.GetString(FileName).Trim
250+
End Function
251+
252+
Public Function GetFullFileName() As String
253+
Dim File = GetFileName()
254+
Dim Ext = GetFileExtension()
255+
247256
If Ext <> "" Then
248257
File = File & "." & Ext
249258
End If
@@ -348,7 +357,7 @@ Namespace DiskImage
348357

349358
Private Sub InitSubDirectory()
350359
If IsDirectory() And Not IsDeleted() Then
351-
_SubDirectory = New DiskImage.Directory(_Parent, _FatClusterList)
360+
_SubDirectory = New DiskImage.Directory(_Parent, _FatClusterList, FileSize)
352361
Else
353362
_SubDirectory = Nothing
354363
End If

DiskImageTool/DiskImage/Disk.vb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,20 @@
359359
Return Result
360360
End Function
361361

362+
Public Function GetVolumeLabel() As String
363+
Dim VolumeLabel As String = ""
364+
365+
For Counter As UInteger = 1 To _Directory.DirectoryLength
366+
Dim File = _Directory.GetFile(Counter)
367+
If Not File.IsDeleted And File.IsVolumeName Then
368+
VolumeLabel = File.GetFileName & File.GetFileExtension
369+
Exit For
370+
End If
371+
Next
372+
373+
Return VolumeLabel
374+
End Function
375+
362376
Public Function HasUnusedClustersWithData() As Boolean
363377
Dim ClusterSize As UInteger = _BootSector.BytesPerCluster
364378

DiskImageTool/DiskImageTool.vbproj

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
<ProductName>DiskImageTool</ProductName>
3131
<PublisherName>Digitoxin</PublisherName>
3232
<ApplicationRevision>1</ApplicationRevision>
33-
<ApplicationVersion>1.9.0.%2a</ApplicationVersion>
33+
<ApplicationVersion>1.10.0.%2a</ApplicationVersion>
3434
<UseApplicationTrust>false</UseApplicationTrust>
3535
<PublishWizardCompleted>true</PublishWizardCompleted>
3636
<BootstrapperEnabled>true</BootstrapperEnabled>
@@ -90,6 +90,8 @@
9090
<Reference Include="System.Data" />
9191
<Reference Include="System.Deployment" />
9292
<Reference Include="System.Drawing" />
93+
<Reference Include="System.IO.Compression" />
94+
<Reference Include="System.IO.Compression.FileSystem" />
9395
<Reference Include="System.Windows.Forms" />
9496
<Reference Include="System.Xml" />
9597
<Reference Include="System.Core" />
@@ -111,6 +113,7 @@
111113
<Import Include="System.Threading.Tasks" />
112114
</ItemGroup>
113115
<ItemGroup>
116+
<Compile Include="DebugScript.vb" />
114117
<Compile Include="Filters.vb" />
115118
<Compile Include="DiskImage\BootSector.vb" />
116119
<Compile Include="Functions.vb" />
@@ -183,6 +186,9 @@
183186
</EmbeddedResource>
184187
</ItemGroup>
185188
<ItemGroup>
189+
<EmbeddedResource Include="DebugScript\CHOICE.COM" />
190+
<EmbeddedResource Include="DebugScript\CRC32.COM" />
191+
<EmbeddedResource Include="DebugScript\PATCH.BAT" />
186192
<None Include="DiskImageTool_TemporaryKey.pfx" />
187193
<None Include="My Project\Application.myapp">
188194
<Generator>MyApplicationCodeGenerator</Generator>

0 commit comments

Comments
 (0)