1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 #define TITLE "byteshuf - Shuffle or unshuffle bytes in a file"
4 #define COPYR "Copyright (C) 2011 Neill Corlett"
5 //
6 // This program is free software: you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation, either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program 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
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 //
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include "common.h"
22 #include "banner.h"
23 
24 ////////////////////////////////////////////////////////////////////////////////
25 
shuffle(int shuffling,const char * mainfile,const char ** subfiles,size_t subfiles_count,int overwrite)26 static int shuffle(
27     int shuffling,
28     const char* mainfile,
29     const char** subfiles,
30     size_t subfiles_count,
31     int overwrite
32 ) {
33     int returncode = 0;
34     size_t i;
35     int* chars = calloc(1, sizeof(int) * subfiles_count);
36     FILE* mf = NULL;
37     FILE** sf = calloc(1, sizeof(FILE*) * subfiles_count);
38     if(!sf || !chars) {
39         printf("Error: Out of memory\n");
40         goto error;
41     }
42     if(shuffling && !overwrite) {
43         //
44         // Ensure main file doesn't already exist
45         //
46         mf = fopen(mainfile, "rb");
47         if(mf) {
48             printf("Error: %s already exists (use -o to overwrite)\n",
49                 mainfile
50             );
51             goto error;
52         }
53     }
54     if(!shuffling && !overwrite) {
55         //
56         // Ensure sub files don't already exist
57         //
58         for(i = 0; i < subfiles_count; i++) {
59             sf[i] = fopen(subfiles[i], "rb");
60             if(sf[i]) {
61                 printf("Error: %s already exists (use -o to overwrite)\n",
62                     subfiles[i]
63                 );
64                 goto error;
65             }
66         }
67     }
68     if(shuffling) {
69         size_t num_chars_last = subfiles_count;
70 
71         //
72         // Open sub files
73         //
74         for(i = 0; i < subfiles_count; i++) {
75             sf[i] = fopen(subfiles[i], "rb");
76             if(!sf[i]) { goto error_sf_i; }
77             clearerr(sf[i]);
78         }
79         //
80         // Open main file
81         //
82         mf = fopen(mainfile, "wb");
83         if(!mf) { goto error_mf; }
84         clearerr(mf);
85 
86         for(;;) {
87             size_t num_chars = 0;
88             for(i = 0; i < subfiles_count; i++) {
89                 chars[i] = fgetc(sf[i]);
90                 if(chars[i] == EOF) {
91                     if(ferror(sf[i])) { goto error_sf_i; }
92                     chars[i] = 0;
93                 } else {
94                     chars[i] &= 0xFF;
95                     num_chars = i + 1;
96                 }
97             }
98             if(!num_chars) { break; }
99             for(i = num_chars_last; i < subfiles_count; i++) {
100                 if(fputc(0, mf) == EOF) { goto error_mf; }
101             }
102             for(i = 0; i < num_chars; i++) {
103                 if(fputc(chars[i], mf) == EOF) { goto error_mf; }
104             }
105             num_chars_last = num_chars;
106         }
107     } else {
108         //
109         // Open main file
110         //
111         mf = fopen(mainfile, "rb");
112         if(!mf) { goto error_mf; }
113         clearerr(mf);
114         //
115         // Open sub files
116         //
117         for(i = 0; i < subfiles_count; i++) {
118             sf[i] = fopen(subfiles[i], "wb");
119             if(!sf[i]) { goto error_sf_i; }
120             clearerr(sf[i]);
121         }
122 
123         for(;;) {
124             for(i = 0; i < subfiles_count; i++) {
125                 int c = fgetc(mf);
126                 if(c == EOF) {
127                     if(ferror(mf)) { goto error_mf; }
128                     break;
129                 }
130                 if(fputc(c & 0xFF, sf[i]) == EOF) { goto error_sf_i; }
131             }
132             if(i < subfiles_count) { break; }
133         }
134     }
135     goto done;
136 
137 error_mf:
138     printfileerror(mf, mainfile);
139     goto error;
140 error_sf_i:
141     printfileerror(sf[i], subfiles[i]);
142     goto error;
143 error:
144     returncode = 1;
145     goto done;
146 done:
147     if(mf) { fclose(mf); }
148     if(sf) {
149         for(i = 0; i < subfiles_count; i++) {
150             if(sf[i]) { fclose(sf[i]); }
151         }
152         free(sf);
153     }
154     if(chars) { free(chars); }
155     return returncode;
156 }
157 
158 ////////////////////////////////////////////////////////////////////////////////
159 
checkboth(int a,int b,const char * ab)160 static int checkboth(int a, int b, const char* ab) {
161     if(a && b) {
162         printf("Error: Cannot specify both -%c and -%c\n", ab[0], ab[1]);
163         return 1;
164     }
165     return 0;
166 }
167 
checkeither(int a,int b,const char * ab)168 static int checkeither(int a, int b, const char* ab) {
169     if((!a) && (!b)) {
170         printf("Error: Must specify either -%c or -%c\n", ab[0], ab[1]);
171         return 1;
172     }
173     return 0;
174 }
175 
176 ////////////////////////////////////////////////////////////////////////////////
177 
main(int argc,char ** argv)178 int main(int argc, char** argv) {
179     int returncode = 0;
180     struct {
181         int8_t shuffle;
182         int8_t unshuffle;
183         int8_t overwrite;
184         const char* mainfile;
185         const char** files;
186         size_t files_count;
187     } opt;
188     int i;
189 
190     normalize_argv0(argv[0]);
191 
192     memset(&opt, 0, sizeof(opt));
193 
194     //
195     // Check options
196     //
197     if(argc == 1) { goto usage; }
198     for(i = 1; i < argc; i++) {
199         if(argv[i][0] == '-') {
200             // An option
201             if(argv[i][1] == '-' && argv[i][2] == 0) {
202                 // No more options
203                 i++;
204                 break;
205             } else if(argv[i][1] == 's' && argv[i][2] == 0) {
206                 if(opt.shuffle) { goto error_dup; }
207                 if(i >= (argc - 1)) { goto error_missing; }
208                 opt.shuffle = 1;
209                 opt.mainfile = argv[++i];
210                 continue;
211             } else if(argv[i][1] == 'u' && argv[i][2] == 0) {
212                 if(opt.unshuffle) { goto error_dup; }
213                 if(i >= (argc - 1)) { goto error_missing; }
214                 opt.unshuffle = 1;
215                 opt.mainfile = argv[++i];
216                 continue;
217             } else if(argv[i][1] == 'o' && argv[i][2] == 0) {
218                 opt.overwrite = 1;
219                 continue;
220             }
221             printf("Error: Unknown option: %s\n", argv[i]);
222             goto error_usage;
223         } else {
224             // Not an option - stop here
225             break;
226         }
227     }
228 
229     if(checkeither(opt.shuffle, opt.unshuffle, "su")) { goto error_usage; }
230     if(checkboth  (opt.shuffle, opt.unshuffle, "su")) { goto error_usage; }
231     //
232     // At least two files must be specified
233     //
234     opt.files       = (const char**)(argv + i);
235     opt.files_count =               (argc - i);
236     if(opt.files_count < 2) {
237         printf("Error: Must specify at least two subfiles\n");
238         goto error_usage;
239     }
240 
241     //
242     // Go
243     //
244     if(opt.shuffle) {
245         returncode = shuffle(
246             1, opt.mainfile, opt.files, opt.files_count, opt.overwrite
247         );
248     } else if(opt.unshuffle) {
249         returncode = shuffle(
250             0, opt.mainfile, opt.files, opt.files_count, opt.overwrite
251         );
252     }
253     goto done;
254 
255 error_dup:
256     printf("Error: Specified %s twice\n", argv[i]);
257     goto error_usage;
258 error_missing:
259     printf("Error: Missing parameter for %s\n", argv[i]);
260     goto error_usage;
261 error_usage:
262     printf("\n");
263     goto usage;
264 usage:
265     banner();
266     printf(
267         "Usage:\n"
268         "  To unshuffle: %s [-o] -u source      [subfiles...]\n"
269         "  To shuffle:   %s [-o] -s destination [subfiles...]\n"
270         "\n"
271         "Options:\n"
272         "  -o       Force overwrite\n"
273         "\n"
274         "For example, \"%s -u abc def0 def1\" will split all the even bytes from\n"
275         "\"abc\" into \"def0\", and the odd bytes into \"def1\".\n",
276         argv[0], argv[0], argv[0]
277     );
278     goto error;
279 
280 error:
281     returncode = 1;
282     goto done;
283 
284 done:
285     return returncode;
286 }
287 
288 ////////////////////////////////////////////////////////////////////////////////
289