/*
# _____     ___ ____     ___ ____
#  ____|   |    ____|   |        | |____|    https://github.com/pspdev
# |     ___|   |     ___|    ____| |    \    PSPDEV Open Source Project.
#-----------------------------------------------------------------------
# (c) 2005 Naomi Peori <naomi@peori.ca>
# Licenced under Academic Free License version 2.0
# Review pspsdk README & LICENSE files for further details.
#
# 2006-12-30 - Andrew Whatson <whatson@gmail.com>
#   - rewrote for easier reading
#   - gave "correct" names to UNKNOWN.* files
#   - improved memory efficiency with large PBPs
#   - output name of each file as it's added
#
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef WORDS_BIGENDIAN

// Swap the bytes of an int for big-endian machines
static int swap_int(int n)
{
  return ((n>>24)&0xff)|((n>>8)&0xff00)|((n<<8)&0xff0000)|((n<<24)&0xff000000);
}

#endif

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif

// Struct to describe the header of a PBP file
struct {
   char signature[4];
   char version[4];
   int offset[8];
} header = {
   { 0x00, 0x50, 0x42, 0x50 },
   { 0x00, 0x00, 0x01, 0x00 },
   { 40, 0, 0, 0, 0, 0, 0, 0 }
};

// Maximum size of read buffer
int maxbuffer = 16 * 1024 * 1024;

int main(int argc, char *argv[]) { int loop0, result;
   FILE *infile;
   FILE *outfile;
   int filesize[8];
   
   // Check for the correct number of arguments
   if (argc != 10) {
      printf("USAGE: %s <output.pbp> <param.sfo> <icon0.png> <icon1.pmf> <pic0.png> <pic1.png> <snd0.at3> <data.psp> <data.psar>\n", argv[0]);
      return -1;
   }
   
   // For each file in the PBP
   for (loop0 = 0; loop0 < 8; loop0++) {
      
      // If the specificed filename is NULL or -, skip the file.
      if (strncmp(argv[2 + loop0], "NULL", 4) == 0 || strncmp(argv[2 + loop0], "-", 4) == 0) {
         filesize[loop0] = 0;
      } else {
         
         // Open the file
         infile = fopen(argv[2 + loop0], "rb");
         if (infile == NULL) {
            printf("ERROR: Could not open the file. (%s)\n", argv[2 + loop0]);
            return -1;
         }
         
         // Read in the file size
         fseek(infile, 0, SEEK_END);
         filesize[loop0] = ftell(infile);
         fseek(infile, 0, SEEK_SET);
         if (filesize[loop0] < 0) {
            printf("ERROR: Could not read in the file size. (%s)\n", argv[2 + loop0]);
            return -1;
         }
         
         // Close the file
         result = fclose(infile);
         if (result < 0) {
            printf("ERROR: Could not close the file. (%s)\n", argv[2 + loop0]);
            return -1;
         }
      }
   }

   // Set the header offset values for each file
   for (loop0 = 1; loop0 < 8; loop0++) {
      header.offset[loop0] = header.offset[loop0 - 1] + filesize[loop0 - 1];
   }
   
#ifdef WORDS_BIGENDIAN
   
   // Swap the bytes of the offsets for big-endian machines
   for (loop0 = 0; loop0 < 8; loop0++) {
      header.offset[loop0] = swap_int(header.offset[loop0]);
   }
   
#endif
   
   // Open the output file
   outfile = fopen(argv[1], "wb");
   if (outfile == NULL) {
      printf("ERROR: Could not open the output file. (%s)\n", argv[1]);
      return -1;
   }
   
   // Write out the header
   result = fwrite(&header, sizeof(header), 1, outfile);
   if (result < 0) {
      printf("ERROR: Could not write out the file header. (%s)\n", argv[1]);
      return -1;
   }
   
   // For each file in the PBP
   for (loop0 = 0; loop0 < 8; loop0++) {
      void *buffer;
      int readsize;
      
      // Print out the file details
      printf("[%d] %10d bytes | %s\n", loop0, filesize[loop0], argv[2 + loop0]);
      
      // If this file is empty, skip it
      if (!filesize[loop0]) continue;
      
      // Open the file
      infile = fopen(argv[2 + loop0], "rb");
      if (infile == NULL) {
         printf("ERROR: Could not open the file. (%s)\n", argv[2 + loop0]);
         return -1;
      }
      
      do {
         // Make sure we don't exceed the maximum buffer size
         if (filesize[loop0] > maxbuffer) {
            readsize = maxbuffer;
         } else {
            readsize = filesize[loop0];
         }
         filesize[loop0] -= readsize;
         
         // Create the read buffer
         buffer = malloc(readsize);
         if (buffer == NULL) {
            printf("ERROR: Could not allocate the file data space. (%s)\n", argv[2 + loop0]);
            return -1;
         }
         
         // Read in the data from the file
         size_t result = fread(buffer, readsize, 1, infile);
         if (result < 0) {
            printf("ERROR: Could not read in the file data. (%s)\n", argv[2 + loop0]);
            return -1;
         }
         
         // Write the contents of the buffer to the PBP
         if (fwrite(buffer, readsize, 1, outfile) < 0) {
            printf("ERROR: Could not write out the file data. (%s)\n", argv[1]);
            return -1;
         }
         
         // Clean up the buffer
         free(buffer);
         
         // Repeat if we haven't finished writing the file
      } while (filesize[loop0]);
   }
   
   // Close the output file.
   result = fclose(outfile);
   if (result < 0) {
      printf("ERROR: Could not close the output file. (%s)\n", argv[1]);
      return -1;
   }
   
   // Exit successful
   return 0;
} 
