<commit_before>#include <sys/syslimits.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <3ds.h>
#include <3ds/services/am.h>

#include "../../list.h"
#include "../../error.h"
#include "../../../util.h"
#include "../../../screen.h"
#include "task.h"

typedef struct {
    list_item* items;
    u32* count;
    u32 max;

    Handle cancelEvent;
} populate_titles_data;

static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) {
    Result res = 0;

    AM_TitleEntry entry;
    if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, 1, &titleId, &entry))) {
        title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
        if(titleInfo != NULL) {
            titleInfo->mediaType = mediaType;
            titleInfo->titleId = titleId;
            AM_GetTitleProductCode(mediaType, titleId, titleInfo->productCode);
            titleInfo->version = entry.version;
            titleInfo->installedSize = entry.size;
            titleInfo->twl = false;
            titleInfo->hasMeta = false;

            list_item* item = &data->items[*data->count];

            static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
            static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData};
            u32 archivePath[] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), mediaType, 0x00000000};
            FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}};

            Handle fileHandle;
            if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) {
                SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
                if(smdh != NULL) {
                    u32 bytesRead;
                    if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) {
                        if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') {
                            titleInfo->hasMeta = true;

                            u8 systemLanguage = CFG_LANGUAGE_EN;
                            CFGU_GetSystemLanguage(&systemLanguage);

                            utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, NAME_MAX - 1);

                            utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription) - 1);
                            utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription) - 1);
                            utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher) - 1);
                            titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
                        }
                    }

                    free(smdh);
                }

                FSFILE_Close(fileHandle);
            }

            bool empty = strlen(item->name) == 0;
            if(!empty) {
                empty = true;

                char* curr = item->name;
                while(*curr) {
                    if(*curr != ' ') {
                        empty = false;
                        break;
                    }

                    curr++;
                }
            }

            if(empty) {
                snprintf(item->name, NAME_MAX, "%016llX", titleId);
            }

            if(mediaType == MEDIATYPE_NAND) {
                item->rgba = COLOR_NAND;
            } else if(mediaType == MEDIATYPE_SD) {
                item->rgba = COLOR_SD;
            } else if(mediaType == MEDIATYPE_GAME_CARD) {
                item->rgba = COLOR_GAME_CARD;
            }

            item->data = titleInfo;

            (*data->count)++;
        } else {
            res = R_FBI_OUT_OF_MEMORY;
        }
    }

    return res;
}

static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) {
    Result res = 0;

    u64 realTitleId = 0;
    char productCode[12] = {'\0'};
    u16 version = 0;
    u64 installedSize = 0;

    AM_TitleEntry entry;
    if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, 1, &titleId, &entry))) {
        realTitleId = titleId;
        AM_GetTitleProductCode(mediaType, titleId, productCode);
        version = entry.version;
        installedSize = entry.size;
    } else {
        u8* header = (u8*) calloc(1, 0x3B4);
        if(header != NULL) {
            if(R_SUCCEEDED(res = FSUSER_GetLegacyRomHeader(mediaType, titleId, header))) {
                realTitleId = titleId != 0 ? titleId : *(u64*) &header[0x230];
                memcpy(productCode, header, 0x00C);
                version = header[0x01E];
                installedSize = (header[0x012] & 0x2) != 0 ? *(u32*) &header[0x210] : *(u32*) &header[0x080];
            }

            free(header);
        } else {
            res = R_FBI_OUT_OF_MEMORY;
        }
    }

    if(R_SUCCEEDED(res)) {
        title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
        if(titleInfo != NULL) {
            titleInfo->mediaType = mediaType;
            titleInfo->titleId = realTitleId;
            strncpy(titleInfo->productCode, productCode, 12);
            titleInfo->version = version;
            titleInfo->installedSize = installedSize;
            titleInfo->twl = true;
            titleInfo->hasMeta = false;

            list_item* item = &data->items[*data->count];

            BNR* bnr = (BNR*) calloc(1, sizeof(BNR));
            if(bnr != NULL) {
                if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) bnr))) {
                    titleInfo->hasMeta = true;

                    u8 systemLanguage = CFG_LANGUAGE_EN;
                    CFGU_GetSystemLanguage(&systemLanguage);

                    char title[0x100] = {'\0'};
                    utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], sizeof(title) - 1);

                    if(strchr(title, '\n') == NULL) {
                        size_t len = strlen(title);
                        strncpy(item->name, title, len);
                        strncpy(titleInfo->meta.shortDescription, title, len);
                    } else {
                        char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher};
                        int currDest = 0;

                        char* last = title;
                        char* curr = NULL;

                        while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
                            if(currDest == 0) {
                                strncpy(item->name, last, curr - last);
                            }

                            strncpy(destinations[currDest++], last, curr - last);
                            last = curr + 1;
                        }

                        strncpy(item->name, title, last - title);
                        if(currDest < 3) {
                            strncpy(destinations[currDest], last, strlen(title) - (last - title));
                        }
                    }

                    u8 icon[32 * 32 * 2];
                    for(u32 x = 0; x < 32; x++) {
                        for(u32 y = 0; y < 32; y++) {
                            u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1);
                            u32 srcShift = (x & 1) * 4;
                            u16 srcPx = bnr->mainIconPalette[(bnr->mainIconBitmap[srcPos] >> srcShift) & 0xF];

                            u8 r = (u8) (srcPx & 0x1F);
                            u8 g = (u8) ((srcPx >> 5) & 0x1F);<commit_msg>Minor fix to TWL title name listing.<commit_after>#include <sys/syslimits.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <3ds.h>
#include <3ds/services/am.h>

#include "../../list.h"
#include "../../error.h"
#include "../../../util.h"
#include "../../../screen.h"
#include "task.h"

typedef struct {
    list_item* items;
    u32* count;
    u32 max;

    Handle cancelEvent;
} populate_titles_data;

static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) {
    Result res = 0;

    AM_TitleEntry entry;
    if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, 1, &titleId, &entry))) {
        title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
        if(titleInfo != NULL) {
            titleInfo->mediaType = mediaType;
            titleInfo->titleId = titleId;
            AM_GetTitleProductCode(mediaType, titleId, titleInfo->productCode);
            titleInfo->version = entry.version;
            titleInfo->installedSize = entry.size;
            titleInfo->twl = false;
            titleInfo->hasMeta = false;

            list_item* item = &data->items[*data->count];

            static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
            static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData};
            u32 archivePath[] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), mediaType, 0x00000000};
            FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}};

            Handle fileHandle;
            if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) {
                SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
                if(smdh != NULL) {
                    u32 bytesRead;
                    if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) {
                        if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') {
                            titleInfo->hasMeta = true;

                            u8 systemLanguage = CFG_LANGUAGE_EN;
                            CFGU_GetSystemLanguage(&systemLanguage);

                            utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, NAME_MAX - 1);

                            utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription) - 1);
                            utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription) - 1);
                            utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher) - 1);
                            titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
                        }
                    }

                    free(smdh);
                }

                FSFILE_Close(fileHandle);
            }

            bool empty = strlen(item->name) == 0;
            if(!empty) {
                empty = true;

                char* curr = item->name;
                while(*curr) {
                    if(*curr != ' ') {
                        empty = false;
                        break;
                    }

                    curr++;
                }
            }

            if(empty) {
                snprintf(item->name, NAME_MAX, "%016llX", titleId);
            }

            if(mediaType == MEDIATYPE_NAND) {
                item->rgba = COLOR_NAND;
            } else if(mediaType == MEDIATYPE_SD) {
                item->rgba = COLOR_SD;
            } else if(mediaType == MEDIATYPE_GAME_CARD) {
                item->rgba = COLOR_GAME_CARD;
            }

            item->data = titleInfo;

            (*data->count)++;
        } else {
            res = R_FBI_OUT_OF_MEMORY;
        }
    }

    return res;
}

