use 'const char *' for imbuf and file ops.
[blender.git] / source / blender / blenkernel / intern / packedFile.c
1 /**
2  * blenkernel/packedFile.c - (cleaned up mar-01 nzc)
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 #include <stdio.h>
33 #include <fcntl.h>
34 #include <sys/stat.h>
35
36 #ifndef WIN32 
37 #include <unistd.h>
38 #else
39 #include <io.h>
40 #endif
41 #include <string.h>
42 #include "MEM_guardedalloc.h"
43
44 #include "DNA_image_types.h"
45 #include "DNA_sound_types.h"
46 #include "DNA_vfont_types.h"
47 #include "DNA_packedFile_types.h"
48
49 #include "BLI_blenlib.h"
50
51 #include "BKE_utildefines.h"
52 #include "BKE_global.h"
53 #include "BKE_main.h"
54 #include "BKE_sound.h"
55 #include "BKE_image.h"
56 #include "BKE_packedFile.h"
57 #include "BKE_report.h"
58
59 #ifdef _WIN32
60 #define open _open
61 #define close _close
62 #define read _read
63 #define write _write
64 #endif
65
66
67 int seekPackedFile(PackedFile *pf, int offset, int whence)
68 {
69         int oldseek = -1, seek = 0;
70
71         if (pf) {
72                 oldseek = pf->seek;
73                 switch(whence) {
74                 case SEEK_CUR:
75                         seek = oldseek + offset;
76                         break;
77                 case SEEK_END:
78                         seek = pf->size + offset;
79                         break;
80                 case SEEK_SET:
81                         seek = offset;
82                         break;
83                 default:
84                         oldseek = -1;
85                 }
86                 if (seek < 0) {
87                         seek = 0;
88                 } else if (seek > pf->size) {
89                         seek = pf->size;
90                 }
91                 pf->seek = seek;
92         }
93
94         return(oldseek);
95 }
96         
97 void rewindPackedFile(PackedFile *pf)
98 {
99         seekPackedFile(pf, 0, SEEK_SET);
100 }
101
102 int readPackedFile(PackedFile *pf, void *data, int size)
103
104         if ((pf != NULL) && (size >= 0) && (data != NULL)) {
105                 if (size + pf->seek > pf->size) {
106                         size = pf->size - pf->seek;
107                 }
108
109                 if (size > 0) {
110                         memcpy(data, ((char *) pf->data) + pf->seek, size);
111                 } else {
112                         size = 0;
113                 }
114
115                 pf->seek += size;
116         } else {
117                 size = -1;
118         }
119
120         return(size);
121 }
122
123 int countPackedFiles(Main *bmain)
124 {
125         Image *ima;
126         VFont *vf;
127         bSound *sound;
128         int count = 0;
129         
130         // let's check if there are packed files...
131         for(ima=bmain->image.first; ima; ima=ima->id.next)
132                 if(ima->packedfile)
133                         count++;
134
135         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
136                 if(vf->packedfile)
137                         count++;
138
139         for(sound=bmain->sound.first; sound; sound=sound->id.next)
140                 if(sound->packedfile)
141                         count++;
142
143         return count;
144 }
145
146 void freePackedFile(PackedFile *pf)
147 {
148         if(pf) {
149                 MEM_freeN(pf->data);
150                 MEM_freeN(pf);
151         }
152         else
153                 printf("freePackedFile: Trying to free a NULL pointer\n");
154 }
155         
156 PackedFile *newPackedFileMemory(void *mem, int memlen)
157 {
158         PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
159         pf->data = mem;
160         pf->size = memlen;
161         
162         return pf;
163 }
164
165 PackedFile *newPackedFile(ReportList *reports, const char *filename)
166 {
167         PackedFile *pf = NULL;
168         int file, filelen;
169         char name[FILE_MAXDIR+FILE_MAXFILE];
170         void *data;
171         
172         /* render result has no filename and can be ignored
173          * any other files with no name can be ignored too */
174         if(filename[0]=='\0')
175                 return NULL;
176
177         //XXX waitcursor(1);
178         
179         // convert relative filenames to absolute filenames
180         
181         strcpy(name, filename);
182         BLI_path_abs(name, G.main->name);
183         
184         // open the file
185         // and create a PackedFile structure
186
187         file= open(name, O_BINARY|O_RDONLY);
188         if (file <= 0) {
189                 BKE_reportf(reports, RPT_ERROR, "Unable to pack file, source path not found: \"%s\"", name);
190         } else {
191                 filelen = BLI_filesize(file);
192
193                 if (filelen == 0) {
194                         // MEM_mallocN complains about MEM_mallocN(0, "bla");
195                         // we don't care....
196                         data = MEM_mallocN(1, "packFile");
197                 } else {
198                         data = MEM_mallocN(filelen, "packFile");
199                 }
200                 if (read(file, data, filelen) == filelen) {
201                         pf = newPackedFileMemory(data, filelen);
202                 }
203
204                 close(file);
205         }
206
207         //XXX waitcursor(0);
208                 
209         return (pf);
210 }
211
212 void packAll(Main *bmain, ReportList *reports)
213 {
214         Image *ima;
215         VFont *vf;
216         bSound *sound;
217         
218         for(ima=bmain->image.first; ima; ima=ima->id.next) {
219                 if(ima->packedfile == NULL && ima->id.lib==NULL) { 
220                         if(ima->source==IMA_SRC_FILE) {
221                                 ima->packedfile = newPackedFile(reports, ima->name);
222                         }
223                         else if(ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
224                                 BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported.", ima->id.name+2);
225                         }
226                 }
227         }
228
229         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
230                 if(vf->packedfile == NULL && vf->id.lib==NULL && strcmp(vf->name, FO_BUILTIN_NAME) != 0)
231                         vf->packedfile = newPackedFile(reports, vf->name);
232
233         for(sound=bmain->sound.first; sound; sound=sound->id.next)
234                 if(sound->packedfile == NULL && sound->id.lib==NULL)
235                         sound->packedfile = newPackedFile(reports, sound->name);
236 }
237
238
239 /*
240
241 // attempt to create a function that generates an unique filename
242 // this will work when all funtions in fileops.c understand relative filenames...
243
244 static char *find_new_name(char *name)
245 {
246         char tempname[FILE_MAXDIR + FILE_MAXFILE];
247         char *newname;
248         
249         if (fop_exists(name)) {
250                 for (number = 1; number <= 999; number++) {
251                         sprintf(tempname, "%s.%03d", name, number);
252                         if (! fop_exists(tempname)) {
253                                 break;
254                         }
255                 }
256         }
257         
258         newname = mallocN(strlen(tempname) + 1, "find_new_name");
259         strcpy(newname, tempname);
260         
261         return(newname);
262 }
263         
264 */
265
266 int writePackedFile(ReportList *reports, const char *filename, PackedFile *pf, int guimode)
267 {
268         int file, number, remove_tmp = FALSE;
269         int ret_value = RET_OK;
270         char name[FILE_MAXDIR + FILE_MAXFILE];
271         char tempname[FILE_MAXDIR + FILE_MAXFILE];
272 /*      void *data; */
273         
274         if (guimode) {} //XXX  waitcursor(1);
275         
276         strcpy(name, filename);
277         BLI_path_abs(name, G.main->name);
278         
279         if (BLI_exists(name)) {
280                 for (number = 1; number <= 999; number++) {
281                         sprintf(tempname, "%s.%03d_", name, number);
282                         if (! BLI_exists(tempname)) {
283                                 if (BLI_copy_fileops(name, tempname) == RET_OK) {
284                                         remove_tmp = TRUE;
285                                 }
286                                 break;
287                         }
288                 }
289         }
290         
291         // make sure the path to the file exists...
292         BLI_make_existing_file(name);
293         
294         file = open(name, O_BINARY + O_WRONLY + O_CREAT + O_TRUNC, 0666);
295         if (file >= 0) {
296                 if (write(file, pf->data, pf->size) != pf->size) {
297                         BKE_reportf(reports, RPT_ERROR, "Error writing file: %s", name);
298                         ret_value = RET_ERROR;
299                 }
300                 close(file);
301         } else {
302                 BKE_reportf(reports, RPT_ERROR, "Error creating file: %s", name);
303                 ret_value = RET_ERROR;
304         }
305         
306         if (remove_tmp) {
307                 if (ret_value == RET_ERROR) {
308                         if (BLI_rename(tempname, name) != 0) {
309                                 BKE_reportf(reports, RPT_ERROR, "Error restoring tempfile. Check files: '%s' '%s'", tempname, name);
310                         }
311                 } else {
312                         if (BLI_delete(tempname, 0, 0) != 0) {
313                                 BKE_reportf(reports, RPT_ERROR, "Error deleting '%s' (ignored)", tempname);
314                         }
315                 }
316         }
317         
318         if(guimode) {} //XXX waitcursor(0);
319
320         return (ret_value);
321 }
322         
323 /* 
324
325 This function compares a packed file to a 'real' file.
326 It returns an integer indicating if:
327
328 PF_EQUAL                - the packed file and original file are identical
329 PF_DIFFERENT    - the packed file and original file differ
330 PF_NOFILE               - the original file doens't exist
331
332 */
333
334 int checkPackedFile(const char *filename, PackedFile *pf)
335 {
336         struct stat st;
337         int ret_val, i, len, file;
338         char buf[4096];
339         char name[FILE_MAXDIR + FILE_MAXFILE];
340         
341         strcpy(name, filename);
342         BLI_path_abs(name, G.main->name);
343         
344         if (stat(name, &st)) {
345                 ret_val = PF_NOFILE;
346         } else if (st.st_size != pf->size) {
347                 ret_val = PF_DIFFERS;
348         } else {
349                 // we'll have to compare the two...
350                 
351                 file = open(name, O_BINARY | O_RDONLY);
352                 if (file < 0) {
353                         ret_val = PF_NOFILE;
354                 } else {
355                         ret_val = PF_EQUAL;
356                         
357                         for (i = 0; i < pf->size; i += sizeof(buf)) {
358                                 len = pf->size - i;
359                                 if (len > sizeof(buf)) {
360                                         len = sizeof(buf);
361                                 }
362                                 
363                                 if (read(file, buf, len) != len) {
364                                         // read error ...
365                                         ret_val = PF_DIFFERS;
366                                         break;
367                                 } else {
368                                         if (memcmp(buf, ((char *)pf->data) + i, len)) {
369                                                 ret_val = PF_DIFFERS;
370                                                 break;
371                                         }
372                                 }
373                         }
374                         
375                         close(file);
376                 }
377         }
378         
379         return(ret_val);
380 }
381
382 /*
383
384    unpackFile() looks at the existing files (abs_name, local_name) and a packed file.
385
386 It returns a char *to the existing file name / new file name or NULL when
387 there was an error or when the user desides to cancel the operation.
388
389 */
390
391 char *unpackFile(ReportList *reports, char *abs_name, char *local_name, PackedFile *pf, int how)
392 {
393         char *newname = NULL, *temp = NULL;
394         
395         // char newabs[FILE_MAXDIR + FILE_MAXFILE];
396         // char newlocal[FILE_MAXDIR + FILE_MAXFILE];
397         
398         if (pf != NULL) {
399                 switch (how) {
400                         case -1:
401                         case PF_KEEP:
402                                 break;
403                         case PF_REMOVE:
404                                 temp= abs_name;
405                                 break;
406                         case PF_USE_LOCAL:
407                                 // if file exists use it
408                                 if (BLI_exists(local_name)) {
409                                         temp = local_name;
410                                         break;
411                                 }
412                                 // else fall through and create it
413                         case PF_WRITE_LOCAL:
414                                 if (writePackedFile(reports, local_name, pf, 1) == RET_OK) {
415                                         temp = local_name;
416                                 }
417                                 break;
418                         case PF_USE_ORIGINAL:
419                                 // if file exists use it
420                                 if (BLI_exists(abs_name)) {
421                                         temp = abs_name;
422                                         break;
423                                 }
424                                 // else fall through and create it
425                         case PF_WRITE_ORIGINAL:
426                                 if (writePackedFile(reports, abs_name, pf, 1) == RET_OK) {
427                                         temp = abs_name;
428                                 }
429                                 break;
430                         default:
431                                 printf("unpackFile: unknown return_value %d\n", how);
432                                 break;
433                 }
434                 
435                 if (temp) {
436                         newname = MEM_mallocN(strlen(temp) + 1, "unpack_file newname");
437                         strcpy(newname, temp);
438                 }
439         }
440         
441         return (newname);
442 }
443
444
445 int unpackVFont(ReportList *reports, VFont *vfont, int how)
446 {
447         char localname[FILE_MAXDIR + FILE_MAXFILE], fi[FILE_MAXFILE];
448         char *newname;
449         int ret_value = RET_ERROR;
450         
451         if (vfont != NULL) {
452                 strcpy(localname, vfont->name);
453                 BLI_splitdirstring(localname, fi);
454                 
455                 sprintf(localname, "//fonts/%s", fi);
456                 
457                 newname = unpackFile(reports, vfont->name, localname, vfont->packedfile, how);
458                 if (newname != NULL) {
459                         ret_value = RET_OK;
460                         freePackedFile(vfont->packedfile);
461                         vfont->packedfile = 0;
462                         strcpy(vfont->name, newname);
463                         MEM_freeN(newname);
464                 }
465         }
466         
467         return (ret_value);
468 }
469
470 int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
471 {
472         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
473         char *newname;
474         int ret_value = RET_ERROR;
475
476         if (sound != NULL) {
477                 strcpy(localname, sound->name);
478                 BLI_splitdirstring(localname, fi);
479                 sprintf(localname, "//sounds/%s", fi);
480
481                 newname = unpackFile(reports, sound->name, localname, sound->packedfile, how);
482                 if (newname != NULL) {
483                         strcpy(sound->name, newname);
484                         MEM_freeN(newname);
485
486                         freePackedFile(sound->packedfile);
487                         sound->packedfile = 0;
488
489                         sound_load(bmain, sound);
490
491                         ret_value = RET_OK;
492                 }
493         }
494         
495         return(ret_value);
496 }
497
498 int unpackImage(ReportList *reports, Image *ima, int how)
499 {
500         char localname[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
501         char *newname;
502         int ret_value = RET_ERROR;
503         
504         if (ima != NULL) {
505                 strcpy(localname, ima->name);
506                 BLI_splitdirstring(localname, fi);
507                 sprintf(localname, "//textures/%s", fi);
508                         
509                 newname = unpackFile(reports, ima->name, localname, ima->packedfile, how);
510                 if (newname != NULL) {
511                         ret_value = RET_OK;
512                         freePackedFile(ima->packedfile);
513                         ima->packedfile = NULL;
514                         strcpy(ima->name, newname);
515                         MEM_freeN(newname);
516                         BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
517                 }
518         }
519         
520         return(ret_value);
521 }
522
523 void unpackAll(Main *bmain, ReportList *reports, int how)
524 {
525         Image *ima;
526         VFont *vf;
527         bSound *sound;
528
529         for(ima=bmain->image.first; ima; ima=ima->id.next)
530                 if(ima->packedfile)
531                         unpackImage(reports, ima, how);
532
533         for(vf=bmain->vfont.first; vf; vf=vf->id.next)
534                 if(vf->packedfile)
535                         unpackVFont(reports, vf, how);
536
537         for(sound=bmain->sound.first; sound; sound=sound->id.next)
538                 if(sound->packedfile)
539                         unpackSound(bmain, reports, sound, how);
540 }
541