mirror of
				https://github.com/kovidgoyal/calibre.git
				synced 2025-11-03 19:17:02 -05:00 
			
		
		
		
	This is a second commit, removing the white space processing my editor did. I thought I had made a PR but there is no record of it. Apologies if this is a duplicate.
		
			
				
	
	
		
			620 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			620 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#ifndef UNICODE
 | 
						|
#define UNICODE
 | 
						|
#endif
 | 
						|
 | 
						|
#ifndef _UNICODE
 | 
						|
#define _UNICODE
 | 
						|
#endif
 | 
						|
 | 
						|
#include <Windows.h>
 | 
						|
#include <Shlobj.h>
 | 
						|
#include <Shlwapi.h>
 | 
						|
#include <Shellapi.h>
 | 
						|
#include <Psapi.h>
 | 
						|
#include <wchar.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <io.h>
 | 
						|
#include <sys/types.h>
 | 
						|
#include <sys/stat.h>
 | 
						|
#include <VersionHelpers.h>
 | 
						|
 | 
						|
#include <easylzma/decompress.h>
 | 
						|
#include "XUnzip.h"
 | 
						|
 | 
						|
#define BUFSIZE 4096
 | 
						|
 | 
						|
// Error handling {{{
 | 
						|
 | 
						|
static void show_error(LPCWSTR msg) {
 | 
						|
    MessageBeep(MB_ICONERROR);
 | 
						|
    MessageBox(NULL, msg, L"Error", MB_OK|MB_ICONERROR);
 | 
						|
}
 | 
						|
 | 
						|
static void show_detailed_error(LPCWSTR preamble, LPCWSTR msg, int code) {
 | 
						|
    LPWSTR buf;
 | 
						|
    buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)*
 | 
						|
            (wcslen(msg) + wcslen(preamble) + 80));
 | 
						|
 | 
						|
    _snwprintf_s(buf,
 | 
						|
        LocalSize(buf) / sizeof(WCHAR), _TRUNCATE,
 | 
						|
        L"%s\r\n  %s (Error Code: %d)\r\n",
 | 
						|
        preamble, msg, code);
 | 
						|
 | 
						|
    show_error(buf);
 | 
						|
    LocalFree(buf);
 | 
						|
}
 | 
						|
 | 
						|
static void show_zip_error(LPCWSTR preamble, LPCWSTR msg, ZRESULT code) {
 | 
						|
    LPWSTR buf;
 | 
						|
    char msgbuf[1024] = {0};
 | 
						|
 | 
						|
    FormatZipMessage(code, msgbuf, 1024);
 | 
						|
 | 
						|
    buf = (LPWSTR)LocalAlloc(LMEM_ZEROINIT, sizeof(WCHAR)*
 | 
						|
            (wcslen(preamble) + wcslen(msg) + 1100));
 | 
						|
 | 
						|
    _snwprintf_s(buf,
 | 
						|
        LocalSize(buf) / sizeof(WCHAR), _TRUNCATE,
 | 
						|
        L"%s\r\n  %s (Error: %S)\r\n",
 | 
						|
        preamble, msg, msgbuf);
 | 
						|
 | 
						|
    show_error(buf);
 | 
						|
    LocalFree(buf);
 | 
						|
}
 | 
						|
 | 
						|
static void show_last_error(LPCWSTR preamble) {
 | 
						|
    WCHAR *msg = NULL;
 | 
						|
    DWORD dw = GetLastError();
 | 
						|
 | 
						|
    FormatMessage(
 | 
						|
        FORMAT_MESSAGE_ALLOCATE_BUFFER |
 | 
						|
        FORMAT_MESSAGE_FROM_SYSTEM |
 | 
						|
        FORMAT_MESSAGE_IGNORE_INSERTS,
 | 
						|
        NULL,
 | 
						|
        dw,
 | 
						|
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
 | 
						|
        (LPWSTR)&msg,
 | 
						|
        0, NULL );
 | 
						|
 | 
						|
    show_detailed_error(preamble, msg, (int)dw);
 | 
						|
}
 | 
						|
 | 
						|
// }}}
 | 
						|
 | 
						|
// Load, decompress and extract data {{{
 | 
						|
 | 
						|
