Scrambling
As a form of piracy protection, when booting from MIL-CD formatted discs, the Dreamcast loads the main program binary specified in the IP.BIN using a unique "de-scramble" decryption algorithm. Therefore, in order to load a program into memory from disc correctly, the binary must be encrypted using the correct "scramble" algorithm. This process does not apply to programs stored on GD-ROM media.
When pirate groups in the warez scene started releasing "self-boot" versions of Dreamcast games, they chose the alternative route of implementing the algorithm inside the IP.BIN, so that the program is stored normally on disc, becomes "de-scrambled" into memory by the system at load time, and then is "re-scrambled" by the injected IP.BIN code just before execution.
A scramble/descramble algorithm was published by Dreamcast hobbyist pioneer Marcus Comstedt into the public domain. His algorithm is published below.
1 #include <stdio.h>
2 #include <stdlib.h>
3
4 #define MAXCHUNK (2048*1024)
5
6 static unsigned int seed;
7
8 void my_srand(unsigned int n)
9 {
10 seed = n & 0xffff;
11 }
12
13 unsigned int my_rand()
14 {
15 seed = (seed * 2109 + 9273) & 0x7fff;
16 return (seed + 0xc000) & 0xffff;
17 }
18
19 void load(FILE *fh, unsigned char *ptr, unsigned long sz)
20 {
21 if(fread(ptr, 1, sz, fh) != sz)
22 {
23 fprintf(stderr, "Read error!\n");
24 exit(1);
25 }
26 }
27
28 void load_chunk(FILE *fh, unsigned char *ptr, unsigned long sz)
29 {
30 static int idx[MAXCHUNK/32];
31 int i;
32
33 /* Convert chunk size to number of slices */
34 sz /= 32;
35
36 /* Initialize index table with unity,
37 so that each slice gets loaded exactly once */
38 for(i = 0; i < sz; i++)
39 idx[i] = i;
40
41 for(i = sz-1; i >= 0; --i)
42 {
43 /* Select a replacement index */
44 int x = (my_rand() * i) >> 16;
45
46 /* Swap */
47 int tmp = idx[i];
48 idx[i] = idx[x];
49 idx[x] = tmp;
50
51 /* Load resulting slice */
52 load(fh, ptr+32*idx[i], 32);
53 }
54 }
55
56 void load_file(FILE *fh, unsigned char *ptr, unsigned long filesz)
57 {
58 unsigned long chunksz;
59
60 my_srand(filesz);
61
62 /* Descramble 2 meg blocks for as long as possible, then
63 gradually reduce the window down to 32 bytes (1 slice) */
64 for(chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
65 while(filesz >= chunksz)
66 {
67 load_chunk(fh, ptr, chunksz);
68 filesz -= chunksz;
69 ptr += chunksz;
70 }
71
72 /* Load final incomplete slice */
73 if(filesz)
74 load(fh, ptr, filesz);
75 }
76
77 void read_file(char *filename, unsigned char **ptr, unsigned long *sz)
78 {
79 FILE *fh = fopen(filename, "rb");
80 if(fh == NULL)
81 {
82 fprintf(stderr, "Can't open \"%s\".\n", filename);
83 exit(1);
84 }
85 if(fseek(fh, 0, SEEK_END)<0)
86 {
87 fprintf(stderr, "Seek error.\n");
88 exit(1);
89 }
90 *sz = ftell(fh);
91 *ptr = malloc(*sz);
92 if( *ptr == NULL )
93 {
94 fprintf(stderr, "Out of memory.\n");
95 exit(1);
96 }
97 if(fseek(fh, 0, SEEK_SET)<0)
98 {
99 fprintf(stderr, "Seek error.\n");
100 exit(1);
101 }
102 load_file(fh, *ptr, *sz);
103 fclose(fh);
104 }
105
106 void save(FILE *fh, unsigned char *ptr, unsigned long sz)
107 {
108 if(fwrite(ptr, 1, sz, fh) != sz)
109 {
110 fprintf(stderr, "Write error!\n");
111 exit(1);
112 }
113 }
114
115 void save_chunk(FILE *fh, unsigned char *ptr, unsigned long sz)
116 {
117 static int idx[MAXCHUNK/32];
118 int i;
119
120 /* Convert chunk size to number of slices */
121 sz /= 32;
122
123 /* Initialize index table with unity,
124 so that each slice gets saved exactly once */
125 for(i = 0; i < sz; i++)
126 idx[i] = i;
127
128 for(i = sz-1; i >= 0; --i)
129 {
130 /* Select a replacement index */
131 int x = (my_rand() * i) >> 16;
132
133 /* Swap */
134 int tmp = idx[i];
135 idx[i] = idx[x];
136 idx[x] = tmp;
137
138 /* Save resulting slice */
139 save(fh, ptr+32*idx[i], 32);
140 }
141 }
142
143 void save_file(FILE *fh, unsigned char *ptr, unsigned long filesz)
144 {
145 unsigned long chunksz;
146
147 my_srand(filesz);
148
149 /* Descramble 2 meg blocks for as long as possible, then
150 gradually reduce the window down to 32 bytes (1 slice) */
151 for(chunksz = MAXCHUNK; chunksz >= 32; chunksz >>= 1)
152 while(filesz >= chunksz)
153 {
154 save_chunk(fh, ptr, chunksz);
155 filesz -= chunksz;
156 ptr += chunksz;
157 }
158
159 /* Save final incomplete slice */
160 if(filesz)
161 save(fh, ptr, filesz);
162 }
163
164 void write_file(char *filename, unsigned char *ptr, unsigned long sz)
165 {
166 FILE *fh = fopen(filename, "wb");
167 if(fh == NULL)
168 {
169 fprintf(stderr, "Can't open \"%s\".\n", filename);
170 exit(1);
171 }
172 save_file(fh, ptr, sz);
173 fclose(fh);
174 }
175
176 void descramble(char *src, char *dst)
177 {
178 unsigned char *ptr = NULL;
179 unsigned long sz = 0;
180 FILE *fh;
181
182 read_file(src, &ptr, &sz);
183
184 fh = fopen(dst, "wb");
185 if(fh == NULL)
186 {
187 fprintf(stderr, "Can't open \"%s\".\n", dst);
188 exit(1);
189 }
190 if( fwrite(ptr, 1, sz, fh) != sz )
191 {
192 fprintf(stderr, "Write error.\n");
193 exit(1);
194 }
195 fclose(fh);
196 free(ptr);
197 }
198
199 void scramble(char *src, char *dst)
200 {
201 unsigned char *ptr = NULL;
202 unsigned long sz = 0;
203 FILE *fh;
204
205 fh = fopen(src, "rb");
206 if(fh == NULL)
207 {
208 fprintf(stderr, "Can't open \"%s\".\n", src);
209 exit(1);
210 }
211 if(fseek(fh, 0, SEEK_END)<0)
212 {
213 fprintf(stderr, "Seek error.\n");
214 exit(1);
215 }
216 sz = ftell(fh);
217 ptr = malloc(sz);
218 if( ptr == NULL )
219 {
220 fprintf(stderr, "Out of memory.\n");
221 exit(1);
222 }
223 if(fseek(fh, 0, SEEK_SET)<0)
224 {
225 fprintf(stderr, "Seek error.\n");
226 exit(1);
227 }
228 if( fread(ptr, 1, sz, fh) != sz )
229 {
230 fprintf(stderr, "Read error.\n");
231 exit(1);
232 }
233 fclose(fh);
234
235 write_file(dst, ptr, sz);
236
237 free(ptr);
238 }
239
240 int main(int argc, char *argv[])
241 {
242 int opt = 0;
243
244 if(argc > 1 && !strcmp(argv[1], "-d"))
245 opt ++;
246
247 if(argc != 3+opt)
248 {
249 fprintf(stderr, "Usage: %s [-d] from to\n", argv[0]);
250 exit(1);
251 }
252
253 if(opt)
254 descramble(argv[2], argv[3]);
255 else
256 scramble(argv[1], argv[2]);
257
258 return 0;
259 }