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