summaryrefslogtreecommitdiff
path: root/src/common/graph.c
blob: a68d39ce0d13f756ea9e50ca3fb3d99b4aec8c57 (plain) (blame)
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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
// Copyright (c) Athena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

// graph creation is enabled
#define ENABLE_GRAPH

#ifdef ENABLE_GRAPH

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
	#include <unistd.h>
#endif
#ifdef MINGW
	#include <io.h>
#endif

#include "../common/core.h"
#include "../common/timer.h"
#include "../common/grfio.h"
#include "../common/malloc.h"
#include "graph.h"

struct graph {
	int   width;
	int   height;
	int   pallet_count;
	int   png_len;
	int   png_dirty;
	unsigned char* raw_data;
	unsigned char* png_data;
	int          * graph_value;
	int            graph_max;
};

void graph_write_dword(unsigned char* p,unsigned int v) {
	p[0] = (unsigned char)((v >> 24) & 0xFF);
	p[1] = (unsigned char)((v >> 16) & 0xFF);
	p[2] = (unsigned char)((v >>  8) & 0xFF);
	p[3] = (unsigned char)(v         & 0xFF);
}

struct graph* graph_create(unsigned int x,unsigned int y) {
	struct graph *g = (struct graph*)aCalloc(sizeof(struct graph),1);
	if(g == NULL) return NULL;
	// 256 * 3   : �p���b�g�f�[�^
	// x * y * 2 : �C���[�W�̃o�b�t�@
	// 256       : �`�����N�f�[�^�Ȃǂ̗\��
	g->png_data = (unsigned char *) aMalloc(4 * 256 + (x + 1) * y * 2);
	g->raw_data = (unsigned char *) aCalloc( (x + 1) * y , 1);
	memcpy(
		g->png_data,
		"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A\x00\x00\x00\x0D\x49\x48\x44\x52"
		"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x08\x03\x00\x00\x00\xFF\xFF\xFF"
		"\xFF\x00\x00\x00\x03\x50\x4C\x54\x45\xFF\xFF\xFF\xA7\xC4\x1B\xC8",0x30
	);
	graph_write_dword(g->png_data + 0x10,x);
	graph_write_dword(g->png_data + 0x14,y);
	graph_write_dword(g->png_data + 0x1D,grfio_crc32(g->png_data+0x0C,0x11));
	g->pallet_count = 1;
	g->width        = x;
	g->height       = y;
	g->png_dirty    = 1;
	g->graph_value  = (int *) aCalloc(x,sizeof(int));
	g->graph_max    = 1;
	return g;
}

void graph_pallet(struct graph* g, int index,unsigned long c) {
	if(g == NULL || c >= 256) return;

	if(g->pallet_count <= index) {
		memset(g->png_data + 0x29 + 3 * g->pallet_count,0,(index - g->pallet_count) * 3);
		g->pallet_count = index + 1;
	}
	g->png_data[0x29 + index * 3    ] = (unsigned char)((c >> 16) & 0xFF); // R
	g->png_data[0x29 + index * 3 + 1] = (unsigned char)((c >>  8) & 0xFF); // G
	g->png_data[0x29 + index * 3 + 2] = (unsigned char)( c        & 0xFF); // B
	graph_write_dword(g->png_data + 0x21,g->pallet_count * 3);
	graph_write_dword(
		g->png_data + 0x29 + g->pallet_count * 3,
		grfio_crc32(g->png_data + 0x25,g->pallet_count * 3 + 4)
	);
	g->png_dirty = 1;
}

void graph_setpixel(struct graph* g,int x,int y,int color) {
	if(g == NULL || color >= 256) { return; }
	if(x < 0) x = 0;
	if(y < 0) y = 0;
	if(x >= g->width)  { x = g->width  - 1; }
	if(y >= g->height) { y = g->height - 1; }
	if(color >= g->pallet_count) { graph_pallet(g,color,graph_rgb(0,0,0)); }

	g->raw_data[y * (g->width + 1) + x + 1] = (unsigned char)color;
	g->png_dirty = 1;
}

