├── ipf
├── ipf.ico
├── update.png
├── App.xaml
├── BufferWindow.xaml
├── App.xaml.cs
├── Properties
│ ├── Settings.settings
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Settings.Designer.cs
│ └── Resources.resx
├── App.config
├── Floppy.cs
├── ipf.dox
├── MainWindow.xaml
├── BufferWindow.xaml.cs
├── utilities.cs
├── ipf.csproj
├── MainWindow.xaml.cs
├── IPFStruct.cs
└── IPFWriter.cs
├── pasti
├── PastiStruct.cs
├── App.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── App.xaml
├── App.xaml.cs
├── BufferWindow.xaml
├── pasti.dox
├── MainWindow.xaml
├── FloppyStruct.cs
├── BufferWindow.xaml.cs
├── pasti.csproj
├── MainWindow.xaml.cs
├── PastiRead.cs
└── PastiWrite.cs
├── kfstream
├── App.config
├── Properties
│ ├── Settings.settings
│ ├── Settings.Designer.cs
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ └── Resources.resx
├── App.xaml
├── App.xaml.cs
├── content.xaml
├── content.xaml.cs
├── MainWindow.xaml
├── KFStream.csproj
├── KFWriter.cs
├── MainWindow.xaml.cs
└── ProcessStream.cs
├── README.md
├── AIR.sln
├── .gitattributes
└── .gitignore
/ipf/ipf.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrCoolzic/AIR/HEAD/ipf/ipf.ico
--------------------------------------------------------------------------------
/ipf/update.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrCoolzic/AIR/HEAD/ipf/update.png
--------------------------------------------------------------------------------
/pasti/PastiStruct.cs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/DrCoolzic/AIR/HEAD/pasti/PastiStruct.cs
--------------------------------------------------------------------------------
/kfstream/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/pasti/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/kfstream/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/pasti/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/pasti/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/kfstream/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ipf/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/pasti/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace Pasti {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/kfstream/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace KFStream {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application {
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ipf/BufferWindow.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/kfstream/content.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/pasti/BufferWindow.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/ipf/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Configuration;
4 | using System.Data;
5 | using System.Linq;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 |
9 | namespace ipf {
10 | ///
11 | /// Interaction logic for App.xaml
12 | ///
13 | public partial class App : Application {
14 | // used to save size and position
15 | private void Application_Exit(object sender, ExitEventArgs e) {
16 | ipf.Properties.Settings.Default.Save();
17 | }
18 |
19 | }
20 |
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/ipf/Properties/Settings.settings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 0
7 |
8 |
9 | 0
10 |
11 |
12 | 600
13 |
14 |
15 | 900
16 |
17 |
18 | True
19 |
20 |
21 |
--------------------------------------------------------------------------------
/pasti/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34014
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace pasti.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/kfstream/Properties/Settings.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.34209
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace KFStream.Properties {
12 |
13 |
14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
17 |
18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
19 |
20 | public static Settings Default {
21 | get {
22 | return defaultInstance;
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ipf/App.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | 0
15 |
16 |
17 | 0
18 |
19 |
20 | 600
21 |
22 |
23 | 900
24 |
25 |
26 | True
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/ipf/Floppy.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace ipf {
8 |
9 | ///
10 | /// Store the content of a gap element including the gap value
11 | ///
12 | public class GapElement {
13 | public uint gapBytes;
14 | public byte value;
15 | public GapType type;
16 | }
17 |
18 | ///
19 | /// Store the content of a data element. The sample is a list
20 | ///
21 | public class DataElem {
22 | public uint dataBytes;
23 | public List value;
24 | public DataType type;
25 | }
26 |
27 | ///
28 | /// Store the sector/bloc information
29 | ///
30 | public class Sector {
31 | public uint dataBits;
32 | public uint gapBits;
33 | public List gapElems = new List();
34 | public List dataElems = new List();
35 | public BlockFlags flags = BlockFlags.None;
36 | }
37 |
38 | ///
39 | /// Store information about one track
40 | ///
41 | public class Track {
42 | public uint trackBytes;
43 | public uint dataBits;
44 | public uint gapBits;
45 | public Density density;
46 | public uint startBitPos;
47 | public uint blockCount;
48 | public TrackFlags trackFlags;
49 | public List sectors = new List();
50 | }
51 |
52 | ///
53 | /// Store information about all tracks of a FD
54 | ///
55 | /// We also store the info record to be able to rewrite the same info record
56 | public class Floppy {
57 | public Track[,] tracks;
58 | public InfoRecord info;
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ipf/ipf.dox:
--------------------------------------------------------------------------------
1 | /*! @mainpage IPF File Reader /Writer
2 |
3 |
Copyright (C) 2014 Jean Louis-Guerin\n\n
4 | This file is part of the Atari Image Reader (AIR) project.\n
5 | The Atari Image Reader project may be used and distributed without restriction provided
6 | that this copyright statement is not removed from the file and that any
7 | derivative work contains the original copyright notice and the associated
8 | disclaimer.\n
9 | The Atari Image Reader project is free software; you can redistribute it
10 | and/or modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.\n
13 | The Atari Image Reader project is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 | See the GNU General Public License for more details.\n\n
17 | You should have received a copy of the GNU General Public License
18 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
20 |
21 | @section _intro Introduction
22 | The IPF Reader / Writer is a simple program that reads and writes an IPF file.
23 | It displays in the main window the structure of the IPF file
24 | The main goal of this program is to provide an example of how to read / write an IPF File
25 |
26 | @section progdes Program Description
27 | TODO
28 |
29 | @section proguse Program Usage
30 | Usage should be obvious ...
31 |
32 | @author Jean Louis-Guerin
33 |
34 | */
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/pasti/pasti.dox:
--------------------------------------------------------------------------------
1 | /*! @mainpage Pasti Reader
2 |
3 |
Copyright (C) 2014 Jean Louis-Guerin\n\n
4 | This file is part of the Atari Image Reader (AIR) project.\n
5 | The Atari Image Reader project may be used and distributed without restriction provided
6 | that this copyright statement is not removed from the file and that any
7 | derivative work contains the original copyright notice and the associated
8 | disclaimer.\n
9 | The Atari Image Reader project is free software; you can redistribute it
10 | and/or modify it under the terms of the GNU General Public License
11 | as published by the Free Software Foundation; either version 2
12 | of the License, or (at your option) any later version.\n
13 | The Atari Image Reader project is distributed in the hope that it will be useful,
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 | See the GNU General Public License for more details.\n\n
17 | You should have received a copy of the GNU General Public License
18 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
19 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
Copyright (C) 2014 Jean Louis-Guerin\n\n
6 | This file is part of the Atari Image Reader (AIR) project.\n
7 | The Atari Image Reader project may be used and distributed without restriction provided
8 | that this copyright statement is not removed from the file and that any
9 | derivative work contains the original copyright notice and the associated
10 | disclaimer.\n
11 | The Atari Image Reader project is free software; you can redistribute it
12 | and/or modify it under the terms of the GNU General Public License
13 | as published by the Free Software Foundation; either version 2
14 | of the License, or (at your option) any later version.\n
15 | The Atari Image Reader project is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 | See the GNU General Public License for more details.\n\n
19 | You should have received a copy of the GNU General Public License
20 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
Copyright (C) 2011 Jean Louis-Guerin\n\n
7 | This file is part of the Atari Universal FD Image Tool project.\n\n
8 | The Atari Universal FD Image Tool project may be used and distributed without restriction provided
9 | that this copyright statement is not removed from the file and that any
10 | derivative work contains the original copyright notice and the associated
11 | disclaimer.\n\n
12 | The Atari Universal FD Image Tool project is free software; you can redistribute it
13 | and/or modify it under the terms of the GNU General Public License
14 | as published by the Free Software Foundation; either version 3
15 | of the License, or (at your option) any later version.\n\n
16 | The Atari Universal FD Image Tool project is distributed in the hope that it will be useful,
17 | but WITHOUT ANY WARRANTY; without even the implied warranty of
18 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n
19 | See the GNU General Public License for more details.\n\n
20 | You should have received a copy of the GNU General Public License
21 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
22 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
23 |
24 | @author Jean Louis-Guerin
25 | */
26 |
27 | using System;
28 | using System.Collections.Generic;
29 | using System.IO;
30 | using System.Linq;
31 | using System.Text;
32 | using System.Threading.Tasks;
33 | using System.Windows;
34 | using System.Windows.Controls;
35 | using System.Windows.Data;
36 | using System.Windows.Documents;
37 | using System.Windows.Input;
38 | using System.Windows.Media;
39 | using System.Windows.Media.Imaging;
40 | using System.Windows.Shapes;
41 |
42 |
43 | namespace Pasti {
44 | ///
45 | /// Interaction logic for TrackBuffer.xaml
46 | ///
47 | public partial class BufferWindow : Window {
48 | ///
49 | /// Create a new BufferWindow object.
50 | ///
51 | public BufferWindow() {
52 | InitializeComponent();
53 | }
54 |
55 | ///
56 | /// Draw the content of the buffer
57 | ///
58 | /// Pointer to the floppy disk structure
59 | /// The track number of the track to display
60 | /// The side of the track to display
61 | public void displaySectorBuffer(Floppy floppy, int track, int side) {
62 |
63 | displayBuffer.FontFamily = new FontFamily("Consolas");
64 | displayBuffer.FontSize = 16;
65 | displayBuffer.FontStyle = FontStyles.Normal;
66 | displayBuffer.FontWeight = FontWeights.Normal;
67 |
68 | Track t = floppy.tracks[track, side];
69 |
70 | displayBuffer.AppendText(String.Format("Track {0:D2}.{1} has {2} sectors\n", track, side, t.sectorCount));
71 | for (int sect = 0; sect < t.sectorCount; sect++) {
72 | displayBuffer.AppendText(String.Format("Sector {0}", t.sectors[sect].id.number));
73 | displayBuffer.AppendText(String.Format(" T={0} H={1} N={2} S={3} CRC={4:X4}",
74 | t.sectors[sect].id.track, t.sectors[sect].id.side, t.sectors[sect].id.number, t.sectors[sect].id.size, t.sectors[sect].id.crc));
75 |
76 | if (t.sectors[sect].sectorData != null) {
77 | displayBuffer.AppendText(String.Format(" has {0} bytes\n", t.sectors[sect].sectorData.Count()));
78 | displayBuffer.AppendText(String.Format(" bitPosition {0}, Flags {1:X2}", t.sectors[sect].bitPosition, t.sectors[sect].fdcFlags));
79 | displayBuffer.AppendText(String.Format(" {0} {1}\n", ((t.sectors[sect].fdcFlags & SectorDesc.CRC_ERR) == 0) ?
80 | "Good CRC" : "Bad CRC", (t.sectors[sect].fuzzyData != null) ? "Has Fuzzy bytes" : ""));
81 | drawBuffer(t.sectors[sect].sectorData);
82 | if (t.sectors[sect].fuzzyData != null) {
83 | displayBuffer.AppendText(String.Format("\nFuzzy bytes for sector {0}\n", t.sectors[sect].fuzzyData.Count()));
84 | drawBuffer(t.sectors[sect].fuzzyData);
85 | }
86 | if (t.sectors[sect].timmingData != null) {
87 | displayBuffer.AppendText(String.Format("\nTiming values for sector {0}\n", t.sectors[sect].timmingData.Count()));
88 | byte[] timming = new byte[t.sectors[sect].timmingData.Count()];
89 | for (int i = 0; i < t.sectors[sect].timmingData.Count(); i++)
90 | timming[i] = (byte)t.sectors[sect].timmingData[i];
91 | drawBuffer(timming);
92 | }
93 |
94 | }
95 | else
96 | displayBuffer.AppendText(" *** Sector has no data ***\n");
97 | displayBuffer.AppendText("\n");
98 | }
99 | }
100 |
101 |
102 |
103 |
104 | ///
105 | /// Display the content of the Track Information in a textbox
106 | ///
107 | /// Specifies the track from the disk
108 | /// Specifies the side from the disk
109 | /// The floppy disk structure
110 | public void displayTrackBuffer(Floppy floppy, int track, int side) {
111 | if (floppy == null) return;
112 | Track t = floppy.tracks[track, side];
113 | displayBuffer.FontFamily = new FontFamily("Consolas");
114 | displayBuffer.FontSize = 16;
115 | displayBuffer.FontStyle = FontStyles.Normal;
116 | displayBuffer.FontWeight = FontWeights.Normal;
117 |
118 | displayBuffer.AppendText(String.Format("Track {0:D2}.{1} {2} bytes with {3} sectors\n",
119 | track, side, t.byteCount, t.sectors.Count()));
120 | if (t.trackData != null)
121 | drawBuffer(t.trackData);
122 | else
123 | displayBuffer.AppendText("Track has no Track Image Data Record");
124 | }
125 |
126 | ///
127 | /// Display the content of TrackBuffer buffer in the displayBuffer TextBox
128 | ///
129 | /// The buffer to display
130 | /// an optional message to display before the buffer
131 | public void drawBuffer(byte[] buffer, string message = null) {
132 | displayBuffer.FontFamily = new FontFamily("Consolas");
133 | displayBuffer.FontSize = 16;
134 | displayBuffer.FontStyle = FontStyles.Normal;
135 | displayBuffer.FontWeight = FontWeights.Normal;
136 | if (message != null)
137 | displayBuffer.AppendText(message);
138 |
139 | for (int i = 0; i < buffer.Count(); i += 16) {
140 | displayBuffer.AppendText(String.Format("{0:D5} ", i));
141 |
142 | string ascii = " ";
143 | for (int j = 0; j < 16; j++) {
144 | if ((i + j) < buffer.Count()) {
145 | displayBuffer.AppendText(String.Format("{0:X2} ", buffer[i + j]));
146 | char ch = Convert.ToChar(buffer[i + j]);
147 | ascii += Char.IsControl(ch) ? "." : ch.ToString();
148 | }
149 | else
150 | displayBuffer.AppendText(String.Format(" "));
151 | } // one more line
152 | displayBuffer.AppendText(ascii);
153 | displayBuffer.AppendText(String.Format("\n"));
154 | } // all lines
155 | }
156 |
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/kfstream/ProcessStream.cs:
--------------------------------------------------------------------------------
1 | /*!
2 | @file ProcessStream.cs
3 | Process information from the KryoFlux Stream File
4 |
5 |
Copyright (C) 2013-2015 Jean Louis-Guerin\n\n
6 | This file is part of the Atari Universal FD Image Tool project.\n\n
7 | The Atari Universal FD Image Tool project may be used and distributed without restriction provided
8 | that this copyright statement is not removed from the file and that any
9 | derivative work contains the original copyright notice and the associated
10 | disclaimer.\n\n
11 | The Atari Universal FD Image Tool project is free software; you can redistribute it
12 | and/or modify it under the terms of the GNU General Public License
13 | as published by the Free Software Foundation; either version 3
14 | of the License, or (at your option) any later version.\n\n
15 | The Atari Universal FD Image Tool project is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n
18 | See the GNU General Public License for more details.\n\n
19 | You should have received a copy of the GNU General Public License
20 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
22 |
23 | @author Jean Louis-Guerin
24 | */
25 |
26 | using System;
27 | using System.Collections.Generic;
28 | using System.Linq;
29 | using System.Text;
30 | using System.Threading;
31 | using System.Threading.Tasks;
32 | using System.Windows;
33 | using System.Windows.Controls;
34 |
35 | using KFStream;
36 | using KFStreamPackage;
37 |
38 | namespace KFStreamPackage {
39 |
40 | ///
41 | /// The FluxData structure contains the flux transition information
42 | ///
43 | ///
44 | /// This class is used when flux samples file is provided e.g. SCP or KF-RAW file.
45 | ///
46 | public class FluxData {
47 | ///
48 | /// Array of flux transitions recorded for this track (including all revolutions).
49 | ///
50 | /// The values are stored in nanoseconds
51 | public int[] fluxValue;
52 |
53 | /// Total number of flux transitions for ALL revolutions of this track
54 | public int totalFluxCount;
55 |
56 | /// Value of the shortest flux transition recorded for this track
57 | /// The values is stored in nanoseconds
58 | public int minFlux;
59 |
60 | /// Value of the largest flux transition recorded for this track in sample clock
61 | /// The values is stored in nanoseconds
62 | public int maxFlux;
63 | }
64 |
65 |
66 | ///
67 | /// The FluxDataRev structure contains the information for one revolution of one track
68 | /// This information is to be used with the FluxData class
69 | ///
70 | ///
71 | /// The FluxDataRev are kept in an array with an entry for each revolution and contains
72 | /// pointers to information in the FluxData class.
73 | ///
74 | public struct FluxDataRev {
75 | ///
76 | /// The time it takes to complete a revolution in nanoseconds (time between two indexes).
77 | ///
78 | public int revolutionTime;
79 |
80 | ///
81 | /// Index in FluxValue array of the first flux for this revolution.
82 | ///
83 | public int firstFluxIndex;
84 |
85 | ///
86 | /// The number of flux transitions for this revolution.
87 | ///
88 | public int fluxCount;
89 |
90 | /// Gives the position of the index pulse inside the flux that contains it. The position
91 | /// before the index signal is given in nanoseconds . Note that in the case of no
92 | /// flux area at the beginning of the track this value can be huge
93 | public int preIndexTime;
94 | }
95 |
96 |
97 | ///
98 | /// Class to Process all KryoFlux Stream files
99 | ///
100 | public class ProcessStream {
101 | private FluxData _fluxData;
102 | private FluxDataRev[] _fluxDataRev;
103 | private KFReader _reader;
104 |
105 | public FluxData Data { get { return _fluxData; }}
106 | public FluxDataRev[] Rev { get { return _fluxDataRev; }}
107 | public KFReader Reader { get { return _reader; } }
108 |
109 | ///
110 | /// Reads, Parses and fills the FluxData and FluxDataRev structures
111 | ///
112 | /// Complete name of the stream file to read
113 | /// Text box used to display debug information
114 | /// True if file processed without problem; false if error while processing
115 | public StreamStatus readStreamTrack(string fileName, TextBox infoBox) {
116 | _reader = new KFReader();
117 | StreamStatus status = _reader.readStream(fileName);
118 |
119 | if (status == StreamStatus.sdsOk) {
120 | int fluxMin = Int32.MaxValue;
121 | int fluxMax = 0;
122 | double tick = 1000000000.0 / _reader.SampleClock;
123 | double sample = tick;
124 |
125 | _fluxData = new FluxData();
126 | _fluxData.fluxValue = new int[_reader.FluxCount];
127 | _fluxData.totalFluxCount = _reader.FluxCount;
128 | _fluxDataRev = new FluxDataRev[_reader.IndexCount - 1];
129 |
130 | // convert samples before first index
131 | for (int i = 0; i <= _reader.Indexes[0].fluxPosition; i++ )
132 | _fluxData.fluxValue[i] = (int)((double)_reader.FluxValues[i] * sample);
133 |
134 | for (int rev = 0; rev < _reader.IndexCount - 1; rev++) {
135 | // compute corrected sample clock
136 | //sample = 200000000.0 / (double)_reader.Indexes[rev + 1].indexTime;
137 | _fluxDataRev[rev].revolutionTime = (int)((double)_reader.Indexes[rev + 1].indexTime * sample);
138 | _fluxDataRev[rev].fluxCount = _reader.Indexes[rev + 1].fluxPosition - _reader.Indexes[rev].fluxPosition;
139 | int offset = _reader.Indexes[rev].fluxPosition;
140 | _fluxDataRev[rev].firstFluxIndex = offset;
141 | _fluxDataRev[rev].preIndexTime = (int)((double)_reader.Indexes[rev].preIndexTime * sample);
142 |
143 | for (int f = 0; f < _fluxDataRev[rev].fluxCount; f++) {
144 | // convert samples for current revolution
145 | _fluxData.fluxValue[offset + f] = (int)((double)_reader.FluxValues[offset + f] * sample);
146 | int value = _fluxData.fluxValue[offset + f];
147 | if (value > fluxMax) fluxMax = value;
148 | if (value < fluxMin) fluxMin = value;
149 | if ((double)_reader.Indexes[rev].preIndexTime * sample > 10000.0) {
150 | // If we have a large NFA at border of revolution we split at position of the index.
151 | if (f == 0)
152 | _fluxData.fluxValue[offset + f] -= (int)((double)_reader.Indexes[rev].preIndexTime * sample);
153 | else if (f == (_fluxDataRev[rev].fluxCount - 1))
154 | _fluxData.fluxValue[offset + f] += (int)((double)(_reader.Indexes[rev].preIndexTime * sample));
155 | } // NFA
156 | } // all flux in current revolution
157 | } // all revolutions
158 |
159 | _fluxData.maxFlux = fluxMax;
160 | _fluxData.minFlux = fluxMin;
161 | } // read correctly
162 |
163 | return status;
164 | }
165 |
166 | ///
167 | /// This is an asynchronous wrapper of the readTrack() function
168 | ///
169 | /// Name of the stream file to read
170 | /// Store information for this track
171 | /// Store the information for this side
172 | /// True if file processed without problem; false if error while processing
173 | /// See also function.
174 | public Task readStreamTrackAsync(string name, TextBox infoBox) {
175 | return Task.Run(() => readStreamTrack(name, infoBox));
176 | }
177 |
178 | } // streamProcess Class
179 | } // name space
--------------------------------------------------------------------------------
/pasti/pasti.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {6C747094-3588-42A3-A868-341CC2148933}
8 | WinExe
9 | Properties
10 | pasti
11 | pasti
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | true
17 | ftp://info-coach.fr/atari/software/pc-projects/_utilities/pasti/Publish/
18 | true
19 | Web
20 | true
21 | Background
22 | 7
23 | Days
24 | false
25 | false
26 | true
27 | http://info-coach.fr/atari/software/pc-projects/_utilities/pasti/Publish/
28 | http://info-coach.fr/atari/software/pc-projects/_utilities/pasti/Publish/
29 | Pasti Reader Writer
30 | DrCoolZic
31 | AIR
32 | true
33 | publish.html
34 | 2
35 | 1.0.0.%2a
36 | false
37 | true
38 | true
39 |
40 |
41 | AnyCPU
42 | true
43 | full
44 | false
45 | bin\Debug\
46 | DEBUG;TRACE
47 | prompt
48 | 4
49 |
50 |
51 | AnyCPU
52 | pdbonly
53 | true
54 | bin\Release\
55 | TRACE
56 | prompt
57 | 4
58 |
59 |
60 | 00B29AB1C93AB667A5E82F34DA242D0F024D07DC
61 |
62 |
63 | Pasti_2_TemporaryKey.pfx
64 |
65 |
66 | true
67 |
68 |
69 | true
70 |
71 |
72 | false
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | 4.0
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 | MSBuild:Compile
92 | Designer
93 |
94 |
95 | Designer
96 | MSBuild:Compile
97 |
98 |
99 | MSBuild:Compile
100 | Designer
101 |
102 |
103 | App.xaml
104 | Code
105 |
106 |
107 | BufferWindow.xaml
108 |
109 |
110 |
111 | MainWindow.xaml
112 | Code
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 | Code
121 |
122 |
123 | True
124 | True
125 | Resources.resx
126 |
127 |
128 | True
129 | Settings.settings
130 | True
131 |
132 |
133 | ResXFileCodeGenerator
134 | Resources.Designer.cs
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 | SettingsSingleFileGenerator
143 | Settings.Designer.cs
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 | False
153 | Microsoft .NET Framework 4.5 %28x86 and x64%29
154 | true
155 |
156 |
157 | False
158 | .NET Framework 3.5 SP1 Client Profile
159 | false
160 |
161 |
162 | False
163 | .NET Framework 3.5 SP1
164 | false
165 |
166 |
167 |
168 |
175 |
--------------------------------------------------------------------------------
/ipf/ipf.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {AA467937-452A-4AE0-AD9D-674EEF0811EB}
8 | WinExe
9 | Properties
10 | ipf
11 | ipf
12 | v4.5
13 | 512
14 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
15 | 4
16 | true
17 | ftp://info-coach.fr/atari/software/pc-projects/_utilities/ipf/Publish/
18 | true
19 | Web
20 | false
21 | Background
22 | 7
23 | Days
24 | false
25 | false
26 | true
27 | http://info-coach.fr/atari/software/pc-projects/_utilities/ipf/Publish/
28 | http://info-coach.fr/atari/software/pc-projects/_utilities/ipf/Publish/
29 | IPF Reader Writer
30 | DrCoolZic
31 | Air
32 | true
33 | publish.html
34 | 9
35 | 1.0.0.%2a
36 | false
37 | true
38 | true
39 |
40 |
41 | AnyCPU
42 | true
43 | full
44 | false
45 | bin\Debug\
46 | DEBUG;TRACE
47 | prompt
48 | 4
49 |
50 |
51 | AnyCPU
52 | pdbonly
53 | true
54 | bin\Release\
55 | TRACE
56 | prompt
57 | 4
58 |
59 |
60 | 259CD416095ABA506BE91C011212602A1F509A2E
61 |
62 |
63 | IPF_2_TemporaryKey.pfx
64 |
65 |
66 | true
67 |
68 |
69 | true
70 |
71 |
72 | ipf.ico
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | 4.0
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | MSBuild:Compile
93 | Designer
94 |
95 |
96 | Designer
97 | MSBuild:Compile
98 |
99 |
100 | MSBuild:Compile
101 | Designer
102 |
103 |
104 | App.xaml
105 | Code
106 |
107 |
108 | BufferWindow.xaml
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 | MainWindow.xaml
117 | Code
118 |
119 |
120 |
121 |
122 | Code
123 |
124 |
125 | True
126 | True
127 | Resources.resx
128 |
129 |
130 | True
131 | Settings.settings
132 | True
133 |
134 |
135 | ResXFileCodeGenerator
136 | Resources.Designer.cs
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 | SettingsSingleFileGenerator
145 | Settings.Designer.cs
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | False
155 | Microsoft .NET Framework 4.5 %28x86 and x64%29
156 | true
157 |
158 |
159 | False
160 | .NET Framework 3.5 SP1 Client Profile
161 | false
162 |
163 |
164 | False
165 | .NET Framework 3.5 SP1
166 | false
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
183 |
--------------------------------------------------------------------------------
/ipf/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Win32;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Documents;
11 | using System.Windows.Input;
12 | using System.Windows.Media;
13 | using System.Windows.Media.Imaging;
14 | using System.Windows.Navigation;
15 | using System.Windows.Shapes;
16 | using System.Deployment.Application;
17 | using System.IO;
18 |
19 | namespace ipf {
20 | //http://www.thomaslevesque.com/2008/11/18/wpf-binding-to-application-settings-using-a-markup-extension/
21 | public class SettingBindingExtension : Binding {
22 | public SettingBindingExtension() {
23 | Initialize();
24 | }
25 |
26 | public SettingBindingExtension(string path)
27 | : base(path) {
28 | Initialize();
29 | }
30 |
31 | private void Initialize() {
32 | this.Source = ipf.Properties.Settings.Default;
33 | this.Mode = BindingMode.TwoWay;
34 | }
35 | }
36 |
37 | ///
38 | /// Interaction logic for MainWindow.xaml
39 | ///
40 | public partial class MainWindow : Window {
41 | Floppy _fd;
42 | BufferWindow _trackWindow = null;
43 | BufferWindow _sectorWindow = null;
44 | bool _trackWindowOpen = false;
45 | bool _sectorWindowOpen = false;
46 |
47 | public MainWindow() {
48 | InitializeComponent();
49 | Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
50 | DateTime dateTimeOfBuild = new DateTime(2000, 1, 1)
51 | + new TimeSpan(version.Build, 0, 0, 0)
52 | + TimeSpan.FromSeconds(version.Revision * 2);
53 | Title = "IPF File Reader / Writer " + version.Major + "." + version.Minor + " - " + dateTimeOfBuild;
54 | }
55 |
56 | private bool checkForUpdate(bool showMessage) {
57 | bool doUpdate = false;
58 | UpdateCheckInfo info = null;
59 |
60 | if (ApplicationDeployment.IsNetworkDeployed) {
61 | ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
62 |
63 | try {
64 | info = ad.CheckForDetailedUpdate();
65 | }
66 | catch (DeploymentDownloadException dde) {
67 | MessageBox.Show("A new version of the application cannot be downloaded at this time. \n\nPlease check your network connection, or try again later. Error: " + dde.Message);
68 | return false;
69 | }
70 | catch (InvalidDeploymentException ide) {
71 | MessageBox.Show("Cannot check for a new version of the application. The ClickOnce deployment is corrupt. Please redeploy the application and try again. Error: " + ide.Message);
72 | return false;
73 | }
74 | catch (InvalidOperationException ioe) {
75 | MessageBox.Show("This application cannot be updated. It is likely not a ClickOnce application. Error: " + ioe.Message);
76 | return false;
77 | }
78 |
79 | if (info.UpdateAvailable) {
80 | doUpdate = true;
81 | if (showMessage) {
82 | MessageBoxResult result = MessageBox.Show(
83 | "An update is available. Would you like to update the application now?",
84 | "Update Available", MessageBoxButton.OKCancel, MessageBoxImage.Question, MessageBoxResult.OK);
85 | if (result != MessageBoxResult.OK)
86 | doUpdate = false;
87 | }
88 | }
89 | else
90 | if (showMessage) MessageBox.Show("No Update Available at this time");
91 |
92 | } // network deployed
93 | else
94 | MessageBox.Show("This application is not a ClickOnce application");
95 | return doUpdate;
96 | }
97 |
98 |
99 | private void InstallUpdate() {
100 | ApplicationDeployment ad = ApplicationDeployment.CurrentDeployment;
101 | try {
102 | ad.Update();
103 | MessageBox.Show("The application has been updated and will be restarted");
104 | //System.Diagnostics.Process.Start(Application.ResourceAssembly.Location);
105 | String ApplicationEntryPoint = ApplicationDeployment.CurrentDeployment.UpdatedApplicationFullName;
106 | System.Diagnostics.Process.Start(ApplicationEntryPoint);
107 | Application.Current.Shutdown();
108 | }
109 | catch (DeploymentDownloadException dde) {
110 | MessageBox.Show("Cannot install the latest version of the application. \n\nPlease check your network connection, or try again later. Error: " + dde);
111 | return;
112 | }
113 | }
114 |
115 |
116 | private void btFileClick(object sender, RoutedEventArgs e) {
117 | OpenFileDialog ofd = new OpenFileDialog();
118 | ofd.Filter = "IPF/IPX file|*.ipf;*ipx|All Files|*.*";
119 | bool? ok = ofd.ShowDialog();
120 | if (ok == true) {
121 | processFile(ofd.FileName);
122 | }
123 | }
124 |
125 |
126 | private void processFile(string file) {
127 | fileName.Text = file;
128 | IPFReader ipf = new IPFReader(infoBox, cbDataElem);
129 | _fd = new Floppy();
130 | ipf.readIPF(file, _fd);
131 | }
132 |
133 | private void btWriteClick(object sender, RoutedEventArgs e) {
134 | if (_fd == null) {
135 | MessageBox.Show("Nothing to write", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
136 | return;
137 | }
138 |
139 | SaveFileDialog sfd = new SaveFileDialog();
140 | sfd.Filter = "IPF file|*.ipf|All Files|*.*";
141 |
142 | bool? ok = sfd.ShowDialog();
143 | if (ok == true) {
144 | fileName.Text = sfd.FileName;
145 | //if (File.Exists(sfd.FileName)) {
146 | // MessageBoxResult result = MessageBox.Show("Do you want to overwrite?", "File already exist", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.No);
147 | // if (result == MessageBoxResult.Cancel) return;
148 | // if (result == MessageBoxResult.No) goto askOutput;
149 | //}
150 | IPFWriter ipf = new IPFWriter(infoBox);
151 | ipf.writeIPF(sfd.FileName, _fd);
152 | }
153 |
154 | }
155 |
156 |
157 | private void btBlocksClick(object sender, RoutedEventArgs e) {
158 | if (_fd == null) {
159 | tbStatus.Text = "Nothing to display";
160 | return;
161 | }
162 | int trackNumber;
163 | int sideNumber;
164 | tbStatus.Clear();
165 | if (Int32.TryParse(tbTrack.Text, out trackNumber) == false) {
166 | tbStatus.Text = "Invalid track number - please correct and try again";
167 | return;
168 | }
169 | if (Int32.TryParse(tbSide.Text, out sideNumber) == false) {
170 | tbStatus.Text = "Invalid side number - please correct and try again";
171 | return;
172 | }
173 | if ((trackNumber < 0) || (trackNumber > 84) || (sideNumber < 0) || (sideNumber > 1)) {
174 | tbStatus.Text = "Track or Side out of range - please correct and try again";
175 | return;
176 | }
177 |
178 | if (_sectorWindowOpen)
179 | _sectorWindow.Close();
180 |
181 | _sectorWindow = new BufferWindow();
182 | _sectorWindow.displayBlocksBuffer(_fd, trackNumber, sideNumber);
183 | _sectorWindow.Closed += new EventHandler(sectorWindowClosed);
184 | _sectorWindowOpen = true;
185 | _sectorWindow.Show();
186 |
187 | }
188 |
189 |
190 | void sectorWindowClosed(object sender, EventArgs e) {
191 | _sectorWindowOpen = false;
192 | }
193 |
194 |
195 | private void mainWinClosing(object sender, System.ComponentModel.CancelEventArgs e) {
196 | if (_sectorWindowOpen) _sectorWindow.Close();
197 | if (_trackWindowOpen) _trackWindow.Close();
198 | }
199 |
200 |
201 | private void fileDropped(object sender, DragEventArgs e) {
202 | string[] droppedFiles = null;
203 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
204 | droppedFiles = e.Data.GetData(DataFormats.FileDrop, true) as string[];
205 | }
206 |
207 | if ((null == droppedFiles) || (!droppedFiles.Any())) { return; }
208 | processFile(droppedFiles[0]);
209 | }
210 |
211 |
212 | private void dragEnter(object sender, DragEventArgs e) {
213 | e.Handled = true;
214 | }
215 |
216 |
217 | private void btUpdateClick(object sender, RoutedEventArgs e) {
218 | bool update = checkForUpdate(true);
219 | if (update)
220 | InstallUpdate();
221 | }
222 |
223 | private void writeLogClick(object sender, RoutedEventArgs e) {
224 | SaveFileDialog sfd = new SaveFileDialog();
225 | //sfd.InitialDirectory = DirectoryName;
226 | //sfd.RestoreDirectory = true;
227 |
228 | sfd.Filter = "Text file|*.txt|All Files|*.*";
229 | if (sfd.ShowDialog() == true)
230 | File.WriteAllText(sfd.FileName, infoBox.Text);
231 |
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/ipf/IPFStruct.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Runtime.InteropServices;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace ipf {
9 | /// Type of media imaged
10 | public enum MediaType : uint { Unknown, Floppy_Disk };
11 |
12 | /// Image encoder Type
13 | public enum EncoderType : uint { Unknown, CAPS, SPS };
14 |
15 | /// Platform on which the image can be run
16 | /// Platform can take the following values:
17 | /// - 01 = Amiga
18 | /// - 02 = Atari ST
19 | /// - 03 = PC
20 | /// - 04 = Amstrad CPC
21 | /// - 05 = Spectrum
22 | /// - 06 = Sam Coupe
23 | /// - 07 = Archimedes
24 | /// - 08 = C64
25 | /// - 09 = Atari 8bit
26 | ///
27 | public enum Platform : uint {
28 | Unknown, Amiga, Atari_ST, PC, Amstrad_CPC, Spectrum, Sam_Coupe, Archimedes, C64, Atari_8bit
29 | }
30 |
31 | /// The cell density for this track
32 | public enum Density : uint {
33 | Unknown, Noise, Auto, Copylock_Amiga, Copylock_Amiga_New, Copylock_ST, Speedlock_Amiga,
34 | Speedlock_Amiga_Old, Adam_Brierley_Amiga, Adam_Brierley_Key_Amiga
35 | }
36 |
37 | /// Signal Processing Type
38 | public enum SignalType : uint { Unknown, cell_2us }
39 |
40 | /// Flags for the track
41 | [Flags]
42 | public enum TrackFlags : uint {
43 | None = 0x0,
44 | Fuzzy = 0x1
45 | }
46 |
47 | /// Block Encoder Type
48 | public enum BlockEncoderType : uint { Unknown, MFM, RAW }
49 |
50 |
51 | /// Flags for a block descriptor
52 | [Flags]
53 | public enum BlockFlags : uint {
54 | None = 0x00,
55 | FwGap = 0x01,
56 | BwGap = 0x02,
57 | DataInBit = 0x04
58 | }
59 |
60 | // Flags
61 | // https://msdn.microsoft.com/fr-fr/library/cc138362.aspx
62 | //if ((bd.blockFlags & BlockFlags.BwGap) == BlockFlags.BwGap) {
63 | //if (bd.blockFlags.HasFlag(BlockFlags.BwGap)) {
64 | //if(bd.blockFlags.Equals(BlockFlags.None))
65 |
66 |
67 | /// Type of Data in data element
68 | public enum DataType : uint { Unknown, Sync, Data, IGap, Raw, Fuzzy }
69 |
70 | /// Type of a gap element
71 | public enum GapType : uint { Unknown, Forward, Backward }
72 |
73 | /// Type of information in a gap element
74 | public enum GapElemType { Unknown, GapLength, SampleLength }
75 |
76 | /// IPF record Header Descriptor
77 | /// Each record in IPF file starts with a record header
78 | public class RecordHeader {
79 | /// Identify the record
80 | public char[] type = new char[4];
81 |
82 | /// Length of the record
83 | public uint length;
84 |
85 | /// CRC32 for the complete record
86 | public uint crc;
87 |
88 | /// Override ToString() to display record header info
89 | /// The record header string
90 | public override string ToString() {
91 | return String.Format("Record={0} Size={1} CRC={2:X8}",
92 | new string(type), length, crc);
93 | }
94 | } // RecordHeader
95 |
96 |
97 | ///
98 | /// The info record definition
99 | ///
100 | public class InfoRecord {
101 | /// Type of media imaged
102 | /// The only type currently recognized is floppy disks (value=1)
103 | public MediaType mediaType;
104 |
105 | /// image encoder type
106 | /// Depending on the encoder ID some fields in the IMGE records are interpreted differently.
107 | /// Currently this field can take the following values: 01 = CAPS encoder or 02 = SPS encoder
108 | ///
109 | public EncoderType encoderType;
110 |
111 | /// Image encoder revision
112 | /// Currently only revision 1 exists for CAPS or SPS encoders
113 | public uint encoderRev;
114 |
115 | ///
116 | /// Each IPF file has a unique ID that can be used as a unique key for database
117 | ///
118 | /// More than one file can have the same Key for example for a game that has more than one disk.
119 | public uint fileKey;
120 |
121 | /// Revision of the file.
122 | /// Normally the revision is one
123 | public uint fileRev;
124 |
125 | /// Reference to original source
126 | public uint origin;
127 |
128 | /// The first track number of the floppy image.
129 | /// Usually 0
130 | public uint minTrack;
131 |
132 | /// The last track number of the floppy image.
133 | /// Usually 83
134 | public uint maxTrack;
135 |
136 | /// The lowest head (side) number of the floppy image.
137 | /// Usually 0
138 | public uint minSide;
139 |
140 | /// The highest head (side) number of the floppy image. Usually 1
141 | public uint maxSide;
142 |
143 | /// The image creation date: Specify the year, the month, the day
144 | /// the date is packed into a 4 bytes integers.
145 | public uint creationDate;
146 |
147 | /// The image creation time: Specify the hour, the minute, the second, and the tick
148 | /// the time is packed into a 4 bytes integers.
149 | public uint creationTime;
150 |
151 | /// Array of four possible platforms
152 | /// This array contains 4 values that identifies on which platforms the image can be run
153 | /// (one image can be run on several platform).
154 | public Platform[] platforms = new Platform[4];
155 |
156 | /// Number of the disk in a multi-disc release otherwise 0
157 | public uint diskNumber;
158 |
159 | /// A unique user ID of the disk image creator
160 | public uint creatorId;
161 |
162 | /// reserved for future
163 | public uint[] reserved = new uint[3];
164 |
165 |
166 | /// Override ToString() to display info record content
167 | /// The record header string
168 | public override string ToString() {
169 | string date = String.Format("{0:D2}/{1:D2}/{2:D2}", creationDate / 10000, (creationDate/100) % 100, creationDate % 100);
170 | string time = String.Format("{0:D2}:{1:D2}:{2:D2}.{3:D3}", creationTime/10000000, (creationTime/100000) % 100,
171 | (creationTime/1000)%100, creationTime % 1000);
172 | string pstring = null;
173 | foreach (Platform p in platforms)
174 | if (p != 0) pstring += (p.ToString() + " ");
175 |
176 | return String.Format(
177 | " Type={0} Encoder={1}(V{2}) File={3}(V{4}) Disk={5} Origin={6:X8} Creator={7:X8}\n" +
178 | " Track={8:D2}-{9:D2} Side={10}-{11} Date={12} Time={13} Platforms={14}\n",
179 | mediaType.ToString(), encoderType.ToString(), encoderRev, fileKey, fileRev, diskNumber, origin,
180 | creatorId, minTrack, maxTrack, minSide, maxSide, date, time, pstring);
181 | }
182 | }
183 |
184 | ///
185 | /// The image record definition
186 | ///
187 | public class ImageRecord {
188 | public uint track;
189 | public uint side;
190 | public Density density;
191 | public SignalType signalType;
192 | public uint trackBytes;
193 | public uint startBytePos;
194 | public uint startBitPos;
195 | public uint dataBits;
196 | public uint gapBits;
197 | public uint trackBits;
198 | public uint blockCount;
199 | public uint encoder;
200 | public TrackFlags trackFlags;
201 | public uint dataKey;
202 | /// reserved for future
203 | public uint[] reserved = new uint[3];
204 |
205 | /// Override ToString() to display Image record content
206 | /// The record header string
207 | public override string ToString() {
208 | return String.Format(
209 | " T{0:D2}.{1} Size={2} bytes ({3} bits = Data={4} + Gap={5}) Start Byte={6} Bit={7}\n" +
210 | " DataKey={8:D3} Block={9} Density={10} Signal={11} Encoder={12} Flags={13}\n",
211 | track, side, trackBytes, trackBits, dataBits, gapBits, startBytePos, startBitPos,
212 | dataKey, blockCount, density.ToString(), signalType.ToString(), encoder, trackFlags.ToString());
213 | }
214 | }
215 |
216 |
217 | ///
218 | /// The Data record definition
219 | ///
220 | public class DataRecord {
221 | public uint length;
222 | public uint bitSize;
223 | public uint crc;
224 | public uint key;
225 | public override string ToString() {
226 | return String.Format("== DataKey={0:D3} Length={1} bytes bitSize={2} bits CRC={3:X8}",
227 | key, length, bitSize, crc);
228 | }
229 | }
230 |
231 |
232 | ///
233 | /// Block descriptor definition
234 | ///
235 | /// We use explicit position for an equivalent of union in C++
236 | [StructLayout(LayoutKind.Explicit)]
237 | public struct BlockDescriptor {
238 | [FieldOffset(0)]
239 | public uint dataBits;
240 | [FieldOffset(4)]
241 | public uint gapBits;
242 |
243 | [FieldOffset(8)] // CAPS
244 | public uint dataBytes;
245 | [FieldOffset(12)]
246 | public uint gapBytes;
247 |
248 | [FieldOffset(8)] // SPS
249 | public uint gapOffset;
250 | [FieldOffset(12)]
251 | public SignalType cellType;
252 |
253 | [FieldOffset(16)]
254 | public BlockEncoderType encoderType;
255 | [FieldOffset(20)]
256 | public BlockFlags blockFlags;
257 | [FieldOffset(24)]
258 | public uint gapDefaultValue;
259 | [FieldOffset(28)]
260 | public uint dataOffset;
261 | }
262 |
263 |
264 | public class CteiRecord {
265 | public uint releaseCrc;
266 | public uint analyzerRev;
267 | public uint[] reserved = new uint[14];
268 | public override string ToString() {
269 | return String.Format(" Associated IPF CRC={0:X8} Analyzer revision={1}\n",
270 | releaseCrc, analyzerRev);
271 | }
272 | }
273 |
274 |
275 | public class CtexRecord {
276 | public uint track;
277 | public uint side;
278 | public Density density;
279 | public uint formatId;
280 | public uint fix;
281 | public uint trackSize;
282 | public uint[] reserved = new uint[2];
283 | public override string ToString() {
284 | return String.Format(" T{0:D2}.{1} Density={2} Format={3} Fix={4} TrackSize={5}\n",
285 | track, side, density.ToString(), formatId, fix, trackSize);
286 | }
287 | }
288 |
289 |
290 | }
291 |
--------------------------------------------------------------------------------
/pasti/MainWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | /*!
2 | @file MainWindow.xaml.cs
3 | Main WPF window to call the reader
4 |
5 |
Copyright (C) 2014 Jean Louis-Guerin\n\n
6 | This file is part of the Atari Image Reader (AIR) project.\n
7 | The Atari Image Reader project may be used and distributed without restriction provided
8 | that this copyright statement is not removed from the file and that any
9 | derivative work contains the original copyright notice and the associated
10 | disclaimer.\n
11 | The Atari Image Reader project is free software; you can redistribute it
12 | and/or modify it under the terms of the GNU General Public License
13 | as published by the Free Software Foundation; either version 2
14 | of the License, or (at your option) any later version.\n
15 | The Atari Image Reader project is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 | See the GNU General Public License for more details.\n\n
19 | You should have received a copy of the GNU General Public License
20 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
22 |
23 | @author Jean Louis-Guerin
24 | */
25 |
26 | using Microsoft.Win32;
27 | using System;
28 | using System.Collections.Generic;
29 | using System.IO;
30 | using System.Linq;
31 | using System.Text;
32 | using System.Threading.Tasks;
33 | using System.Windows;
34 | using System.Windows.Controls;
35 | using System.Windows.Data;
36 | using System.Windows.Documents;
37 | using System.Windows.Input;
38 | using System.Windows.Media;
39 | using System.Windows.Media.Imaging;
40 | using System.Windows.Navigation;
41 | using System.Windows.Shapes;
42 |
43 | namespace Pasti {
44 | ///
45 | /// Interaction logic for MainWindow.xaml
46 | ///
47 | public partial class MainWindow : Window {
48 | Floppy _fd;
49 | BufferWindow _trackWindow = null;
50 | BufferWindow _sectorWindow = null;
51 | bool _trackWindowOpen = false;
52 | bool _sectorWindowOpen = false;
53 |
54 | public MainWindow() {
55 | InitializeComponent();
56 | //string version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
57 | Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
58 | DateTime dateTimeOfBuild = new DateTime(2000, 1, 1)
59 | + new TimeSpan(version.Build, 0, 0, 0)
60 | + TimeSpan.FromSeconds(version.Revision * 2);
61 | Title = "Pasti STX File Reader-Writer " + version.Major + "." + version.Minor + " - " + dateTimeOfBuild;
62 | }
63 |
64 |
65 | private void btFileClick(object sender, RoutedEventArgs e) {
66 | OpenFileDialog ofd = new OpenFileDialog();
67 | ofd.Filter = "Pasti file|*.stx|All Files|*.*";
68 | bool? ok = ofd.ShowDialog();
69 | if (ok == true) {
70 | processFile(ofd.FileName);
71 | }
72 | }
73 |
74 |
75 | private void processFile(string file) {
76 | PastiReader pasti = new PastiReader(infoBox);
77 | _fd = new Floppy();
78 | fileName.Text = file;
79 | pasti.readPasti(file, _fd);
80 | }
81 |
82 |
83 |
84 | private void btWriteClick(object sender, RoutedEventArgs e) {
85 |
86 | if (_fd == null) {
87 | MessageBox.Show("Nothing to write", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
88 | return;
89 | }
90 |
91 | SaveFileDialog sfd = new SaveFileDialog();
92 | sfd.Filter = "Pasti file|*.stx|All Files|*.*";
93 | // askOutput:
94 |
95 | bool? ok = sfd.ShowDialog();
96 | if (ok == true) {
97 | fileName.Text = sfd.FileName;
98 | //if (File.Exists(sfd.FileName)) {
99 | // MessageBoxResult result = MessageBox.Show("Do you want to overwrite?", "File already exist", MessageBoxButton.YesNoCancel, MessageBoxImage.Question, MessageBoxResult.No);
100 | // if (result == MessageBoxResult.Cancel) return;
101 | // if (result == MessageBoxResult.No) goto askOutput;
102 | //}
103 | PastiWriter pasti = new PastiWriter(infoBox);
104 | pasti.writePasti(sfd.FileName, _fd);
105 | }
106 | }
107 |
108 | private void btTrackClick(object sender, RoutedEventArgs e) {
109 | if (_fd == null) {
110 | tbStatus.Text = "Nothing to display";
111 | return;
112 | }
113 | int trackNumber;
114 | int sideNumber;
115 | tbStatus.Clear();
116 | if (Int32.TryParse(tbTrack.Text, out trackNumber) == false)
117 | tbStatus.Text = "Invalid track number - please correct and try again";
118 | if (Int32.TryParse(tbSide.Text, out sideNumber) == false)
119 | tbStatus.Text = "Invalid side number - please correct and try again";
120 |
121 | if ((trackNumber < 0) || (trackNumber > 84) || (sideNumber < 0) || (sideNumber > 1))
122 | tbStatus.Text = "Track or Side out of range - please correct and try again";
123 | if (_fd == null)
124 | tbStatus.Text = "Nothing to display";
125 |
126 | if (_trackWindowOpen)
127 | _trackWindow.Close();
128 |
129 | _trackWindow = new BufferWindow();
130 | _trackWindow.displayTrackBuffer(_fd, trackNumber, sideNumber);
131 | _trackWindow.Closed += new EventHandler(trackWindowClosed);
132 | _trackWindowOpen = true;
133 | _trackWindow.Show();
134 | }
135 |
136 | void trackWindowClosed(object sender, EventArgs e) {
137 | _trackWindowOpen = false;
138 | }
139 |
140 |
141 | private void btSectorsClick(object sender, RoutedEventArgs e) {
142 | if (_fd == null) {
143 | tbStatus.Text = "Nothing to display";
144 | return;
145 | }
146 | int trackNumber;
147 | int sideNumber;
148 | tbStatus.Clear();
149 | if (Int32.TryParse(tbTrack.Text, out trackNumber) == false)
150 | tbStatus.Text = "Invalid track number - please correct and try again";
151 | if (Int32.TryParse(tbSide.Text, out sideNumber) == false)
152 | tbStatus.Text = "Invalid side number - please correct and try again";
153 |
154 | if ((trackNumber < 0) || (trackNumber > 84) || (sideNumber < 0) || (sideNumber > 1))
155 | tbStatus.Text = "Track or Side out of range - please correct and try again";
156 | if (_fd == null)
157 | tbStatus.Text = "Nothing to display";
158 |
159 | if (_sectorWindowOpen)
160 | _sectorWindow.Close();
161 |
162 | _sectorWindow = new BufferWindow();
163 | _sectorWindow.displaySectorBuffer(_fd, trackNumber, sideNumber);
164 | _sectorWindow.Closed += new EventHandler(sectorWindowClosed);
165 | _sectorWindowOpen = true;
166 | _sectorWindow.Show();
167 | }
168 |
169 |
170 | void sectorWindowClosed(object sender, EventArgs e) {
171 | _sectorWindowOpen = false;
172 | }
173 |
174 |
175 | private void btAllSectorsClick(object sender, RoutedEventArgs e) {
176 | tbStatus.Clear();
177 | if (_fd == null) {
178 | tbStatus.Text = "Nothing to display";
179 | return;
180 | }
181 |
182 | SaveFileDialog sfd = new SaveFileDialog();
183 | sfd.Filter = "Text file|*.txt|All Files|*.*";
184 | if (sfd.ShowDialog() == true) {
185 | displayAllSectorBuffer(_fd, sfd.FileName);
186 | }
187 |
188 |
189 | }
190 |
191 | ///
192 | /// Display all the sectors
193 | ///
194 | /// Contains the DB
195 | /// The name of the open file
196 | public void displayAllSectorBuffer(Floppy floppy, string filename) {
197 | StringBuilder sb = new StringBuilder();
198 |
199 | for (int track = 0; track < 84; track++) {
200 | for (int side = 0; side < 2; side++) {
201 | Track t = floppy.tracks[track, side];
202 | if (t == null) continue;
203 |
204 | sb.Append(String.Format("Track {0:D2}.{1} has {2} sectors\n", track, side, t.sectorCount));
205 | for (int sect = 0; sect < t.sectorCount; sect++) {
206 | sb.Append(String.Format("Sector {0}", t.sectors[sect].id.number));
207 | sb.Append(String.Format(" T={0} H={1} N={2} S={3} CRC={4:X4}",
208 | t.sectors[sect].id.track, t.sectors[sect].id.side, t.sectors[sect].id.number, t.sectors[sect].id.size, t.sectors[sect].id.crc));
209 |
210 | if (t.sectors[sect].sectorData != null) {
211 | sb.Append(String.Format(" has {0} bytes\n", t.sectors[sect].sectorData.Count()));
212 | sb.Append(String.Format(" bitPosition {0}, Flags {1:X2}", t.sectors[sect].bitPosition, t.sectors[sect].fdcFlags));
213 | sb.Append(String.Format(" {0} {1}\n", ((t.sectors[sect].fdcFlags & SectorDesc.CRC_ERR) == 0) ?
214 | "Good CRC" : "Bad CRC", (t.sectors[sect].fuzzyData != null) ? "Has Fuzzy bytes" : ""));
215 | saveBuffer(t.sectors[sect].sectorData, sb);
216 | if (t.sectors[sect].fuzzyData != null) {
217 | sb.Append(String.Format("\nFuzzy bytes for sector {0}\n", t.sectors[sect].fuzzyData.Count()));
218 | saveBuffer(t.sectors[sect].fuzzyData, sb);
219 | }
220 | if (t.sectors[sect].timmingData != null) {
221 | sb.Append(String.Format("\nTiming values for sector {0}\n", t.sectors[sect].timmingData.Count()));
222 | byte[] timming = new byte[t.sectors[sect].timmingData.Count()];
223 | for (int i = 0; i < t.sectors[sect].timmingData.Count(); i++)
224 | timming[i] = (byte)t.sectors[sect].timmingData[i];
225 | saveBuffer(timming, sb);
226 | }
227 |
228 | }
229 | else
230 | sb.Append(" *** Sector has no data ***\n");
231 | sb.Append("\n");
232 | }
233 | }
234 | }
235 | using (StreamWriter outputfile = new StreamWriter(filename)) {
236 | outputfile.Write(sb.ToString());
237 | }
238 | }
239 |
240 |
241 | ///
242 | /// Save the content of the buffer
243 | ///
244 | /// Buffer to save
245 | /// Save in a string builder
246 | public void saveBuffer(byte[] buffer, StringBuilder sb) {
247 | for (int i = 0; i < buffer.Count(); i += 16) {
248 | sb.Append(String.Format("{0:D5} ", i));
249 |
250 | string ascii = " ";
251 | for (int j = 0; j < 16; j++) {
252 | if ((i + j) < buffer.Count()) {
253 | sb.Append(String.Format("{0:X2} ", buffer[i + j]));
254 | char ch = Convert.ToChar(buffer[i + j]);
255 | ascii += Char.IsControl(ch) ? "." : ch.ToString();
256 | }
257 | else
258 | sb.Append(String.Format(" "));
259 | } // one more line
260 | sb.Append(ascii);
261 | sb.Append(String.Format("\n"));
262 | } // all lines
263 | }
264 |
265 |
266 | private void fileDrop(object sender, DragEventArgs e) {
267 | string[] droppedFiles = null;
268 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
269 | droppedFiles = e.Data.GetData(DataFormats.FileDrop, true) as string[];
270 | }
271 |
272 | if ((null == droppedFiles) || (!droppedFiles.Any())) { return; }
273 | processFile(droppedFiles[0]);
274 | }
275 |
276 |
277 | private void dragEnter(object sender, DragEventArgs e) {
278 | e.Handled = true;
279 | }
280 |
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/ipf/IPFWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Controls;
9 |
10 | namespace ipf {
11 | class IPFWriter {
12 | private TextBox _infoBox;
13 |
14 | ///
15 | /// The IPF Writer Constructor
16 | ///
17 | /// The TextBox used to display information
18 | public IPFWriter(TextBox tb) {
19 | _infoBox = tb;
20 | }
21 |
22 |
23 | ///
24 | /// Write an IPF file from the Floppy structure
25 | ///
26 | /// Name of the Pasti file to read
27 | /// the Floppy parameter
28 | /// The status
29 | public bool writeIPF(string fileName, Floppy fd) {
30 | FileStream fs;
31 |
32 | // encode file into buffer
33 | List buffer = new List();
34 | bool status = encodeIPF(buffer, fd);
35 |
36 | try {
37 | fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read);
38 | }
39 | catch (Exception exc) {
40 | Console.WriteLine("Error: {0}", exc.Message);
41 | return false;
42 | }
43 | fs.Write(buffer.ToArray(), 0, buffer.Count);
44 | fs.Close();
45 |
46 | return status;
47 | }
48 |
49 |
50 | private bool encodeIPF(List buffer, Floppy fd) {
51 | uint crc;
52 | int start;
53 | int crc_pos;
54 |
55 | // write CAPS Record
56 | start = buffer.Count;
57 | writeByte(buffer, (byte)'C'); // File header
58 | writeByte(buffer, (byte)'A');
59 | writeByte(buffer, (byte)'P');
60 | writeByte(buffer, (byte)'S');
61 | writeInt32(buffer, 12); // record length
62 | crc_pos = buffer.Count;
63 | writeInt32(buffer, 0); // CRC
64 |
65 | crc = Utilities.crc32Buffer(buffer, 0, 12);
66 | writeInt32(buffer, crc_pos, crc);
67 |
68 | // in real life creating an info record may look like the commented code below
69 |
70 | //DateTime now = DateTime.Now;
71 | //int date = now.Year * 10000 + now.Month * 100 + now.Day;
72 | //int time = now.Hour * 10000000 + now.Minute * 100000 + now.Second * 1000 + now.Millisecond;
73 |
74 | //InfoRecord info = new InfoRecord();
75 | //info.mediaType = MediaType.Floppy_Disk;
76 | //info.encoderType = EncoderType.SPS;
77 | //info.encoderRev = 1;
78 | //info.fileKey = 0;
79 | //info.fileRev = 1;
80 | //info.origin = 0;
81 | //info.minTrack = 0;
82 | //info.maxTrack = 83;
83 | //info.minSide = 0;
84 | //info.maxSide = 1;
85 | //info.creationDate = (uint)date;
86 | //info.creationTime = (uint)time;
87 | //info.platforms = new Platform[4];
88 | //info.platforms[0] = Platform.Atari_ST;
89 | //info.diskNumber = 1;
90 | //info.creatorId = 0xAFAFAFAF;
91 | //info.reserved = new uint[3];
92 |
93 | // but to compare with original ipf file we just reuse the same content
94 | InfoRecord info = fd.info;
95 |
96 | // Write INFO Record
97 | start = buffer.Count;
98 | writeByte(buffer, (byte)'I'); // File header
99 | writeByte(buffer, (byte)'N');
100 | writeByte(buffer, (byte)'F');
101 | writeByte(buffer, (byte)'O');
102 | writeInt32(buffer, 96); // record length
103 | crc_pos = buffer.Count;
104 | writeInt32(buffer, 0); // CRC
105 |
106 | writeInt32(buffer, (uint)info.mediaType);
107 | writeInt32(buffer, (uint)info.encoderType);
108 | writeInt32(buffer, info.encoderRev);
109 | writeInt32(buffer, info.fileKey);
110 | writeInt32(buffer, info.fileRev);
111 | writeInt32(buffer, info.origin);
112 | writeInt32(buffer, info.minTrack);
113 | writeInt32(buffer, info.maxTrack);
114 | writeInt32(buffer, info.minSide);
115 | writeInt32(buffer, info.maxSide);
116 | writeInt32(buffer, info.creationDate);
117 | writeInt32(buffer, info.creationTime);
118 | for (int i = 0; i < 4; i++)
119 | writeInt32(buffer, (uint)info.platforms[i]);
120 | writeInt32(buffer, info.diskNumber);
121 | writeInt32(buffer, info.creatorId);
122 | for (int i = 0; i < 3; i++)
123 | writeInt32(buffer, info.reserved[i]);
124 |
125 | crc = Utilities.crc32Buffer(buffer, start, 96);
126 | writeInt32(buffer, crc_pos, crc);
127 |
128 | // write IMGE record
129 | uint dataKey = 1;
130 | for (uint track = 0; track < 84; track++) {
131 | for (uint side = 0; side < 2; side++) {
132 | Track t = fd.tracks[track, side];
133 | if (t == null) continue;
134 |
135 | ImageRecord image = new ImageRecord();
136 | image.track = track;
137 | image.side = side;
138 | image.density = t.density;
139 | image.signalType = SignalType.cell_2us;
140 | image.trackBytes = t.trackBytes;
141 | image.startBytePos = t.startBitPos / 8;
142 | image.startBitPos = t.startBitPos;
143 | image.dataBits = t.dataBits;
144 | image.gapBits = t.gapBits;
145 | image.trackBits = t.dataBits + t.gapBits;
146 | image.blockCount = t.blockCount;
147 | image.encoder = 0;
148 | image.trackFlags = t.trackFlags;
149 | image.dataKey = dataKey++;
150 | image.reserved = new uint[4];
151 |
152 | // Write IMGE Record
153 | start = buffer.Count;
154 | writeByte(buffer, (byte)'I'); // File header
155 | writeByte(buffer, (byte)'M');
156 | writeByte(buffer, (byte)'G');
157 | writeByte(buffer, (byte)'E');
158 | writeInt32(buffer, 80);
159 | crc_pos = buffer.Count;
160 | writeInt32(buffer, 0);
161 |
162 | writeInt32(buffer, image.track);
163 | writeInt32(buffer, image.side);
164 | writeInt32(buffer, (uint)image.density);
165 | writeInt32(buffer, (uint)image.signalType);
166 | writeInt32(buffer, image.trackBytes);
167 | writeInt32(buffer, image.startBytePos);
168 | writeInt32(buffer, image.startBitPos);
169 | writeInt32(buffer, image.dataBits);
170 | writeInt32(buffer, image.gapBits);
171 | writeInt32(buffer, image.trackBits);
172 | writeInt32(buffer, image.blockCount);
173 | writeInt32(buffer, image.encoder);
174 | writeInt32(buffer, (uint)image.trackFlags);
175 | writeInt32(buffer, image.dataKey);
176 | for (int i = 0; i < 3; i++)
177 | writeInt32(buffer, image.reserved[i]);
178 |
179 | crc = Utilities.crc32Buffer(buffer, start, 80);
180 | writeInt32(buffer, crc_pos, crc);
181 | }
182 | }
183 |
184 | dataKey = 1;
185 | for (uint track = 0; track < 84; track++) {
186 | for (uint side = 0; side < 2; side++) {
187 | Track t = fd.tracks[track, side];
188 | if (t == null) continue;
189 |
190 | start = buffer.Count;
191 | writeByte(buffer, (byte)'D'); // File header
192 | writeByte(buffer, (byte)'A');
193 | writeByte(buffer, (byte)'T');
194 | writeByte(buffer, (byte)'A');
195 | writeInt32(buffer, 28); // record length
196 | crc_pos = buffer.Count;
197 | writeInt32(buffer, 0); // CRC
198 |
199 | // here we build the Extra DATA Block buffer
200 | List extraDataBlock = new List();
201 |
202 | // we first write the block descriptors
203 | foreach (Sector sector in t.sectors) {
204 | BlockDescriptor bd = new BlockDescriptor();
205 | bd.dataBits = sector.dataBits;
206 | bd.gapBits = sector.gapBits;
207 | bd.gapOffset = 0;
208 | bd.cellType = SignalType.cell_2us;
209 | bd.encoderType = BlockEncoderType.MFM;
210 | bd.blockFlags = sector.flags;
211 | bd.gapDefaultValue = 0;
212 | bd.dataOffset = 0;
213 |
214 | writeInt32(extraDataBlock, bd.dataBits);
215 | writeInt32(extraDataBlock, bd.gapBits);
216 | writeInt32(extraDataBlock, bd.gapOffset);
217 | writeInt32(extraDataBlock, (uint)bd.cellType);
218 | writeInt32(extraDataBlock, (uint)bd.encoderType);
219 | writeInt32(extraDataBlock, (uint)bd.blockFlags);
220 | writeInt32(extraDataBlock, bd.gapDefaultValue);
221 | writeInt32(extraDataBlock, bd.dataOffset);
222 | }
223 |
224 | // then we write the set of Gap stream elements
225 | int gapOffset = t.sectors.Count * 32;
226 | int secNum = 0;
227 | foreach (Sector sector in t.sectors) {
228 |
229 | // correction of gapOffset in block descriptor
230 | if (!sector.flags.Equals(BlockFlags.None))
231 | writeInt32(extraDataBlock, (secNum * 32) + 8, (uint)gapOffset);
232 |
233 | if (sector.flags.HasFlag(BlockFlags.FwGap)) {
234 | foreach (GapElement ge in sector.gapElems) {
235 | if (ge.type != GapType.Forward) continue;
236 | writeGapElem(extraDataBlock, ge.gapBytes, ge.value);
237 | gapOffset += (ge.gapBytes == 0) ? 3 : ((ge.gapBytes * 8) < 256) ? 5 : 6;
238 | }
239 | writeByte(extraDataBlock, 0);
240 | gapOffset++;
241 | }
242 |
243 | if (sector.flags.HasFlag(BlockFlags.BwGap)) {
244 | foreach (GapElement ge in sector.gapElems) {
245 | if (ge.type != GapType.Backward) continue;
246 | writeGapElem(extraDataBlock, ge.gapBytes, ge.value);
247 | gapOffset += (ge.gapBytes == 0) ? 3 : ((ge.gapBytes * 8) < 256) ? 5 : 6;
248 | }
249 | writeByte(extraDataBlock, 0);
250 | gapOffset++;
251 | }
252 |
253 | secNum++;
254 | }
255 |
256 | // then we write the set of Data stream elements
257 | //uint dataOffset = (uint)(t.sectors.Count * 32) + gapOffset;
258 | int dataOffset = gapOffset;
259 | secNum = 0;
260 | foreach (Sector sector in t.sectors) {
261 | // correction of dataOffset in block descriptor
262 | writeInt32(extraDataBlock, (secNum * 32) + 28, (uint)dataOffset);
263 |
264 | foreach (DataElem de in sector.dataElems) {
265 | writeDataElem(extraDataBlock, de.type, de.dataBytes, de.value);
266 | // if fuzzy segment dataBytes is null
267 | dataOffset += ((de.dataBytes < 255) ? 2 : 3) + ((de.type != DataType.Fuzzy) ? (int)de.dataBytes : 0);
268 | }
269 | writeByte(extraDataBlock, 0);
270 | dataOffset++;
271 | secNum++;
272 | }
273 |
274 | // now we have the complete extra data segment
275 | // we can write the end of the Data record
276 | DataRecord data = new DataRecord();
277 | data.length = (uint)extraDataBlock.Count;
278 | data.bitSize = data.length * 8;
279 | data.crc = Utilities.crc32Buffer(extraDataBlock, 0, (int)data.length);
280 | data.key = dataKey++;
281 |
282 | writeInt32(buffer, data.length);
283 | writeInt32(buffer, data.bitSize);
284 | writeInt32(buffer, data.crc);
285 | writeInt32(buffer, data.key);
286 |
287 | crc = Utilities.crc32Buffer(buffer, start, 28);
288 | writeInt32(buffer, crc_pos, crc);
289 |
290 | // and now copy the extra segment to the buffer
291 | foreach (byte b in extraDataBlock)
292 | buffer.Add(b);
293 |
294 | } // all sides
295 | } // all tracks
296 |
297 | return true;
298 | }
299 |
300 |
301 | private void writeGapElem(List buffer, uint byteCount, byte value) {
302 | uint bitCount = byteCount * 8;
303 | // write GAP length
304 | if (bitCount > 0) {
305 | if (bitCount < 256) {
306 | writeByte(buffer, 0x21); // size=1 & type=1
307 | writeByte(buffer, (byte)bitCount);
308 | }
309 | else {
310 | writeByte(buffer, 0x41); // size=2 & type=1
311 | writeByte(buffer, (byte)(bitCount / 256));
312 | writeByte(buffer, (byte)(bitCount & 0xFF));
313 | }
314 | }
315 | // Write GAP sample value
316 | writeByte(buffer, 0x22); // size=1 & type=2
317 | writeByte(buffer, 8); // sample size
318 | writeByte(buffer, value);
319 | }
320 |
321 |
322 | private void writeDataElem(List buffer, DataType type, uint byteCount, List value) {
323 | byte dataHead = (byte)((int)type + ((byteCount < 256) ? 0x20 : 0x40));
324 | writeByte(buffer, dataHead);
325 | if (byteCount < 256) {
326 | writeByte(buffer, (byte)byteCount);
327 | }
328 | else {
329 | writeByte(buffer, (byte)(byteCount / 256));
330 | writeByte(buffer, (byte)(byteCount & 0xFF));
331 | }
332 | // Write value
333 | if (type != DataType.Fuzzy) {
334 | Debug.Assert(byteCount == value.Count, "Length error in dataElem");
335 | for (int i = 0; i < byteCount; i++) {
336 | writeByte(buffer, value[i]);
337 | }
338 | }
339 | }
340 |
341 |
342 | private static void writeInt32(List buffer, int buf_position, uint value) {
343 | buffer[buf_position] = (byte)((value >> 24) & 0xFF);
344 | buffer[buf_position + 1] = (byte)((value >> 16) & 0xFF);
345 | buffer[buf_position + 2] = (byte)((value >> 8) & 0xFF);
346 | buffer[buf_position + 3] = (byte)(value & 0xFF);
347 | }
348 |
349 |
350 | private static void writeInt32(List buffer, uint val) {
351 | buffer.Add((byte)((val >> 24) & 0xFF));
352 | buffer.Add((byte)((val >> 16) & 0xFF));
353 | buffer.Add((byte)((val >> 8) & 0xFF));
354 | buffer.Add((byte)(val & 0xFF));
355 | }
356 |
357 |
358 | private static void writeByte(List buffer, byte val) {
359 | buffer.Add(val);
360 | }
361 |
362 | }
363 | }
364 |
--------------------------------------------------------------------------------
/pasti/PastiRead.cs:
--------------------------------------------------------------------------------
1 | /*!
2 | @file PastiRead.cs
3 | This file provide a function to parse a Pasti file in structures
4 |
5 |
Copyright (C) 2014 Jean Louis-Guerin\n\n
6 | This file is part of the Atari Image Reader (AIR) project.\n
7 | The Atari Image Reader project may be used and distributed without restriction provided
8 | that this copyright statement is not removed from the file and that any
9 | derivative work contains the original copyright notice and the associated
10 | disclaimer.\n
11 | The Atari Image Reader project is free software; you can redistribute it
12 | and/or modify it under the terms of the GNU General Public License
13 | as published by the Free Software Foundation; either version 2
14 | of the License, or (at your option) any later version.\n
15 | The Atari Image Reader project is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 | See the GNU General Public License for more details.\n\n
19 | You should have received a copy of the GNU General Public License
20 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
22 |
23 | @author Jean Louis-Guerin
24 | */
25 |
26 | using System;
27 | using System.Diagnostics;
28 | using System.IO;
29 | using System.Windows;
30 | using System.Windows.Controls;
31 | using System.Windows.Media;
32 |
33 | namespace Pasti {
34 | ///
35 | /// The reader class
36 | ///
37 | public class PastiReader {
38 | TextBox _infoBox;
39 |
40 | ///
41 | /// The Pasti reader constructor
42 | ///
43 | /// The textbox used to display information
44 | public PastiReader(TextBox tb) {
45 | _infoBox = tb;
46 | }
47 |
48 | ///
49 | /// Status returned when reading Pasti file
50 | ///
51 | public enum PastiStatus {
52 | /// File read correctly
53 | Ok = 0,
54 | /// Could not open file for reading or file not found
55 | FileNotFound,
56 | /// The header in the file is not RSY
57 | NotPastiFile,
58 | /// Not version 3
59 | UnsupportedVersion
60 | }
61 |
62 |
63 | ///
64 | /// Read a Pasti file and fills the Floppy structure
65 | ///
66 | /// Name of the Pasti file to read
67 | /// the Floppy parameter
68 | /// The status
69 | public PastiStatus readPasti(string fileName, Floppy fd) {
70 | FileStream fs;
71 | try {
72 | fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read);
73 | }
74 | catch (Exception exc) {
75 | Console.WriteLine("Error: {0}", exc.Message);
76 | return PastiStatus.FileNotFound;
77 | }
78 |
79 | // read file into buffer
80 | byte[] sbuf = new byte[fs.Length];
81 | int bytes = fs.Read(sbuf, 0, (int)fs.Length);
82 | Debug.Assert(bytes == fs.Length); // should be same
83 | fs.Close();
84 | PastiStatus status = PastiStatus.Ok;
85 | try {
86 | status = decodePasti(sbuf, fd);
87 | }
88 | catch (IndexOutOfRangeException e) {
89 | MessageBox.Show("End of File - Missing data", "Error in Pasti File");
90 | return PastiStatus.NotPastiFile;
91 |
92 | }
93 | return status;
94 | }
95 |
96 |
97 | ///
98 | /// Decode the information in a Pasti file and fill the Floppy structure accordingly
99 | ///
100 | /// Contains the data read from Pasti file
101 | /// The Floppy structure to fill
102 | /// The read status
103 | private PastiStatus decodePasti(byte[] buffer, Floppy fd) {
104 | uint bpos = 0;
105 | uint startPos = 0;
106 | uint maxBufPos = 0;
107 |
108 | startPos = bpos;
109 | FileDesc file = new FileDesc();
110 | // Read File Descriptor
111 | file.pastiFileId += readChar(buffer, ref bpos);
112 | file.pastiFileId += readChar(buffer, ref bpos);
113 | file.pastiFileId += readChar(buffer, ref bpos);
114 | file.pastiFileId += readChar(buffer, ref bpos);
115 | file.version = readUInt16(buffer, ref bpos);
116 | file.tool = readUInt16(buffer, ref bpos);
117 | file.reserved_1 = readUInt16(buffer, ref bpos);
118 | file.trackCount = readByte(buffer, ref bpos);
119 | file.revision = readByte(buffer, ref bpos);
120 | file.reserved_2 = readUInt32(buffer, ref bpos);
121 |
122 | if (file.pastiFileId != "RSY\0")
123 | return PastiStatus.NotPastiFile;
124 | if (file.version != 3)
125 | return PastiStatus.UnsupportedVersion;
126 |
127 | _infoBox.Clear();
128 | _infoBox.FontFamily = new FontFamily("Consolas");
129 | _infoBox.FontSize = 14;
130 | _infoBox.AppendText(file.ToString());
131 | _infoBox.AppendText(String.Format(" - ({0}-{1})\n", startPos, bpos - 1));
132 | _infoBox.ScrollToHome();
133 |
134 | //fd.tracks = new Track[file.trackCount]; // create an array of track
135 | fd.tracks = new Track[85, 2];
136 | //fd.trackCount = file.trackCount; // store the number of tracks
137 | //fd.version = (byte)(file.version * 10 + file.revision);
138 | //fd.tool = file.tool;
139 |
140 | TrackDesc td = new TrackDesc();
141 | for (int tnum = 0; tnum < file.trackCount; tnum++) {
142 | uint track_record_start = bpos;
143 | startPos = bpos;
144 | Debug.Assert((startPos % 2) == 0, "Track Descriptor not word aligned", String.Format("Address = {0}", startPos));
145 | // Read Track Descriptors
146 | td.recordSize = readUInt32(buffer, ref bpos);
147 | td.fuzzyCount = readUInt32(buffer, ref bpos);
148 | td.sectorCount = readUInt16(buffer, ref bpos);
149 | td.trackFlags = readUInt16(buffer, ref bpos);
150 | td.trackLength = readUInt16(buffer, ref bpos);
151 | td.trackNumber = readByte(buffer, ref bpos);
152 | td.trackType = readByte(buffer, ref bpos);
153 | maxBufPos = bpos;
154 | int track = td.trackNumber & 0x7F;
155 | int side = (td.trackNumber & 0x80) >> 7;
156 |
157 | _infoBox.AppendText(td.ToString());
158 | _infoBox.AppendText(String.Format(" ({0}-{1})\n", startPos, bpos - 1));
159 |
160 | fd.tracks[track, side] = new Track();
161 |
162 | // create array of sector for this track
163 | fd.tracks[track, side].sectors = new Sector[td.sectorCount];
164 | fd.tracks[track, side].sectorCount = td.sectorCount;
165 | fd.tracks[track, side].byteCount = td.trackLength;
166 | fd.tracks[track, side].side = (uint)(td.trackNumber >> 7);
167 | fd.tracks[track, side].number = (uint)(td.trackNumber & 0x7F);
168 |
169 | // read sector descriptors if specified
170 | if ((td.trackFlags & TrackDesc.TRK_SECT_DESC) != 0) {
171 | SectorDesc[] sectors = new SectorDesc[td.sectorCount];
172 | for (int snum = 0; snum < td.sectorCount; snum++) {
173 | sectors[snum] = new SectorDesc();
174 | startPos = bpos;
175 |
176 | // read sector descriptor
177 | sectors[snum].dataOffset = readUInt32(buffer, ref bpos);
178 | sectors[snum].bitPosition = readUInt16(buffer, ref bpos);
179 | sectors[snum].readTime = readUInt16(buffer, ref bpos);
180 | sectors[snum].id.track = readByte(buffer, ref bpos);
181 | sectors[snum].id.side = readByte(buffer, ref bpos);
182 | sectors[snum].id.number = readByte(buffer, ref bpos);
183 | sectors[snum].id.size = readByte(buffer, ref bpos);
184 | sectors[snum].id.crc = readUInt16(buffer, ref bpos);
185 | sectors[snum].fdcFlags = readByte(buffer, ref bpos);
186 | sectors[snum].reserved = readByte(buffer, ref bpos);
187 |
188 | _infoBox.AppendText(sectors[snum].ToString());
189 | _infoBox.AppendText(String.Format(" ({0}-{1})\n", startPos, bpos - 1));
190 |
191 | fd.tracks[track, side].sectors[snum] = new Sector();
192 | fd.tracks[track, side].sectors[snum].fdcFlags = sectors[snum].fdcFlags;
193 | fd.tracks[track, side].sectors[snum].bitPosition = sectors[snum].bitPosition;
194 | fd.tracks[track, side].sectors[snum].readTime = sectors[snum].readTime;
195 | fd.tracks[track, side].sectors[snum].id = sectors[snum].id;
196 | fd.tracks[track, side].sectors[snum].fdcFlags = sectors[snum].fdcFlags;
197 | } // for all sectors
198 |
199 | byte[] fuzzyMask = null;
200 | // if necessary read fuzzy bytes
201 | if (td.fuzzyCount != 0) {
202 | startPos = bpos;
203 | fuzzyMask = new byte[td.fuzzyCount];
204 | for (int i = 0; i < td.fuzzyCount; i++)
205 | fuzzyMask[i] = readByte(buffer, ref bpos);
206 | _infoBox.AppendText(String.Format(" Reading Fuzzy {0} bytes ({1}-{2})\n", td.fuzzyCount, startPos, bpos - 1));
207 | }
208 |
209 | // the track data are located after the sector descriptor records & fuzzy record
210 | uint track_data_start = bpos; // store the position of data in track record
211 | _infoBox.AppendText(String.Format(" Start of Track data {0}\n", track_data_start));
212 |
213 | // read track data if specified
214 | if ((td.trackFlags & TrackDesc.TRK_IMAGE) != 0) {
215 | // track with sync offset
216 | startPos = bpos;
217 | // read sync if provided
218 | if ((td.trackFlags & TrackDesc.TRK_SYNC) != 0)
219 | fd.tracks[track, side].firstSyncOffset = readUInt16(buffer, ref bpos);
220 | else
221 | fd.tracks[track, side].firstSyncOffset = 0;
222 | // read track data size
223 | fd.tracks[track, side].byteCount = readUInt16(buffer, ref bpos);
224 | // read track data
225 | fd.tracks[track, side].trackData = new byte[fd.tracks[track, side].byteCount];
226 | for (int i = 0; i < fd.tracks[track, side].byteCount; i++)
227 | fd.tracks[track, side].trackData[i] = readByte(buffer, ref bpos);
228 | // seems like we are reading even number
229 | maxBufPos = Math.Max(maxBufPos, bpos + bpos % 2);
230 | fd.tracks[track, side].standardTrack = false;
231 | _infoBox.AppendText(String.Format(" Reading Track Image {0}{1} bytes (including {2} header bytes) SyncPos={3} ({4}-{5})\n",
232 | fd.tracks[track, side].byteCount + (((td.trackFlags & TrackDesc.TRK_SYNC) != 0) ? 4 : 2), (bpos % 2 != 0) ? "+1" : "",
233 | ((td.trackFlags & TrackDesc.TRK_SYNC) != 0) ? 4 : 2, fd.tracks[track, side].firstSyncOffset, startPos, bpos + (bpos % 2) - 1));
234 | } // read track_data
235 |
236 | // read all sectors data
237 | bool bitWidth = false;
238 | for (int snum = 0; snum < td.sectorCount; snum++) {
239 | if (((sectors[snum].fdcFlags & SectorDesc.BIT_WIDTH) != 0) && (file.revision == 2))
240 | bitWidth = true;
241 |
242 | // create sector buffer and read data if necessary
243 | if ((sectors[snum].fdcFlags & SectorDesc.SECT_RNF) == 0) {
244 | fd.tracks[track, side].sectors[snum].sectorData = new byte[128 << sectors[snum].id.size];
245 | bpos = track_data_start + sectors[snum].dataOffset;
246 | startPos = bpos;
247 | for (int i = 0; i < 128 << sectors[snum].id.size; i++)
248 | fd.tracks[track, side].sectors[snum].sectorData[i] = readByte(buffer, ref bpos);
249 | maxBufPos = Math.Max(maxBufPos, bpos);
250 | _infoBox.AppendText(String.Format(" Reading Sector {0} {1} bytes ({2}-{3}) {4}\n",
251 | fd.tracks[track, side].sectors[snum].id.number, 128 << sectors[snum].id.size, startPos, bpos - 1,
252 | (((td.trackFlags & TrackDesc.TRK_IMAGE) != 0) && (sectors[snum].dataOffset < fd.tracks[track, side].byteCount)) ? " in Track Image" : ""));
253 | } // read sector data
254 | } // for all sectors
255 |
256 | // if we have timing record we read
257 | ushort[] timing = null;
258 | if (bitWidth) {
259 | bpos = maxBufPos;
260 | startPos = bpos;
261 | ushort timingFlags = readUInt16(buffer, ref bpos);
262 | ushort timingSize = readUInt16(buffer, ref bpos);
263 | int entries = (timingSize - 4) / 2;
264 | timing = new ushort[entries];
265 | for (int i = 0; i < entries; i++) {
266 | // Big Indian value
267 | timing[i] = (ushort)(readByte(buffer, ref bpos) << 8);
268 | timing[i] = readByte(buffer, ref bpos);
269 | }
270 | maxBufPos = Math.Max(maxBufPos, bpos);
271 | _infoBox.AppendText(String.Format(" Reading Timing {0} bytes Flag={1} ({2}-{3})\n", timingSize, timingFlags, startPos, bpos - 1));
272 | } // sector has timing data
273 |
274 | // now we transfer the fuzzy bytes and timing bytes if necessary
275 | int startFuzzy = 0;
276 | int startTiming = 0;
277 | for (int snum = 0; snum < td.sectorCount; snum++) {
278 | // fuzzy bytes
279 | if ((fd.tracks[track, side].sectors[snum].fdcFlags & SectorDesc.FUZZY_BITS) != 0) {
280 | int fsize = 128 << sectors[snum].id.size;
281 | fd.tracks[track, side].sectors[snum].fuzzyData = new byte[fsize];
282 | for (int i = 0; i < fsize; i++)
283 | fd.tracks[track, side].sectors[snum].fuzzyData[i] = fuzzyMask[i + startFuzzy];
284 | startFuzzy += fsize;
285 | _infoBox.AppendText(String.Format(" Transferring {0} Fuzzy bytes to sect {1}\n", fsize, fd.tracks[track, side].sectors[snum].id.number));
286 | }
287 | // timing bytes
288 | if ((sectors[snum].fdcFlags & SectorDesc.BIT_WIDTH) != 0) {
289 | int tsize = (128 << sectors[snum].id.size) / 16;
290 | fd.tracks[track, side].sectors[snum].timmingData = new ushort[tsize];
291 | if (file.revision == 2) {
292 | for (int i = 0; i < tsize; i++)
293 | fd.tracks[track, side].sectors[snum].timmingData[i] = timing[i + startTiming];
294 | startTiming += tsize;
295 | _infoBox.AppendText(String.Format(" Transferring {0} Timing values to sect {1}\n", tsize, fd.tracks[track, side].sectors[snum].id.number));
296 | } // revision == 2 => read timing from file
297 | else {
298 | for (int i = 0; i < tsize; i++) {
299 | if (i < (tsize / 4)) fd.tracks[track, side].sectors[snum].timmingData[i] = 127;
300 | else if (i < (tsize / 2)) fd.tracks[track, side].sectors[snum].timmingData[i] = 133;
301 | else if (i < ((3 * tsize) / 2)) fd.tracks[track, side].sectors[snum].timmingData[i] = 121;
302 | else fd.tracks[track, side].sectors[snum].timmingData[i] = 127;
303 | }
304 | } // if revision != 2 => we simulate with a table
305 | }
306 | }
307 | if (timing != null)
308 | Debug.Assert(timing.Length == startTiming, "invalid timing size");
309 | if (fuzzyMask != null)
310 | Debug.Assert(fuzzyMask.Length == startFuzzy, "invalid fuzzy size");
311 | } // TRK_SECT provided
312 |
313 | else {
314 | // read all standard sectors
315 | for (int snum = 0; snum < td.sectorCount; snum++) {
316 | startPos = bpos;
317 | fd.tracks[track, side].sectors[snum] = new Sector();
318 | fd.tracks[track, side].sectors[snum].sectorData = new byte[512];
319 | for (int i = 0; i < 512; i++)
320 | fd.tracks[track, side].sectors[snum].sectorData[i] = readByte(buffer, ref bpos);
321 |
322 | fd.tracks[track, side].sectors[snum].fdcFlags = 0;
323 | fd.tracks[track, side].sectors[snum].bitPosition = 0;
324 | fd.tracks[track, side].sectors[snum].readTime = 0;
325 | fd.tracks[track, side].sectors[snum].fuzzyData = null;
326 | fd.tracks[track, side].sectors[snum].timmingData = null;
327 | fd.tracks[track, side].sectors[snum].id.track = (byte)(td.trackNumber & 0x7F);
328 | fd.tracks[track, side].sectors[snum].id.side = (byte)(((td.trackNumber & 0x80) == 0x80) ? 1 : 0);
329 | fd.tracks[track, side].sectors[snum].id.number = (byte)snum;
330 | fd.tracks[track, side].sectors[snum].id.crc = 0;
331 | _infoBox.AppendText(String.Format(" Standard Sector {0} {1} bytes ({2}-{3})\n", snum, 512, startPos, bpos));
332 | maxBufPos = Math.Max(maxBufPos, bpos);
333 | } // read all sector
334 | } // standard non protected sectors
335 |
336 | // we set record size to even value
337 | maxBufPos += maxBufPos % 2;
338 |
339 | Debug.Assert(maxBufPos == track_record_start + td.recordSize, "Invalid Track Size",
340 | String.Format("Track {0} Current pointer position = {1} next track = {2}", td.trackNumber, bpos, track_record_start + td.recordSize));
341 | bpos = track_record_start + td.recordSize;
342 | } // for all tracks
343 |
344 | return PastiStatus.Ok;
345 |
346 | } // pasti/pasti file
347 |
348 |
349 | private static uint readUInt32(byte[] buf, ref uint pos) {
350 | uint val = (uint)buf[pos] + (uint)(buf[pos + 1] << 8) + (uint)(buf[pos + 2] << 16) + (uint)(buf[pos + 3] << 24);
351 | pos += 4;
352 | return val;
353 | }
354 |
355 |
356 | private static ushort readUInt16(byte[] buf, ref uint pos) {
357 | ushort val = (ushort)buf[pos];
358 | val += (ushort)(buf[pos + 1] << 8);
359 | pos += 2;
360 | return val;
361 | }
362 |
363 |
364 | private static byte readByte(byte[] buf, ref uint pos) {
365 | return buf[pos++];
366 | }
367 |
368 |
369 | private static char readChar(byte[] buf, ref uint pos) {
370 | return (char)buf[pos++];
371 | }
372 |
373 | }
374 |
375 | }
--------------------------------------------------------------------------------
/pasti/PastiWrite.cs:
--------------------------------------------------------------------------------
1 | /*!
2 | @file PastiWrite.cs
3 | This file provide a function to write a Pasti file from structures
4 |
5 |
Copyright (C) 2014 Jean Louis-Guerin\n\n
6 | This file is part of the Atari Image Reader (AIR) project.\n
7 | The Atari Image Reader project may be used and distributed without restriction provided
8 | that this copyright statement is not removed from the file and that any
9 | derivative work contains the original copyright notice and the associated
10 | disclaimer.\n
11 | The Atari Image Reader project is free software; you can redistribute it
12 | and/or modify it under the terms of the GNU General Public License
13 | as published by the Free Software Foundation; either version 2
14 | of the License, or (at your option) any later version.\n
15 | The Atari Image Reader project is distributed in the hope that it will be useful,
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 | See the GNU General Public License for more details.\n\n
19 | You should have received a copy of the GNU General Public License
20 | along with the Atari Universal FD Image Tool project; if not, write to the Free Software
21 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA\n
22 |
23 | @author Jean Louis-Guerin
24 | */
25 | using System;
26 | using System.Collections.Generic;
27 | using System.Diagnostics;
28 | using System.IO;
29 | using System.Linq;
30 | using System.Text;
31 | using System.Threading.Tasks;
32 | using System.Windows.Controls;
33 | using System.Windows.Media;
34 |
35 | namespace Pasti {
36 | ///
37 | /// The Pasti Writer Class
38 | ///
39 | class PastiWriter {
40 | TextBox _infoBox;
41 |
42 | ///
43 | /// The Pasti Writer Constructor
44 | ///
45 | /// The textbox used to display information
46 | public PastiWriter(TextBox tb) {
47 | _infoBox = tb;
48 | }
49 |
50 | ///
51 | /// Status returned when reading Pasti file
52 | ///
53 | public enum WriteStatus {
54 | /// File read correctly
55 | Ok = 0,
56 | /// Could not open file for reading or file not found
57 | FileNotFound,
58 | /// The header in the file is not RSY
59 | NotPastiFile,
60 | /// Not version 3
61 | UnsupportedVersion
62 | }
63 |
64 |
65 | ///
66 | /// Read a Pasti file and fills the Floppy structure
67 | ///
68 | /// Name of the Pasti file to read
69 | /// the Floppy parameter
70 | /// The status
71 | public WriteStatus writePasti(string fileName, Floppy fd) {
72 | FileStream fs;
73 |
74 | // encode file into buffer
75 | List sbuf = new List();
76 | WriteStatus status = encodePasti(sbuf, fd);
77 |
78 |
79 | try {
80 | fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.Read);
81 | }
82 | catch (Exception exc) {
83 | Console.WriteLine("Error: {0}", exc.Message);
84 | return WriteStatus.FileNotFound;
85 | }
86 | fs.Write(sbuf.ToArray(), 0 , sbuf.Count);
87 | fs.Close();
88 |
89 | return status;
90 | }
91 |
92 |
93 | ///
94 | /// Encode the information in a Floppy structure into a buffer using Pasti file structure
95 | ///
96 | /// Contains the data read from Pasti file
97 | /// The Floppy structure to fill
98 | /// The read status
99 | private WriteStatus encodePasti(List buffer, Floppy floppy) {
100 | int start = 0;
101 |
102 | // compute the number of tracks in file
103 | int trackCount = 0;
104 | for (int track = 0; track < 84; track++)
105 | for (int side = 0; side < 2; side++)
106 | if (floppy.tracks[track, side] != null) trackCount++;
107 |
108 | FileDesc fd = new FileDesc();
109 | fd.version = 3;
110 | fd.revision = 2;
111 | //fd.trackCount = floppy.trackCount;
112 | fd.tool = 0x0FF;
113 |
114 | writeByte(buffer, (byte)'R'); // File header
115 | writeByte(buffer, (byte)'S');
116 | writeByte(buffer, (byte)'Y');
117 | writeByte(buffer, 0);
118 |
119 | writeInt16(buffer, fd.version); // version
120 | writeInt16(buffer, fd.tool); // tool
121 | writeInt16(buffer, 0); // reserved 1
122 | writeByte(buffer, fd.trackCount); // trackCount
123 | writeByte(buffer, fd.revision); // revision
124 | writeInt32(buffer, 0); // reserved 2
125 |
126 | _infoBox.Clear();
127 | _infoBox.ScrollToHome();
128 | _infoBox.FontFamily = new FontFamily("Consolas");
129 | _infoBox.FontSize = 14;
130 | _infoBox.AppendText(fd.ToString());
131 | _infoBox.AppendText(String.Format(" - ({0}-{1})\n", start, buffer.Count-1));
132 |
133 | // Write all track records
134 | //for (int tnum = 0; tnum < floppy.trackCount; tnum++) {
135 | for (int track = 0; track < 84; track++)
136 | for (int side = 0; side < 2; side++) {
137 | if (floppy.tracks[track, side] == null) continue;
138 | int startTrackRecord = buffer.Count;
139 | TrackDesc td = new TrackDesc();
140 |
141 | td.recordSize = 16; // initial value = size of TrackDesc
142 | //track.trackFlags = 0x0010; // Only bit 5 set to 1 initially
143 | td.sectorCount = (ushort)floppy.tracks[track, side].sectors.Count();
144 | td.trackLength = (ushort)floppy.tracks[track, side].byteCount;
145 | td.trackNumber = (byte)floppy.tracks[track, side].number;
146 | if (floppy.tracks[track, side].side == 1) td.trackNumber |= 0x80;
147 | td.trackType = 0;
148 |
149 | // do we have sector descriptors
150 | if (trackNeedSectorDesc(floppy.tracks[track, side])) {
151 | td.trackFlags |= TrackDesc.TRK_SECT_DESC;
152 | td.recordSize += (uint)(16 * td.sectorCount);
153 | }
154 |
155 | // do we have fuzzy record
156 | for (int sect = 0; sect < floppy.tracks[track, side].sectors.Count(); sect++)
157 | if (floppy.tracks[track, side].sectors[sect].fuzzyData != null)
158 | td.fuzzyCount += (uint)floppy.tracks[track, side].sectors[sect].fuzzyData.Count();
159 | td.recordSize += td.fuzzyCount;
160 |
161 | bool trkSync = false;
162 | // do we have track data
163 | if (trackNeedTrackData(floppy.tracks[track, side])) {
164 | if (trkSync) {
165 | td.trackFlags |= TrackDesc.TRK_IMAGE | TrackDesc.TRK_SYNC;
166 | td.recordSize += 4; // header
167 | }
168 | else {
169 | td.trackFlags |= TrackDesc.TRK_IMAGE;
170 | td.recordSize += 2; // header
171 | }
172 | td.recordSize += floppy.tracks[track, side].byteCount;
173 | // we write even number of bytes
174 | td.recordSize += floppy.tracks[track, side].byteCount % 2;
175 | }
176 |
177 | // do we have timing record
178 | int timingEntries = 0;
179 | for (int sect = 0; sect < floppy.tracks[track, side].sectors.Count(); sect++)
180 | if (floppy.tracks[track, side].sectors[sect].timmingData != null)
181 | timingEntries += floppy.tracks[track, side].sectors[sect].timmingData.Count();
182 |
183 | if (timingEntries > 0)
184 | td.recordSize += (uint)((2 * timingEntries) + 4);
185 |
186 | // we had up the sector data - here we do not optimize (data shared with track data)
187 | for (int sect = 0; sect < floppy.tracks[track, side].sectors.Count(); sect++)
188 | if (floppy.tracks[track, side].sectors[sect].sectorData != null)
189 | td.recordSize += (uint)floppy.tracks[track, side].sectors[sect].sectorData.Count();
190 |
191 | //// seems like track record size is always kept even
192 | //td.recordSize += td.recordSize % 2;
193 |
194 | // Track Descriptor is ready => write it
195 | start = buffer.Count;
196 | writeInt32(buffer, td.recordSize);
197 | writeInt32(buffer, td.fuzzyCount);
198 | writeInt16(buffer, td.sectorCount);
199 | writeInt16(buffer, td.trackFlags);
200 | writeInt16(buffer, td.trackLength);
201 | writeByte(buffer, td.trackNumber);
202 | writeByte(buffer, td.trackType);
203 |
204 | _infoBox.AppendText(td.ToString());
205 | _infoBox.AppendText(String.Format(" ({0}-{1})\n", start, buffer.Count - 1));
206 |
207 | // write sector descriptor if needed
208 | if (trackNeedSectorDesc(floppy.tracks[track, side])) {
209 | uint offset = 0;
210 | if (trackNeedTrackData(floppy.tracks[track, side]))
211 | offset = (uint)(floppy.tracks[track, side].byteCount + (floppy.tracks[track, side].byteCount % 2) + (trkSync ? 4 : 2));
212 | for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
213 | SectorDesc sd = new SectorDesc();
214 | sd.dataOffset = offset;
215 | sd.bitPosition = floppy.tracks[track, side].sectors[sect].bitPosition;
216 | sd.readTime = floppy.tracks[track, side].sectors[sect].readTime;
217 | sd.id = floppy.tracks[track, side].sectors[sect].id; // TODO ?
218 | sd.fdcFlags = floppy.tracks[track, side].sectors[sect].fdcFlags;
219 | sd.reserved = 0;
220 | if ((floppy.tracks[track, side].sectors[sect].fdcFlags & SectorDesc.SECT_RNF) == 0)
221 | offset += (uint)(128 << floppy.tracks[track, side].sectors[sect].id.size);
222 |
223 | start = buffer.Count;
224 | writeInt32(buffer, sd.dataOffset);
225 | writeInt16(buffer, sd.bitPosition);
226 | writeInt16(buffer, sd.readTime);
227 |
228 | writeByte(buffer, sd.id.track);
229 | writeByte(buffer, sd.id.side);
230 | writeByte(buffer, sd.id.number);
231 | writeByte(buffer, sd.id.size);
232 | writeInt16(buffer, sd.id.crc);
233 |
234 | writeByte(buffer, sd.fdcFlags);
235 | writeByte(buffer, sd.reserved);
236 |
237 | _infoBox.AppendText(sd.ToString());
238 | _infoBox.AppendText(String.Format(" ({0}-{1})\n", start, buffer.Count - 1));
239 | }
240 | }
241 |
242 | // write fuzzy mask if needed
243 | for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
244 | if (floppy.tracks[track, side].sectors[sect].fuzzyData != null) {
245 | start = buffer.Count;
246 | for (int i = 0; i < floppy.tracks[track, side].sectors[sect].fuzzyData.Count(); i++)
247 | writeByte(buffer, floppy.tracks[track, side].sectors[sect].fuzzyData[i]);
248 | _infoBox.AppendText(String.Format(" Writing Fuzzy {0} bytes for sector {1} ({2}-{3})\n",
249 | floppy.tracks[track, side].sectors[sect].fuzzyData.Count(), floppy.tracks[track, side].sectors[sect].id.number, start, buffer.Count - 1));
250 | } // fuzzy found
251 | } // all sect
252 |
253 |
254 | // the track data are located after the sector descriptor records & fuzzy record
255 | int track_data_start = buffer.Count; // store the position of data in track record
256 | _infoBox.AppendText(String.Format(" Start of Track data {0}\n", track_data_start));
257 |
258 | // write track data if needed
259 | if (trackNeedTrackData(floppy.tracks[track, side])) {
260 | start = buffer.Count;
261 | if (trkSync)
262 | writeInt16(buffer, floppy.tracks[track, side].firstSyncOffset);
263 | writeInt16(buffer, (ushort)floppy.tracks[track, side].byteCount);
264 | for (int i = 0; i < floppy.tracks[track, side].byteCount; i++)
265 | writeByte(buffer, floppy.tracks[track, side].trackData[i]);
266 | // we write even number
267 | if ((floppy.tracks[track, side].byteCount % 2) != 0)
268 | writeByte(buffer, 0xFF);
269 |
270 | _infoBox.AppendText(String.Format(" Writing Track {0}{1} bytes SyncPos={2} ({3}-{4})\n",
271 | floppy.tracks[track, side].byteCount + ((trkSync) ? 4 : 2), ((floppy.tracks[track, side].byteCount % 2) != 0) ? "(+1)" : "",
272 | floppy.tracks[track, side].firstSyncOffset, start, buffer.Count - 1));
273 | }
274 |
275 | // write all sectors data
276 | for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
277 | start = buffer.Count;
278 | if ((floppy.tracks[track, side].sectors[sect].fdcFlags & SectorDesc.SECT_RNF) == 0) {
279 | for (int i = 0; i < floppy.tracks[track, side].sectors[sect].sectorData.Count(); i++)
280 | writeByte(buffer, floppy.tracks[track, side].sectors[sect].sectorData[i]);
281 | _infoBox.AppendText(String.Format(" Writing Sector {0} {1} bytes ({2}-{3})\n",
282 | floppy.tracks[track, side].sectors[sect].id.number, floppy.tracks[track, side].sectors[sect].sectorData.Count(), start, buffer.Count - 1));
283 | }
284 | }
285 |
286 | // write timing if necessary
287 | if (timingEntries > 0) {
288 | // write header
289 | start = buffer.Count;
290 | writeInt16(buffer, 0x05);
291 | writeInt16(buffer, (ushort)(2 * timingEntries + 4));
292 | _infoBox.AppendText(String.Format(" Writing Timing header Flag=5 size={0} ({1}-{2})\n", 2 * timingEntries + 4, start, buffer.Count - 1));
293 | // write timing
294 | for (int sect = 0; sect < floppy.tracks[track, side].sectorCount; sect++) {
295 | if (floppy.tracks[track, side].sectors[sect].timmingData != null) {
296 | start = buffer.Count;
297 | for (int i = 0; i < floppy.tracks[track, side].sectors[sect].timmingData.Count(); i++) {
298 | // Big Indian value
299 | buffer.Add((byte)(floppy.tracks[track, side].sectors[sect].timmingData[i] << 8));
300 | buffer.Add((byte)floppy.tracks[track, side].sectors[sect].timmingData[i]);
301 | }
302 | _infoBox.AppendText(String.Format(" Writing Timing {0} values for sector {1} ({2}-{3})\n",
303 | floppy.tracks[track, side].sectors[sect].timmingData.Count(), floppy.tracks[track, side].sectors[sect].id.number, start, buffer.Count - 1));
304 | } // timing found
305 | } // all sect
306 | }
307 |
308 | if (buffer.Count % 2 != 0) {
309 | writeByte(buffer, 0xFF); // dummy extra byte
310 | } // even
311 |
312 | Debug.Assert(buffer.Count == startTrackRecord + td.recordSize, "Invalid Track Size",
313 | String.Format("Track {0} Current pointer position = {1} next track = {2}", td.trackNumber, buffer.Count, startTrackRecord + td.recordSize));
314 | } // for all tracks
315 |
316 | return WriteStatus.Ok;
317 |
318 | } // encode pasti
319 |
320 |
321 | private static void writeInt32(List buffer, uint val) {
322 | buffer.Add((byte)(val & 0xFF));
323 | buffer.Add((byte)((val >> 8) & 0xFF));
324 | buffer.Add((byte)((val >> 16) & 0xFF));
325 | buffer.Add((byte)((val >> 24) & 0xFF));
326 | }
327 |
328 |
329 | private static void writeInt16(List buffer, ushort val) {
330 | buffer.Add((byte)(val & 0xFF));
331 | buffer.Add((byte)(val >> 8));
332 | }
333 |
334 | private static void writeByte(List buffer, byte val) {
335 | buffer.Add(val);
336 | }
337 |
338 |
339 | private bool trackNeedSectorDesc(Track track) {
340 | // here we test for
341 | // - All the sectors of the track are numbered sequentially from 1 to n
342 | // - All the sectors use valid sector numbers from 0x00-0xF4
343 | // - All the sectors of the track have unique sector number
344 | // - All sectors in the track have standard timing values
345 | // - invalid track number
346 | // - Special Flags: No data, CRC, DDAM (cover sector no data)
347 |
348 | int secnum = 1;
349 | for (int sect = 0; sect < track.sectorCount; sect++) {
350 | // sequential sector number 1..n
351 | if (track.sectors[sect].id.number != secnum++)
352 | track.standardSectors = false;
353 |
354 | // duplicate sector
355 | for (int j = sect + 1; j < track.sectorCount; j++)
356 | if (track.sectors[sect].id.number == track.sectors[j].id.number)
357 | track.standardSectors = false;
358 |
359 | // sector with invalid sector number
360 | if ((track.sectors[sect].id.number >= 0xF5) && (track.sectors[sect].id.number <= 0xF7))
361 | track.standardSectors = false;
362 |
363 | // sector with invalid track number
364 | if (track.sectors[sect].id.track != track.number)
365 | track.standardSectors = false;
366 |
367 | // sector with invalid side/head
368 | if (track.sectors[sect].id.side != track.side)
369 | track.standardSectors = false;
370 |
371 | // sector with invalid size
372 | if (track.sectors[sect].id.size > 3)
373 | track.standardSectors = false;
374 |
375 | // short long sector time
376 | if (track.sectors[sect].readTime != 0)
377 | track.standardSectors = false;
378 |
379 | // FDC Flags - Here we read the saved flags
380 |
381 | // RNF = sector with no data
382 | //if ((track.sectors[sect].fdcFlags & SectorDesc.SECT_RNF) != 0)
383 | if (track.sectors[sect].sectorData == null)
384 | track.standardSectors = false;
385 |
386 | // CRC error = ID / Data
387 | if ((track.sectors[sect].fdcFlags & SectorDesc.CRC_ERR) != 0)
388 | track.standardSectors = false;
389 |
390 | // Record type = DDAM
391 | if ((track.sectors[sect].fdcFlags & SectorDesc.REC_TYPE) != 0)
392 | track.standardSectors = false;
393 |
394 | // FDC - Fuzzy
395 | if (track.sectors[sect].fuzzyData != null)
396 | track.standardSectors = false;
397 |
398 | // FDC - Timing
399 | if (track.sectors[sect].timmingData != null)
400 | track.standardSectors = false;
401 | }
402 | if (trackNeedTrackData(track))
403 | return true;
404 | return !track.standardSectors;
405 | }
406 |
407 |
408 | private bool trackNeedTrackData(Track track) {
409 | // We need Optional Track Data when:
410 | // - Single Data Segment = ID found but no data
411 | // - Invalid Data in Gap ?
412 | // - Shifted Track: Data over Index + Data beyond Index + ID over index
413 | // - Invalid sync mark sequence
414 | // - Sector within Sector
415 | // - No Flux Area
416 | // - Short long track ??
417 | // - SWS
418 | // - no data
419 |
420 | // for now we are cheating by using the TRK_IMAGE flag
421 | return !track.standardTrack;
422 | }
423 |
424 | }
425 |
426 | }
427 |
--------------------------------------------------------------------------------