static BOOL load_data(LPVOID *data, DWORD *sz) {
 | 
						|
    HRSRC rsrc;
 | 
						|
    HGLOBAL h;
 | 
						|
 | 
						|
    rsrc = FindResourceW(NULL, L"extra", L"extra");
 | 
						|
    if (rsrc == NULL) { show_last_error(L"Failed to find portable data in exe"); return false; }
 | 
						|
 | 
						|
    h = LoadResource(NULL, rsrc);
 | 
						|
    if (h == NULL) { show_last_error(L"Failed to load portable data from exe"); return false; }
 | 
						|
 | 
						|
    *data = LockResource(h);
 | 
						|
    if (*data == NULL) { show_last_error(L"Failed to lock portable data in exe"); return false; }
 | 
						|
 | 
						|
    *sz = SizeofResource(NULL, rsrc);
 | 
						|
    if (sz == 0) { show_last_error(L"Failed to get size of portable data in exe"); return false; }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static BOOL unzip(HZIP zipf, int nitems, IProgressDialog *pd) {
 | 
						|
    int i = 0;
 | 
						|
    ZRESULT res;
 | 
						|
    ZIPENTRYW ze;
 | 
						|
 | 
						|
    for (i = 0; i < nitems; i++) {
 | 
						|
        res = GetZipItem(zipf, i, &ze);
 | 
						|
        if (res != ZR_OK) { show_zip_error(L"Failed to get zip item", L"", res); return false;}
 | 
						|
 | 
						|
        res = UnzipItem(zipf, i, ze.name, 0, ZIP_FILENAME);
 | 
						|
        if (res != ZR_OK) { CloseZip(zipf); show_zip_error(L"Failed to extract zip item (is your disk full?):", ze.name, res); return false;}
 | 
						|
 | 
						|
        pd->SetLine(2, ze.name, true, NULL);
 | 
						|
        pd->SetProgress(i, nitems);
 | 
						|
    }
 | 
						|
 | 
						|
    CloseZip(zipf);
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static HANDLE temp_file(LPWSTR name) {
 | 
						|
    UINT res;
 | 
						|
    HANDLE h;
 | 
						|
 | 
						|
    res = GetTempFileNameW(L".", L"portable_data", 0, name);
 | 
						|
 | 
						|
    if (res == 0) { show_last_error(L"Failed to create temporary file to decompress portable data"); return INVALID_HANDLE_VALUE; }
 | 
						|
 | 
						|
    h = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
 | 
						|
    if (h == INVALID_HANDLE_VALUE) { show_last_error(L"Failed to open temp file to decompress portable data"); }
 | 
						|
    return h;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
struct DataStream
 | 
						|
{
 | 
						|
    const unsigned char *in_data;
 | 
						|
    size_t in_len;
 | 
						|
 | 
						|
    HANDLE out;
 | 
						|
    IProgressDialog *pd;
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
input_callback(void *ctx, void *buf, size_t * size)
 | 
						|
{
 | 
						|
    size_t rd = 0;
 | 
						|
    struct DataStream * ds = (struct DataStream *) ctx;
 | 
						|
 | 
						|
    rd = (ds->in_len < *size) ? ds->in_len : *size;
 | 
						|
 | 
						|
    if (rd > 0) {
 | 
						|
        memcpy(buf, (void*) ds->in_data, rd);
 | 
						|
        ds->in_data += rd;
 | 
						|
        ds->in_len -= rd;
 | 
						|
    }
 | 
						|
 | 
						|
    *size = rd;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int output_error_shown = 0;
 | 
						|
 | 
						|
static size_t
 | 
						|
output_callback(void *ctx, const void *buf, size_t size)
 | 
						|
{
 | 
						|
    struct DataStream * ds = (struct DataStream *) ctx;
 | 
						|
    DWORD written = 0;
 | 
						|
 | 
						|
    if (size > 0) {
 | 
						|
        if (!WriteFile(ds->out, buf, (DWORD)size, &written, NULL)) {
 | 
						|
            show_last_error(L"Failed to write uncompressed data to temp file");
 | 
						|
            output_error_shown = 1;
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
        written = SetFilePointer(ds->out, 0, NULL, FILE_CURRENT);
 | 
						|
        ds->pd->SetProgress(written, UNCOMPRESSED_SIZE);
 | 
						|
    }
 | 
						|
 | 
						|
    return size;
 | 
						|
}
 | 
						|
 | 
						|
static BOOL decompress(LPVOID src, DWORD src_sz, HANDLE out, IProgressDialog *pd) {
 | 
						|
    elzma_decompress_handle h;
 | 
						|
    struct DataStream ds;
 | 
						|
    int rc;
 | 
						|
 | 
						|
    h = elzma_decompress_alloc();
 | 
						|
 | 
						|
    if (h == NULL) { show_error(L"Out of memory"); return false; }
 | 
						|
 | 
						|
    ds.in_data = (unsigned char*)src;
 | 
						|
    ds.in_len = src_sz;
 | 
						|
    ds.out = out;
 | 
						|
    ds.pd = pd;
 | 
						|
 | 
						|
    rc = elzma_decompress_run(h, input_callback, (void *) &ds, output_callback,
 | 
						|
            (void *) &ds, ELZMA_lzip);
 | 
						|
 | 
						|
    if (rc != ELZMA_E_OK) {
 | 
						|
        if (!output_error_shown) show_detailed_error(L"Failed to decompress portable data", L"", rc);
 | 
						|
        elzma_decompress_free(&h);
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    elzma_decompress_free(&h);
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static BOOL extract(LPVOID cdata, DWORD csz) {
 | 
						|
    HANDLE h;
 | 
						|
    WCHAR tempnam[MAX_PATH+1] = {0};
 | 
						|
    BOOL ret = true;
 | 
						|
    HZIP zipf;
 | 
						|
    ZIPENTRYW ze;
 | 
						|
    ZRESULT res;
 | 
						|
    int nitems;
 | 
						|
    HRESULT hr;
 | 
						|
    IProgressDialog *pd = NULL;
 | 
						|
 | 
						|
    hr = CoCreateInstance(CLSID_ProgressDialog, NULL,
 | 
						|
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pd));
 | 
						|
 | 
						|
    if (FAILED(hr)) { show_error(L"Failed to create progress dialog"); return false; }
 | 
						|
    pd->SetTitle(L"Extracting Calibre Portable");
 | 
						|
    pd->SetLine(1, L"Decompressing data...", true, NULL);
 | 
						|
 | 
						|
    h = temp_file(tempnam);
 | 
						|
    if (h == INVALID_HANDLE_VALUE) return false;
 | 
						|
 | 
						|
    pd->StartProgressDialog(NULL, NULL, PROGDLG_NORMAL | PROGDLG_AUTOTIME | PROGDLG_NOCANCEL, NULL);
 | 
						|
    if (!decompress(cdata, csz, h, pd)) { ret = false; goto end; }
 | 
						|
    SetFilePointer(h, 0, NULL, FILE_BEGIN);
 | 
						|
    zipf = OpenZip(h, 0, ZIP_HANDLE);
 | 
						|
    if (zipf == 0) { show_last_error(L"Failed to open zipped portable data"); ret = false; goto end; }
 | 
						|
 | 
						|
    res = GetZipItem(zipf, -1, &ze);
 | 
						|
    if (res != ZR_OK) { show_zip_error(L"Failed to get count of items in portable data", L"", res); ret = false; goto end;}
 | 
						|
    nitems = ze.index;
 | 
						|
 | 
						|
    pd->SetLine(1, L"Copying files...", true, NULL);
 | 
						|
    if (!unzip(zipf, nitems, pd)) { ret = false; goto end; }
 | 
						|
end:
 | 
						|
    pd->StopProgressDialog();
 | 
						|
    pd->Release();
 | 
						|
    CloseHandle(h);
 | 
						|
    DeleteFile(tempnam);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
// }}}
 | 
						|
 | 
						|
// Find calibre portable folder and install/upgrade into it {{{
 | 
						|
 | 
						|
static BOOL directory_exists( LPCWSTR path )
 | 
						|
{
 | 
						|
  if( _waccess_s( path, 0 ) == 0 )
 | 
						|
  {
 | 
						|
    struct _stat status;
 | 
						|
    _wstat( path, &status );
 | 
						|
    return (status.st_mode & S_IFDIR) != 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static BOOL file_exists( LPCWSTR path )
 | 
						|
{
 | 
						|
  if( _waccess_s( path, 0 ) == 0 )
 | 
						|
  {
 | 
						|
    struct _stat status;
 | 
						|
    _wstat( path, &status );
 | 
						|
    return (status.st_mode & S_IFREG) != 0;
 | 
						|
  }
 | 
						|
 | 
						|
  return FALSE;
 | 
						|
}
 | 
						|
 | 
						|
static LPWSTR get_directory_from_user() {
 | 
						|
    WCHAR name[MAX_PATH+1] = {0};
 | 
						|
    LPWSTR path = NULL;
 | 
						|
    PIDLIST_ABSOLUTE ret;
 | 
						|
 | 
						|
    path = (LPWSTR)calloc(2*MAX_PATH, sizeof(WCHAR));
 | 
						|
    if (path == NULL) { show_error(L"Out of memory"); return NULL; }
 | 
						|
 | 
						|
    int image = 0;
 | 
						|
    BROWSEINFO bi = { NULL, NULL, name,
 | 
						|
        L"Select the folder where you want to install or update Calibre Portable",
 | 
						|
        BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_USENEWUI,
 | 
						|
        NULL, NULL, image };
 | 
						|
 | 
						|
    ret = SHBrowseForFolder(&bi);
 | 
						|
    if (ret == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!SHGetPathFromIDList(ret, path)) {
 | 
						|
        show_detailed_error(L"The selected folder is not valid: ", name, 0);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    return path;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static bool is_dots(LPCWSTR name) {
 | 
						|
    return wcscmp(name, L".") == 0 || wcscmp(name, L"..") == 0;
 | 
						|
}
 | 
						|
 | 
						|
static bool rmtree(LPCWSTR path) {
 | 
						|
    SHFILEOPSTRUCTW op;
 | 
						|
    WCHAR buf[4*MAX_PATH + 2] = {0};
 | 
						|
 | 
						|
    if (GetFullPathName(path, 4*MAX_PATH, buf, NULL) == 0) return false;
 | 
						|
 | 
						|
    op.hwnd = NULL;
 | 
						|
    op.wFunc = FO_DELETE;
 | 
						|
    op.pFrom = buf;
 | 
						|
    op.pTo = NULL;
 | 
						|
    op.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMMKDIR;
 | 
						|
    op.fAnyOperationsAborted = false;
 | 
						|
    op.hNameMappings = NULL;
 | 
						|
    op.lpszProgressTitle = NULL;
 | 
						|
 | 
						|
    return SHFileOperationW(&op) == 0;
 | 
						|
}
 | 
						|
 | 
						|
static BOOL find_portable_dir(LPCWSTR base, LPWSTR *result, BOOL *existing) {
 | 
						|
    WCHAR buf[4*MAX_PATH] = {0};
 | 
						|
 | 
						|
    _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\calibre-portable.exe", base);
 | 
						|
    *existing = true;
 | 
						|
 | 
						|
    if (file_exists(buf)) {
 | 
						|
        *result = _wcsdup(base);
 | 
						|
        if (*result == NULL) { show_error(L"Out of memory"); return false; }
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
 | 
						|
    WIN32_FIND_DATA fdFile;
 | 
						|
    HANDLE hFind = NULL;
 | 
						|
    _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\*", base);
 | 
						|
 | 
						|
    if((hFind = FindFirstFileEx(buf, FindExInfoStandard, &fdFile, FindExSearchLimitToDirectories, NULL, 0)) != INVALID_HANDLE_VALUE) {
 | 
						|
        do {
 | 
						|
            if(is_dots(fdFile.cFileName)) continue;
 | 
						|
 | 
						|
            if(fdFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
 | 
						|
                _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\%s\\calibre-portable.exe", base, fdFile.cFileName);
 | 
						|
                if (file_exists(buf)) {
 | 
						|
                    *result = _wcsdup(buf);
 | 
						|
                    if (*result == NULL) { show_error(L"Out of memory"); return false; }
 | 
						|
                    PathRemoveFileSpec(*result);
 | 
						|
                    FindClose(hFind);
 | 
						|
                    return true;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } while(FindNextFile(hFind, &fdFile));
 | 
						|
        FindClose(hFind);
 | 
						|
    }
 | 
						|
 | 
						|
    *existing = false;
 | 
						|
    _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE, L"%s\\Calibre Portable", base);
 | 
						|
    if (!CreateDirectory(buf, NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
 | 
						|
        show_last_error(L"Failed to create Calibre Portable folder");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    *result = _wcsdup(buf);
 | 
						|
    if (*result == NULL) { show_error(L"Out of memory"); return false; }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static LPWSTR make_unpack_dir() {
 | 
						|
    WCHAR buf[4*MAX_PATH] = {0};
 | 
						|
    LPWSTR ans = NULL;
 | 
						|
 | 
						|
    if (directory_exists(L"_unpack_calibre_portable"))
 | 
						|
        rmtree(L"_unpack_calibre_portable");
 | 
						|
 | 
						|
    if (!CreateDirectory(L"_unpack_calibre_portable", NULL) && GetLastError() != ERROR_ALREADY_EXISTS) {
 | 
						|
        show_last_error(L"Failed to create temporary folder to unpack into");
 | 
						|
        return ans;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!GetFullPathName(L"_unpack_calibre_portable", 4*MAX_PATH, buf, NULL)) {
 | 
						|
        show_last_error(L"Failed to resolve path");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ans = _wcsdup(buf);
 | 
						|
    if (ans == NULL) show_error(L"Out of memory");
 | 
						|
    return ans;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static BOOL move_program() {
 | 
						|
    if (MoveFileEx(L"Calibre Portable\\calibre-portable.exe",
 | 
						|
                L"..\\calibre-portable.exe", MOVEFILE_REPLACE_EXISTING) == 0) {
 | 
						|
        show_last_error(L"Failed to move calibre-portable.exe, make sure calibre is not running");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    if (MoveFileEx(L"Calibre Portable\\ebook-viewer-portable.exe",
 | 
						|
                L"..\\ebook-viewer-portable.exe", MOVEFILE_REPLACE_EXISTING) == 0) {
 | 
						|
        show_last_error(L"Failed to move ebook-viewer-portable.exe, make sure calibre is not running");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
    if (MoveFileEx(L"Calibre Portable\\ebook-edit-portable.exe",
 | 
						|
                L"..\\ebook-edit-portable.exe", MOVEFILE_REPLACE_EXISTING) == 0) {
 | 
						|
        show_last_error(L"Failed to move ebook-edit-portable.exe, make sure calibre is not running");
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (directory_exists(L"..\\Calibre")) {
 | 
						|
        if (!rmtree(L"..\\Calibre")) {
 | 
						|
            show_error(L"Failed to delete the Calibre program folder. Make sure calibre is not running.");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (MoveFileEx(L"Calibre Portable\\Calibre", L"..\\Calibre", 0) == 0) {
 | 
						|
        Sleep(4000); // Sleep and try again
 | 
						|
        if (MoveFileEx(L"Calibre Portable\\Calibre", L"..\\Calibre", 0) == 0) {
 | 
						|
            show_last_error(L"Failed to move calibre program folder. This is usually caused by an antivirus program or a file sync program like DropBox. Turn them off temporarily and try again. Underlying error: ");
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!directory_exists(L"..\\Calibre Library")) {
 | 
						|
        MoveFileEx(L"Calibre Portable\\Calibre Library", L"..\\Calibre Library", 0);
 | 
						|
    }
 | 
						|
 | 
						|
    if (!directory_exists(L"..\\Calibre Settings")) {
 | 
						|
        MoveFileEx(L"Calibre Portable\\Calibre Settings", L"..\\Calibre Settings", 0);
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
// }}}
 | 
						|
 | 
						|
static BOOL ensure_not_running() {
 | 
						|
    DWORD processes[4096], needed, num;
 | 
						|
    unsigned int i;
 | 
						|
    WCHAR name[4*MAX_PATH] = L"<unknown>";
 | 
						|
    HANDLE h;
 | 
						|
    DWORD len;
 | 
						|
    LPWSTR fname = NULL;
 | 
						|
 | 
						|
    if ( !EnumProcesses( processes, sizeof(processes), &needed ) ) {
 | 
						|
        return true;
 | 
						|
    }
 | 
						|
    num = needed / sizeof(DWORD);
 | 
						|
 | 
						|
    for (i = 0; i < num; i++) {
 | 
						|
        if (processes[i] == 0) continue;
 | 
						|
        h = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, processes[i] );
 | 
						|
        if (h != NULL) {
 | 
						|
            len = GetProcessImageFileNameW(h, name, 4*MAX_PATH);
 | 
						|
            CloseHandle(h);
 | 
						|
            if (len != 0) {
 | 
						|
                name[len] = 0;
 | 
						|
                fname = PathFindFileName(name);
 | 
						|
                if (wcscmp(fname, L"calibre.exe") == 0) {
 | 
						|
                    show_error(L"Calibre appears to be running on your computer. Please quit it before trying to install Calibre Portable.");
 | 
						|
                    return false;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return true;
 | 
						|
}
 | 
						|
 | 
						|
static void launch_calibre() {
 | 
						|
    STARTUPINFO si;
 | 
						|
    PROCESS_INFORMATION pi;
 | 
						|
 | 
						|
    ZeroMemory( &si, sizeof(si) );
 | 
						|
    si.cb = sizeof(si);
 | 
						|
    ZeroMemory( &pi, sizeof(pi) );
 | 
						|
 | 
						|
 | 
						|
    if (CreateProcess(_wcsdup(L"calibre-portable.exe"), NULL,
 | 
						|
            NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_PROCESS_GROUP,
 | 
						|
            NULL, NULL, &si, &pi)
 | 
						|
            == 0) {
 | 
						|
        show_last_error(L"Failed to launch calibre portable");
 | 
						|
    }
 | 
						|
 | 
						|
    // Close process and thread handles.
 | 
						|
    CloseHandle( pi.hProcess );
 | 
						|
    CloseHandle( pi.hThread );
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void makedirs(LPWSTR path) {
 | 
						|
    WCHAR *p = path;
 | 
						|
    while (*p) {
 | 
						|
        if ((*p == L'\\' || *p == L'/') && p != path && *(p-1) != L':') {
 | 
						|
            *p = 0;
 | 
						|
            CreateDirectory(path, NULL);
 | 
						|
            *p = L'\\';
 | 
						|
        }
 | 
						|
        p++;
 | 
						|
    }
 | 
						|
    CreateDirectory(path, NULL);
 | 
						|
}
 | 
						|
 | 
						|
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
 | 
						|
{
 | 
						|
	(void)hPrevInstance; (void)pCmdLine; (void)nCmdShow; (void)hInstance;
 | 
						|
    LPVOID cdata = NULL;
 | 
						|
    DWORD csz = 0;
 | 
						|
    int ret = 1, argc;
 | 
						|
    HRESULT hr;
 | 
						|
    LPWSTR tgt = NULL, dest = NULL, *argv, unpack_dir = NULL;
 | 
						|
    BOOL existing = false, launch = false, automated = false;
 | 
						|
    WCHAR buf[4*MAX_PATH] = {0}, mb_msg[4*MAX_PATH] = {0}, fdest[4*MAX_PATH] = {0};
 | 
						|
 | 
						|
    if (!load_data(&cdata, &csz)) return ret;
 | 
						|
    if (!IsWindows10OrGreater()) { show_error(L"Your version of Windows is too old. calibre requires Windows 10 or greater."); return ret; }
 | 
						|
 | 
						|
    hr = CoInitialize(NULL);
 | 
						|
    if (FAILED(hr)) { show_error(L"Failed to initialize COM"); return ret; }
 | 
						|
 | 
						|
    // Get the target folder for installation
 | 
						|
    argv = CommandLineToArgvW(GetCommandLine(), &argc);
 | 
						|
    if (argv == NULL) { show_last_error(L"Failed to get command line"); return ret; }
 | 
						|
    if (argc > 1) {
 | 
						|
        tgt = argv[1];
 | 
						|
        automated = true;
 | 
						|
        if (!directory_exists(tgt)) {
 | 
						|
            if (GetFullPathName(tgt, MAX_PATH*4, fdest, NULL) == 0) {
 | 
						|
                show_last_error(L"Failed to resolve target folder");
 | 
						|
                goto end;
 | 
						|
            }
 | 
						|
            makedirs(fdest);
 | 
						|
        }
 | 
						|
 | 
						|
    } else {
 | 
						|
        tgt = get_directory_from_user();
 | 
						|
        if (tgt == NULL) goto end;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!directory_exists(tgt)) {
 | 
						|
        show_detailed_error(L"The specified folder does not exist: ",
 | 
						|
                tgt, 1);
 | 
						|
        goto end;
 | 
						|
    }
 | 
						|
 | 
						|
    // Ensure the path to Calibre Portable is not too long
 | 
						|
    do {
 | 
						|
        if (!find_portable_dir(tgt, &dest, &existing)) goto end;
 | 
						|
 | 
						|
        if (GetFullPathName(dest, MAX_PATH*4, fdest, NULL) == 0) {
 | 
						|
            show_last_error(L"Failed to resolve target folder");
 | 
						|
            goto end;
 | 
						|
        }
 | 
						|
        free(dest); dest = NULL;
 | 
						|
 | 
						|
        if (wcslen(fdest) > 58) {
 | 
						|
            _snwprintf_s(buf, 4*MAX_PATH, _TRUNCATE,
 | 
						|
                L"Path to Calibre Portable (%s) too long. It must be less than 59 characters.", fdest);
 | 
						|
            if (!existing) RemoveDirectory(fdest);
 | 
						|
            show_error(buf);
 | 
						|
            tgt = get_directory_from_user();
 | 
						|
            if (tgt == NULL) goto end;
 | 
						|
        }
 | 
						|
    } while (wcslen(fdest) > 58);
 | 
						|
 | 
						|
    // Confirm the user wants to upgrade
 | 
						|
    if (existing && !automated) {
 | 
						|
        _snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE,
 | 
						|
            L"An existing install of Calibre Portable was found at %s. Do you want to upgrade it?",
 | 
						|
            fdest);
 | 
						|
        if (MessageBox(NULL, mb_msg,
 | 
						|
                L"Upgrade Calibre Portable?", MB_ICONEXCLAMATION | MB_YESNO | MB_TOPMOST) != IDYES)
 | 
						|
            goto end;
 | 
						|
    }
 | 
						|
 | 
						|
    if (existing) {
 | 
						|
        if (!ensure_not_running()) goto end;
 | 
						|
    }
 | 
						|
 | 
						|
    // Make a temp dir to unpack into
 | 
						|
    if (!SetCurrentDirectoryW(fdest)) { show_detailed_error(L"Failed to change to unzip folder: ", fdest, 0); goto end; }
 | 
						|
 | 
						|
    if ( (unpack_dir = make_unpack_dir()) == NULL ) goto end;
 | 
						|
    if (!SetCurrentDirectoryW(unpack_dir)) { show_detailed_error(L"Failed to change to unpack folder: ", fdest, 0); goto end; }
 | 
						|
 | 
						|
    // Extract files
 | 
						|
    if (!extract(cdata, csz)) goto end;
 | 
						|
 | 
						|
    // Move files from temp dir to the install dir
 | 
						|
    if (!move_program()) goto end;
 | 
						|
 | 
						|
    ret = 0;
 | 
						|
    if (!automated) {
 | 
						|
        _snwprintf_s(mb_msg, 4*MAX_PATH, _TRUNCATE,
 | 
						|
            L"Calibre Portable successfully installed to %s. Launch calibre?",
 | 
						|
            fdest);
 | 
						|
        launch = MessageBox(NULL, mb_msg,
 | 
						|
            L"Success", MB_ICONINFORMATION | MB_YESNO | MB_TOPMOST) == IDYES;
 | 
						|
    }
 | 
						|
 | 
						|
end:
 | 
						|
    if (unpack_dir != NULL) { SetCurrentDirectoryW(L".."); rmtree(unpack_dir); free(unpack_dir); }
 | 
						|
    CoUninitialize();
 | 
						|
    if (launch) launch_calibre();
 | 
						|
    return ret;
 | 
						|
}
 |