223 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/**
 | 
						|
 * Copyright (c) 2024 Dominic Masters
 | 
						|
 * 
 | 
						|
 * This software is released under the MIT License.
 | 
						|
 * https://opensource.org/licenses/MIT
 | 
						|
 */
 | 
						|
 | 
						|
#include "asset.h"
 | 
						|
#include "assert/assert.h"
 | 
						|
#include "util/math.h"
 | 
						|
#include "util/memory.h"
 | 
						|
 | 
						|
asset_t ASSET;
 | 
						|
 | 
						|
void assetInit() {
 | 
						|
  const char_t *assetFilename = "dusk.tar";
 | 
						|
  char_t assetPath[FILENAME_MAX];
 | 
						|
  const char_t* scanLocations[] = {
 | 
						|
    EXECUTABLE_DIRECTORY
 | 
						|
  };
 | 
						|
 | 
						|
  memoryZero(&ASSET, sizeof(asset_t));
 | 
						|
 | 
						|
  // Try and find the asset file.
 | 
						|
  for(int32_t i = 0; i < sizeof(scanLocations) / sizeof(char_t*); i++) {
 | 
						|
    sprintf(assetPath, "%s/%s", scanLocations[i], assetFilename);
 | 
						|
 | 
						|
    FILE *file = fopen(assetPath, "rb");
 | 
						|
    if(file == NULL) continue;
 | 
						|
 | 
						|
    // File found.
 | 
						|
    ASSET.file = file;
 | 
						|
  }
 | 
						|
 | 
						|
  // Ensure we found it.
 | 
						|
  assertNotNull(ASSET.file, "Failed to find asset file!");
 | 
						|
}
 | 
						|
 | 
						|
void assetOpen(const char_t *path) {
 | 
						|
  assertNotNull(path, "Path is not valid!");
 | 
						|
  assertStrLen(path, FILENAME_MAX, "Path is too long!");
 | 
						|
  assertStrLenMin(path, 1, "Path is empty!");
 | 
						|
 | 
						|
  // Make sure things are clean
 | 
						|
  assertNull(ASSET.archive, "Archive is not NULL!");
 | 
						|
  assertNull(ASSET.entry, "Entry is not NULL!");
 | 
						|
  assertNotNull(ASSET.file, "File is NULL!");
 | 
						|
 | 
						|
  // Store path
 | 
						|
  strcpy(ASSET.path, path);
 | 
						|
 | 
						|
  // Prepare data
 | 
						|
  ASSET.archive = archive_read_new();
 | 
						|
  assertNotNull(ASSET.archive, "Failed to init archive reader");
 | 
						|
 | 
						|
  // Set up the reader
 | 
						|
  // archive_read_support_filter_bzip2(ASSET_ARCHIVE);
 | 
						|
  archive_read_support_format_tar(ASSET.archive);
 | 
						|
 | 
						|
  // Set the archive reader callbacks
 | 
						|
  archive_read_set_open_callback(ASSET.archive, &assetArchiveOpen);
 | 
						|
  archive_read_set_read_callback(ASSET.archive, &assetArchiveRead);
 | 
						|
  archive_read_set_seek_callback(ASSET.archive, &assetArchiveSeek);
 | 
						|
  archive_read_set_close_callback(ASSET.archive, &assetArchiveClose);
 | 
						|
  archive_read_set_callback_data(ASSET.archive, ASSET.buffer);// TODO: Not needed?
 | 
						|
 | 
						|
  // Open the archive
 | 
						|
  int32_t ret =  archive_read_open1(ASSET.archive);
 | 
						|
  assertTrue(ret == ARCHIVE_OK, "Failed to open archive!");
 | 
						|
 | 
						|
  // Iterate over each file.
 | 
						|
  while(archive_read_next_header(ASSET.archive, &ASSET.entry) == ARCHIVE_OK) {
 | 
						|
    // What file is at this position?
 | 
						|
    const char_t *headerFile = (char_t*)archive_entry_pathname(ASSET.entry);
 | 
						|
 | 
						|
    // Compare if this is the file we are looking for, if it is just exit the
 | 
						|
    // function
 | 
						|
    if(strcmp(headerFile, ASSET.path) == 0) return;
 | 
						|
 | 
						|
    // It is not, skip it.
 | 
						|
    int32_t ret = archive_read_data_skip(ASSET.archive);
 | 
						|
    assertTrue(ret == ARCHIVE_OK, "Failed to skip data!");
 | 
						|
  }
 | 
						|
 | 
						|
  // If we get here we did not find the find in the archive.
 | 
						|
  assertUnreachable("Failed to find file!");
 | 
						|
}
 | 
						|
 | 
						|
