From d49b4246ee98f8493a3ab5771a70e93bb0f2c914 Mon Sep 17 00:00:00 2001 From: Giuliano Belinassi Date: Sun, 7 Aug 2022 18:38:30 -0300 Subject: [PATCH] Add support for DS expansion pak in VanillaTD --- arm7/audio.cpp | 58 ++++- common/CMakeLists.txt | 2 +- common/alloc.cpp | 38 ++- common/audio_fifocommon.h | 39 ++++ common/buff.cpp | 4 +- common/buff.h | 4 +- common/expansionpak_nds.cpp | 455 ++++++++++++++++++++++++++++++++++++ common/expansionpak_nds.h | 45 ++++ common/font.cpp | 37 ++- common/keyframe.cpp | 15 +- common/memflag.h | 1 + common/mixfile.h | 18 +- common/paths_nds.cpp | 39 ++++ common/rawfile.cpp | 2 + common/soundio_nds.cpp | 55 +++++ common/video_nds.cpp | 86 ++++++- common/winstub.cpp | 4 +- redalert/startup.cpp | 6 - redalert/winstub.cpp | 8 + tiberiandawn/credits.cpp | 12 +- tiberiandawn/defines.h | 10 +- tiberiandawn/display.cpp | 2 +- tiberiandawn/externs.h | 1 + tiberiandawn/globals.cpp | 2 + tiberiandawn/init.cpp | 16 ++ tiberiandawn/startup.cpp | 13 -- tiberiandawn/winstub.cpp | 16 +- 27 files changed, 930 insertions(+), 58 deletions(-) create mode 100644 common/expansionpak_nds.cpp create mode 100644 common/expansionpak_nds.h diff --git a/arm7/audio.cpp b/arm7/audio.cpp index dc16cb90..25af3256 100644 --- a/arm7/audio.cpp +++ b/arm7/audio.cpp @@ -34,6 +34,13 @@ // Calculate the ceil of division x / y. #define CEIL_DIV(x, y) (1 + (((x) - 1) / (y))) +/* Variable used to busy wait while the ARM9 copy data from an unreachable + address to somewhere we can reach. */ +volatile bool WaitingARM9 = false; + +/* Pointer to an region of memory that the ARM9 can write. */ +unsigned char *SharedArea = NULL; + // Provide an implementation of timerElapsed. Stolen from libnds. //--------------------------------------------------------------------------------- @@ -72,7 +79,7 @@ enum VOLUME_MAX = 255, PRIORITY_MIN = 0, PRIORITY_MAX = 255, - MAX_SAMPLE_TRACKERS = 5, + MAX_SAMPLE_TRACKERS = NUM_TRACKERS, DECOMP_BUFFER_COUNT = 2, BUFFER_CHUNK_SIZE = 4096, UNCOMP_BUFFER_SIZE = 2098, @@ -81,17 +88,6 @@ enum INVALID_FILE_HANDLE = -1, }; -/* -** Define the different type of sound compression avaliable to the westwood -** library. -*/ -typedef enum -{ - SCOMP_NONE = 0, // No compression -- raw data. - SCOMP_WESTWOOD = 1, // Special sliding window delta compression. - SCOMP_SOS = 99 // SOS frame compression. -} SCompressType; - class SoundTracker; static inline int __attribute__((pure)) Get_Channel_Index(SoundTracker*); @@ -225,6 +221,27 @@ class SoundTracker SoundHandle = handle; IsMusic = is_music; + /* If the AUD is at an unreachable address, ask for the ARM9 to copy it + to somewhere we can reach. This should never run on DSi mode. */ + if ((u32)sample >= 0x08000000) { + int index = Get_Channel_Index(); + + const void *src = sample; + void *dst = &SharedArea[SHARED_CHUNK_SIZE * index]; + + USR2::FifoMemcpyMessage msg; + msg.src = src; + msg.dst = dst; + + fifoSendDatamsg(FIFO_USER_02, sizeof(msg), (u8*)&msg); + + while (WaitingARM9) + ; // Busy wait the ARM9 to answer. + + /* Override unreachable pointer with the one that I can reach. */ + sample = dst; + } + // Load the AUD header; AUDHeaderType raw_header; memcpy(&raw_header, sample, sizeof(raw_header)); @@ -261,9 +278,17 @@ class SoundTracker sosinfo.dwUnCompSize = raw_header.Size * (sosinfo.wBitSize / 4); sosCODECInitStream(&sosinfo); } else if (Compression == SCOMP_WESTWOOD) { + if (!isDSiMode()) { + /* SCOMP_WESTWOOD on retail DS crashes the system for some + resason. */ + Stop_Sample(); + return 0; + } + /* There is a bug in SCOMP_WESTWOOD on DS in which sounds compressed by it get wavely loud in some audios. So we lower their volume so that it doesn't bother the user too much. */ + Volume = Volume / 6; Bits = 8; } @@ -869,12 +894,19 @@ void user01CommandHandler(u32 command, void* userdata) case USR1::STOP_SAMPLE_HANDLE: Trackers.Stop_Sample_Handle(data); + break; case USR1::STOP_SAMPLE: Trackers.Stop_Sample(data); + break; case USR1::SET_MUSIC_VOL: Trackers.Set_Music_Vol(data); + break; + + case USR1::ARM9_AUDCPY_DONE: + WaitingARM9 = false; + break; default: break; @@ -913,6 +945,8 @@ void Process_Queue() SCHANNEL_ENABLE | SOUND_VOL(volume) | SOUND_PAN(64) | (format << 29) | (SOUND_REPEAT); SCHANNEL_REPEAT_POINT(VQA_CHANNEL) = 0; + } else if (msg.type == USR1::SET_SHARED_AREA) { + SharedArea = (unsigned char *) msg.SetSharedArea.ptr; } } diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index efd48d39..188522cd 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -107,7 +107,7 @@ set(COMMON_SRC if (WIN32) list(APPEND COMMON_SRC file_win.cpp paths_win.cpp wintimer.cpp) elseif (NDS) - list(APPEND COMMON_SRC fnmatch.cpp file_posix.cpp paths_nds.cpp wintimer_nds.cpp memcpy.s rmemcpy.s memmove.s) + list(APPEND COMMON_SRC fnmatch.cpp file_posix.cpp paths_nds.cpp wintimer_nds.cpp expansionpak_nds.cpp memcpy.s rmemcpy.s memmove.s) else() list(APPEND COMMON_SRC file_posix.cpp paths_posix.cpp wintimer.cpp) endif() diff --git a/common/alloc.cpp b/common/alloc.cpp index 52b5b82d..96daddd4 100644 --- a/common/alloc.cpp +++ b/common/alloc.cpp @@ -47,6 +47,7 @@ #include #include #include +#include "expansionpak_nds.h" #endif #if defined(__unix__) || defined(__unix) @@ -101,15 +102,44 @@ extern void (*Memory_Error_Exit)(char* string) = NULL; * 03/09/1995 JLB : Fixed * * 09/28/1995 ST : Simplified for win95 * *=========================================================================*/ + +#ifdef _NDS +bool ExpansionMemoryInstalled; +size_t StackTop = 0; +void *ExpansionAddr; + +#endif + void* Alloc(size_t bytes_to_alloc, MemoryFlagType flags) { + void* mem_ptr; +#ifdef _NDS + static bool expansion_initialized = false; + if (flags & MEM_EXPANSION) { + if (!expansion_initialized) { + expansion_initialized = true; + ExpansionMemoryInstalled = ram_init(DETECT_RAM); + ExpansionAddr = (void *) ram_unlock(); + } + + if (ExpansionMemoryInstalled) { + /* 4 bytes aligned. */ + mem_ptr = (void*) ((((uintptr_t)ExpansionAddr + 3UL) & ~3UL) + StackTop); + StackTop += (bytes_to_alloc + 3UL) & ~3UL; + + return mem_ptr; + } + } +#endif + #ifdef MEM_CHECK bytes_to_alloc += sizeof(uintptr_t) * 8; #endif // MEM_CHECK mem_ptr = malloc(bytes_to_alloc); + if (mem_ptr == NULL) { DBG_LOG("Unable to allocate memory\n"); } @@ -178,7 +208,13 @@ void Free(void const* pointer) pointer = (void*)(((char*)pointer) - 16); #endif // MEM_CHECK - +#ifdef _NDS + /* Expansion memory is not administrated by malloc. */ + if ((uintptr_t)pointer >= 0x08000000) { + Memory_Calls--; + return; + } +#endif free((void*)pointer); Memory_Calls--; } diff --git a/common/audio_fifocommon.h b/common/audio_fifocommon.h index ada0910f..388ad974 100644 --- a/common/audio_fifocommon.h +++ b/common/audio_fifocommon.h @@ -7,6 +7,24 @@ // have to stream it from the SD Card. #define MUSIC_CHUNK_SIZE 32768 +// Chunk size of the shared memory area per tracker. Must be at least equal +// to the largest AUD in a *cached* mixfile. +#define SHARED_CHUNK_SIZE (70 * 1024) + +// Number of trackers to use; +#define NUM_TRACKERS 5 + +/* +** Define the different type of sound compression avaliable to the westwood +** library. +*/ +typedef enum +{ + SCOMP_NONE = 0, // No compression -- raw data. + SCOMP_WESTWOOD = 1, // Special sliding window delta compression. + SCOMP_SOS = 99 // SOS frame compression. +} SCompressType; + // USR1: ARM9 to ARM7 namespace USR1 { @@ -32,6 +50,10 @@ namespace USR1 // Set volume of music. SET_MUSIC_VOL = 4 << 20, + + // Confirmation that copy from ARM9 is done. + ARM9_AUDCPY_DONE = 5 << 20, + } FifoSoundCommand; // Define message kinds. Used to distinguish packages one from another. @@ -42,6 +64,11 @@ namespace USR1 // Sound message comming from VQA Player. SOUND_VQA_MESSAGE, + + // Set shared memory area to store stuff comming from regions that + // the ARM7 can't reach. + SET_SHARED_AREA, + } FifoSoundMessageType; // Define what can be in a message. Message must have a maximum length of @@ -71,6 +98,11 @@ namespace USR1 u8 volume; u8 bits; } SoundVQAChunk; + + struct + { + void* ptr; + } SetSharedArea; }; } ALIGN(4) FifoSoundMessage; @@ -85,6 +117,13 @@ namespace USR2 // Ask the ARM9 for more music data. MUSIC_REQUEST_CHUNK = 1 << 20, } SoundMusicChunk; + + // Parameters of a memcpy call. + typedef struct FifoMessage + { + const void *src; + void *dst; + } ALIGN(4) FifoMemcpyMessage; } // namespace USR2 #endif //AUDIO_FIFOCOMMON diff --git a/common/buff.cpp b/common/buff.cpp index e1c29a7c..da123275 100644 --- a/common/buff.cpp +++ b/common/buff.cpp @@ -100,13 +100,13 @@ Buffer::Buffer(void const* buffer, int size) * HISTORY: * * 07/29/1996 JLB : Created. * *=============================================================================================*/ -Buffer::Buffer(int size) +Buffer::Buffer(int size, MemoryFlagType flags) : BufferPtr(NULL) , Size(size) , IsAllocated(false) { if (size > 0) { - BufferPtr = new char[size]; + BufferPtr = new (flags) char[size]; IsAllocated = true; } } diff --git a/common/buff.h b/common/buff.h index dc063b64..9b4054a7 100644 --- a/common/buff.h +++ b/common/buff.h @@ -52,6 +52,8 @@ typedef int bool; #endif #endif +#include "memflag.h" + /* ** A general purpose buffer pointer handler object. It holds not only the pointer to the ** buffer, but its size as well. By using this class instead of separate pointer and size @@ -63,7 +65,7 @@ class Buffer Buffer(char* ptr, int size = 0); Buffer(void* ptr = 0, int size = 0); Buffer(void const* ptr, int size = 0); - Buffer(int size); + Buffer(int size, MemoryFlagType flags = MEM_NORMAL); Buffer(Buffer const& buffer); ~Buffer(void); diff --git a/common/expansionpak_nds.cpp b/common/expansionpak_nds.cpp new file mode 100644 index 00000000..31f1d7e3 --- /dev/null +++ b/common/expansionpak_nds.cpp @@ -0,0 +1,455 @@ +//============================================================================// +// // +// Copyright 2007 Rick "Lick" Wong // +// // +// This library is licensed as described in the included readme. // +// // +//============================================================================// + +#include "expansionpak_nds.h" +#include + +//===================================// +// // +// Device Ram Drivers ! // +// // +//===================================// + +//======================== +vu16 *_sc_unlock () +//======================== +{ + *(vu16*)0x9FFFFFE = 0xA55A; + *(vu16*)0x9FFFFFE = 0xA55A; + *(vu16*)0x9FFFFFE = 0x5; // RAM_RW + *(vu16*)0x9FFFFFE = 0x5; + + return (vu16*)0x8000000; +} + +//======================== +void _sc_lock () +//======================== +{ + *(vu16*)0x9FFFFFE = 0xA55A; + *(vu16*)0x9FFFFFE = 0xA55A; + *(vu16*)0x9FFFFFE = 0x3; // MEDIA + *(vu16*)0x9FFFFFE = 0x3; +} + +//======================== +vu16 *_m3_unlock () +//======================== +{ + u32 mode = 0x00400006; // RAM_RW + vu16 tmp; + tmp = *(vu16*)0x08E00002; + tmp = *(vu16*)0x0800000E; + tmp = *(vu16*)0x08801FFC; + tmp = *(vu16*)0x0800104A; + tmp = *(vu16*)0x08800612; + tmp = *(vu16*)0x08000000; + tmp = *(vu16*)0x08801B66; + tmp = *(vu16*)(0x08000000 + (mode << 1)); + tmp = *(vu16*)0x0800080E; + tmp = *(vu16*)0x08000000; + tmp = *(vu16*)0x080001E4; + tmp = *(vu16*)0x080001E4; + tmp = *(vu16*)0x08000188; + tmp = *(vu16*)0x08000188; + + return (vu16*)0x8000000; +} + +//======================== +void _m3_lock () +//======================== +{ + u32 mode = 0x00400003; // MEDIA + vu16 tmp; + tmp = *(vu16*)0x08E00002; + tmp = *(vu16*)0x0800000E; + tmp = *(vu16*)0x08801FFC; + tmp = *(vu16*)0x0800104A; + tmp = *(vu16*)0x08800612; + tmp = *(vu16*)0x08000000; + tmp = *(vu16*)0x08801B66; + tmp = *(vu16*)(0x08000000 + (mode << 1)); + tmp = *(vu16*)0x0800080E; + tmp = *(vu16*)0x08000000; + tmp = *(vu16*)0x080001E4; + tmp = *(vu16*)0x080001E4; + tmp = *(vu16*)0x08000188; + tmp = *(vu16*)0x08000188; +} + +//======================== +vu16 *_opera_unlock () +//======================== +{ + *(vu16*)0x8240000 = 1; + + return (vu16*)0x9000000; +} + +//======================== +void _opera_lock () +//======================== +{ + *(vu16*)0x8240000 = 0; +} + + +//======================== +vu16 *_g6_unlock () +//======================== +{ + u32 mode = 6; // RAM_RW + vu16 tmp; + tmp = *(vu16*)0x09000000; + tmp = *(vu16*)0x09FFFFE0; + tmp = *(vu16*)0x09FFFFEC; + tmp = *(vu16*)0x09FFFFEC; + tmp = *(vu16*)0x09FFFFEC; + tmp = *(vu16*)0x09FFFFFC; + tmp = *(vu16*)0x09FFFFFC; + tmp = *(vu16*)0x09FFFFFC; + tmp = *(vu16*)0x09FFFF4A; + tmp = *(vu16*)0x09FFFF4A; + tmp = *(vu16*)0x09FFFF4A; + tmp = *(vu16*)(0x09200000 + (mode << 1)); + tmp = *(vu16*)0x09FFFFF0; + tmp = *(vu16*)0x09FFFFE8; + + return (vu16*)0x8000000; +} + +//======================== +void _g6_lock () +//======================== +{ + u32 mode = 3; // MEDIA + vu16 tmp; + tmp = *(vu16*)0x09000000; + tmp = *(vu16*)0x09FFFFE0; + tmp = *(vu16*)0x09FFFFEC; + tmp = *(vu16*)0x09FFFFEC; + tmp = *(vu16*)0x09FFFFEC; + tmp = *(vu16*)0x09FFFFFC; + tmp = *(vu16*)0x09FFFFFC; + tmp = *(vu16*)0x09FFFFFC; + tmp = *(vu16*)0x09FFFF4A; + tmp = *(vu16*)0x09FFFF4A; + tmp = *(vu16*)0x09FFFF4A; + tmp = *(vu16*)(0x09200000 + (mode << 1)); + tmp = *(vu16*)0x09FFFFF0; + tmp = *(vu16*)0x09FFFFE8; +} + +//======================== +vu16 *_ez_unlock () +//======================== +{ + return (vu16*)0x8400000; +} + +//======================== +void _ez_lock () +//======================== +{ +} + + +//===================================// +// // +// Ram API ! // +// // +//===================================// + +static vu16* (*_unlock) () = 0; +static void (*_lock) () = 0; +static u32 _size = 0; +static RAM_TYPE _type = DETECT_RAM; +const char *_types[] = {"Unknown", "Supercard", "M3", "Opera", "G6", "EZ"}; + + +//========================================================== +static bool _ram_test () +//========================================================== +{ + vu16 *ram = _unlock(); + + ram[0] = 0x1234; + if(ram[0] == 0x1234) // test writability + { + _lock(); + + ram[0] = 0x4321; + if(ram[0] != 0x4321) // test non-writability + { + return true; + } + } + + return false; +} + +// Based on DSLinux Amadeus' detection +//========================================================== +static bool _ram_test_ez () +//========================================================== +{ + vu16 *ram; + + *(vu16*)0x9FE0000 = 0xD200; // SetRompage (OS mode) + *(vu16*)0x8000000 = 0x1500; + *(vu16*)0x8020000 = 0xD200; + *(vu16*)0x8040000 = 0x1500; + *(vu16*)0x9880000 = 0x8000; + *(vu16*)0x9FC0000 = 0x1500; + + *(vu16*)0x9FE0000 = 0xD200; // OpenNorWrite + *(vu16*)0x8000000 = 0x1500; + *(vu16*)0x8020000 = 0xD200; + *(vu16*)0x8040000 = 0x1500; + *(vu16*)0x9C40000 = 0x1500; + *(vu16*)0x9FC0000 = 0x1500; + + + ram = (vu16*)0x08400000; + + ram[0] = 0x1234; + if(ram[0] == 0x1234) // test writability + { + ram = (vu16*)0x08000000; + + ram[0] = 0x4321; + if(ram[0] != 0x4321) // test non-writability + { + return true; + } + } + + *(vu16*)0x9FE0000 = 0xD200; // CloseNorWrite + *(vu16*)0x8000000 = 0x1500; + *(vu16*)0x8020000 = 0xD200; + *(vu16*)0x8040000 = 0x1500; + *(vu16*)0x9C40000 = 0xD200; + *(vu16*)0x9FC0000 = 0x1500; + + *(vu16*)0x9FE0000 = 0xD200; // SetRompage (352) + *(vu16*)0x8000000 = 0x1500; + *(vu16*)0x8020000 = 0xD200; + *(vu16*)0x8040000 = 0x1500; + *(vu16*)0x9880000 = 352; + *(vu16*)0x9FC0000 = 0x1500; + + *(vu16*)0x9FE0000 = 0xD200; // OpenNorWrite + *(vu16*)0x8000000 = 0x1500; + *(vu16*)0x8020000 = 0xD200; + *(vu16*)0x8040000 = 0x1500; + *(vu16*)0x9C40000 = 0x1500; + *(vu16*)0x9FC0000 = 0x1500; + + ram = (vu16*)0x08400000; + + ram[0] = 0x1234; + if(ram[0] == 0x1234) // test writability + { + return true; + } + + return false; +} + + +//========================================================== +static void _ram_precalc_size () +//========================================================== +{ + if(_unlock == 0 || _lock == 0) + return; + + vu16 *ram = _unlock(); + _size = 0; + + ram[0] = 0x2468; + while(ram[_size] == 0x2468) + { + ram[_size] = 0; + _size += 512; + ram[_size] = 0x2468; + } + _size<<=1; + + _lock(); +} + + +//========================================================== +bool ram_init (RAM_TYPE type) +//========================================================== +{ + if (isDSiMode()) + return false; + + sysSetBusOwners(BUS_OWNER_ARM9, BUS_OWNER_ARM9); + + switch(type) + { + case SC_RAM: + { + _unlock = _sc_unlock; + _lock = _sc_lock; + _type = SC_RAM; + } + break; + + case M3_RAM: + { + _unlock = _m3_unlock; + _lock = _m3_lock; + _type = M3_RAM; + } + break; + + case OPERA_RAM: + { + _unlock = _opera_unlock; + _lock = _opera_lock; + _type = OPERA_RAM; + } + break; + + case G6_RAM: + { + _unlock = _g6_unlock; + _lock = _g6_lock; + _type = G6_RAM; + } + break; + + case EZ_RAM: + { + _unlock = _ez_unlock; + _lock = _ez_lock; + _type = EZ_RAM; + } + break; + + case DETECT_RAM: + default: + { + // try ez + _unlock = _ez_unlock; + _lock = _ez_lock; + _type = EZ_RAM; + + if(_ram_test_ez()) + { + break; + } + + // try supercard + _unlock = _sc_unlock; + _lock = _sc_lock; + _type = SC_RAM; + + if(_ram_test()) + { + break; + } + + // try m3 + _unlock = _m3_unlock; + _lock = _m3_lock; + _type = M3_RAM; + + if(_ram_test()) + { + break; + } + + // try opera + _unlock = _opera_unlock; + _lock = _opera_lock; + _type = OPERA_RAM; + + if(_ram_test()) + { + break; + } + + // try g6 + _unlock = _g6_unlock; + _lock = _g6_lock; + _type = G6_RAM; + + if(_ram_test()) + { + break; + } + + // fail + _unlock = 0; + _lock = 0; + _type = DETECT_RAM; + + return false; + } + break; + } + + _ram_precalc_size(); + + return true; +} + + +//========================================================== +RAM_TYPE ram_type () +//========================================================== +{ + return _type; +} + + +//========================================================== +const char* ram_type_string () +//========================================================== +{ + return _types[(int)_type]; +} + + +//========================================================== +u32 ram_size () +//========================================================== +{ + return _size; +} + + +//========================================================== +vu16* ram_unlock () +//========================================================== +{ + sysSetBusOwners(BUS_OWNER_ARM9, BUS_OWNER_ARM9); + + if(_unlock) + return _unlock(); + return 0; +} + + +//========================================================== +void ram_lock () +//========================================================== +{ + sysSetBusOwners(BUS_OWNER_ARM9, BUS_OWNER_ARM9); + + if(_lock) + _lock(); +} + + diff --git a/common/expansionpak_nds.h b/common/expansionpak_nds.h new file mode 100644 index 00000000..be8a5448 --- /dev/null +++ b/common/expansionpak_nds.h @@ -0,0 +1,45 @@ +//============================================================================// +// // +// Copyright 2007 Rick "Lick" Wong // +// // +// This library is licensed as described in the included readme. // +// // +//============================================================================// + + +#ifndef __RAM +#define __RAM + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum { DETECT_RAM=0, SC_RAM, M3_RAM, OPERA_RAM, G6_RAM, EZ_RAM } RAM_TYPE; + +// Call this before the others +bool ram_init (RAM_TYPE); + +// Returns the type of the RAM device +RAM_TYPE ram_type (); + +// Returns the type of the RAM device in a string +const char* ram_type_string (); + +// Returns the total amount of RAM in bytes +u32 ram_size (); + + +// Unlocks the RAM and returns a pointer to the begin +vu16* ram_unlock (); + +// Locks the RAM +void ram_lock (); + + +#ifdef __cplusplus +} +#endif +#endif diff --git a/common/font.cpp b/common/font.cpp index aedf3aa8..661a6126 100644 --- a/common/font.cpp +++ b/common/font.cpp @@ -41,6 +41,10 @@ #include #include +#ifdef _NDS +#include +#endif + int FontXSpacing = 0; int FontYSpacing = 0; void const* FontPtr = nullptr; @@ -324,6 +328,20 @@ struct FontHeader * 01/17/1995 PWG : Created. * * 18/08/2020 OmniBlade : Translation to C++ added. * *=========================================================================*/ + +#ifdef _NDS +/* On retail DS, the video RAM discards 8-bit writes. This function enforces + a 16-bit write. */ +static void write8_16(unsigned char* addr, unsigned char val) +{ + unsigned short* aligned_ptr = (unsigned short*)((intptr_t)addr & ~1); // iirc the hw auto aligns 16 bit writes, so you could try leaving the & ~1 out + unsigned short aligned_val = *aligned_ptr; + aligned_val &= 0xFF00 >> (((intptr_t)addr & 1)*8); + aligned_val |= val << (((intptr_t)addr & 1)*8); + *aligned_ptr = aligned_val; +} +#endif + int Buffer_Print(void* thisptr, const char* string, int x, int y, int fground, int bground) { GraphicViewPortClass& vp = *static_cast(thisptr); @@ -333,6 +351,11 @@ int Buffer_Print(void* thisptr, const char* string, int x, int y, int fground, i unsigned char* dst = x + offset; int char_width = 0; int base_x = x; +#ifdef _NDS + /* On retail DS, the video RAM discards 8-bit writes. Check if dest pointer + goes to video memory. DSi doesn't have this problem. */ + bool must_use_16_writes = ((uintptr_t )dst >= 0x06000000) && !isDSiMode(); +#endif if (FontPtr != nullptr) { const unsigned short* datalist = reinterpret_cast(reinterpret_cast(FontPtr) @@ -443,7 +466,12 @@ int Buffer_Print(void* thisptr, const char* string, int x, int y, int fground, i unsigned char color = ColorXlat[0][color_packed & 0x0F]; if (color) { - *char_dst = color; +#ifdef _NDS + if (must_use_16_writes) + write8_16(char_dst, color); + else +#endif + *char_dst = color; } ++char_dst; @@ -456,7 +484,12 @@ int Buffer_Print(void* thisptr, const char* string, int x, int y, int fground, i color = ColorXlat[0][color_packed >> 4]; if (color) { - *char_dst = color; +#ifdef _NDS + if (must_use_16_writes) + write8_16(char_dst, color); + else +#endif + *char_dst = color; } ++char_dst; diff --git a/common/keyframe.cpp b/common/keyframe.cpp index f363c87a..5ac35894 100644 --- a/common/keyframe.cpp +++ b/common/keyframe.cpp @@ -238,6 +238,12 @@ void Enable_Uncompressed_Shapes() #define FIXIT_SCORE_CRASH + +#ifdef _NDS +/* A memcpy version that never generates 8-bit writes. */ +void *tonccpy(void *dst, const void *src, unsigned size); +#endif + uintptr_t Build_Frame(void const* dataptr, unsigned short framenumber, void* buffptr) { #ifdef FIXIT_SCORE_CRASH @@ -286,10 +292,10 @@ uintptr_t Build_Frame(void const* dataptr, unsigned short framenumber, void* buf if (Get_Running_Game() == GAME_TD) { /* TD for some reason requires more memory to run when loading maps and score screen. */ - eps = 600 * 1024; + eps = 450 * 1024; } else { /* Game is RA. It can run with less free memory. */ - eps = 300 * 1024; + eps = 400 * 1024; } size_t ram_free = Ram_Free(MEM_NORMAL); @@ -354,8 +360,13 @@ uintptr_t Build_Frame(void const* dataptr, unsigned short framenumber, void* buf TotalSlotsUsed++; } // Commit back to the original pointer. +#ifdef _NDS + /* Also inclues the keyframe.y on this copy. */ + tonccpy(Add_Long_To_Pointer(dataptr, offsetof(KeyFrameHeaderType, x)), &keyfr.x, 4); +#else memcpy(Add_Long_To_Pointer(dataptr, offsetof(KeyFrameHeaderType, x)), &keyfr.x, sizeof(unsigned short)); memcpy(Add_Long_To_Pointer(dataptr, offsetof(KeyFrameHeaderType, y)), &keyfr.y, sizeof(unsigned short)); +#endif /* ** Allocate and clear the memory for the shape info diff --git a/common/memflag.h b/common/memflag.h index 45971721..505e23ce 100644 --- a/common/memflag.h +++ b/common/memflag.h @@ -48,6 +48,7 @@ typedef enum MEM_REAL = 0x0004, // Clear memory before returning. MEM_TEMP = 0x0008, // Clear memory before returning. MEM_LOCK = 0x0010, // Lock the memory that we allocated + MEM_EXPANSION = 0x0020, // Allocate memory in some kind of expansion memory. } MemoryFlagType; /* diff --git a/common/mixfile.h b/common/mixfile.h index 16ac97c5..91b2bf02 100644 --- a/common/mixfile.h +++ b/common/mixfile.h @@ -29,6 +29,7 @@ #include "shastraw.h" #include "wwstd.h" #include "rndstraw.h" +#include "memflag.h" #ifndef _WIN32 #include // For basename() @@ -212,7 +213,7 @@ template MixFileClass::~MixFileClass(void) free((char*)Filename); } if (Data != NULL && IsAllocated) { - delete[] static_cast(Data); + ::Free(Data); IsAllocated = false; } Data = NULL; @@ -576,6 +577,9 @@ template bool MixFileClass::Cache(char const* fil * 08/08/1994 JLB : Created. * * 07/12/1996 JLB : Handles attached message digest. * *=============================================================================================*/ + +int DS_Cache_File(void* data, int datasize, Straw *); + template bool MixFileClass::Cache(Buffer const* buffer) { /* @@ -593,7 +597,7 @@ template bool MixFileClass::Cache(Buffer const* b Data = buffer->Get_Buffer(); } } else { - Data = new char[DataSize]; + Data = Alloc(DataSize, MEM_EXPANSION); IsAllocated = true; } @@ -629,9 +633,13 @@ template bool MixFileClass::Cache(Buffer const* b ** Fetch the whole mixfile data in one step. If the number of bytes retrieved ** does not equal that requested, then this indicates a serious error. */ +#ifdef _NDS + int actual = DS_Cache_File(Data, DataSize, straw); +#else int actual = straw->Get(Data, DataSize); +#endif if (actual != DataSize) { - delete[] Data; + ::Free(Data); Data = NULL; file.Error(EIO); return (false); @@ -648,7 +656,7 @@ template bool MixFileClass::Cache(Buffer const* b sha.Result(digest2); fstraw.Get(digest1, sizeof(digest1)); if (memcmp(digest1, digest2, sizeof(digest1)) != 0) { - delete[] Data; + ::Free(Data); Data = NULL; return (false); } @@ -680,7 +688,7 @@ template bool MixFileClass::Cache(Buffer const* b template void MixFileClass::Free(void) { if (Data != NULL && IsAllocated) { - delete[] Data; + ::Free(Data); } Data = NULL; IsAllocated = false; diff --git a/common/paths_nds.cpp b/common/paths_nds.cpp index d8af068f..1850e673 100644 --- a/common/paths_nds.cpp +++ b/common/paths_nds.cpp @@ -21,6 +21,13 @@ #include +#include "xstraw.h" +#include "pkstraw.h" +#include "shastraw.h" +#include "rndstraw.h" + +#define MIN(a, b) (((a) > (b)) ? (b) : (a)) + /* DS is quasi-posix compliant: it don't provide some functions. */ extern "C" { @@ -38,6 +45,8 @@ const char* basename(const char* path) } } +void Set_Video_Mode(int, int, int); + /* Nintendo DS require its filesystem structures to be explicitely initialized. */ void DS_Filesystem_Init() { @@ -47,6 +56,7 @@ void DS_Filesystem_Init() return; if (!fatInitDefault()) { + Set_Video_Mode(320, 200, 8); DBG_LOG("FATAL ERROR: Unable to initialize file system"); swiWaitForVBlank(); while (1) @@ -56,6 +66,35 @@ void DS_Filesystem_Init() fs_initialized = true; } +/* Nintendo DS Expansion Pak requires that writes are 16-bit wide. This + function ensures that the file is cached correctly. */ +int DS_Cache_File(void *data, int datasize, Straw *straw) +{ + if (isDSiMode()) { + /* DSi doesn't support the memory expansion pak, so load it directly + into the RAM. */ + return straw->Get(data, datasize); + } + + /* Define a buffer on on-board memory, as it is more flexible. */ + unsigned char buf[4096]; + unsigned char* datab = (unsigned char*) data; + int actual; + int i; + + while (actual < datasize) { + int to_read = MIN(datasize - actual, 4096); + i = straw->Get(buf, to_read); + + /* If the number of bytes read isn't multiple of 2, then we align upwards + to ensure a 16-bit read. */ + memcpy((u16*) &datab[actual], buf, i + (i & 1)); + actual += i; + } + + return actual; +} + namespace { const std::string& User_Home() diff --git a/common/rawfile.cpp b/common/rawfile.cpp index c2a8efef..54844314 100644 --- a/common/rawfile.cpp +++ b/common/rawfile.cpp @@ -68,6 +68,7 @@ #endif #include +#include /*********************************************************************************************** * RawFileClass::Error -- Handles displaying a file error message. * @@ -438,6 +439,7 @@ void RawFileClass::Close(void) * HISTORY: * * 10/18/1994 JLB : Created. * *=============================================================================================*/ + int RawFileClass::Read(void* buffer, int size) { int bytesread = 0; // Running count of the number of bytes read into the buffer. diff --git a/common/soundio_nds.cpp b/common/soundio_nds.cpp index 188451a3..5894c35f 100644 --- a/common/soundio_nds.cpp +++ b/common/soundio_nds.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "audio_fifocommon.h" /** Sound interface between C&C and the ARM7 chip. @@ -248,6 +249,38 @@ void user02CommandHandler(u32 command, void* userdata) } } +// Declare the USR2 mensage parser that will decode the messages from our ARM7 +// chip. + +void user02DatamsgHandler(int bytes, void *data) +{ + USR2::FifoMemcpyMessage msg; + fifoGetDatamsg(FIFO_USER_02, bytes, (u8*)&msg); + + const void *src = msg.src; + void *dst = msg.dst; + size_t n; + + AUDHeaderType raw_header; + memcpy(&raw_header, src, sizeof(raw_header)); + + // Infantry dying causes the game to crash for some reason. + if (SCompressType(raw_header.Compression) != SCOMP_WESTWOOD) { + + n = raw_header.Size + sizeof(AUDHeaderType); + // Align to word. + n = (n + 3) & ~3UL; + + /* Perform a memcpy to the address request. Src may be unaligned. */ + memcpy(dst, src, n); + DC_FlushRange(dst, n); + } + + + /* Send confirmation to ARM7 that we are done. */ + fifoSendValue32(FIFO_USER_01, USR1::ARM9_AUDCPY_DONE); +} + /* -------------------------------------------------------------------- * Everything down here is an interface to the game's engine. * -------------------------------------------------------------------- @@ -284,14 +317,34 @@ void* Load_Sample(char const* filename) // Unused but required by game engine. void Free_Sample(void const* sample){}; +// Allocate a shared region in case it needs to access stuff that is +// unreachable to it. (like the ExpansionPak in the GBA slot). +static unsigned char *SharedArea; + // Initialize audio-related structures. bool Audio_Init(int bits_per_sample, bool stereo, int rate, bool reverse_channels) { // Initialize Nintendo DS sound system. soundEnable(); + // On original NDS we cache the sound effects on an address that the ARM7 + // can't reach, so we allocate a temporary buffer where we will copy + // those sound effects. + if (!isDSiMode()) { + SharedArea = (unsigned char *) Alloc(SHARED_CHUNK_SIZE * NUM_TRACKERS, MEM_CLEAR); + } + // Install ARM7 to ARM9 Queue, used to request music data. fifoSetValue32Handler(FIFO_USER_02, user02CommandHandler, 0); + fifoSetDatamsgHandler(FIFO_USER_02, user02DatamsgHandler, 0); + + USR1::FifoMessage msg; + msg.type = USR1::SET_SHARED_AREA; + msg.SetSharedArea.ptr = SharedArea; + + // Asynchronous send sound play command + fifoSendDatamsg(FIFO_USER_01, sizeof(msg), (u8*)&msg); + // Set Global structures required by game's API. SoundType = SFX_ALFX; @@ -305,6 +358,8 @@ bool Audio_Init(int bits_per_sample, bool stereo, int rate, bool reverse_channel // Unused, but requited by game engine. void Sound_End(void) { + if (SharedArea) + ::Free(SharedArea); } // Stop a sample by its handle. diff --git a/common/video_nds.cpp b/common/video_nds.cpp index 9f1dff10..b9e6926d 100644 --- a/common/video_nds.cpp +++ b/common/video_nds.cpp @@ -151,6 +151,11 @@ class HardwareCursor // shape in case it changes. if (Raw != Last_Raw) { Raw = Last_Raw; + /* On retail DS writes 8bit writes to VRAM are discarded. So + create a temporary surface to transform the bitmap shape + into tiled. */ + uint8_t tiled_surface[24 * 32]; + memset(tiled_surface, 0, sizeof(tiled_surface)); // DS sprites are tiled, so we remap the texture to be displayed // correctly. @@ -162,11 +167,13 @@ class HardwareCursor int real_i = 8 * i + ii; if (real_j < w && real_i < h) - dst[256 * i + 8 * ii + 64 * j + jj] = src[real_i * w + real_j]; + tiled_surface[256 * i + 8 * ii + 64 * j + jj] = src[real_i * w + real_j]; } } } } + /* Blt to sprite. */ + memcpy(dst, tiled_surface, 32*24); } } @@ -674,3 +681,80 @@ VideoSurface* Video::CreateSurface(int w, int h, GBC_Enum flags) { return new VideoSurfaceNDS(w, h, flags); } + +//! VRAM-safe cpy. Stolen from libtonc +/*! This version mimics memcpy in functionality, with + the benefit of working for VRAM as well. It is also + slightly faster than the original memcpy, but faster + implementations can be made. + \param dst Destination pointer. + \param src Source pointer. + \param size Fill-length in bytes. + \return \a dst. + \note The pointers and size need not be word-aligned. +*/ +void *tonccpy(void *dst, const void *src, size_t size) +{ + if (isDSiMode()) + return memcpy(dst, src, size); + + uint count; + u16 *dst16; // hword destination + u8 *src8; // byte source + + // Ideal case: copy by 4x words. Leaves tail for later. + if( ((u32)src|(u32)dst)%4==0 && size>=4) + { + u32 *src32= (u32*)src, *dst32= (u32*)dst; + + count= size/4; + uint tmp= count&3; + count /= 4; + + // Duff, bitch! + switch(tmp) { + do { *dst32++ = *src32++; + case 3: *dst32++ = *src32++; + case 2: *dst32++ = *src32++; + case 1: *dst32++ = *src32++; + case 0: ; } while(count--); + } + + // Check for tail + size &= 3; + if(size == 0) + return dst; + + src8= (u8*)src32; + dst16= (u16*)dst32; + } + else // Unaligned. + { + uint dstOfs= (u32)dst&1; + src8= (u8*)src; + dst16= (u16*)(dst-dstOfs); + + // Head: 1 byte. + if(dstOfs != 0) + { + *dst16= (*dst16 & 0xFF) | *src8++<<8; + dst16++; + if(--size==0) + return dst; + } + } + + // Unaligned main: copy by 2x byte. + count= size/2; + while(count--) + { + *dst16++ = src8[0] | src8[1]<<8; + src8 += 2; + } + + // Tail: 1 byte. + if(size&1) + *dst16= (*dst16 &~ 0xFF) | *src8; + + return dst; +} diff --git a/common/winstub.cpp b/common/winstub.cpp index ca9dd270..d61c5a31 100644 --- a/common/winstub.cpp +++ b/common/winstub.cpp @@ -21,8 +21,10 @@ #include "iff.h" #include "gbuffer.h" -#include "filepcx.h" #include "debugstring.h" +#include "filepcx.h" + +GraphicBufferClass* Read_PCX_File (const char* name, char* palette,void *buff, int size); /*********************************************************************************************** * Load_Title_Screen -- loads the title screen into the given video buffer * diff --git a/redalert/startup.cpp b/redalert/startup.cpp index e8614578..54ec4de1 100644 --- a/redalert/startup.cpp +++ b/redalert/startup.cpp @@ -282,12 +282,6 @@ int main(int argc, char* argv[]) UtfArgs args(argc, argv); WWDebugString("RA95 - Starting up.\n"); - if (Ram_Free(MEM_NORMAL) < 7000000) { - printf(TEXT_NO_RAM); - - return (EXIT_FAILURE); - } - /* ** Remember the current working directory and drive. */ diff --git a/redalert/winstub.cpp b/redalert/winstub.cpp index d966280f..e9e60ea3 100644 --- a/redalert/winstub.cpp +++ b/redalert/winstub.cpp @@ -614,6 +614,14 @@ void Memory_Error_Handler(void) Show_Mouse(); }; WWMessageBox().Process(TEXT_MEMORY_ERROR, TEXT_ABORT, nullptr, nullptr, false); +#ifdef _NDS + printf("\n" + "** This game requires the DSi **" + "** model **\n" + "If you see this message on DSi, then launch it in DSi mode.\n" + "\n" + "If you see this message in-game, report as a bug.\n"); +#endif ReadyToQuit = 1; diff --git a/tiberiandawn/credits.cpp b/tiberiandawn/credits.cpp index cd08b924..adb20783 100644 --- a/tiberiandawn/credits.cpp +++ b/tiberiandawn/credits.cpp @@ -36,6 +36,9 @@ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" +#ifdef _NDS +#include +#endif /*********************************************************************************************** * CreditClass::CreditClass -- Default constructor for the credit class object. * @@ -91,13 +94,20 @@ void CreditClass::Graphic_Logic(bool forced) ** Play a sound effect when the money display changes, but only if a sound ** effect was requested. */ - if (IsAudible) { + bool is_audible = IsAudible; +#ifdef _NDS + /* FIXME: There is a bug on retail DS which makes the credits sound to + glich. */ + is_audible = is_audible && isDSiMode(); +#endif + if (is_audible) { if (IsUp) { Sound_Effect(VOC_UP, VOL_1); } else { Sound_Effect(VOC_DOWN, VOL_1); } } + /* ** Display the new current value. */ diff --git a/tiberiandawn/defines.h b/tiberiandawn/defines.h index 7777bf7f..6d915340 100644 --- a/tiberiandawn/defines.h +++ b/tiberiandawn/defines.h @@ -241,7 +241,15 @@ typedef enum DiffType : unsigned char ** changed, be sure to update the makefile and rebuild all of the shape ** data files. */ -#define SHAPE_BUFFER_SIZE 131072L +// PG set this to 131072 +#define SHAPE_BUFFER_SIZE 40000L + + +/********************************************************************** +** This is the size of the theater buffer. This buffer is used to load +** theater specific assets. Should match the largest mixfile. +*/ +#define THEATER_BUFFER_SIZE 680000L // Use this to allow keep track of versions as they affect saved games. #define VERSION_NUMBER 1 diff --git a/tiberiandawn/display.cpp b/tiberiandawn/display.cpp index 179f6c62..a390aa34 100644 --- a/tiberiandawn/display.cpp +++ b/tiberiandawn/display.cpp @@ -382,7 +382,7 @@ void DisplayClass::Init_Theater(TheaterType theater) delete TheaterData; } TheaterData = new MFCD(fullname); - TheaterData->Cache(); + TheaterData->Cache(TheaterBuffer); } #endif diff --git a/tiberiandawn/externs.h b/tiberiandawn/externs.h index d4e98974..8c09db9a 100644 --- a/tiberiandawn/externs.h +++ b/tiberiandawn/externs.h @@ -148,6 +148,7 @@ extern MouseClass Map; extern ScoreClass Score; extern MonoClass MonoArray[MonoClass::MAX_MONO_PAGES]; extern MFCD* ScoreMix; +extern Buffer *TheaterBuffer; extern MFCD* TheaterData; extern MFCD* LowTheaterData; extern MFCD* MoviesMix; diff --git a/tiberiandawn/globals.cpp b/tiberiandawn/globals.cpp index 5769e660..23d9410c 100644 --- a/tiberiandawn/globals.cpp +++ b/tiberiandawn/globals.cpp @@ -223,6 +223,8 @@ bool GameInFocus = true; /*************************************************************************** ** This holds the theater specific mixfiles. */ +Buffer *TheaterBuffer; + MFCD* TheaterData = NULL; MFCD* TheaterIcons = NULL; MFCD* LowTheaterData; diff --git a/tiberiandawn/init.cpp b/tiberiandawn/init.cpp index e61937be..0e01efb4 100644 --- a/tiberiandawn/init.cpp +++ b/tiberiandawn/init.cpp @@ -167,6 +167,14 @@ bool Init_Game(int, char*[]) CCDebugString("C&C95 - About to call Set_Shape_Buffer\n"); Set_Shape_Buffer(new unsigned char[SHAPE_BUFFER_SIZE], SHAPE_BUFFER_SIZE); + + /* + ** Allocate the theater buffer block. + */ + CCDebugString("C&C95 - About to allocate theater buffer\n"); + TheaterBuffer = new Buffer(THEATER_BUFFER_SIZE, MEM_EXPANSION); + assert(TheaterBuffer != NULL); + /* ** Bootstrap enough of the system so that the error dialog box can sucessfully ** be displayed. @@ -635,6 +643,14 @@ void Uninit_Game(void) { Map.Free_Cells(); + /* + ** Deallocate the theater buffer block. + */ + if (TheaterBuffer) { + delete TheaterBuffer; + TheaterBuffer = NULL; + } + delete[] SpeechBuffer; CCFileClass::Clear_Search_Drives(); diff --git a/tiberiandawn/startup.cpp b/tiberiandawn/startup.cpp index df1fb970..1f78af6c 100644 --- a/tiberiandawn/startup.cpp +++ b/tiberiandawn/startup.cpp @@ -205,19 +205,6 @@ int main(int argc, char** argv) UtfArgs args(argc, argv); CCDebugString("C&C95 - Starting up.\n"); - if (Ram_Free(MEM_NORMAL) < 5000000) { -#ifdef GERMAN - printf("Zuwenig Hauptspeicher verf?gbar.\n"); -#else -#ifdef FRENCH - printf("M�moire vive (RAM) insuffisante.\n"); -#else - printf("Insufficient RAM available.\n"); -#endif -#endif - return (EXIT_FAILURE); - } - #ifdef JAPANESE ForceEnglish = false; #endif diff --git a/tiberiandawn/winstub.cpp b/tiberiandawn/winstub.cpp index 8135f696..bdfea6eb 100644 --- a/tiberiandawn/winstub.cpp +++ b/tiberiandawn/winstub.cpp @@ -438,20 +438,20 @@ bool Any_Locked() *=============================================================================================*/ void Memory_Error_Handler(void) { - -#ifdef _NDS - DBG_LOG("Error - out of memory"); - swiWaitForVBlank(); - while (1) - ; -#endif - GlyphX_Debug_Print("Error - out of memory."); VisiblePage.Clear(); Set_Palette(GamePalette); while (Get_Mouse_State()) { Show_Mouse(); }; +#ifdef _NDS + printf("\n" + "** This game requires the DS **" + "** expansion pak **\n" + "On DSi, ensure the game is running in DSi mode.\n" + "\n" + "If you see this message in-game, report as a bug.\n"); +#endif WWMessageBox().Process("Error - out of memory.", "Abort", nullptr, nullptr, false); // Nope. ST - 1/10/2019 10:38AM