// (c) 2005-2007 Electronic Arts Inc.

#include "stdgimex.h"
#include "stdgimex.cpp"
#if defined(_MSC_VER)
#include <windows.h>
#endif

// 2004.5.12 - allow 0 byte pad files.  more exact write error checking
// 2004.5.14 - int64, maxsize to allow multiple files to be generated on xbox, where a single file can not exceed 1 GB
// 2004.5.18 - use proper MB*1024*1024
// 2004.5.26 - seek to end and write 1 byte for speed and quick failure if disk is not big enough
// 2004.5.27 - append # to end of name before extension
// 2004.6.01 - maxpad 2040
// 2005.1.31 - iso file to diff size from
// 2005.2.16 - remove upper limit by default
// 2005.9.19 - SetEndOfFile for PC

//
//  ABSTRACT
//
//  makepad - make a padfile of specified size
//
//  SUMMARY
//
//  MakePad <file.pad> <size> <maxsize>
//  <file.pad> Name of file to create
//  <size>     Size of pad file(s) in  MB.  -size to specify in exact bytes
//  <maxsize>  Is the maximum bytes to put in one file. + for MB, - for bytes
//  <isofile>  File to get size of and create pad with difference of specified size and iso file size
//             A number will be appended to the filename. note - keep to ISO 8.3 names. ie aaaa.pad
//
//  DESCRIPTION
//
//  makepad makes an empty file of the specified size.  This is useful for filling DVDs to improve load time.
//  A file (isofile) can be specified and the size of that file will be subtracked from the specified size
//  before generating the padfile.
//
//  RETURNS
//
//  0 always returns 0
//
//  SEE ALSO
//
//  paddvd, du
//
//  EXAMPLE
//
//  The following nant script was used to build a padded ps2 disc on NBA Live 2006
//
//  <exec
//      program="${package.dir}\bin\makepad.exe"
//      workingdir="${root-data-dir}"
//      >
//      <arg value="boot.pad 0"/> <!-- create an empty placeholder file for layout -->
//  </exec>
//      <!-- pass1: build temporary iso to get size estimate for padding -->
//  <exec
//      program="${package.dir}\bin\LuxPak.exe"
//      workingdir="${root-data-dir}"
//      >
//      <arg value="-V1"/> <!-- verbose output -->
//      <arg value="-D"/>  <!-- check for 8.3 compliant file names -->
//      <arg value="-license=${ps2-dvd-license-file}"/>   <!-- license file -->
//      <arg value="-root="/>
//      <arg value="-A32768"/>
//      <arg value="-sDFE"/>
//      <arg value="-map1=${sector-info}"/>
//      <arg value="@${dvd-response-file}"/>    <!-- response file contains all of the files to be put on the disc -->
//      <arg value="${package.dir}/images/tmp.${ps2-dvd-iso}"/>                     <!-- iso name -->
//  </exec>
//      <!-- build pad file to fill dvd, pushing elf to inside and data to outside of dvd -->
//      <exec
//      program="${package.dir}\bin\makepad.exe"
//      workingdir="${root-data-dir}"
//      >
//      <arg value="boot.pad -4676016128 0 ${package.dir}/images/tmp.${ps2-dvd-iso}"/> <!-- create an empty placeholder file for layout -->
//  </exec>
//      <!-- pass2: build final ccz and iso -->
//      <exec
//      program="${package.dir}\bin\LuxPak.exe"
//      workingdir="${root-data-dir}"
//      >
//      <arg value="-V1"/> <!-- verbose output -->
//      <arg value="-D"/>  <!-- check for 8.3 compliant file names -->
//      <arg value="-license=${ps2-dvd-license-file}"/>   <!-- license file -->
//      <arg value="-root="/>
//      <arg value="-A32768"/>
//      <arg value="-sDFE"/>
//      <arg value="-map1=${sector-info}"/>
//      <arg value="-end=2284216"/>
//      <arg value="@${dvd-response-file}"/>    <!-- response file contains all of the files to be put on the disc -->
//      <arg value="${package.dir}/images/${ps2-dvd-ccz}"/>                     <!-- iso name -->
//  </exec>
//
//  END ABSTRACT
//

