shithub: rgbds

ref: 6fbb25c0dae9d755ef5eeb1326472de46811df77
dir: /src/link/object.c/

View raw version
/*
 * This file is part of RGBDS.
 *
 * Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
 *
 * SPDX-License-Identifier: MIT
 */

/*
 * Here we have the routines that read an objectfile
 */

#include <ctype.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"

#include "extern/err.h"

#include "link/assign.h"
#include "link/mylink.h"
#include "link/main.h"

struct sSymbol **tSymbols;
struct sSection *pSections;
struct sSection *pLibSections;
uint8_t oReadLib;

/*
 * Read 32-bit values with the correct endianness
 */
static int32_t readlong(FILE *f)
{
	uint32_t r;

	r = ((uint32_t)(uint8_t)fgetc(f));
	r |= ((uint32_t)(uint8_t)fgetc(f)) << 8;
	r |= ((uint32_t)(uint8_t)fgetc(f)) << 16;
	r |= ((uint32_t)(uint8_t)fgetc(f)) << 24;

	return (int32_t)r;
}

/*
 * Read a NULL terminated string from a file
 */
int32_t readasciiz(char **dest, FILE *f)
{
	size_t r = 0;

	size_t bufferLength = 16;
	char *start = malloc(bufferLength);
	char *s = start;

	if (!s)
		err(1, "%s: Couldn't allocate memory", __func__);

	while (((*s++) = fgetc(f)) != 0) {
		r += 1;

		if (r >= bufferLength) {
			bufferLength *= 2;
			start = realloc(start, bufferLength);
			if (!start) {
				err(1, "%s: Couldn't allocate memory",
				    __func__);
			}
			s = start + r;
		}
	}

	*dest = start;
	return (r + 1);
}

/*
 * Allocate a new section and link it into the list
 */
struct sSection *AllocSection(void)
{
	struct sSection **ppSections;

	if (oReadLib == 1)
		ppSections = &pLibSections;
	else
		ppSections = &pSections;

	while (*ppSections)
		ppSections = &((*ppSections)->pNext);

	*ppSections = malloc(sizeof **ppSections);
	if (!*ppSections)
		err(1, "%s: Couldn't allocate memory", __func__);

	(*ppSections)->tSymbols = tSymbols;
	(*ppSections)->pNext = NULL;
	(*ppSections)->pPatches = NULL;
	(*ppSections)->oAssigned = 0;
	return *ppSections;
}

/*
 * Read a symbol from a file
 */
struct sSymbol *obj_ReadSymbol(FILE *f, char *tzObjectfile)
{
	struct sSymbol *pSym;

	pSym = malloc(sizeof(*pSym));
	if (!pSym)
		err(1, "%s: Couldn't allocate memory", __func__);

	readasciiz(&pSym->pzName, f);
	pSym->Type = (enum eSymbolType)fgetc(f);

	pSym->pzObjFileName = tzObjectfile;

	if (pSym->Type != SYM_IMPORT) {
		readasciiz(&pSym->pzFileName, f);
		pSym->nFileLine = readlong(f);

		pSym->nSectionID = readlong(f);
		pSym->nOffset = readlong(f);
	}

	return pSym;
}

/*
 * RGB object reader routines
 */
struct sSection *obj_ReadRGBSection(FILE *f)
{
	struct sSection *pSection;
	char *pzName;

	readasciiz(&pzName, f);
	if (IsSectionNameInUse(pzName))
		errx(1, "Section name \"%s\" is already in use.", pzName);

	pSection = AllocSection();
	pSection->pzName = pzName;

	pSection->nByteSize = readlong(f);
	pSection->Type = (enum eSectionType)fgetc(f);
	pSection->nOrg = readlong(f);
	pSection->nBank = readlong(f);
	pSection->nAlign = readlong(f);

	if ((options & OPT_TINY) && (pSection->Type == SECT_ROMX))
		errx(1,  "ROMX sections can't be used with option -t.");

	if ((options & OPT_CONTWRAM) && (pSection->Type == SECT_WRAMX))
		errx(1, "WRAMX sections can't be used with options -w or -d.");

	if (options & OPT_DMG_MODE) {
		/* WRAMX sections are checked for OPT_CONTWRAM */
		if (pSection->Type == SECT_VRAM && pSection->nBank == 1)
			errx(1, "VRAM bank 1 can't be used with option -d.");
	}

	uint32_t maxsize = 0;

	/* Verify that the section isn't too big */
	switch (pSection->Type) {
	case SECT_ROM0:
		maxsize = (options & OPT_TINY) ? 0x8000 : 0x4000;
		break;
	case SECT_ROMX:
		maxsize = 0x4000;
		break;
	case SECT_VRAM:
	case SECT_SRAM:
		maxsize = 0x2000;
		break;
	case SECT_WRAM0:
		maxsize = (options & OPT_CONTWRAM) ? 0x2000 : 0x1000;
		break;
	case SECT_WRAMX:
		maxsize = 0x1000;
		break;
	case SECT_OAM:
		maxsize = 0xA0;
		break;
	case SECT_HRAM:
		maxsize = 0x7F;
		break;
	default:
		errx(1, "Section \"%s\" has an invalid section type.", pzName);
		break;
	}
	if (pSection->nByteSize > maxsize) {
		errx(1, "Section \"%s\" is bigger than the max size for that type: 0x%X > 0x%X",
		     pzName, pSection->nByteSize, maxsize);
	}

	/*
	 * If the section doesn't contain data, it is ready
	 */
	if ((pSection->Type != SECT_ROMX) && (pSection->Type != SECT_ROM0))
		return pSection;