size_t assetGetSize() {
 | 
						|
  assertNotNull(ASSET.archive, "Archive is NULL!");
 | 
						|
  assertNotNull(ASSET.entry, "Entry is NULL!");
 | 
						|
  assertTrue(archive_entry_size_is_set(ASSET.entry), "Entry size is not set!");
 | 
						|
  return archive_entry_size(ASSET.entry);
 | 
						|
}
 | 
						|
 | 
						|
size_t assetRead(
 | 
						|
  uint8_t *buffer,
 | 
						|
  const size_t bufferSize
 | 
						|
) {
 | 
						|
  assertNotNull(ASSET.archive, "Archive is NULL!");
 | 
						|
  assertNotNull(ASSET.entry, "Entry is NULL!");
 | 
						|
  assertNotNull(buffer, "Buffer is NULL!");
 | 
						|
  assertTrue(bufferSize > 0, "Buffer size must be greater than 0!");
 | 
						|
 | 
						|
  ssize_t read = archive_read_data(ASSET.archive, buffer, bufferSize);
 | 
						|
  if(read == ARCHIVE_FATAL) {
 | 
						|
    assertUnreachable(archive_error_string(ASSET.archive));
 | 
						|
    return -1;
 | 
						|
  }
 | 
						|
  
 | 
						|
  assertTrue(read != ARCHIVE_RETRY, "Failed to read data (RETRY)!");
 | 
						|
  assertTrue(read != ARCHIVE_WARN, "Failed to read data (WARN)!");
 | 
						|
 | 
						|
  return read;
 | 
						|
}
 | 
						|
 | 
						|
size_t assetReadUntil(
 | 
						|
  uint8_t *buffer,
 | 
						|
  const char_t c,
 | 
						|
  const size_t maxLength
 | 
						|
) {
 | 
						|
  if(buffer == NULL) {
 | 
						|
    assertTrue(
 | 
						|
      maxLength == -1, "If no buffer is provided, maxLength must be -1."
 | 
						|
    );
 | 
						|
    uint8_t tBuffer[1];
 | 
						|
    size_t read = 0;
 | 
						|
    while(assetRead(tBuffer, 1) == 1 && (char_t)tBuffer[0] != c) read++;
 | 
						|
    return read;
 | 
						|
  }
 | 
						|
 | 
						|
  size_t read = 0;
 | 
						|
  while(read < maxLength) {
 | 
						|
    // TODO: Read more than 1 char at a time.
 | 
						|
    read += assetRead(buffer + read, 1);
 | 
						|
    if((char_t)buffer[read-1] == c) return read - 1;
 | 
						|
  }
 | 
						|
 | 
						|
  return -1;
 | 
						|
}
 | 
						|
 | 
						|
void assetSkip(const size_t length) {
 | 
						|
  assertNotNull(ASSET.archive, "Archive is NULL!");
 | 
						|
  assertNotNull(ASSET.entry, "Entry is NULL!");
 | 
						|
  
 | 
						|
  // Asset archive does not support skipping, so we have to read and discard.
 | 
						|
  uint8_t buffer[ASSET_BUFFER_SIZE];
 | 
						|
  size_t remaining = length;
 | 
						|
  do {
 | 
						|
    size_t toRead = mathMin(remaining, ASSET_BUFFER_SIZE);
 | 
						|
    size_t read = assetRead(buffer, toRead);
 | 
						|
    assertTrue(read == toRead, "Failed to skip data! (overskip?)");
 | 
						|
    remaining -= read;
 | 
						|
  } while(remaining > 0);
 | 
						|
}
 | 
						|
 | 
						|