static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) {
    Result res = 0;

    u64 realTitleId = 0;
    char productCode[12] = {'\0'};
    u16 version = 0;
    u64 installedSize = 0;

    AM_TitleEntry entry;
    if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, 1, &titleId, &entry))) {
        realTitleId = titleId;
        AM_GetTitleProductCode(mediaType, titleId, productCode);
        version = entry.version;
        installedSize = entry.size;
    } else {
        u8* header = (u8*) calloc(1, 0x3B4);
        if(header != NULL) {
            if(R_SUCCEEDED(res = FSUSER_GetLegacyRomHeader(mediaType, titleId, header))) {
                realTitleId = titleId != 0 ? titleId : *(u64*) &header[0x230];
                memcpy(productCode, header, 0x00C);
                version = header[0x01E];
                installedSize = (header[0x012] & 0x2) != 0 ? *(u32*) &header[0x210] : *(u32*) &header[0x080];
            }

            free(header);
        } else {
            res = R_FBI_OUT_OF_MEMORY;
        }
    }

    if(R_SUCCEEDED(res)) {
        title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info));
        if(titleInfo != NULL) {
            titleInfo->mediaType = mediaType;
            titleInfo->titleId = realTitleId;
            strncpy(titleInfo->productCode, productCode, 12);
            titleInfo->version = version;
            titleInfo->installedSize = installedSize;
            titleInfo->twl = true;
            titleInfo->hasMeta = false;

            list_item* item = &data->items[*data->count];

            BNR* bnr = (BNR*) calloc(1, sizeof(BNR));
            if(bnr != NULL) {
                if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) bnr))) {
                    titleInfo->hasMeta = true;

                    u8 systemLanguage = CFG_LANGUAGE_EN;
                    CFGU_GetSystemLanguage(&systemLanguage);

                    char title[0x100] = {'\0'};
                    utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], sizeof(title) - 1);

                    if(strchr(title, '\n') == NULL) {
                        size_t len = strlen(title);
                        strncpy(item->name, title, len);
                        strncpy(titleInfo->meta.shortDescription, title, len);
                    } else {
                        char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher};
                        int currDest = 0;

                        char* last = title;
                        char* curr = NULL;

                        while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
                            strncpy(destinations[currDest++], last, curr - last);
                            last = curr + 1;
                            *curr = ' ';
                        }

                        strncpy(item->name, title, last - title);
                        if(currDest < 3) {
                            strncpy(destinations[currDest], last, strlen(title) - (last - title));
                        }
                    }

                    u8 icon[32 * 32 * 2];
                    for(u32 x = 0; x < 32; x++) {
                        for(u32 y = 0; y < 32; y++) {
                            u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1);
                            u32 srcShift = (x & 1) * 4;
                            u16 srcPx = bnr->mainIconPalette[(bnr->mainIconBitmap[srcPos] >> srcShift) & 0xF];

                            u8 r = (u8) (srcPx & 0x1F);
                            u8 g = (u8) ((srcPx >> 5) & 0x1F);
{'fraction_non_alphanumeric': 0.12816505157861832, 'fraction_numerical': 0.030071897467958738, 'mean_word_length': 1.6391684540504867, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 152, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 14, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4608059', 'n_tokens_mistral': 5307, 'n_tokens_neox': 4790, 'n_words': 914}