1 /***************************************************************************
2  *                                                                         *
3  *    LIBDSK: General floppy and diskimage access library                  *
4  *    Copyright (C) 2006  John Elliott <seasip.webmaster@gmail.com>            *
5  *                                                                         *
6  *    This library is free software; you can redistribute it and/or        *
7  *    modify it under the terms of the GNU Library General Public          *
8  *    License as published by the Free Software Foundation; either         *
9  *    version 2 of the License, or (at your option) any later version.     *
10  *                                                                         *
11  *    This library is distributed in the hope that it will be useful,      *
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of       *
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
14  *    Library General Public License for more details.                     *
15  *                                                                         *
16  *    You should have received a copy of the GNU Library General Public    *
17  *    License along with this library; if not, write to the Free           *
18  *    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,      *
19  *    MA 02111-1307, USA                                                   *
20  *                                                                         *
21  ***************************************************************************/
22 
23 #include "apriboot.h"
24 
25 /* This file deals with transforming an Apricot boot sector to/from PCDOS
26  * format. In an attempt to make repeated conversion easier, it saves
27  * the vital bits of the original boot sector in unused areas of the
28  * converted sector, and indicates this with a magic number. */
29 
30 unsigned char apri_magic[12] =
31 {
32 	0x04, 0x35, 'A', 'P', 'R', 'I', 'B', 'O', 'O', 'T', '=', '>'
33 };
34 
35 unsigned char pcd_magic[12] =
36 {
37 	0x05, 0x35, 'A', 'P', 'R', 'I', 'B', 'O', 'O', 'T', '=', '>'
38 };
39 
40 unsigned char pcdos_dummy[] =
41 {
42 /*     0     1     2     3     4     5     6     7     8     9     a     b     c     d     e     f */
43 	0xE9, 0x40, 0x90,  'I',  'B',  'M',  ' ',  ' ',  '3',  '.',  '3', 0x00, 0x02, 0x01, 0x01, 0x00,
44 	0x02, 0x80, 0x00, 0x76, 0x02, 0xFC, 0x02, 0x00, 0x09, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
45 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47 	0x90, 0x90, 0xCD, 0x18
48 };
49 
50 
51 unsigned char apricot_dummy[] =
52 {
53 /*     0     1     2     3     4     5     6     7     8     9     a     b     c     d     e     f */
54 	0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
55 	0x09, 0x00, 0x50, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x28, 0x00, 0x00, 0x00, 0x0e, 0x00,
56 	0x00, 0x00, 0x00, 0x1E, 0x00, 0x03, 0x00, 0x1E, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
57 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
58 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
59     0x00, 0x02, 0x02, 0x01, 0x00, 0x02, 0xB0, 0x00, 0xA0, 0x05, 0xFE, 0x03, 0x00, 0x02, 0x00, 0x00,
60      'F',  'O',  'N',  'T',  '=',  'B',  'R',  'I',  'T',  '0',  '3',  ' ',  ' ',  ' ',  ' ',  ' ',
61      'K',  'E',  'Y',  'S',  '=',  'A',  'C',  'T',  '0',  '0',  '2',  ' ',  ' ',  ' ',  ' ',  ' ',
62 	0x06, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x22, 0x14, 0x02, 0x36, 0x00, 0x12, 0x00, 0x26, 0x00,
63     0x08, 0x01, 0x19, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 	0x01, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
65 	0x0E, 0x0E, 0x08, 0x08, 0x02, 0x00, 0x00, 0x00, 0x00, 0x11, 0x13, 0x1E, 0x00, 0x00, 0x00, 0x00
66 };
67 
68 
pcdos_to_apricot(byte * buf,const char * filename,int verbose)69 static int pcdos_to_apricot(byte *buf, const char *filename, int verbose)
70 {
71 	byte buf2[512];
72 
73 	memset(buf2, 0, sizeof(buf2));
74 	/* Is there a saved copy of the PCDOS boot sector? If so, use it */
75 	if (!memcmp(buf + 0x144, apri_magic, sizeof(apri_magic)))
76 	{
77 		memcpy(buf2, buf + 0x150, 0xA0);				/* Copy in saved sector */
78 	}
79 	else	/* If not, generate the Apricot sector from the PCDOS one */
80 	{
81 		int bootsecs = buf[14] + 256 * buf[15];
82 		int fatcopies= buf[16];
83 		int dirsize  = buf[17] + 256 * buf[18];
84 		long totsecs = buf[19] + 256 * buf[20];
85 		int fatsize  = buf[22] + 256 * buf[23];
86 		int sectors  = buf[24] + 256 * buf[25];
87 		int heads    = buf[26] + 256 * buf[27];
88 	/* Might want to expand this later on to handle 32-bit sector counts */
89 		long cylinders = totsecs / (heads * sectors);
90 		long dataoff   = bootsecs + (fatcopies * fatsize) + ((dirsize + 15) / 16);
91 
92 		memcpy(buf2, apricot_dummy, sizeof(apricot_dummy));
93 		memcpy(buf2,	  buf + 3, 8);		/* OEM ID */
94 		buf2[0x10] = buf[0x18];				/* Count of sectors */
95 		buf2[0x11] = buf[0x19];
96 		buf2[0x12] = (byte)(cylinders & 0xFF);
97 		buf2[0x13] = (byte)((cylinders >>  8) & 0xFF);
98 		buf2[0x14] = (byte)((cylinders >> 16) & 0xFF);
99 		buf2[0x15] = (byte)((cylinders >> 24) & 0xFF);
100 		buf2[0x16] = buf[0x1A];				/* Count of heads */
101 
102 	/* Offset to data area */
103 		buf2[0x28] = (byte)(dataoff & 0xFF);
104 		buf2[0x29] = (byte)((dataoff >> 8)  & 0xFF);
105 		buf2[0x2a] = (byte)((dataoff >> 16) & 0xFF);
106 		buf2[0x2b] = (byte)((dataoff >> 24) & 0xFF);
107 
108 		memcpy(buf2 + 0x50, buf + 0x0b, 13); /* BPB */
109 		/* Disk type */
110 		if (heads == 1)
111 		{
112 			if (cylinders == 70)
113 		 		 buf2[0x54] = 0;
114 			else buf2[0x54] = 1;
115 		}
116 		else     buf2[0x54] = 2;
117 	}
118 	/* Save the original PCDOS boot sector */
119 	memcpy(buf2 + 0x144, pcd_magic, sizeof(pcd_magic));
120 	memcpy(buf2 + 0x150, buf, 0xA0);
121 
122 	memcpy(buf, buf2, sizeof(buf2));
123 	if (verbose) fprintf(stderr,"%s: Bootsector converted to Apricot format\n", filename);
124 	return 0;
125 }
126 
127 
apricot_to_pcdos(byte * buf,const char * filename,int verbose)128 static int apricot_to_pcdos(byte *buf, const char *filename, int verbose)
129 {
130 	byte buf2[512];
131 
132 	memset(buf2, 0, sizeof(buf2));
133 	/* Is there a saved copy of the PCDOS boot sector? If so, use it */
134 	if (!memcmp(buf + 0x144, pcd_magic, sizeof(pcd_magic)))
135 	{
136 		memcpy(buf2, buf + 0x150, 0xA0);				/* Copy in saved sector */
137 		memcpy(buf2, pcdos_dummy, 3);  /* and patch its code to be */
138 		memcpy(buf2 + 0x40, pcdos_dummy + 0x40, 4);
139 						/* the dummy boot sequence */
140 	}
141 	else	/* If not, generate the PCDOS sector from the Apricot one */
142 	{
143 		int heads     = buf[0x16];
144 		int sectors   = buf[0x10] + 256 * buf[0x11];
145 
146 		memcpy(buf2, pcdos_dummy, sizeof(pcdos_dummy));
147 		memcpy(buf2 + 3,    buf,         8);		/* OEM ID */
148 		memcpy(buf2 + 0x0b, buf + 0x50, 13);		/* BPB */
149 		buf2[24] = sectors & 0xFF;
150 		buf2[25] = sectors >> 8;
151 		buf2[26] = heads & 0xFF;
152 		buf2[27] = heads >> 8;
153 	}
154 	/* Save the original Apricot boot sector */
155 	memcpy(buf2 + 0x144, apri_magic, sizeof(apri_magic));
156 	memcpy(buf2 + 0x150, buf, 0xA0);
157 	buf2[0x1FE] = 0x55;
158 	buf2[0x1FF] = 0xAA;	/* Boot record signature */
159 
160 	memcpy(buf, buf2, sizeof(buf2));
161 	if (verbose) fprintf(stderr,"%s: Bootsector converted to PCDOS format\n", filename);
162 	return 0;
163 }
164 
165 
166 
167 /* Check for a PCDOS BPB. */
sector_is_pcdos(byte * buf)168 static int sector_is_pcdos(byte *buf)
169 {
170 	if (buf[0] != 0 && buf[0] != 0xE9 && buf[0] != 0xEB)
171 	{
172 		return 0;	/* Must start with an IBM or Atari jump */
173 	}
174 	if (buf[11] != 0 || buf[12] != 2)
175 	{
176 		return 0;	/* Must have a 512-byte sector size */
177 	}
178 	return 1;
179 }
180 
181 
sector_is_apricot(byte * buf)182 static int sector_is_apricot(byte *buf)
183 {
184 	/* Check that the OEM ID is either ASCII or zeroes */
185 	int n;
186 
187 	for (n = 0; n < 8; n++)
188 	{
189 		if (buf[n] != 0 && (buf[n] < ' ' || buf[n] > '~'))
190 		{
191 			return 0;
192 		}
193 	}
194 	/* Check that we have 512-byte sectors */
195 	if (buf[80] != 0 || buf[81] != 2)
196 	{
197 		return 0;
198 	}
199 	/* XXX More validation here */
200 	return 1;
201 }
202 
203 
204 
transform(MODE md,byte * buf,const char * filename,int verbose)205 int transform(MODE md, byte *buf, const char *filename, int verbose)
206 {
207 	int is_pcdos = 0;
208 
209 	/* OK. Firstly, determine whether this is a PC-DOS bootsector,
210 	  an Apricot bootsector, or something else. */
211 	if (sector_is_pcdos(buf))
212 	{
213 		is_pcdos = 1;
214 		if (md == FORCE_PCDOS) 	/* Nothing to do! */
215 		{
216 			fprintf(stderr, "%s: Is already in PCDOS format\n",
217 				filename);
218 			return 0;
219 		}
220 	}
221 	else if (sector_is_apricot(buf))
222 	{
223 		if (md == FORCE_APRICOT)
224 		{
225 			fprintf(stderr, "%s: Is already in Apricot format\n",
226 				filename);
227 			return 0;
228 		}
229 	}
230 	else
231 	{
232 		fprintf(stderr, "%s: Could not identify the boot sector\n",
233 			filename);
234 		return -1;
235 	}
236 	/* Need to swap. */
237 	if (is_pcdos) return pcdos_to_apricot(buf, filename, verbose);
238 	else	      return apricot_to_pcdos(buf, filename, verbose);
239 }
240