<commit_before>#include "gba-rr.h"

#include "gba.h"
#include "util/vfs.h"

enum {
	GBA_RR_BLOCK_SIZE = 1018
};

#define FILE_INPUTS "input.log"

struct GBARRBlock {
	union GBARRInput {
		struct {
			uint16_t keys : 10;
			uint16_t : 4;
			bool reset : 1;
			bool : 1;
		};
		uint16_t packed;
	} inputs[GBA_RR_BLOCK_SIZE];
	size_t numInputs;
	struct GBARRBlock* next;
};

static void _allocBlock(struct GBARRContext* rr);

void GBARRContextCreate(struct GBA* gba) {
	gba->rr = calloc(1, sizeof(*gba->rr));
}

void GBARRContextDestroy(struct GBA* gba) {
	if (!gba->rr) {
		return;
	}

	struct GBARRBlock* block = gba->rr->rootBlock;
	while (block) {
		struct GBARRBlock* nextBlock = block->next;
		free(block);
		block = nextBlock;
	}
	gba->rr->rootBlock = 0;
	gba->rr->currentBlock = 0;
	gba->rr->playbackBlock = 0;
	free(gba->rr);
	gba->rr = 0;
}

bool GBARRSave(struct GBARRContext* rr, struct VDir* vdir) {
	if (!rr) {
		return false;
	}

	struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_WRONLY | O_CREAT | O_TRUNC);
	if (!inputs) {
		return false;
	}

	ssize_t written = 0;
	struct GBARRBlock* inputBlock;
	for (inputBlock = rr->rootBlock; inputBlock; inputBlock = inputBlock->next) {
		ssize_t thisWrite = inputs->write(inputs, inputBlock->inputs, sizeof(*inputBlock->inputs) * inputBlock->numInputs);
		if (!thisWrite) {
			written = -1;
			break;
		}
		written += thisWrite;
	}

	if (!inputs->close(inputs)) {
		return false;
	}

	return written >= 0;
}

bool GBARRLoad(struct GBARRContext* rr, struct VDir* vdir) {
	if (!rr) {
		return false;
	}

	struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_RDONLY);
	if (!inputs) {
		return false;
	}

	struct GBARRBlock block;
	ssize_t read;
	do {
		read = inputs->read(inputs, block.inputs, sizeof(block.inputs));
		if (read) {
			struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock));
			memcpy(newBlock, &block, sizeof(*newBlock));
			if (!rr->rootBlock) {
				rr->rootBlock = newBlock;
			}
			if (rr->currentBlock) {
				rr->currentBlock->next = newBlock;
			}
			rr->currentBlock = newBlock;
		}
	} while (read > 0);

	if (!inputs->close(inputs)) {
		return false;
	}

	return read >= 0;
}

bool GBARRStartPlaying(struct GBA* gba) {
	if (!gba->rr) {
		return false;
	}
	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
		return false;
	}

	gba->rr->playbackBlock = gba->rr->rootBlock;
	gba->rr->inputId = 0;
	return !!gba->rr->playbackBlock;
}

void GBARRStopPlaying(struct GBA* gba) {
	if (!gba->rr) {
		return;
	}
<commit_msg>Fix loading too many states from a file<commit_after>#include "gba-rr.h"

#include "gba.h"
#include "util/vfs.h"

enum {
	GBA_RR_BLOCK_SIZE = 1018
};

#define FILE_INPUTS "input.log"

struct GBARRBlock {
	union GBARRInput {
		struct {
			uint16_t keys : 10;
			uint16_t : 4;
			bool reset : 1;
			bool : 1;
		};
		uint16_t packed;
	} inputs[GBA_RR_BLOCK_SIZE];
	size_t numInputs;
	struct GBARRBlock* next;
};

static void _allocBlock(struct GBARRContext* rr);

void GBARRContextCreate(struct GBA* gba) {
	gba->rr = calloc(1, sizeof(*gba->rr));
}

void GBARRContextDestroy(struct GBA* gba) {
	if (!gba->rr) {
		return;
	}

	struct GBARRBlock* block = gba->rr->rootBlock;
	while (block) {
		struct GBARRBlock* nextBlock = block->next;
		free(block);
		block = nextBlock;
	}
	gba->rr->rootBlock = 0;
	gba->rr->currentBlock = 0;
	gba->rr->playbackBlock = 0;
	free(gba->rr);
	gba->rr = 0;
}

bool GBARRSave(struct GBARRContext* rr, struct VDir* vdir) {
	if (!rr) {
		return false;
	}

	struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_WRONLY | O_CREAT | O_TRUNC);
	if (!inputs) {
		return false;
	}

	ssize_t written = 0;
	struct GBARRBlock* inputBlock;
	for (inputBlock = rr->rootBlock; inputBlock; inputBlock = inputBlock->next) {
		ssize_t thisWrite = inputs->write(inputs, inputBlock->inputs, sizeof(*inputBlock->inputs) * inputBlock->numInputs);
		if (!thisWrite) {
			written = -1;
			break;
		}
		written += thisWrite;
	}

	if (!inputs->close(inputs)) {
		return false;
	}

	return written >= 0;
}

bool GBARRLoad(struct GBARRContext* rr, struct VDir* vdir) {
	if (!rr) {
		return false;
	}

	struct VFile* inputs = vdir->openFile(vdir, FILE_INPUTS, O_RDONLY);
	if (!inputs) {
		return false;
	}

	struct GBARRBlock block = {
		.next = 0,
		.numInputs = GBA_RR_BLOCK_SIZE
	};

	ssize_t read;
	do {
		read = inputs->read(inputs, block.inputs, sizeof(block.inputs));
		if (read) {
			struct GBARRBlock* newBlock = calloc(1, sizeof(*rr->currentBlock));
			memcpy(newBlock, &block, sizeof(*newBlock));
			if (!rr->rootBlock) {
				rr->rootBlock = newBlock;
			}
			if (rr->currentBlock) {
				rr->currentBlock->next = newBlock;
			}
			rr->currentBlock = newBlock;
			newBlock->numInputs = read / sizeof(block.inputs[0]);
		}
	} while (read > 0);

	if (!inputs->close(inputs)) {
		return false;
	}

	return read >= 0;
}

bool GBARRStartPlaying(struct GBA* gba) {
	if (!gba->rr) {
		return false;
	}
	if (GBARRIsRecording(gba) || GBARRIsPlaying(gba)) {
		return false;
	}

	gba->rr->playbackBlock = gba->rr->rootBlock;
	gba->rr->inputId = 0;
	return !!gba->rr->playbackBlock;
}

void GBARRStopPlaying(struct GBA* gba) {
	if (!gba->rr) {
		return;
	}

{'fraction_non_alphanumeric': 0.15414332625072436, 'fraction_numerical': 0.010817075526366621, 'mean_word_length': 3.812267657992565, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 94, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6912550', 'n_tokens_mistral': 2269, 'n_tokens_neox': 2056, 'n_words': 450}