Move reading dialogue text out of the text_loop() to conserve IWRAM

This commit is contained in:
Philippe Symons
2025-05-21 17:39:52 +02:00
parent 903ccb0d21
commit a8d2575f5d
4 changed files with 89 additions and 30 deletions

View File

@@ -32,7 +32,12 @@ public:
/**
* This function returns a pointer to a text entry in the decompression_buffer
*/
const uint8_t* get_text_entry(uint8_t index) const;
const uint8_t* get_text_entry(uint16_t index) const;
/**
* This function returns the text entry size in bytes at the given index
*/
uint16_t get_text_entry_size(uint16_t index) const;
private:
uint8_t *decompression_buffer_;
};
@@ -66,7 +71,12 @@ public:
/**
* This function returns a pointer to a text entry in the decompression_buffer
*/
const uint8_t* get_text_entry(uint8_t index);
const uint8_t* get_text_entry(uint16_t index);
/**
* This function returns the text entry size in bytes at the given index
*/
uint16_t get_text_entry_size(uint16_t index) const;
private:
uint8_t* get_window_start() const;
uint8_t* get_window_end() const;

View File

@@ -2,7 +2,7 @@
#include "zx0_decompressor.h"
#include <cstring>
static uint16_t get_entry_offset_by_index(const uint8_t *text_table, uint8_t index)
static uint16_t get_entry_offset_by_index(const uint8_t *text_table, uint16_t index)
{
return *((uint16_t*)(text_table + 2 + index * 2));
}
@@ -13,6 +13,32 @@ static uint16_t get_entries_start_offset_of(uint8_t num_text_entries)
return 2 + (num_text_entries * 2);
}
static uint16_t get_num_text_entries(const uint8_t *index_buffer)
{
return *((uint16_t*)index_buffer);
}
static uint16_t get_entry_size_in_bytes(const uint8_t *index_buffer, uint16_t index)
{
const uint16_t entry_offset = get_entry_offset_by_index(index_buffer, index);
const uint16_t num_text_entries = get_num_text_entries(index_buffer);
uint16_t entry_size_in_bytes;
if(index != num_text_entries - 1)
{
const uint16_t next_entry_offset = get_entry_offset_by_index(index_buffer, index + 1);
entry_size_in_bytes = next_entry_offset - entry_offset;
}
else
{
const uint16_t entry_byte_offset = get_entries_start_offset_of(num_text_entries) + entry_offset;
// we don't have a next entry. So we need to consider the end of the file
const uint16_t decompressed_size = static_cast<uint16_t>(zx0_decompressor_get_decompressed_size());
entry_size_in_bytes = decompressed_size - entry_byte_offset;
}
return entry_size_in_bytes;
}
text_data_table::text_data_table(uint8_t *decompression_buffer)
: decompression_buffer_(decompression_buffer)
{
@@ -26,15 +52,20 @@ void text_data_table::decompress(const uint8_t *compressed_table)
uint16_t text_data_table::get_number_of_text_entries() const
{
return *((uint16_t*)decompression_buffer_);
return get_num_text_entries(decompression_buffer_);
}
const uint8_t* text_data_table::get_text_entry(uint8_t index) const
const uint8_t* text_data_table::get_text_entry(uint16_t index) const
{
const uint16_t entry_offset = get_entry_offset_by_index(decompression_buffer_, index);
return decompression_buffer_ + get_entries_start_offset_of(get_number_of_text_entries()) + entry_offset;
}
uint16_t text_data_table::get_text_entry_size(uint16_t index) const
{
return get_entry_size_in_bytes(decompression_buffer_, index);
}
streamed_text_data_table::streamed_text_data_table(uint8_t *decompression_buffer, uint32_t decompression_buffer_size, uint8_t *index_buffer)
: compressed_table_(nullptr)
, decompression_buffer_(decompression_buffer)
@@ -64,34 +95,20 @@ uint16_t streamed_text_data_table::get_number_of_text_entries() const
return *((uint16_t*)index_buffer_);
}
const uint8_t* streamed_text_data_table::get_text_entry(uint8_t index)
const uint8_t* streamed_text_data_table::get_text_entry(uint16_t index)
{
const uint16_t num_text_entries = get_number_of_text_entries();
const uint16_t entries_start_offset = get_entries_start_offset_of(num_text_entries);
const uint16_t entry_offset = get_entry_offset_by_index(index_buffer_, index);
const uint16_t entry_byte_offset = entries_start_offset + entry_offset;
const uint16_t entry_byte_offset = get_entries_start_offset_of(num_text_entries) + get_entry_offset_by_index(index_buffer_, index);
const uint16_t entry_size_in_bytes = get_text_entry_size(index);
const uint16_t space_remaining_outside_lookback_window = decompression_buffer_size_ - ZX0_DEFAULT_WINDOW_SIZE;
const uint16_t current_window_size = get_current_zx0_window_size();
const uint16_t window_start_offset = bytes_decompressed_ - current_window_size;
const uint16_t window_start_offset = bytes_decompressed_ - get_current_zx0_window_size();
uint16_t bytes_to_decompress;
uint16_t chunk_size;
uint16_t entry_size_in_bytes;
uint16_t entry_end_byte_offset;
// figure out how many bytes we need to read to have the entire text entry
// unfortunately ZX0 doesn't have random access, so we need to linearly decompress
// until we have reached the bytes we actually want.
if(index != num_text_entries - 1)
{
const uint16_t next_entry_offset = get_entry_offset_by_index(index_buffer_, index + 1);
entry_size_in_bytes = next_entry_offset - entry_offset;
}
else
{
// we don't have a next entry. So we need to consider the end of the file
const uint16_t decompressed_size = static_cast<uint16_t>(zx0_decompressor_get_decompressed_size());
entry_size_in_bytes = decompressed_size - entry_byte_offset;
}
entry_end_byte_offset = entry_byte_offset + entry_size_in_bytes;
if(entry_end_byte_offset < bytes_decompressed_)
@@ -133,6 +150,11 @@ const uint8_t* streamed_text_data_table::get_text_entry(uint8_t index)
return decompression_buffer_ + ZX0_DEFAULT_WINDOW_SIZE + last_chunk_size_ - entry_size_in_bytes;
}
uint16_t streamed_text_data_table::get_text_entry_size(uint16_t index) const
{
return get_entry_size_in_bytes(index_buffer_, index);
}
uint8_t* streamed_text_data_table::get_window_start() const
{
uint16_t without_last_chunk_size = (bytes_decompressed_ - last_chunk_size_);

View File

@@ -22,6 +22,29 @@ uint line_char_index;
const byte *curr_text;
bool text_exit;
// This function was separated from text_loop to reduce the scope of the text_decompression_buffer.
// if we didn't do this, the decompression_buffer would be kept on the stack (=IWRAM) for the entire duration of the
// text_loop() call. This is particularly bad because the whole mystery_gift_builder sequence is being triggered from within
// text_loop(). And there we need all the IWRAM we can muster.
// Doing it this way does mean that we need to completely restart decompression whenever we switch from dialog entry.
// but given that it requires user input to do so, I believe it's worth it and not time-critical.
// attribute noinline was used to make sure the compiler doesn't inline this code back into text_loop()
static __attribute__((noinline)) const u8* read_dialogue_text_entry(uint8_t index, u8 *output_buffer)
{
u8 text_decompression_buffer[3072];
u8 index_buffer[100];
const u8 *text_entry;
streamed_text_data_table dialogue_table(text_decompression_buffer, sizeof(text_decompression_buffer), index_buffer);
dialogue_table.decompress(get_compressed_PTGB_table());
text_entry = dialogue_table.get_text_entry(index);
memcpy(output_buffer, text_entry, dialogue_table.get_text_entry_size(index));
return output_buffer;
}
void init_text_engine()
{
// Load the TTE
@@ -56,11 +79,9 @@ void init_text_engine()
int text_loop(int script)
{
u8 text_decompression_buffer[3072];
u8 index_buffer[100];
streamed_text_data_table dialogue_table(text_decompression_buffer, sizeof(text_decompression_buffer), index_buffer);
dialogue_table.decompress(get_compressed_PTGB_table());
// we have restricted the dialog entries to 1024 bytes in the text_helper main.py
// so we shouldn't run into problems when we only use 1 KB to contain a text entry.
u8 diag_entry_text_buffer[1024];
switch (script)
{
case BTN_TRANSFER:
@@ -72,7 +93,7 @@ int text_loop(int script)
break;
}
curr_text = (curr_line.has_text()) ? dialogue_table.get_text_entry(curr_line.get_text_entry_index()) : NULL;
curr_text = (curr_line.has_text()) ? read_dialogue_text_entry(curr_line.get_text_entry_index(), diag_entry_text_buffer) : NULL;
REG_BG1CNT = (REG_BG1CNT && !BG_PRIO_MASK) | BG_PRIO(2); // Show Fennel
show_text_box();
@@ -99,7 +120,7 @@ int text_loop(int script)
break;
}
curr_text = (curr_line.has_text()) ? dialogue_table.get_text_entry(curr_line.get_text_entry_index()) : NULL;
curr_text = (curr_line.has_text()) ? read_dialogue_text_entry(curr_line.get_text_entry_index(), diag_entry_text_buffer) : NULL;
char_index = 0;
if (text_exit)

View File

@@ -7,6 +7,7 @@ import requests
from collections import defaultdict
import copy
import math
import sys
update = True
@@ -389,6 +390,11 @@ def write_text_bin_file(filename, dictionary):
linedata = bytes.fromhex(dictionary[key]['bytes'])
bindata.extend(linedata)
current_offset += len(linedata)
if len(linedata) > 1024:
print(f"Error: entry '{key}' numBytes exceeds 1024 (got {len(linedata)})", file=sys.stderr)
sys.exit(1)
num += 1
# Write the index and bindata to the file