sdk: const correctness
[sdk] / ear / extract / extract.ec
1 #ifdef ECERE_STATIC
2 import static "ecere"
3 #else
4 import "ecere"
5 #endif
6
7 static char archivePath[MAX_LOCATION], archive[MAX_LOCATION];
8
9 #define BUFFERSIZE 0x10000
10
11 void ExtractFileFromArchive(ProgressBar progressBar, const char * path, const char * outputFile)
12 {
13    char fileName[MAX_LOCATION];
14    FileAttribs exists = FileExists(path);
15    bool setTime = false;
16    FileStats stats;
17
18    if(exists.isDirectory)
19    {
20       FileListing listing { path };
21
22       if(outputFile[0])
23       {
24          if(MakeDir(outputFile))
25          {
26             setTime = true;
27             FileGetStats(path, &stats);
28          }
29       }
30
31       while(listing.Find())
32       {
33          if(strcmp(listing.path, "<:>ecere"))
34          {
35             strcpy(fileName, outputFile);
36
37             // Tweak file name if out
38             if(outputFile[0])
39             {
40                if(!strcmp(fileName, ".")) fileName[0] = '\0';
41                if(listing.name[0] == '/' || listing.name[0] == '\\')
42                {
43                   char * afterSlash, rest[MAX_LOCATION];
44                   for(afterSlash = fileName; *afterSlash == '/' || *afterSlash == '\\'; afterSlash++);
45                   strcpy(rest, afterSlash);
46                   PathCat(fileName, "_root");
47                   PathCat(fileName, rest);
48                }
49                else if(listing.name[1] == ':')
50                {
51                   char letter[10];
52                   sprintf(letter, "_%cdrive", toupper(listing.name[0]));
53                   PathCat(fileName, letter);
54                   PathCat(fileName, listing.name[2] ? (listing.name + 3) : (listing.name + 2));
55                }
56                else
57                   PathCat(fileName, listing.name);
58             }
59             else
60                PathCat(fileName, listing.name);
61             if(!strcmp(fileName, "/") || !strcmp(fileName, "\\"))
62                strcpy(fileName, DIR_SEPS);
63             ExtractFileFromArchive(progressBar, listing.path, fileName);
64          }
65       }
66    }
67    else if(exists)
68    {
69       File input = FileOpen(path, read);
70       if(input)
71       {
72          File output = FileOpen(outputFile, write);
73          if(output)
74          {
75             char fileName[MAX_FILENAME];
76             uint position = progressBar.progress;
77             FileSize dataSize;
78             uint c;
79             byte buffer[BUFFERSIZE];
80
81             FileGetSize(path, &dataSize);
82             GetLastDirectory(outputFile, fileName);
83             progressBar.statusBar.SetText($"Extracting %s...", fileName);
84
85             app.UpdateDisplay();
86
87             for(c = 0; c<dataSize; c += BUFFERSIZE)
88             {
89                uint size = (dataSize > c + BUFFERSIZE) ? BUFFERSIZE : (dataSize - c);
90                input.Read(buffer, 1, size);
91                output.Write(buffer, 1, size);
92
93                progressBar.progress = position + c + size;
94                app.UpdateDisplay();
95             }
96             delete output;
97             setTime = true;
98             FileGetStats(path, &stats);
99          }
100          delete input;
101       }
102    }
103    if(setTime)
104       FileSetTime(outputFile, stats.created, 0, stats.modified);
105 }
106
107 class SelfExtract : Window
108 {
109    background = activeBorder;
110    borderStyle = fixed;
111    minClientSize = Size { 500, 112 };
112    hasClose = true;
113    tabCycle = true;
114    size = Size { 506, 138 };
115    anchor = Anchor { vert = -0.15 };
116
117    Button view { this, text = $"View Files", anchor = Anchor { right = 100, top = 70 }, size = Size { 100 }, hotKey = altV, NotifyClicked = ViewClicked };
118    Button usePaths
119    {
120       this, isCheckbox = true, text = $"Use paths from archive...", position = Point { 10,70 }, hotKey = altU, checked = true;
121
122       bool NotifyClicked(Button control, int x, int y, Modifiers mods)
123       {
124          if(usePaths.checked)
125          {
126             browse.disabled = true;
127             where.disabled = true;
128          }
129          else
130          {
131             browse.disabled = false;
132             where.disabled = false;
133          }
134          whereLabel.Update(null); // Is this still needed?
135          return true;
136       }
137    };
138    EditBox where { this, text = $"Extract to", anchor = Anchor { left = 10, top = 40, right = 100 }, size = Size { h = 22 }, hotKey = altT, disabled = true };
139    Label whereLabel { this, position = Point { 10,20 }, labeledWindow = where };
140    Button browse
141    {
142       this, text = $"Browse...", anchor = Anchor { right = 10, top = 40 }, size = Size { 80 }, hotKey = altB, disabled = true;
143
144       bool NotifyClicked(Button control, int x, int y, Modifiers mods)
145       {
146          fileDialog.currentDirectory = where.contents;
147          if(fileDialog.Modal() == ok)
148             where.contents = fileDialog.filePath;
149          return true;
150       }
151    };
152    Button close { this, text = $"Exit", anchor = Anchor { right = 10, top = 70 }, size = Size { 80 }, hotKey = altX, NotifyClicked = ButtonCloseDialog };
153    Button extract
154    {
155       this, isDefault = true, text = $"Extract", anchor = Anchor { right = 10, top = 10 }, size = Size { 80 }, hotKey = altE;
156
157       bool NotifyClicked(Button control, int x, int y, Modifiers mods)
158       {
159          FileSize totalSize;
160          ProgressBar progressBar { master = this, isModal = true, borderStyle = fixed, hasStatusBar = true, text = $"Extracting Files...", clientSize = Size { 400, 40 } };
161
162          ArchiveQuerySize(archive, &totalSize);
163          progressBar.range = totalSize;
164
165          app.UpdateDisplay();
166
167          if(usePaths.checked)
168             ExtractFileFromArchive(progressBar, archivePath, "");
169          else
170          {
171             char output[MAX_LOCATION];
172             TrimLSpaces(where.contents, output);
173             TrimRSpaces(output, output);
174             if(!output[0]) strcpy(output, ".");
175             ExtractFileFromArchive(progressBar, archivePath, output);
176          }
177          MessageBox { master = progressBar, text = $"Done.", contents = $"Extraction Completed" }.Modal();
178          progressBar.Destroy(0);
179          return true;
180       }
181    };
182    ListBox viewList { this, hasHeader = true, borderStyle = deep, hasHorzScroll = true, hasVertScroll = true, anchor = Anchor { left = 10, top = 112, right = 10,bottom = 10 } };
183    DataField nameField { header = $"File", width = 360 };
184    DataField sizeField { dataType = "FileSize", header = $"Size", width = 96 };
185    DataField mField { dataType = "TimeStamp", header = $"Modified", width = 216 };
186    DataField cField {dataType = "TimeStamp", header = $"Created", width = 216 };
187    FileDialog fileDialog { master = this, type = selectDir, text = $"Select extraction directory" };
188
189    bool HideClicked(Button control, int x, int y, Modifiers mods)
190    {
191       borderStyle = fixed;
192       viewList.visible = false;
193       size = Size {};
194       position = position;
195       view.text = $"View Files";
196       view.hotKey = altV;
197       view.NotifyClicked = ViewClicked;
198       return true;
199    }
200
201    bool ViewClicked(Button control, int x, int y, Modifiers mods)
202    {
203       borderStyle = sizable;
204       position = position;
205       clientSize.h = 300;
206       viewList.visible = true;
207       view.text = $"Hide Files";
208       view.hotKey = altH;
209       view.NotifyClicked = HideClicked;
210       return true;
211    }
212
213    void ViewArchive(const char * path)
214    {
215       FileListing listing { path };
216       char string[MAX_LOCATION], * directory;
217       DataRow row;
218
219       SplitArchivePath(path, string, &directory);
220
221       if(directory[0])
222       {
223          FileStats stats;
224
225          strcpy(string, directory);
226          if(!strcmp(directory, "/") || !strcmp(directory, "\\"))
227             strcpy(string, DIR_SEPS);
228          else
229             strcat(string, DIR_SEPS);
230
231          FileGetStats(path, &stats);
232
233          row = viewList.AddRow();
234          row.SetData(nameField, string);
235          row.SetData(sizeField, 0);
236          {
237             TimeStamp m = stats.modified.local, c = stats.created.local;
238             row.SetData(mField, m);//(void *)stats.modified.local); // .local was missing here
239             row.SetData(cField, c);//(void *)stats.created.local);
240          }
241       }
242
243       while(listing.Find())
244       {
245          if(strcmp(listing.path, "<:>ecere"))
246          {
247             strcpy(string, directory);
248             if(string[0])
249             {
250                if(!strcmp(directory, "/") || !strcmp(directory, "\\"))
251                   strcpy(string, DIR_SEPS);
252                else
253                   strcat(string, DIR_SEPS);
254             }
255             PathCat(string, listing.name);
256             if(listing.stats.attribs.isDirectory)
257                ViewArchive(listing.path);
258             else
259             {
260                row = viewList.AddRow();
261                row.SetData(nameField, string);
262                row.SetData(sizeField, listing.stats.size);
263                {
264                   TimeStamp m = listing.stats.modified.local, c = listing.stats.created.local;
265                   row.SetData(mField, m);//(void *)listing.stats.modified.local);
266                   row.SetData(cField, c);//(void *)listing.stats.created.local);
267                }
268             }
269          }
270       }
271    }
272
273    SelfExtract()
274    {
275       viewList.AddField(nameField);
276       viewList.AddField(sizeField);
277       viewList.AddField(mField);
278       viewList.AddField(cField);
279
280       ViewArchive(archivePath);
281       return true;
282    }
283 }
284
285 class SelfExtractApp : GuiApplication
286 {
287    void Main()
288    {
289       char title[1024];
290
291       app = this;
292
293       if(argc > 1)
294       {
295          strcpy(archive, argv[1]);
296          sprintf(archivePath, "<%s>", archive);
297          sprintf(title, $"ECERE Archive - %s", argv[1]);
298       }
299       else
300       {
301          strcpy(archive, ":");
302          strcpy(archivePath, ":");
303          strcpy(title, $"ECERE Self-Extractable Archive");
304       }
305
306       SetLoggingMode(stdOut, null);
307       SelfExtract { text = title };
308
309       GuiApplication::Main();
310    }
311 }
312
313 SelfExtractApp app;