added bin_to_cso_mp3 tool
[picodrive.git] / tools / amalgamate.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdarg.h>
5 #include <ctype.h>
6
7 #define OUT_FILE "PicoAll.c"
8
9 // files to amalgamate, in order
10 static const char *files[] =
11 {
12         "Pico/Pico.h",
13         // PicoInt.h includes some CD stuff, so start with them
14         "Pico/cd/cd_file.h",
15         "Pico/cd/cd_sys.h",
16         "Pico/cd/LC89510.h",
17         "Pico/cd/gfx_cd.h",
18         "Pico/cd/pcm.h",
19         "Pico/PicoInt.h",
20         "Pico/Patch.h",
21         "Pico/sound/mix.h",
22         // source
23         "Pico/Area.c",
24         "Pico/Cart.c",
25         "Pico/Draw2.c",
26         "Pico/Draw.c",
27         "Pico/VideoPort.c",
28         "Pico/sound/sound.c",
29         "Pico/MemoryCmn.c",
30         "Pico/Memory.c",
31         "Pico/Misc.c",
32         "Pico/Patch.c",
33         "Pico/Sek.c",
34         "Pico/cd/Area.c",
35         "Pico/cd/buffering.c",
36         "Pico/cd/cd_file.c",
37         "Pico/cd/cd_sys.c",
38         "Pico/cd/cell_map.c",
39         "Pico/cd/gfx_cd.c",
40         "Pico/cd/LC89510.c",
41         "Pico/cd/Memory.c",
42         "Pico/cd/Misc.c",
43         "Pico/cd/pcm.c",
44         "Pico/cd/Sek.c",
45         "Pico/cd/Pico.c",
46         "Pico/Pico.c",
47 };
48
49 static char *includes[128];
50
51 static void eprintf(const char *fmt, ...)
52 {
53         va_list args;
54
55         va_start(args, fmt);
56         vprintf(fmt, args);
57         va_end(args);
58
59         exit(1);
60 }
61
62 static void emit_header(FILE *f, const char *fname)
63 {
64         char tmp[128] = "/*                                                            */";
65         memcpy(tmp + 3, fname, strlen(fname));
66         fprintf(f, "\n\n");
67         fprintf(f, "/**************************************************************/\n");
68         fprintf(f, "/**************************************************************/\n");
69         fprintf(f, "%s\n", tmp);
70         fprintf(f, "/**************************************************************/\n");
71 }
72
73 static const char *add_include(const char *include)
74 {
75         int i;
76         char processed_inc[128+4];
77
78         // must first quote relative includes
79         snprintf(processed_inc, sizeof(processed_inc), (include[0] != '<') ? "\"%s\"" : "%s", include);
80
81         // find in include list
82         for (i = 0; includes[i] && i < 128; i++)
83         {
84                 if (strcmp(processed_inc, includes[i]) == 0) break;
85         }
86         if (i == 128) eprintf("add_include: includes overflowed\n");
87         if (includes[i] != NULL)
88         {
89                 printf("already have: %s\n", processed_inc);
90                 return NULL;
91         }
92         else
93         {
94                 printf("adding: %s\n", processed_inc);
95                 includes[i] = strdup(processed_inc);
96                 if (includes[i] == NULL) eprintf("add_include: OOM\n");
97                 return includes[i];
98         }
99 }
100
101 static const char *add_raw_include(const char *include, const char *base)
102 {
103         const char *ps, *pe;
104         char processed_inc[128];
105
106         for (ps = include; *ps && isspace(*ps); ps++);
107
108         if (*ps == '<')
109         {
110                 int len = 1;
111                 // system include, search for '>'
112                 for (pe = ps; *pe && *pe != '>'; pe++, len++);
113                 if (*pe == 0 || len > 127) eprintf("add_raw_include: failed sysinclude, len=%i\n", len);
114                 strncpy(processed_inc, ps, len);
115                 processed_inc[len] = 0;
116         }
117         else if (*ps == '\"')
118         {
119                 int len, pos;
120                 // relative include, make path absolute (or relative to base dir)
121                 strcpy(processed_inc, base);
122                 ps++;
123                 while (*ps == '.')
124                 {
125                         if (strncmp(ps, "../", 3) == 0)
126                         {
127                                 char *p;
128                                 if (processed_inc[0] == 0)
129                                         eprintf("add_raw_include: already in root, can't go down: %s | %s\n", ps, include);
130                                 p = strrchr(processed_inc, '/');
131                                 if (p == NULL) eprintf("add_raw_include: can't happen\n");
132                                 *p = 0;
133                                 p = strrchr(processed_inc, '/');
134                                 if (p != NULL) p[1] = 0;
135                                 else processed_inc[0] = 0;
136                                 ps += 3;
137                         }
138                         else if (strncmp(ps, "./", 2) == 0)
139                         {
140                                 ps += 2; // just skip
141                         }
142                         while (*ps == '/') ps++;
143                 }
144                 if (*ps == 0) eprintf("add_raw_include: failed with %s\n", include);
145
146                 len = pos = strlen(processed_inc);
147                 for (pe = ps; *pe && *pe != '\"'; pe++, len++);
148                 if (*pe == 0 || len > 127) eprintf("add_raw_include: failed with %s, len=%i\n", include, len);
149                 strncpy(processed_inc + pos, ps, len - pos);
150                 processed_inc[len] = 0;
151         }
152         else
153                 eprintf("add_raw_include: unhandled include: %s\n", ps);
154
155         return add_include(processed_inc);
156 }
157
158 // returns pointer to location after part in string
159 static const char *substr_end(const char *string, const char *part)
160 {
161         const char *p = string;
162         int len = strlen(part);
163
164         while (*p && isspace(*p)) p++;
165         return (strncmp(p, part, len) == 0) ? (p + len) : NULL;
166 }
167
168 static void strip_cr(char *str)
169 {
170         int len = strlen(str);
171         char *p = str;
172
173         while ((p = strchr(p, '\r')))
174         {
175                 memmove(p, p + 1, len - (p - str) + 1);
176         }
177         if (strlen(str) > 0)
178         {
179                 p = str + strlen(str) - 1;
180                 while (p >= str && isspace(*p)) { *p = 0; p--; } // strip spaces on line ends
181         }
182         strcat(str, "\n"); // re-add newline
183 }
184
185 int main(void)
186 {
187         char buff[512]; // tmp buffer
188         char path[128]; // path to file being included, with ending slash
189         int i, ifile;
190         FILE *fo;
191
192         memset(includes, 0, sizeof(includes));
193
194         fo = fopen(OUT_FILE, "w");
195         if (fo == NULL) return 1;
196
197         // special header
198         fprintf(fo, "#define PICO_INTERNAL static\n");
199         fprintf(fo, "#define PICO_INTERNAL_ASM\n");
200
201         for (ifile = 0; ifile < sizeof(files) / sizeof(files[0]); ifile++)
202         {
203                 FILE *fi;
204                 const char *file = files[ifile], *p;
205                 p = strrchr(file, '/');
206                 if (p == NULL) eprintf("main: file in root? %s\n", file);
207                 strncpy(path, file, p - file + 1);
208                 path[p - file + 1] = 0;
209
210                 fi = fopen(file, "r");
211                 if (fi == NULL) eprintf("main: failed to open %s\n", file);
212
213                 // if (strcmp(file + strlen(file) - 2, ".h") == 0)
214                 add_include(file);
215                 emit_header(fo, file);
216
217                 while (!feof(fi))
218                 {
219                         p = fgets(buff, sizeof(buff), fi);
220                         if (p == NULL) break;
221                         strip_cr(buff);
222                         // include?
223                         p = substr_end(buff, "#include");
224                         if (p != NULL)
225                         {
226                                 p = add_raw_include(p, path);
227                                 if (p != NULL) fprintf(fo, "#include %s\n", p);
228                                 continue;
229                         }
230                         // passthrough
231                         fputs(buff, fo);
232                 }
233         }
234
235         emit_header(fo, "EOF");
236
237         for (i = 0; includes[i] && i < 128; i++)
238         {
239                 free(includes[i]);
240         }
241
242         fclose(fo);
243
244         return 0;
245 }
246