mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
fusee: Add support for firmware version 6.0.0.
fusee: Implement splash screen rendering. fusee: Add minor notes and update lz library.
This commit is contained in:
parent
39d812f434
commit
f864b0835d
10 changed files with 337 additions and 44 deletions
|
@ -194,7 +194,10 @@ void nx_hwinit()
|
|||
AHB_AHB_SPARE_REG_0 &= 0xFFFFFF9F;
|
||||
pmc->scratch49 = (((pmc->scratch49 >> 1) << 1) & 0xFFFFFFFD);
|
||||
|
||||
/* Apply the memory built-in self test workaround. */
|
||||
mbist_workaround();
|
||||
|
||||
/* Reboot SE. */
|
||||
clkrst_reboot(CARDEVICE_SE);
|
||||
|
||||
/* Initialize the fuse driver. */
|
||||
|
@ -203,9 +206,15 @@ void nx_hwinit()
|
|||
/* Initialize the memory controller. */
|
||||
mc_enable();
|
||||
|
||||
/* Configure oscillators, pinmux and GPIOs. */
|
||||
/* Configure oscillators. */
|
||||
config_oscillators();
|
||||
|
||||
/* Disable pinmux tristate input clamping. */
|
||||
APB_MISC_PP_PINMUX_GLOBAL_0 = 0;
|
||||
|
||||
/* Configure GPIOs. */
|
||||
/* NOTE: In 3.x+ part of the GPIO configuration is skipped if the unit is SDEV. */
|
||||
/* NOTE: In 6.x+ the GPIO configuration's order was changed a bit. */
|
||||
config_gpios();
|
||||
|
||||
/* Uncomment for UART debugging. */
|
||||
|
@ -214,21 +223,34 @@ void nx_hwinit()
|
|||
uart_init(UART_C, 115200);
|
||||
*/
|
||||
|
||||
/* Reboot CL-DVFS. */
|
||||
clkrst_reboot(CARDEVICE_CL_DVFS);
|
||||
|
||||
/* Reboot I2C1. */
|
||||
clkrst_reboot(CARDEVICE_I2C1);
|
||||
|
||||
/* Reboot I2C5. */
|
||||
clkrst_reboot(CARDEVICE_I2C5);
|
||||
|
||||
/* Reboot SE. */
|
||||
/* NOTE: In 4.x+ this was removed. */
|
||||
clkrst_reboot(CARDEVICE_SE);
|
||||
|
||||
/* Reboot unknown device. */
|
||||
clkrst_reboot(CARDEVICE_UNK);
|
||||
|
||||
/* Initialize I2C1 and I2C5. */
|
||||
/* Initialize I2C1. */
|
||||
/* NOTE: In 6.x+ this was moved to after the PMIC is configured. */
|
||||
i2c_init(I2C_1);
|
||||
|
||||
/* Initialize I2C5. */
|
||||
i2c_init(I2C_5);
|
||||
|
||||
/* Configure the PMIC. */
|
||||
uint8_t val = 0x40;
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGBBC, &val, 1);
|
||||
val = 0x78;
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_ONOFFCNFG1, &val, 1);
|
||||
|
||||
val = 0x38;
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_CFG0, &val, 1);
|
||||
val = 0x3A;
|
||||
|
@ -245,19 +267,38 @@ void nx_hwinit()
|
|||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_SD1, &val, 1);
|
||||
val = 0x1B;
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_SD3, &val, 1);
|
||||
|
||||
/* TODO: In 3.x+ this was added. */
|
||||
/*
|
||||
val = 0x22;
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_FPS_GPIO3, &val, 1);
|
||||
*/
|
||||
|
||||
/* TODO: In 3.x+, if the unit is SDEV, the MBLPD bit is set. */
|
||||
/*
|
||||
i2c_query(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGGLBL1, &val, 1);
|
||||
val |= 0x40;
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_CNFGGLBL1, &val, 1);
|
||||
*/
|
||||
|
||||
/* Configure SD0 voltage. */
|
||||
val = 42; /* 42 = (1125000 - 600000) / 12500 -> 1.125V */
|
||||
i2c_send(I2C_5, MAX77620_PWR_I2C_ADDR, MAX77620_REG_SD0, &val, 1);
|
||||
|
||||
/* Configure and lock PMC scratch registers. */
|
||||
/* NOTE: In 4.x+ this was removed. */
|
||||
config_pmc_scratch();
|
||||
|
||||
/* Set super clock burst policy. */
|
||||
car->sclk_brst_pol = ((car->sclk_brst_pol & 0xFFFF8888) | 0x3333);
|
||||
|
||||
/* Configure memory controller carveouts. */
|
||||
/* NOTE: In 4.x+ this was removed. */
|
||||
mc_config_carveout();
|
||||
|
||||
/* Initialize and save SDRAM. */
|
||||
/* Initialize SDRAM. */
|
||||
sdram_init();
|
||||
|
||||
/* Save SDRAM LP0 parameters. */
|
||||
sdram_lp0_save_params(sdram_get_params());
|
||||
}
|
|
@ -78,11 +78,73 @@
|
|||
*************************************************************************/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* Constants used for LZ77 coding
|
||||
*************************************************************************/
|
||||
|
||||
/* Maximum offset (can be any size < 2^31). Lower values give faster
|
||||
compression, while higher values gives better compression. The default
|
||||
value of 100000 is quite high. Experiment to see what works best for
|
||||
you. */
|
||||
#define LZ_MAX_OFFSET 100000
|
||||
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* INTERNAL FUNCTIONS *
|
||||
*************************************************************************/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* _LZ_StringCompare() - Return maximum length string match.
|
||||
*************************************************************************/
|
||||
|
||||
static unsigned int _LZ_StringCompare( unsigned char * str1,
|
||||
unsigned char * str2, unsigned int minlen, unsigned int maxlen )
|
||||
{
|
||||
unsigned int len;
|
||||
|
||||
for( len = minlen; (len < maxlen) && (str1[len] == str2[len]); ++ len );
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* _LZ_WriteVarSize() - Write unsigned integer with variable number of
|
||||
* bytes depending on value.
|
||||
*************************************************************************/
|
||||
|
||||
static int _LZ_WriteVarSize( unsigned int x, unsigned char * buf )
|
||||
{
|
||||
unsigned int y;
|
||||
int num_bytes, i, b;
|
||||
|
||||
/* Determine number of bytes needed to store the number x */
|
||||
y = x >> 3;
|
||||
for( num_bytes = 5; num_bytes >= 2; -- num_bytes )
|
||||
{
|
||||
if( y & 0xfe000000 ) break;
|
||||
y <<= 7;
|
||||
}
|
||||
|
||||
/* Write all bytes, seven bits in each, with 8:th bit set for all */
|
||||
/* but the last byte. */
|
||||
for( i = num_bytes-1; i >= 0; -- i )
|
||||
{
|
||||
b = (x >> (i*7)) & 0x0000007f;
|
||||
if( i > 0 )
|
||||
{
|
||||
b |= 0x00000080;
|
||||
}
|
||||
*buf ++ = (unsigned char) b;
|
||||
}
|
||||
|
||||
/* Return number of bytes written */
|
||||
return num_bytes;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* _LZ_ReadVarSize() - Read unsigned integer with variable number of
|
||||
* bytes depending on value.
|
||||
|
@ -117,6 +179,141 @@ static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf )
|
|||
*************************************************************************/
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* LZ_Compress() - Compress a block of data using an LZ77 coder.
|
||||
* in - Input (uncompressed) buffer.
|
||||
* out - Output (compressed) buffer. This buffer must be 0.4% larger
|
||||
* than the input buffer, plus one byte.
|
||||
* insize - Number of input bytes.
|
||||
* The function returns the size of the compressed data.
|
||||
*************************************************************************/
|
||||
|
||||
int LZ_Compress( const unsigned char *in, unsigned char *out, unsigned int insize )
|
||||
{
|
||||
unsigned char marker, symbol;
|
||||
unsigned int inpos, outpos, bytesleft, i;
|
||||
unsigned int maxoffset, offset, bestoffset;
|
||||
unsigned int maxlength, length, bestlength;
|
||||
unsigned int histogram[ 256 ];
|
||||
unsigned char *ptr1, *ptr2;
|
||||
|
||||
/* Do we have anything to compress? */
|
||||
if( insize < 1 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create histogram */
|
||||
for( i = 0; i < 256; ++ i )
|
||||
{
|
||||
histogram[ i ] = 0;
|
||||
}
|
||||
for( i = 0; i < insize; ++ i )
|
||||
{
|
||||
++ histogram[ in[ i ] ];
|
||||
}
|
||||
|
||||
/* Find the least common byte, and use it as the marker symbol */
|
||||
marker = 0;
|
||||
for( i = 1; i < 256; ++ i )
|
||||
{
|
||||
if( histogram[ i ] < histogram[ marker ] )
|
||||
{
|
||||
marker = (unsigned char) i;
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember the marker symbol for the decoder */
|
||||
out[ 0 ] = marker;
|
||||
|
||||
/* Start of compression */
|
||||
inpos = 0;
|
||||
outpos = 1;
|
||||
|
||||
/* Main compression loop */
|
||||
bytesleft = insize;
|
||||
do
|
||||
{
|
||||
/* Determine most distant position */
|
||||
if( inpos > LZ_MAX_OFFSET ) maxoffset = LZ_MAX_OFFSET;
|
||||
else maxoffset = inpos;
|
||||
|
||||
/* Get pointer to current position */
|
||||
ptr1 = &in[ inpos ];
|
||||
|
||||
/* Search history window for maximum length string match */
|
||||
bestlength = 3;
|
||||
bestoffset = 0;
|
||||
for( offset = 3; offset <= maxoffset; ++ offset )
|
||||
{
|
||||
/* Get pointer to candidate string */
|
||||
ptr2 = &ptr1[ -(int)offset ];
|
||||
|
||||
/* Quickly determine if this is a candidate (for speed) */
|
||||
if( (ptr1[ 0 ] == ptr2[ 0 ]) &&
|
||||
(ptr1[ bestlength ] == ptr2[ bestlength ]) )
|
||||
{
|
||||
/* Determine maximum length for this offset */
|
||||
maxlength = (bytesleft < offset ? bytesleft : offset);
|
||||
|
||||
/* Count maximum length match at this offset */
|
||||
length = _LZ_StringCompare( ptr1, ptr2, 0, maxlength );
|
||||
|
||||
/* Better match than any previous match? */
|
||||
if( length > bestlength )
|
||||
{
|
||||
bestlength = length;
|
||||
bestoffset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Was there a good enough match? */
|
||||
if( (bestlength >= 8) ||
|
||||
((bestlength == 4) && (bestoffset <= 0x0000007f)) ||
|
||||
((bestlength == 5) && (bestoffset <= 0x00003fff)) ||
|
||||
((bestlength == 6) && (bestoffset <= 0x001fffff)) ||
|
||||
((bestlength == 7) && (bestoffset <= 0x0fffffff)) )
|
||||
{
|
||||
out[ outpos ++ ] = (unsigned char) marker;
|
||||
outpos += _LZ_WriteVarSize( bestlength, &out[ outpos ] );
|
||||
outpos += _LZ_WriteVarSize( bestoffset, &out[ outpos ] );
|
||||
inpos += bestlength;
|
||||
bytesleft -= bestlength;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Output single byte (or two bytes if marker byte) */
|
||||
symbol = in[ inpos ++ ];
|
||||
out[ outpos ++ ] = symbol;
|
||||
if( symbol == marker )
|
||||
{
|
||||
out[ outpos ++ ] = 0;
|
||||
}
|
||||
-- bytesleft;
|
||||
}
|
||||
}
|
||||
while( bytesleft > 3 );
|
||||
|
||||
/* Dump remaining bytes, if any */
|
||||
while( inpos < insize )
|
||||
{
|
||||
if( in[ inpos ] == marker )
|
||||
{
|
||||
out[ outpos ++ ] = marker;
|
||||
out[ outpos ++ ] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
out[ outpos ++ ] = in[ inpos ];
|
||||
}
|
||||
++ inpos;
|
||||
}
|
||||
|
||||
return outpos;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
* LZ_Uncompress() - Uncompress a block of data using an LZ77 decoder.
|
||||
* in - Input (compressed) buffer.
|
||||
|
@ -125,8 +322,7 @@ static int _LZ_ReadVarSize( unsigned int * x, const unsigned char * buf )
|
|||
* insize - Number of input bytes.
|
||||
*************************************************************************/
|
||||
|
||||
void LZ_Uncompress( const unsigned char *in, unsigned char *out,
|
||||
unsigned int insize )
|
||||
int LZ_Uncompress( const unsigned char *in, unsigned char *out, unsigned int insize )
|
||||
{
|
||||
unsigned char marker, symbol;
|
||||
unsigned int i, inpos, outpos, length, offset;
|
||||
|
@ -134,7 +330,7 @@ void LZ_Uncompress( const unsigned char *in, unsigned char *out,
|
|||
/* Do we have anything to uncompress? */
|
||||
if( insize < 1 )
|
||||
{
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get marker symbol from input stream */
|
||||
|
@ -176,4 +372,6 @@ void LZ_Uncompress( const unsigned char *in, unsigned char *out,
|
|||
}
|
||||
}
|
||||
while( inpos < insize );
|
||||
|
||||
return outpos;
|
||||
}
|
||||
|
|
|
@ -41,9 +41,8 @@ extern "C" {
|
|||
* Function prototypes
|
||||
*************************************************************************/
|
||||
|
||||
void LZ_Uncompress( const unsigned char *in, unsigned char *out,
|
||||
unsigned int insize );
|
||||
|
||||
int LZ_Compress(const unsigned char *in, unsigned char *out, unsigned int insize);
|
||||
int LZ_Uncompress(const unsigned char *in, unsigned char *out, unsigned int insize);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
|
|||
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
|
||||
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
|
||||
KIPFILES := loader.kip pm.kip sm.kip boot_100.kip boot_200.kip
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin $(KIPFILES)
|
||||
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) exosphere.bin thermosphere.bin splash_screen.bmp $(KIPFILES)
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# use CXX for linking C++ projects, CC for standard C
|
||||
|
@ -166,6 +166,11 @@ $(OFILES_SRC) : $(HFILES_BIN)
|
|||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
%.bmp.o %_bmp.h: %.bmp
|
||||
#---------------------------------------------------------------------------------
|
||||
@echo $(notdir $<)
|
||||
@$(bin2o)
|
||||
|
||||
%.kip.o %_kip.h: %.kip
|
||||
#---------------------------------------------------------------------------------
|
||||
|
|
|
@ -27,9 +27,10 @@
|
|||
#define EXOSPHERE_TARGET_FIRMWARE_300 3
|
||||
#define EXOSPHERE_TARGET_FIRMWARE_400 4
|
||||
#define EXOSPHERE_TARGET_FIRMWARE_500 5
|
||||
#define EXOSPHERE_TARGET_FIRMWARE_600 6
|
||||
|
||||
#define EXOSPHERE_TARGET_FIRMWARE_MIN EXOSPHERE_TARGET_FIRMWARE_100
|
||||
#define EXOSPHERE_TARGET_FIRMWARE_MAX EXOSPHERE_TARGET_FIRMWARE_500
|
||||
#define EXOSPHERE_TARGET_FIRMWARE_MAX EXOSPHERE_TARGET_FIRMWARE_600
|
||||
|
||||
typedef struct {
|
||||
unsigned int magic;
|
||||
|
|
|
@ -75,6 +75,8 @@ static uint32_t nxboot_get_target_firmware(const void *package1loader) {
|
|||
return EXOSPHERE_TARGET_FIRMWARE_400;
|
||||
case 0x0B: /* 5.0.0 - 5.1.0 */
|
||||
return EXOSPHERE_TARGET_FIRMWARE_500;
|
||||
case 0x0E: /* 6.0.0 */
|
||||
return EXOSPHERE_TARGET_FIRMWARE_600;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
@ -158,6 +160,8 @@ static void nxboot_set_bootreason() {
|
|||
static void nxboot_move_bootconfig() {
|
||||
FILE *bcfile;
|
||||
void *bootconfig;
|
||||
uint32_t bootconfig_addr;
|
||||
uint32_t bootconfig_size;
|
||||
|
||||
/* Allocate memory for reading BootConfig. */
|
||||
bootconfig = memalign(0x1000, 0x4000);
|
||||
|
@ -175,9 +179,13 @@ static void nxboot_move_bootconfig() {
|
|||
}
|
||||
fclose(bcfile);
|
||||
|
||||
/* Copy the first 0x3000 bytes into IRAM. */
|
||||
memset((void *)0x4003D000, 0, 0x3000);
|
||||
memcpy((void *)0x4003D000, bootconfig, 0x3000);
|
||||
/* Select the actual BootConfig size and destination address. */
|
||||
bootconfig_addr = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_600) ? 0x4003D000 : 0x4003F800;
|
||||
bootconfig_size = (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) ? 0x3000 : 0x1000;
|
||||
|
||||
/* Copy the BootConfig into IRAM. */
|
||||
memset((void *)bootconfig_addr, 0, bootconfig_size);
|
||||
memcpy((void *)bootconfig_addr, bootconfig, bootconfig_size);
|
||||
|
||||
/* Clean up. */
|
||||
free(bootconfig);
|
||||
|
@ -329,8 +337,10 @@ uint32_t nxboot_main(void) {
|
|||
/* Select the right address for the warmboot firmware. */
|
||||
if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_400) {
|
||||
warmboot_memaddr = (void *)0x8000D000;
|
||||
} else {
|
||||
} else if (MAILBOX_EXOSPHERE_CONFIGURATION->target_firmware < EXOSPHERE_TARGET_FIRMWARE_600) {
|
||||
warmboot_memaddr = (void *)0x4003B000;
|
||||
} else {
|
||||
warmboot_memaddr = (void *)0x4003D800;
|
||||
}
|
||||
|
||||
printf("[NXBOOT]: Copying warmboot firmware...\n");
|
||||
|
@ -399,7 +409,7 @@ uint32_t nxboot_main(void) {
|
|||
printf("[NXBOOT]: Powering on the CCPLEX...\n");
|
||||
|
||||
/* Display splash screen. */
|
||||
display_splash_screen_bmp(loader_ctx->custom_splash_path);
|
||||
display_splash_screen_bmp(loader_ctx->custom_splash_path, (void *)0xC0000000);
|
||||
|
||||
/* Unmount everything. */
|
||||
nxfs_unmount_all();
|
||||
|
|
BIN
fusee/fusee-secondary/src/splash_screen.bmp
Normal file
BIN
fusee/fusee-secondary/src/splash_screen.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.5 MiB |
|
@ -15,20 +15,75 @@
|
|||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "di.h"
|
||||
#include "timers.h"
|
||||
#include "splash_screen.h"
|
||||
#include "fs_utils.h"
|
||||
#include "display/video_fb.h"
|
||||
|
||||
void display_splash_screen_bmp(const char *custom_splash_path) {
|
||||
uint8_t *splash_screen = g_default_splash_screen;
|
||||
#define u8 uint8_t
|
||||
#define u32 uint32_t
|
||||
#include "splash_screen_bmp.h"
|
||||
#undef u8
|
||||
#undef u32
|
||||
|
||||
static void render_bmp(const uint32_t *bmp_data, uint32_t *framebuffer, uint32_t bmp_width, uint32_t bmp_height, uint32_t bmp_pos_x, uint32_t bmp_pos_y) {
|
||||
/* Render the BMP. */
|
||||
for (uint32_t y = bmp_pos_y; y < (bmp_pos_y + bmp_height); y++) {
|
||||
for (uint32_t x = bmp_pos_x; x < (bmp_pos_x + bmp_width); x++) {
|
||||
framebuffer[x + (y * SPLASH_SCREEN_STRIDE)] = bmp_data[(bmp_height + bmp_pos_y - 1 - y) * bmp_width + x - bmp_pos_x];
|
||||
}
|
||||
}
|
||||
|
||||
/* Re-initialize the frame buffer. */
|
||||
display_init_framebuffer(framebuffer);
|
||||
}
|
||||
|
||||
void display_splash_screen_bmp(const char *custom_splash_path, void *fb_address) {
|
||||
uint8_t *splash_screen = (uint8_t *)splash_screen_bmp;
|
||||
|
||||
/* Try to load an external custom splash screen. */
|
||||
if ((custom_splash_path != NULL) && (custom_splash_path[0] != '\x00')) {
|
||||
if (!read_from_file(splash_screen, sizeof(g_default_splash_screen), custom_splash_path)) {
|
||||
if (!read_from_file(splash_screen, sizeof(&splash_screen_bmp), custom_splash_path)) {
|
||||
fatal_error("Failed to read custom splash screen from %s!\n", custom_splash_path);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Display the splash screen. It should be a pointer to a BMP, at this point. */
|
||||
|
||||
/* Check for 'BM' magic. */
|
||||
if ((splash_screen[0] == 'B') && (splash_screen[1] == 'M')) {
|
||||
/* Extract BMP parameters. */
|
||||
uint32_t bmp_size = (splash_screen[0x02] | (splash_screen[0x03] << 8) | (splash_screen[0x04] << 16) | (splash_screen[0x05] << 24));
|
||||
uint32_t bmp_offset = (splash_screen[0x0A] | (splash_screen[0x0B] << 8) | (splash_screen[0x0C] << 16) | (splash_screen[0x0D] << 24));
|
||||
uint32_t bmp_width = (splash_screen[0x12] | (splash_screen[0x13] << 8) | (splash_screen[0x14] << 16) | (splash_screen[0x15] << 24));
|
||||
uint32_t bmp_height = (splash_screen[0x16] | (splash_screen[0x17] << 8) | (splash_screen[0x18] << 16) | (splash_screen[0x19] << 24));
|
||||
uint16_t bmp_bpp = (splash_screen[0x1C] | (splash_screen[0x1D] << 8));
|
||||
uint32_t bmp_data_size = (splash_screen[0x22] | (splash_screen[0x23] << 8) | (splash_screen[0x24] << 16) | (splash_screen[0x25] << 24));
|
||||
|
||||
/* Data size can be wrong or set to 0. In that case, we calculate it instead. */
|
||||
if (!bmp_data_size || (bmp_data_size >= bmp_size))
|
||||
bmp_data_size = (bmp_size - bmp_offset);
|
||||
|
||||
/* Only accept images up to 720x1280 resolution and with 32 BPP. */
|
||||
if ((bmp_width > SPLASH_SCREEN_WIDTH_MAX) || (bmp_height > SPLASH_SCREEN_HEIGHT_MAX)) {
|
||||
fatal_error("Invalid splash screen dimensions!\n");
|
||||
} else if (bmp_bpp != SPLASH_SCREEN_BPP) {
|
||||
fatal_error("Invalid splash screen color depth!\n");
|
||||
} else if (bmp_data_size > SPLASH_SCREEN_SIZE_MAX) {
|
||||
fatal_error("Splash screen data size is too big!\n");
|
||||
}
|
||||
|
||||
/* Calculate screen positions. */
|
||||
uint32_t bmp_pos_x = ((SPLASH_SCREEN_WIDTH_MAX - bmp_width) / 2);
|
||||
uint32_t bmp_pos_y = ((SPLASH_SCREEN_HEIGHT_MAX - bmp_height) / 2);
|
||||
|
||||
/* Advance to data. */
|
||||
splash_screen += bmp_offset;
|
||||
|
||||
/* Render the BMP. */
|
||||
render_bmp((uint32_t *)splash_screen, (uint32_t *)fb_address, bmp_width, bmp_height, bmp_pos_x, bmp_pos_y);
|
||||
} else {
|
||||
fatal_error("Invalid splash screen format!\n");
|
||||
}
|
||||
|
||||
/* Display the splash screen for three seconds. */
|
||||
udelay(3000000);
|
||||
|
|
|
@ -19,9 +19,12 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
/* TODO: Actually make this a real thing. */
|
||||
extern unsigned char g_default_splash_screen[1];
|
||||
#define SPLASH_SCREEN_WIDTH_MAX 720
|
||||
#define SPLASH_SCREEN_HEIGHT_MAX 1280
|
||||
#define SPLASH_SCREEN_BPP 32
|
||||
#define SPLASH_SCREEN_STRIDE 768
|
||||
#define SPLASH_SCREEN_SIZE_MAX (SPLASH_SCREEN_HEIGHT_MAX * SPLASH_SCREEN_STRIDE * 4)
|
||||
|
||||
void display_splash_screen_bmp(const char *custom_splash_path);
|
||||
void display_splash_screen_bmp(const char *custom_splash_path, void *fb_address);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018 Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "splash_screen.h"
|
||||
|
||||
uint8_t g_default_splash_screen[1] = {0};
|
Loading…
Reference in a new issue