int graph_getpixel(struct graph* g,int x,int y) {
	if(x < 0) x = 0;
	if(y < 0) y = 0;
	if(x >= g->width)  { x = g->width  - 1; }
	if(y >= g->height) { y = g->height - 1; }
	return g->raw_data[y * (g->width + 1) + x + 1];
}

const unsigned char* graph_output(struct graph* g,int *len) {
	unsigned long inflate_len;
	unsigned char *p;

	if(g == NULL) return NULL;
	if(g->png_dirty == 0) {
		*len = g->png_len;
		return g->png_data;
	}

	p = g->png_data + 0x2D + 3 * g->pallet_count;
	inflate_len = 2 * (g->width + 1) * g->height;
	memcpy(p + 4,"IDAT",4);
	encode_zip(p + 8,&inflate_len,g->raw_data,(g->width + 1) * g->height);
	graph_write_dword(p,inflate_len);
	graph_write_dword(p + 8 + inflate_len,grfio_crc32(p + 4, inflate_len + 4));

	p += 0x0C + inflate_len;
	memcpy(p,"\x00\x00\x00\x00\x49\x45\x4E\x44\xAE\x42\x60\x82",0x0C);
	p += 0x0C;
	g->png_len   = p - g->png_data;
	g->png_dirty = 0;
	*len = g->png_len;
	return g->png_data;
}

void graph_free(struct graph* g) {
	if(g != NULL) {
		aFree(g->png_data);
		aFree(g->raw_data);
		aFree(g->graph_value);
		aFree(g);
	}
}

// �Ƃ肠�����s�����ŁB��قǏ��������\��
void graph_square(struct graph* g,int x,int y,int xe,int ye,int color) {
	int i,j;
	if(g == NULL) return;
	if(x < 0) { x = 0; }
	if(y < 0) { y = 0; }
	if(xe > g->width)  { xe = g->width;  }
	if(ye > g->height) { ye = g->height; }
	for(i = y;i < ye ; i++) {
		for(j = x; j < xe ; j++) {
			graph_setpixel(g,j,i,color);
		}
	}
}

// �Ƃ肠�����s�����ŁB��قǏ��������\��
void graph_scroll(struct graph* g,int n,int color) {
	int x,y;
	if(g == NULL) return;
	for(y = 0; y < g->height; y++) {
		for(x = 0; x < g->width - n; x++) {
			graph_setpixel(g,x,y,graph_getpixel(g,x + n,y));
		}
		for( ; x < g->width; x++) {
			graph_setpixel(g,x,y,color);
		}
	}
}

void graph_data(struct graph* g,int value) {
	int i, j, start;
	if(g == NULL) return;
	memmove(&g->graph_value[0],&g->graph_value[1],sizeof(int) * (g->width - 1));
	g->graph_value[g->width - 1] = value;
	if(value > g->graph_max) {
		// �ő�l�𒴂����̂ōĕ`��
		g->graph_max = value;
		graph_square(g,0,0,g->width,g->height,0);
		start = 0;
	} else {
		// �X�N���[�����ă|�C���g�ł�
		graph_scroll(g,1,0);
		start = g->width - 1;
	}
	for(i = start; i < g->width; i++) {
		int h0 = (i == 0 ? 0 : g->graph_value[i - 1]) * g->height / g->graph_max;
		int h1 = (g->graph_value[i]                 ) * g->height / g->graph_max;
		int h2 = (h0 < h1 ? 1 : -1);
		for(j = h0; j != h1; j += h2) {
			graph_setpixel(g,i,g->height - 1 - j,1);
		}
		graph_setpixel(g,i,g->height - 1 - h1,1);
	}
}

// ��̊֐��Q�𗘗p���āA�����I�ɃO���t���쐬����^�C�}�[�Q

#define GRP_WIDTH		300 // �O���t�̕�
#define GRP_HEIGHT		200 // �O���t�̍���
#define GRP_COLOR		graph_rgb(0,0,255) // �O���t�̐F
#define GRP_INTERVEL	60*1000 // �O���t�̍X�V�Ԋu

#define GRP_PATH		"httpd/"

