// modified by Luigi Auriemma
// written by Treeki <treeki@gmail.com>
// https://github.com/Treeki/RandomStuff/blob/eeb8c19408ac8a6d4adde6f93dc67b7a2843878e/SegaLZS2Decomp.c

int sega_lzs2(unsigned char *src, int srcSize, unsigned char *out, int decompSize) {
static unsigned char decBuffer[0x1000];
static unsigned char decBuffer2[0x32000];
	unsigned char *buf2ptr = decBuffer2;
	unsigned char *buf2end = &decBuffer2[decompSize];

	// uhhhhh, what a pointless check
	if (buf2ptr < buf2end) {
		// magic happens here

		unsigned char *realBuf2End = &decBuffer2[sizeof(decBuffer2)];

		int currentControlBit = 0;
		int controlByte = 0;

		int dbIndex = 0;

		do {
			// figure out remaining space
			int remainingSpace = (int)(realBuf2End - buf2ptr);

			if (currentControlBit == 0) {
				controlByte = *src;
				currentControlBit = 8;
				src++;
			}

			if (remainingSpace < 0x12) {
				// If we're running out of space, start over in this buffer
				//printf("0x%x bytes written\n", (int)(buf2ptr - decBuffer2));
				//fwrite(decBuffer2, (int)(buf2ptr - decBuffer2), 1, fDest);
                memcpy(out, decBuffer2, (int)(buf2ptr - decBuffer2));
                out += (int)(buf2ptr - decBuffer2);

				// the assembly does some weird stuff involving the output buffer
				// but we don't have it here, we're just writing to files --
				// so we don't need that. I'm not even sure what it's for,
				// I'm pretty sure this should serve the same purpose.
				int howMuchIsLeft = (int)(buf2end - buf2ptr);
				//printf("0x%x bytes remain\n", howMuchIsLeft);
				buf2end = &decBuffer2[howMuchIsLeft];
				buf2ptr = decBuffer2;
			}

			// now process the control bit
			if (controlByte & 0x80) {
				// CONTROL BIT SET

				unsigned short value = (src[0] << 8) | src[1];
				src += 2;

				unsigned char *endCopyAt = buf2ptr + (value >> 12) + 3;

				if (endCopyAt >= buf2ptr) {
					int dunno = value & 0xFFF;
					//unsigned char *dontReallyKnow = decBuffer;

					do {
						unsigned char thisByte = decBuffer[dunno];
						dunno = (dunno + 1) & 0xFFF;

						decBuffer[dbIndex] = thisByte;
						*buf2ptr = thisByte;

						buf2ptr++;
						dbIndex = (dbIndex + 1) & 0xFFF;
					} while (buf2ptr < endCopyAt);
				}

			} else {
				unsigned char thisByte = *src;
				thisByte ^= dbIndex;

				decBuffer[dbIndex] = thisByte;

				dbIndex = (dbIndex + 1) & 0xFFF;

				src++;

				*buf2ptr = thisByte;
				buf2ptr++;
			}

			controlByte <<= 1;

			currentControlBit--;
		} while (buf2ptr < buf2end);
	}

	int remainingAmount = (int)(buf2ptr - decBuffer2);

	if (remainingAmount > 0) {
		//fwrite(decBuffer2, remainingAmount, 1, fDest);
        memcpy(out, decBuffer2, remainingAmount);
        out += remainingAmount;
    }

    return(buf2ptr - decBuffer2);
}

