summaryrefslogtreecommitdiff
path: root/memview.c
blob: 95a3c6c7d48d1d02bdd47c9fc30b5963784f15d9 (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
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <SDL.h>

#include "memview.h"
#include "procfs.h"

struct viewer {
	int fd;
	SDL_Renderer *renderer;
	SDL_Texture *page_texture;
	uint8_t page_buffer[PAGE_SIZE];
};

#define BIT_OF(X, I) (((X) >> (I)) & 1ULL)

static uintptr_t zorder(unsigned int x, unsigned int y) {
	uintptr_t z = 0;
	// interleaving bits results in a point on the z-order curve
	for (int i = 0; i < sizeof(x) * CHAR_BIT; i++)
		z |= (BIT_OF(x, i) << i*2) | (BIT_OF(y, i) << (i*2 + 1));
	return z;
}

static void unzorder(uintptr_t z, unsigned int *x, unsigned int *y) {
	*x = 0; *y = 0;
	for (int i = 0; i < sizeof(z) * CHAR_BIT / 2; i++) {
		*x |= BIT_OF(z, i*2) << i;
		*y |= BIT_OF(z, i*2 + 1) << i;
	}
}

uintptr_t to_addr(int x, int y) {
	unsigned int ux, uy;
	memcpy(&ux, &x, sizeof(unsigned int));
	memcpy(&uy, &y, sizeof(unsigned int));
	uintptr_t page = zorder(ux / PAGE_WIDTH, uy / PAGE_HEIGHT);
	int offset =
		x % PAGE_WIDTH / CHAR_BIT + y % PAGE_HEIGHT * PAGE_WIDTH / CHAR_BIT;
	return page * PAGE_SIZE + offset;
}

void to_pos(uintptr_t addr,  int *x, int *y) {
	unsigned int px, py;
	unzorder(addr / PAGE_SIZE, &px, &py);
	int offset = addr % PAGE_SIZE;
	unsigned int ux = px * PAGE_WIDTH + offset % PAGE_WIDTH;
	unsigned int uy = py * PAGE_HEIGHT + offset / PAGE_WIDTH;
	memcpy(x, &ux, sizeof(int));
	memcpy(y, &uy, sizeof(int));
}

struct viewer *create_viewer(int fd, SDL_Renderer *renderer) {
	struct viewer *v = calloc(1, sizeof(struct viewer));
	if (!v) return NULL;
	v->fd = fd; v->renderer = renderer;
	v->page_texture = SDL_CreateTexture(v->renderer,
			SDL_PIXELFORMAT_RGBA32,
			SDL_TEXTUREACCESS_STREAMING,
			PAGE_WIDTH, PAGE_HEIGHT);
	if (!v->page_texture) {free(v); return NULL;}
	return v;
}

void destroy_viewer(struct viewer *v) {
	SDL_DestroyTexture(v->page_texture);
	free(v);
}

static void render_page(SDL_Texture *tex, uint8_t *data) {
	uint32_t *pixels; int pitch;
	assert(SDL_LockTexture(tex, NULL, (void **) &pixels, &pitch) != -1);
	if (data) {
		for (int i = 0; i < PAGE_SIZE; i++) {
			for (int j = 0; j < CHAR_BIT; j++) {
				uint8_t bit = (data[i] >> j) & 1;
				pixels[i * CHAR_BIT + j] = bit ? 0xFFFFFFFF : 0xFF000000;
			}
		}
	} else {
		for (int i = 0; i < PAGE_WIDTH * PAGE_HEIGHT; i++)
			pixels[i] =
				((i / 16) + i / PAGE_WIDTH / 16) % 2 ? 0xFF111111 : 0xFF333333;
	}
	SDL_UnlockTexture(tex);
}

static uint64_t fnv(uint8_t *data, size_t size) {
	uint64_t hash = 0xcbf29ce484222325;
	for (size_t i = 0; i < size; i++) {
		hash ^= data[i];
		hash *= 0x100000001b3;
	}
	return hash;
}

static uint64_t hashes[1024] = {0};

bool render_pages(struct viewer *v,
		int x, int y, int width, int height, double scale, bool refresh) {
	int page_x = x / PAGE_WIDTH;
	int page_y = y / PAGE_HEIGHT;

	int offs_x = x % PAGE_WIDTH * scale;
	int offs_y = y % PAGE_HEIGHT * scale;

	int page_width = PAGE_WIDTH * scale;
	int page_height = PAGE_HEIGHT * scale;

	int view_width = width / page_width;
	int view_height = height / page_height;

	bool present = false;
	for (int draw_y = -1; draw_y <= view_height + 1; draw_y++) {
		for (int draw_x = -1; draw_x <= view_width + 1; draw_x++) {
			SDL_Rect src = {0, 0, PAGE_WIDTH, PAGE_HEIGHT};
			SDL_Rect dest = {
				draw_x * page_width - offs_x, draw_y * page_height - offs_y,
				page_width, page_height
			};
			uintptr_t page = zorder(page_x + draw_x, page_y + draw_y);
			bool success = read_page(v->fd, page, v->page_buffer) != -1;

			uint64_t hash = fnv(v->page_buffer, success ? PAGE_SIZE : 0);
			if (refresh || hashes[page % 1024] != hash) {
				render_page(v->page_texture, success ? v->page_buffer : NULL);
				SDL_RenderCopy(v->renderer, v->page_texture, &src, &dest);
				present = true;
			}
			hashes[page % 1024] = hash;
		}
	}
	return present;
}

static void write_bit(int fd, int x, int y, bool bit) {
	uintptr_t addr = to_addr(x, y);
	int bit_offs = x % CHAR_BIT;
	uint8_t byte;
	if (read_mem(fd, addr, &byte, 1) == -1) return;
	byte = (byte & ~(1 << bit_offs)) | (bit << bit_offs);
	write_mem(fd, addr, &byte, 1);
}

void pencil(struct viewer *v,
		double x1, double y1, double x2, double y2, bool bit) {
	double dx = (x2 - x1);
	double dy = (y2 - y1);
	int step = abs(dx) >= abs(dy) ? abs(dx) : abs(dy);
	dx = dx / step; dy = dy / step;
	double x = x1; double y = y1;
	for (int i = 0; i <= step; i++) {
		write_bit(v->fd, x, y, bit);
		x = x + dx;
		y = y + dy;
	}
}