struct graph_sensor {
	struct graph* graph;
	char* str;
	char hash[32];
	int   scanid;
	int   drawid;
	int   interval;
	unsigned int (*func)(void);
};

static struct graph_sensor *sensor;
static int sensor_max;

static int graph_scan_timer(int tid,unsigned int tick,int id,int data)
{
	if(id >= 0 && id < sensor_max)
		graph_data(sensor[id].graph,sensor[id].func());
	return 0;
}

// modified by Celest -- i'm trying to separate it from httpd if possible ^^;
static int graph_draw_timer(int tid,unsigned int tick,int id,int data)
{
	char png_file[24];
	FILE *fp;
	
	// create/update the png file
	do {
		const char *png_data;
		int len;
		sprintf (png_file, GRP_PATH"%s.png", sensor[id].hash);
		fp = fopen(png_file, "w");
		// if another png of the same hash exists
		// (i.e 2nd login server with the same sensors)
		// this will fail = not good >.<
		if (fp == NULL)
			break;
		png_data = graph_output(sensor[id].graph, &len);
		fwrite(png_data,1,len,fp);
		fclose(fp);
	} while (0);
	
	// create/update text snippet
	do {
		char buf[8192], *p;
		p = buf;
		sprintf (png_file, GRP_PATH"%s.graph", sensor[id].hash);
		fp = fopen(png_file, "w");
		if (fp == NULL)
			break;
		p += sprintf(p,"<h2>%s</h2>\n\n",
			sensor[id].str);
		p += sprintf(p,"<p><img src=\"%s.png\" width=\"%d\" height=\"%d\"></p>\n",
			sensor[id].hash, GRP_WIDTH,GRP_HEIGHT);
		p += sprintf(p,"<p>Max: %d, Interval: %d sec</p>\n\n",
			sensor[id].graph->graph_max, sensor[id].interval / 1000);
		fprintf(fp, buf);
		fclose(fp);
	} while (0);

	return 0;
}

void graph_add_sensor(const char* string, int interval, unsigned int (*callback_func)(void))
{
	int draw_interval = interval * 2;
	struct graph *g = graph_create(GRP_WIDTH,GRP_HEIGHT);
	graph_pallet(g,1,GRP_COLOR);

	sensor = (struct graph_sensor *) aRealloc(sensor, sizeof(struct graph_sensor) * (sensor_max + 1));
	sensor[sensor_max].graph    = g;
	sensor[sensor_max].str      = aStrdup(string);
	// create crc32 hash of the sensor's name
	sprintf (sensor[sensor_max].hash, "%lu%c", grfio_crc32(string,strlen(string)), 'a' + SERVER_TYPE);
	sensor[sensor_max].func     = callback_func;
	sensor[sensor_max].scanid   = add_timer_interval(gettick() + 500, graph_scan_timer, sensor_max, 0, interval);
	sensor[sensor_max].drawid   = add_timer_interval(gettick() + 1000, graph_draw_timer, sensor_max, 0, draw_interval < 60000 ? 60000 : draw_interval);
	sensor[sensor_max].interval = interval;
	sensor_max++;

}

void graph_final (void)
{
	int i;
	for(i = 0; i < sensor_max; i++) {
		char png_file[24];
		// remove the png and snippet file
		sprintf (png_file, GRP_PATH"%s.png", sensor[i].hash);
		unlink (png_file);
		sprintf (png_file, GRP_PATH"%s.graph", sensor[i].hash);
		unlink (png_file);
		graph_free(sensor[i].graph);
		aFree(sensor[i].str);
		//delete_timer(sensor[i].scanid,graph_scan_timer);
		//delete_timer(sensor[i].drawid,graph_draw_timer);
	}
	aFree(sensor);
	sensor_max = 0;
}

void graph_init (void)
{
	graph_add_sensor ("Memory Usage", 1000, malloc_usage);
	add_timer_func_list(graph_scan_timer, "graph_scan_timer");
	add_timer_func_list(graph_draw_timer, "graph_draw_timer");
}

#else
void graph_init (void) {}
void graph_final (void) {}
#endif