#ifndef DATA_H #define DATA_H #include #include #include #include "err.h" #include "util.h" #include "win32.h" using namespace std::string_literals; struct XmlError : public std::exception { const char* msg; inline XmlError() { msg = xmlGetLastError()->message; } inline virtual const char* what() const noexcept { return msg; } }; /* Fetch data from the web. */ void FetchData(unsigned char* sig); void FetchScreenwriters(unsigned char* sig); /* Wait for thread. */ struct Window; void WaitFor(Window& window, void (*f)(unsigned char*)); /* The structs ending with A are written as-is to disk. As such, they * should be regarded as immutable. If the format needs to be changed * in the future, then new structs ending with B should be added. */ /* Note that unsigned chars are 1-byte-aligned, shorts and wchar_t are * 2-byte-aligned and ints are 4-byte-aligned (on x86 Windows). */ /* Basic episode data presented in episode list view. */ struct ElvDataA { unsigned char version = 'a'; unsigned char rating = 0; unsigned char bWatched = 0; unsigned char bTVOriginal = 0; wchar_t sRating[4] = {0}; wchar_t siEp[6] = {0}; wchar_t title[128] = {0}; }; /* Extra episode data presented in data list view. */ struct DlvDataA { unsigned char version = 'a'; unsigned char pad[3] = {0}; wchar_t date[32] = {0}; wchar_t source[48] = {0}; wchar_t screenwriter[48] = {0}; wchar_t hint[128] = {0}; wchar_t wiki[192] = {0}; }; /* Configuration. */ struct CfgA { unsigned char version = 'a'; unsigned char bViewWatched = 1; unsigned char bViewTVOriginal = 1; signed char iSortCol = 1; unsigned short cEp = 4096; unsigned short iFocus = 0; unsigned short heightDlv = 0; wchar_t limitToScreenwriter[48] = {0}; wchar_t root[260] = {0}; wchar_t glob[64] = {0}; wchar_t url[192] = {0}; wchar_t prefixUrl[48] = {0}; }; /* Variable template for obtaining the version of a given struct. */ template constexpr inline unsigned char Version = T().version; template constexpr inline bool HasVersion = false; template constexpr inline bool HasVersion = true; inline void FreeView(void* view) { FlushViewOfFile(view, 0); UnmapViewOfFile(view); } /* FileView objects manage a memory-mapped file. The view buffer may * be treated as an array of a given type T. Note that reading and * writing a view can throw structured exceptions. We ignore these. */ template struct FileView { Unique hf; Unique hm; Unique view; const wchar_t* filename; size_t c; static FileView Initialized(const wchar_t* filename, size_t c, size_t cInit = 0) { /* If file didn't exist, initialize it with defaults. */ if (cInit && GetFileAttributes(filename) == INVALID_FILE_ATTRIBUTES) { FileView fv(filename, c); T t; cInit = cInit? cInit: c; for (size_t i = 0; i < cInit; i++) memcpy(fv+i, &t, sizeof(T)); return fv; } else return {filename, c}; } FileView(const wchar_t* filename, size_t c) : filename(filename), c(c) { hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); if (hf.Bad(INVALID_HANDLE_VALUE)) { if (GetLastError() == ERROR_FILE_NOT_FOUND) { hf = CreateFile(filename, GENERIC_READ|GENERIC_WRITE, 0, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); if (hf.Bad(INVALID_HANDLE_VALUE)) throw Err(WINDOWS, L"File "s + filename + L" could not be created"); } else throw Err(WINDOWS, L"File "s + filename + L" could not be opened"); } LARGE_INTEGER cbMap; cbMap.QuadPart = c*sizeof(T); hm = CreateFileMapping(hf.v, nullptr, PAGE_READWRITE, cbMap.HighPart, cbMap.LowPart, nullptr); if (hm.Bad(0)) throw Err(WINDOWS, L"File mapping for "s + filename + L" could not be created"); view = reinterpret_cast(MapViewOfFile(hm.v, FILE_MAP_ALL_ACCESS, 0, 0, 0)); if (view.Bad(0)) throw Err(WINDOWS, L"View for "s + filename + L"could not be mapped"); } /* Access element by index, performing bounds check and, if * applicable for T, version validation as well as * initialization, if needed. */ T& At(size_t i) { if (i >= c) throw Err(GENERIC, L"Element in "s + filename + L" could not be accessed: Index larger than buffer"); T& t = view.v[i]; if constexpr (HasVersion) { if (t.version == 0) { T t_; memcpy(&t, &t_, sizeof(T)); } else if (t.version != Version) throw Err(GENERIC, L"Element in "s + filename + L" could not be accessed: Invalid format version"); } /* TODO: Use a custom exception type that informs the * user of possible data corruption. */ return t; } T* begin() noexcept { return *view; } T* end() noexcept { return view[c-1]; } inline operator T*() noexcept { return view.v; } inline T* operator ->() noexcept { return view.v; } }; #endif