	/* If there is no data to read, exit */
	if (pSection->nByteSize == 0) {
		/* Skip number of patches */
		readlong(f);
		pSection->pData = NULL;
		return pSection;
	}

	pSection->pData = malloc(pSection->nByteSize);
	if (!pSection->pData)
		err(1, "%s: Couldn't allocate memory", __func__);

	int32_t nNumberOfPatches;
	struct sPatch **ppPatch, *pPatch;

	if (fread(pSection->pData, sizeof(uint8_t), pSection->nByteSize, f)
	    != pSection->nByteSize) {
		err(1, "%s: Read error", __func__);
	}

	nNumberOfPatches = readlong(f);
	ppPatch = &pSection->pPatches;

	/*
	 * And patches...
	 */
	while (nNumberOfPatches--) {
		pPatch = malloc(sizeof(*pPatch));
		if (!pPatch)
			err(1, "%s: Couldn't allocate memory", __func__);

		*ppPatch = pPatch;
		readasciiz(&pPatch->pzFilename, f);
		pPatch->nLineNo = readlong(f);
		pPatch->nOffset = readlong(f);
		pPatch->Type = (enum ePatchType)fgetc(f);
		pPatch->nRPNSize = readlong(f);

		if (pPatch->nRPNSize > 0) {
			pPatch->pRPN = malloc(pPatch->nRPNSize);
			if (!pPatch->pRPN) {
				err(1, "%s: Couldn't allocate memory",
				    __func__);
			}

			if (fread(pPatch->pRPN, sizeof(uint8_t),
				  pPatch->nRPNSize, f) != pPatch->nRPNSize) {
				errx(1, "%s: Read error", __func__);
			}
		} else {
			pPatch->pRPN = NULL;
		}

		pPatch->pNext = NULL;
		ppPatch = &(pPatch->pNext);
	}

	return pSection;
}

void obj_ReadRGB(FILE *pObjfile, char *tzObjectfile)
{
	struct sSection *pFirstSection;
	int32_t nNumberOfSymbols, nNumberOfSections, i;

	nNumberOfSymbols = readlong(pObjfile);
	nNumberOfSections = readlong(pObjfile);

	/* First comes the symbols */

	if (nNumberOfSymbols) {
		tSymbols = malloc(nNumberOfSymbols * sizeof(*tSymbols));
		if (!tSymbols)
			err(1, "%s: Couldn't allocate memory", __func__);

		for (i = 0; i < nNumberOfSymbols; i += 1)
			tSymbols[i] = obj_ReadSymbol(pObjfile, tzObjectfile);
	} else {
		tSymbols = NULL;
	}

	/* Next we have the sections */

	pFirstSection = NULL;
	while (nNumberOfSections--) {
		struct sSection *pNewSection;

		pNewSection = obj_ReadRGBSection(pObjfile);
		pNewSection->nNumberOfSymbols = nNumberOfSymbols;
		if (pFirstSection == NULL)
			pFirstSection = pNewSection;
	}

	/*
	 * Fill in the pSection entry in the symbolstructure.
	 * This REALLY needs some cleaning up... but, hey, it works
	 */

	for (i = 0; i < nNumberOfSymbols; i += 1) {
		struct sSection *pConvSect = pFirstSection;

		if ((tSymbols[i]->Type != SYM_IMPORT) &&
		    (tSymbols[i]->nSectionID != -1)) {
			int32_t j = 0;

			while (j != tSymbols[i]->nSectionID) {
				j += 1;
				pConvSect = pConvSect->pNext;
			}
			tSymbols[i]->pSection = pConvSect;
		} else {
			tSymbols[i]->pSection = NULL;
		}
	}
}

/*
 * The main objectfileloadroutine (phew)
 */
void obj_ReadOpenFile(FILE *pObjfile, char *tzObjectfile)
{
	char tzHeader[strlen(RGBDS_OBJECT_VERSION_STRING) + 1];

	if (fread(tzHeader, sizeof(char), strlen(RGBDS_OBJECT_VERSION_STRING),
		  pObjfile) != strlen(RGBDS_OBJECT_VERSION_STRING)) {
		errx(1, "%s: Read error", tzObjectfile);
	}

	tzHeader[strlen(RGBDS_OBJECT_VERSION_STRING)] = 0;

	if (strncmp(tzHeader, RGBDS_OBJECT_VERSION_STRING,
		    strlen(RGBDS_OBJECT_VERSION_STRING)) == 0) {
		obj_ReadRGB(pObjfile, tzObjectfile);
	} else {
		int32_t i;

		for (i = 0; i < strlen(RGBDS_OBJECT_VERSION_STRING); i++)
			if (!isprint(tzHeader[i]))
				tzHeader[i] = '?';

		errx(1, "%s: Invalid file or object file version [%s]",
		     tzObjectfile, tzHeader);
	}
}

void obj_Readfile(char *tzObjectfile)
{
	FILE *pObjfile;

	if (options & OPT_SMART_C_LINK)
		oReadLib = 1;
	else
		oReadLib = 0;

	pObjfile = fopen(tzObjectfile, "rb");
	if (pObjfile == NULL)
		err(1, "Unable to open object '%s'", tzObjectfile);

	obj_ReadOpenFile(pObjfile, tzObjectfile);
	fclose(pObjfile);

	oReadLib = 0;
}

int32_t file_Length(FILE *f)
{
	uint32_t r, p;

	p = ftell(f);
	fseek(f, 0, SEEK_END);
	r = ftell(f);
	fseek(f, p, SEEK_SET);

	return r;
}