void assetClose() {
 | 
						|
  if(ASSET.archive == NULL) return;
 | 
						|
 | 
						|
  assertNotNull(ASSET.archive, "Archive is NULL!");
 | 
						|
  assertNotNull(ASSET.entry, "Entry is NULL!");
 | 
						|
  
 | 
						|
  int32_t ret = archive_read_free(ASSET.archive);
 | 
						|
  assertTrue(ret == ARCHIVE_OK, "Failed to close archive!");
 | 
						|
 | 
						|
  assertNull(ASSET.archive, "Archive is not NULL? Cleanup incorrect.");
 | 
						|
}
 | 
						|
 | 
						|
void assetDispose() {
 | 
						|
  assertNull(ASSET.archive, "Asset disposing but asset is currently open?");
 | 
						|
  assertNull(ASSET.entry, "Asset disposing but entry is currently open?");
 | 
						|
  assertNotNull(ASSET.file, "Asset disposing but file is NULL?");
 | 
						|
  
 | 
						|
  fclose(ASSET.file);
 | 
						|
  memoryZero(&ASSET, sizeof(asset_t));
 | 
						|
}
 | 
						|
 | 
						|
// Libarchive callbacks
 | 
						|
 | 
						|
ssize_t assetArchiveRead(
 | 
						|
  struct archive *archive,
 | 
						|
  void *data,
 | 
						|
  const void **buffer
 | 
						|
) {
 | 
						|
  assertNotNull(archive, "Archive is NULL!");
 | 
						|
  assertNotNull(data, "Data is NULL!");
 | 
						|
  assertNotNull(buffer, "Buffer is NULL!");
 | 
						|
 | 
						|
  *buffer = data;
 | 
						|
  size_t read = fread(data, 1, ASSET_BUFFER_SIZE, ASSET.file);
 | 
						|
  if(ferror(ASSET.file)) return ARCHIVE_FATAL;
 | 
						|
  return read;
 | 
						|
}
 | 
						|
 | 
						|
int64_t assetArchiveSeek(
 | 
						|
  struct archive *archive,
 | 
						|
  void *data,
 | 
						|
  int64_t offset,
 | 
						|
  int32_t whence
 | 
						|
) {
 | 
						|
  assertNotNull(archive, "Archive is NULL!");
 | 
						|
  assertNotNull(data, "Data is NULL!");
 | 
						|
  assertTrue(offset > 0, "Offset must be greater than 0!");
 | 
						|
  assertNotNull(ASSET.file, "File is NULL!");
 | 
						|
  int32_t ret = fseek(ASSET.file, offset, whence);
 | 
						|
  assertTrue(ret == 0, "Failed to seek!");
 | 
						|
  return ftell(ASSET.file);
 | 
						|
}
 | 
						|
 | 
						|
int32_t assetArchiveOpen(struct archive *a, void *data) {
 | 
						|
  int32_t ret = fseek(ASSET.file, 0, SEEK_SET);
 | 
						|
  assertTrue(ret == 0, "Failed to seek to start of file!");
 | 
						|
  return ARCHIVE_OK;
 | 
						|
}
 | 
						|
 | 
						|
int32_t assetArchiveClose(struct archive *a, void *data) {
 | 
						|
  assertNotNull(ASSET.file, "File is NULL!");
 | 
						|
  
 | 
						|
  ASSET.archive = NULL;
 | 
						|
  ASSET.entry = NULL;
 | 
						|
 | 
						|
  return ARCHIVE_OK;
 | 
						|
} |