aboutsummaryrefslogtreecommitdiff
path: root/c/data.h
blob: 919e1b4ad8ac2139a91de622ffd45f6a2bb6e5d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#ifndef DATA_H
#define DATA_H

#include <stdexcept>
#include <windows.h>
#include <libxml/xmlerror.h>

#include "util.h"
#include "win32.h"

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};
};

/* Variable template for obtaining the version of a given struct. */
template <typename T>
constexpr inline unsigned char Version = T().version;

template <typename T, typename = int>
constexpr inline bool HasVersion = false;

template <typename T>
constexpr inline bool HasVersion<T, decltype((void) T::version, 0)> = 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 <typename T>
struct FileView
{
	Unique<HANDLE, CloseHandle> hf;
	Unique<HANDLE, CloseHandle> hm;
	Unique<T*, FreeView> view;
	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) : 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 Win32Error();
			} else
				throw Win32Error();
		}

		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 Win32Error();

		view = reinterpret_cast<T*>(MapViewOfFile(hm.v, FILE_MAP_ALL_ACCESS, 0, 0, 0));
		if (view.Bad(0))
			throw Win32Error();
	}

	/* 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 std::out_of_range("index larger than buffer");

		T& t = view.v[i];

		if constexpr (HasVersion<T>) {
			if (t.version == 0) {
				T t_;
				memcpy(&t, &t_, sizeof(T));
			} else if (t.version != Version<T>)
				throw std::runtime_error("invalid struct 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