typedef GPOS filesize_t;

#if defined(_MSC_VER)
bool zerofile(const char *filename, filesize_t len)
{
    HANDLE hFile = CreateFile(filename,    // create filename
             GENERIC_WRITE,                // open for writing
             0,                            // do not share
             NULL,                         // no security
             CREATE_ALWAYS,                // overwrite existing
             FILE_ATTRIBUTE_NORMAL |       // normal file
             FILE_FLAG_OVERLAPPED,         // asynchronous I/O
             NULL);                        // no attr. template

    BOOL seekok = SetFilePointerEx(hFile,*(LARGE_INTEGER *)&len,NULL,FILE_BEGIN);
    SetEndOfFile(hFile);
    CloseHandle(hFile);

    bool ok=true;

    return ok;
}

#else

bool zerofile(const char *filename, filesize_t len)
{
    GSTREAM *g = gwopen(filename);
    if (!g)
    {
        printf("MakePad ERROR.  File %s failed to open for write\n", filename);
        exit(100);
    }

    bool ok=true;
    if (len>0)
    {
        ok = gseek(g,len-1);
        if (ok)
        {
            filesize_t wrote = gwrite(g, "\373", 1);  // write 1 byte signature at the end
            if (wrote!=1)
                ok = false;
        }
    }
    gclose(g);

    return ok;
}
#endif

int main(int argc, char **argv)
{
    if(argc<3)
    {
        printf(
            "MakePad 2006.9.19 by Frank Barchard\n"
            "USAGE: MakePad <file.pad> <size> <maxsize>\n"
            "  <file.pad> Name of file to create\n"
            "  <size>     Size of pad file(s) in  MB.  -size to specify in exact bytes\n"
            "  <maxsize>  Is the maximum bytes to put in one file. + for MB, - for bytes\n"
            "  <isofile>  File to get size of and create pad with difference of specified size and iso file size\n"
            "             A number will be appended to the filename. note - keep to ISO 8.3 names. ie aaaa.pad\n");
        exit(1);
    }
#if defined(_MSC_VER)
    filesize_t len = _atoi64(argv[2]);
#else
    filesize_t len = (filesize_t) atoll(argv[2]);   // long long is hopefully 64 or more bits
#endif
    if (len<0)
        len = -len;
    else
        len *= 1024*1024;

    filesize_t maxlen = 0x7fffffffffffffffLL;
    if (argc>3)
    {
#if defined(_MSC_VER)
        maxlen = _atoi64(argv[3]);
#else
        maxlen = (filesize_t) atoll(argv[3]);
#endif

        if (maxlen<0)
            maxlen = -maxlen;
        else
            maxlen *= 1024*1024;
    }

    if (argc>4)
    {
        GSTREAM *giso = gopen(argv[4]);
        if (giso)
        {
            filesize_t isolen = glen(giso);
            len -= isolen;
            gclose(giso);
        }
    }

    int chopcount=0;
    filesize_t originallen = len;
    do
    {
        filesize_t nextlen = len;
        if (maxlen && (nextlen>maxlen))
            nextlen = maxlen;

        char out[512];
        char *ext = strrchr(argv[1],'.');
        if (ext) *ext++ = '\0';
        else      ext = "pad";
        if (maxlen && (originallen>maxlen))
            sprintf(out,"%s%d.%s",argv[1],chopcount,ext);
        else
            sprintf(out,"%s.%s",argv[1],ext);

        printf("Making %s size %I64d bytes\n",out,nextlen);

        bool ok = zerofile(out,nextlen);

        if (!ok)
        {
            printf("MakePad ERROR.  File %s failed on write.  Out of disk space?\n", out);
            exit(100);
        }
        len -= nextlen;
        ++chopcount;
    } while (len>0);

    return 0;
}
