diff --git a/include/revolution/NWC24/NWC24MBoxCtrl.h b/include/revolution/NWC24/NWC24MBoxCtrl.h index f226a154..3c011147 100644 --- a/include/revolution/NWC24/NWC24MBoxCtrl.h +++ b/include/revolution/NWC24/NWC24MBoxCtrl.h @@ -9,6 +9,61 @@ extern "C" { NWC24Err NWC24iOpenMBox(void); +typedef struct MountInfoStruct { + u32 count; // 0x00 + s32 type; // 0x04 +} MountInfoStruct; + +typedef struct MBoxControlHeader { + u32 magic; // 0x00 + u32 version; // 0x04 + u32 msgCount; // 0x08 + u32 capacity; // 0x0C + u32 totalMsgSize; // 0x10 + u32 mailDataOffset; // 0x14 + u32 nextMsgId; // 0x18 + u32 nextFreeEntry; // 0x1C + u32 oldestMsgId; // 0x20 + u32 freeSpace; // 0x24 + char padding[0x58]; // to 0x80 +} MBoxControlHeader; + +typedef struct MBoxControlEntry { + u32 id; // 0x00 + u32 flags; // 0x04 + u32 length; // 0x08 + u32 appId; // 0x0C + u32 UNK_0x10; // 0x10 + u32 tag; // 0x14 + u32 ledPattern; // 0x18 + u32 nextFreeOrCreationMs; // 0x1C + u64 fromId; // 0x20 + u32 createTime; // 0x28 + u32 UNK_0x2C; // 0x2C + u8 numTo; // 0x30 + u8 numAttached; // 0x31 + u16 groupId; // 0x32 + u32 packedSubjectText; // 0x34 + u32 packedTextSubjectSize; // 0x38 + u32 packedSubjectTextSize; // 0x3C + u32 packedTextSizeContentType; // 0x40 + u32 packedContentTypeTransferEnc; // 0x44 + u32 textPtr; // 0x48 + u32 textSize; // 0x4C + u32 attached0Ptr; // 0x50 + u32 attached0Size; // 0x54 + u32 attached1Ptr; // 0x58 + u32 attached1Size; // 0x5C + u32 attached0OrigSize; // 0x60 + u32 attached1OrigSize; // 0x64 + u32 attached0_type; // 0x68 + u32 attached1_type; // 0x6C + u32 textOrigSize; // 0x70 + u32 UNK_0x74; // 0x74 + u32 UNK_0x78; // 0x78 + u32 UNK_0x7C; // 0x7C +} MBoxControlEntry; + #ifdef __cplusplus } #endif diff --git a/include/revolution/NWC24/NWC24Manage.h b/include/revolution/NWC24/NWC24Manage.h index 9a7b3b5e..977b6ad1 100644 --- a/include/revolution/NWC24/NWC24Manage.h +++ b/include/revolution/NWC24/NWC24Manage.h @@ -26,7 +26,7 @@ typedef struct NWC24Work { char WORK_0x1180[128]; char WORK_0x1200[128]; char WORK_0x1280[128]; - u8 base64Work[256]; // at 0x1300 + s8 base64Work[256]; // at 0x1300 char WORK_0x1400[0x2400 - 0x1400]; u8 flHeader[WORK_SIZE(NWC24FLHeader)]; // at 0x2800 u8 secretFlHeader[WORK_SIZE(NWC24SecretFLHeader)]; // at 0x2800 diff --git a/include/revolution/NWC24/NWC24Mime.h b/include/revolution/NWC24/NWC24Mime.h index dbbc3bd6..ec581e2e 100644 --- a/include/revolution/NWC24/NWC24Mime.h +++ b/include/revolution/NWC24/NWC24Mime.h @@ -5,7 +5,7 @@ extern "C" { #endif -void NWC24InitBase64Table(u8* table); +void NWC24InitBase64Table(s8* table); #ifdef __cplusplus } diff --git a/include/revolution/NWC24/NWC24MsgCommit.h b/include/revolution/NWC24/NWC24MsgCommit.h index 200908d9..61076619 100644 --- a/include/revolution/NWC24/NWC24MsgCommit.h +++ b/include/revolution/NWC24/NWC24MsgCommit.h @@ -3,15 +3,23 @@ #include #include +#include #ifdef __cplusplus extern "C" { #endif +#define NWC24_WORK_BUFFER_SIZE 1024 + // Forward declarations typedef struct NWC24MsgObj NWC24MsgObj; NWC24Err NWC24CommitMsg(NWC24MsgObj*); +typedef union { + u64 id; + NWC24Data data; +} NWC24AddrId; + #ifdef __cplusplus } #endif diff --git a/include/revolution/NWC24/NWC24MsgObj.h b/include/revolution/NWC24/NWC24MsgObj.h index a49cfd96..6d3fc115 100644 --- a/include/revolution/NWC24/NWC24MsgObj.h +++ b/include/revolution/NWC24/NWC24MsgObj.h @@ -33,18 +33,18 @@ typedef struct NWC24MsgObj { u32 flags; // at 0x4 u32 length; // at 0x8 u32 appId; // at 0xC - char UNK_0x10[0x4]; + s32 UNK_0x10; u32 tag; // at 0x14 u32 ledPattern; // at 0x18 u64 fromId; // at 0x20 - u32 WORD_0x28; + u32 createTime; // at 0x28 u32 WORD_0x2C; NWC24Data DATA_0x30; NWC24Data DATA_0x38; NWC24Data subject; // at 0x40 NWC24Data text; // at 0x48 - NWC24Data DATA_0x50; - NWC24Data DATA_0x58; + NWC24Data contentType; // at 0x50 + NWC24Data transferEncoding; // at 0x58 NWC24Charset charset; // at 0x60 NWC24Encoding encoding; // at 0x64 NWC24Data attached[NWC24_MSG_ATTACHMENT_MAX]; // at 0x68 @@ -70,7 +70,8 @@ typedef struct NWC24MsgObj { NWC24Data DATA_0xD0; NWC24Data face; // at 0xD8 NWC24Data alt; // at 0xE0 - char UNK_0xE8[0x100 - 0xE8]; + NWC24Data altMeta; + char UNK_0xF0[0x100 - 0xF0]; } NWC24MsgObj; NWC24Err NWC24InitMsgObj(NWC24MsgObj* msg, NWC24MsgType type); diff --git a/include/revolution/NWC24/NWC24Utils.h b/include/revolution/NWC24/NWC24Utils.h index bfaad363..2f9781c6 100644 --- a/include/revolution/NWC24/NWC24Utils.h +++ b/include/revolution/NWC24/NWC24Utils.h @@ -20,6 +20,18 @@ typedef struct NWC24Date { u8 BYTE_0x7; } NWC24Date; +typedef struct NWC24Calendar { + /* 0x00 */ u32 sec; + /* 0x04 */ u32 min; + /* 0x08 */ u32 hour; + /* 0x0C */ u32 day; + /* 0x10 */ u32 month; + /* 0x14 */ u32 year; + /* 0x18 */ u32 UNK_0x18; + /* 0x1C */ u32 UNK_0x1C; + /* 0x20 */ u32 UNK_0x20; +} NWC24Calendar; + void NWC24Data_Init(NWC24Data* data); void NWC24Data_SetDataP(NWC24Data* data, const void* ptr, u32 size); void NWC24Date_Init(NWC24Date* date); diff --git a/src/revolution/NWC24/NWC24DateParser.c b/src/revolution/NWC24/NWC24DateParser.c new file mode 100644 index 00000000..ae81f8c6 --- /dev/null +++ b/src/revolution/NWC24/NWC24DateParser.c @@ -0,0 +1,193 @@ +#include + +const u8 DAYS_OF_MONTH[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 0, 0, 0, 0}; +const u16 DAYS_OF_YEAR[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; + +NWC24Err NWC24iDateToMinutes(s32 *outMinutes, const NWC24Date *date); +NWC24Err NWC24iEpochSecondsToDate(NWC24Date *date, s64 timestamp); +NWC24Err NWC24iDateToOSCalendarTime(OSCalendarTime *time, const NWC24Date *date); +NWC24Err NWC24iIsValidDate(u16 year, u8 month, u8 day); +static BOOL IsLeapYear(u16 year); +s32 ConvertDateToDays(u16 year, u16 month, u16 day); +void ConvertDaysToDate(u16 *year, u8 *month, u8 *day, s32 days); +static s32 ConvertTimeToMinutes(u8 hour, u8 sec); +static s32 ConvertDaysToMinutes(s32 days); +static s32 ConvertMinutesToDays(s32 minutes); + +NWC24Err NWC24iDateToMinutes(s32 *outMinutes, const NWC24Date *date) { + s32 days; + s32 minutes; + + days = ConvertDateToDays(date->year, date->month, date->day); + + if (days == -1) { + return NWC24_ERR_FAILED; + } + + minutes = ConvertTimeToMinutes(date->hour, date->min); + if (minutes == -1 || date->sec > 60) { + return NWC24_ERR_FAILED; + } + + *outMinutes = ConvertDaysToMinutes(days) + minutes; + return NWC24_OK; +} + +NWC24Err NWC24iEpochSecondsToDate(NWC24Date *date, s64 timestamp) { + s64 adjusted = -0x7c558180U; + s32 minutes; + u32 days; + + if (0 > (timestamp + adjusted)) { + timestamp = -adjusted; + } + + timestamp += adjusted; + date->sec = (u8)(timestamp % 60); + minutes = timestamp / 60; + if (minutes < 0) { + minutes = 0; + } + + days = ConvertMinutesToDays(minutes); + minutes %= (24 * 60); + + date->hour = (u8)(minutes / 60); + date->min = (u8)(minutes % 60); + ConvertDaysToDate(&date->year, &date->month, &date->day, days); + return NWC24_OK; +} + +NWC24Err NWC24iDateToOSCalendarTime(OSCalendarTime *time, const NWC24Date *date) { + s32 daysSinceEpoch; + + time->year = date->year; + time->month = date->month - 1; + time->mday = date->day; + time->hour = date->hour; + time->min = date->min; + time->sec = date->sec; + time->msec = 0; + time->usec = 0; + time->yday = (DAYS_OF_YEAR[time->month] + date->day) - 1; + + if (IsLeapYear(date->year) && date->month > 2) { + time->yday++; + } + + daysSinceEpoch = ConvertDateToDays(date->year, date->month, date->day); + // Possibly NWC24iGetDayOfWeek(daysSinceEpoch)? + time->wday = (daysSinceEpoch + 1) % 7; + return NWC24_OK; +} + +NWC24Err NWC24iIsValidDate(u16 year, u8 month, u8 day) { + s32 days = ConvertDateToDays(year, month, day); + s32 result = NWC24_OK; + + if (days == -1) { + result = NWC24_ERR_INVALID_VALUE; + } + + return result; +} + +static BOOL IsLeapYear(u16 year) { + return (((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) != 0); +} + +s32 ConvertDateToDays(u16 year, u16 month, u16 day) { + s32 dayOfYear; + s32 yearsSince1900; + s32 days; + s32 leapDays; + s32 gregorianCorrection; + + if (year < 1900 || month < 1 || month > 12) { + return -1; + } + + if (month == 2 && IsLeapYear(year)) { + if (day < 1 || day > 29) { + return -1; + } + } else { + if (day < 1 || (u16)DAYS_OF_MONTH[month - 1] < day) { + return -1; + } + } + + dayOfYear = day - 1; + dayOfYear += DAYS_OF_YEAR[month - 1]; + + if (month >= 3 && IsLeapYear(year)) { + dayOfYear++; + } + + yearsSince1900 = year - 1900; + + leapDays = (yearsSince1900 - 1) / 4 - (yearsSince1900 - 1) / 100; + gregorianCorrection = (yearsSince1900 + 299) / 400; + + days = yearsSince1900 * 365 + dayOfYear; + days += leapDays + gregorianCorrection; + + return days; +} + +void ConvertDaysToDate(u16 *year, u8 *month, u8 *day, s32 days) { + s32 remaining; + + *year = 1900; + *month = 1; + *day = 1; + + if (days < 0) { + return; + } + + while (1) { + remaining = days; + days -= IsLeapYear(*year) ? 366 : 365; + + if (days < 0) { + days = remaining; + break; + } + + (*year)++; + } + + while (1) { + remaining = days; + + if (*month == 2 && IsLeapYear(*year)) { + days -= 29; + } else { + days -= DAYS_OF_MONTH[*month - 1]; + } + + if (days < 0) { + break; + } + + (*month)++; + } + + *day += remaining; +} + +static s32 ConvertTimeToMinutes(u8 hour, u8 sec) { + if (hour > 23 || sec > 59) { + return -1; + } + return (sec + (hour * 60)); +} + +static s32 ConvertDaysToMinutes(s32 days) { + return days * (24 * 60); +} + +static s32 ConvertMinutesToDays(s32 minutes) { + return minutes / (24 * 60); +} diff --git a/src/revolution/NWC24/NWC24MBoxCtrl.c b/src/revolution/NWC24/NWC24MBoxCtrl.c new file mode 100644 index 00000000..7790acc6 --- /dev/null +++ b/src/revolution/NWC24/NWC24MBoxCtrl.c @@ -0,0 +1,954 @@ +#include + +MountInfoStruct MountInfo; + +int Mail_sprintf(char* buffer, const char* format, ...); + +NWC24Err NWC24iOpenMBox(void); +NWC24Err NWC24iMBoxOpenNewMsg(s32 mboxType, NWC24File* file, u32* msgId); +NWC24Err NWC24iMBoxCloseMsg(NWC24File* file); +NWC24Err NWC24iMBoxCancelMsg(NWC24File* file, s32 mboxType, u32 msgId); +NWC24Err NWC24iMBoxAddMsgObj(s32 mboxType, NWC24MsgObj* msg); +NWC24Err NWC24iMBoxFlushHeader(s32 mboxType); +NWC24Err NWC24iMBoxCheck(s32 mboxType, u32 requiredSize); +static NWC24Err GetMBoxFilePath(char* buffer, s32 mboxType, u32 msgId); +static NWC24Err MakeMailPath(char* buffer, s32 mboxType); +static BOOL IsFileThere(const char* path); +NWC24Err DeleteMsg(s32 mboxType, u32 msgId, u32 flags); +NWC24Err DuplicationCheck(MBoxControlHeader* header, const NWC24MsgObj* msg, NWC24File* file, s32 mboxType); +NWC24Err GetCachedMBCHeader(s32 mboxType, MBoxControlHeader** headerOut); +static NWC24Err LoadMBCHeader(NWC24File* file, MBoxControlHeader* header); +static NWC24Err SaveMBCHeader(NWC24File* file, MBoxControlHeader* header); +NWC24Err AddMBCEntry(MBoxControlHeader* header, const NWC24MsgObj* msg, NWC24File* file); +static NWC24Err ClearMBCEntry(MBoxControlHeader* header, u32 offset, NWC24File* file); +static NWC24Err GetNewMsgId(MBoxControlHeader* header, u32* msgIdOut); +inline static s32 CompareMsgId(u32 id1, u32 id2); +NWC24Err MountVFMBox(s32 mboxType); +inline static NWC24Err UnmountVFMBox(void); +NWC24Err CopyMsgObjToMBCFmt(const NWC24MsgObj* src, MBoxControlEntry* dst); + +inline static NWC24Err UnmountVFMBox(void) { + s32 count; + u32 freeSize; + NWC24Err err; + NWC24Err unmountErr; + MBoxControlHeader* header; + + count = MountInfo.count; + if (count == 0) { + unmountErr = NWC24_OK; + } else { + count--; + MountInfo.count = count; + if (count > 0) { + unmountErr = NWC24_OK; + } else { + err = GetCachedMBCHeader(MountInfo.type, &header); + if (err != NWC24_OK) { + unmountErr = err; + } else { + err = NWC24CheckSizeVF("@24", &freeSize); + if (err != NWC24_OK) { + unmountErr = err; + } else { + header->freeSpace = freeSize; + unmountErr = NWC24UnmountVF("@24"); + } + } + } + } + return unmountErr; +} + +NWC24Err NWC24iOpenMBox(void) { + NWC24Err err; + MBoxControlHeader* header; + char* pathWork; + const char* mboxDir; + + pathWork = NWC24WorkP->pathWork; + + Mail_memset(NWC24WorkP->WORK_0x1100, 0, sizeof(MBoxControlHeader)); + err = GetCachedMBCHeader(0, &header); + if (err != NWC24_OK) { + return err; + } + + Mail_memset(NWC24WorkP->WORK_0x1180, 0, sizeof(MBoxControlHeader)); + err = GetCachedMBCHeader(1, &header); + if (err != NWC24_OK) { + return err; + } + + mboxDir = NWC24GetMBoxDir(); + if (STD_strnlen(mboxDir, 0x40) + 14 > 0x100) { + err = NWC24_ERR_NOMEM; + } else { + Mail_sprintf(pathWork, "%s%s", mboxDir, "/wc24recv.mbx"); + err = NWC24_OK; + } + + if (err != NWC24_OK) { + return err; + } + + err = IsFileThere(pathWork); + if (err != NWC24_OK) { + return err; + } + + mboxDir = NWC24GetMBoxDir(); + if (STD_strnlen(mboxDir, 0x40) + 14 > 0x100) { + err = NWC24_ERR_NOMEM; + } else { + Mail_sprintf(pathWork, "%s%s", mboxDir, "/wc24send.mbx"); + err = NWC24_OK; + } + + if (err != NWC24_OK) { + return err; + } + + err = IsFileThere(pathWork); + if (err != NWC24_OK) { + return err; + } + + MountInfo.count = 0; + MountInfo.type = 0; + return NWC24_OK; +} + +NWC24Err NWC24iMBoxOpenNewMsg(s32 mboxType, NWC24File *file, u32 *msgId) { + NWC24Err err; + MBoxControlHeader *header; + char *path; + + err = GetCachedMBCHeader(mboxType, &header); + if (err != NWC24_OK) { + return err; + } + + err = GetNewMsgId(header, msgId); + if (err != NWC24_OK) { + return err; + } + + err = MountVFMBox(mboxType); + if (err != NWC24_OK) { + return err; + } + + path = NWC24WorkP->pathWork; + + err = GetMBoxFilePath(path, mboxType, *msgId); + if (err != NWC24_OK) { + return err; + } + + err = NWC24FOpen(file, path, 0x109); + return err; +} + +NWC24Err NWC24iMBoxCloseMsg(NWC24File* file) { + NWC24Err err; + u32 freeSize; + MBoxControlHeader* header; + s32 count; + NWC24Err unmountErr; + NWC24Err closeErr; + + closeErr = NWC24FClose(file); + // unmountErr = UnmountVFMBox(); + // This code could be replaced with a call to UnmountVFMBox but that causes issues with the assembly matching. + + count = MountInfo.count; + if (count == 0) { + unmountErr = NWC24_OK; + } else { + count--; + MountInfo.count = count; + if (count > 0) { + unmountErr = NWC24_OK; + } else { + err = GetCachedMBCHeader(MountInfo.type, &header); + if (err != NWC24_OK) { + unmountErr = err; + } else { + err = NWC24CheckSizeVF("@24", &freeSize); + if (err != NWC24_OK) { + unmountErr = err; + } else { + header->freeSpace = freeSize; + unmountErr = NWC24UnmountVF("@24"); + } + } + } + } + + if (closeErr != NWC24_OK && unmountErr != NWC24_OK) { + return unmountErr; + } + return closeErr; +} + +NWC24Err NWC24iMBoxCancelMsg(NWC24File* file, s32 mboxType, u32 msgId) { + s32 result; + char* pathWork; + NWC24Err tempErr; + NWC24Err deleteErr; + NWC24Err closeErr; + NWC24Err unmountErr; + + closeErr = NWC24FClose(file); + unmountErr = MountVFMBox(mboxType); + + if (unmountErr != NWC24_OK) { + result = unmountErr; + } else { + pathWork = NWC24WorkP->pathWork; + deleteErr = GetMBoxFilePath(pathWork, mboxType, msgId); + // TODO(Alex9303) Permuter fake(?)match + if ((deleteErr) != NWC24_OK) { + result = deleteErr; + } else { + deleteErr = NWC24FDeleteVF(pathWork); + tempErr = deleteErr; + unmountErr = UnmountVFMBox(); + if (tempErr != NWC24_OK) { + result = tempErr; + } else { + result = unmountErr; + } + } + } + + MountInfo.count = 1; + + unmountErr = UnmountVFMBox(); + if (closeErr != NWC24_OK) { + return closeErr; + } + if (result != NWC24_OK) { + return result; + } + if (unmountErr != NWC24_OK) { + return unmountErr; + } + return NWC24_OK; +} + +NWC24Err NWC24iMBoxAddMsgObj(s32 mboxType, NWC24MsgObj* msg) { + NWC24Err err; + NWC24Err closeErr; + MBoxControlHeader* header; + MBoxControlHeader* header2; + char* pathWork; + NWC24File file; + + err = GetCachedMBCHeader(mboxType, &header); + if (err != NWC24_OK) { + return err; + } + + if (header->msgCount == header->capacity) { + return NWC24_ERR_FULL; + } + + pathWork = NWC24WorkP->pathWork; + err = MakeMailPath(pathWork, mboxType); + if (err != NWC24_OK) { + return err; + } + + err = NWC24FOpen(&file, pathWork, 4); + if (err != NWC24_OK) { + return err; + } + + err = AddMBCEntry(header, msg, &file); + if (err == NWC24_OK) { + err = DuplicationCheck(header, msg, &file, mboxType); + if (err == NWC24_OK) { + // TODO(Alex9303) Fakematch: (possibly) Missing inline function + header2 = header; + NWC24FSeek(&file, 0, NWC24_SEEK_BEG); + err = NWC24FWrite(header2, sizeof(MBoxControlHeader), &file); + } + } + + closeErr = NWC24FClose(&file); + if (err != NWC24_OK) { + return err; + } + + return closeErr; +} + +NWC24Err NWC24iMBoxFlushHeader(s32 mboxType) { + NWC24Err err; + NWC24Err closeErr; + MBoxControlHeader* header; + char* pathWork; + NWC24File file; + + err = GetCachedMBCHeader(mboxType, &header); + if (err != NWC24_OK) { + return err; + } + + pathWork = NWC24WorkP->pathWork; + err = MakeMailPath(pathWork, mboxType); + if (err != NWC24_OK) { + return err; + } + + err = NWC24FOpen(&file, pathWork, 4); + if (err != NWC24_OK) { + return err; + } + + err = SaveMBCHeader(&file, header); + closeErr = NWC24FClose(&file); + + if (err != NWC24_OK) { + return err; + } + + return closeErr; +} + +NWC24Err NWC24iMBoxCheck(s32 mboxType, u32 requiredSize) { + NWC24Err err; + MBoxControlHeader* header; + volatile long oldestMsgId; + u32 spaceWithBuffer; + + if (requiredSize >= 0x31C00) { + return NWC24_ERR_OVERFLOW; + } + + spaceWithBuffer = requiredSize + 0x4000; + + err = GetCachedMBCHeader(mboxType, &header); + if (err != NWC24_OK) { + return err; + } + + if (mboxType == 0) { + if (header->msgCount >= header->capacity) { + return NWC24_ERR_FULL; + } + if (header->freeSpace <= spaceWithBuffer) { + return NWC24_ERR_FULL; + } + } else if (mboxType == 1) { + while (header->msgCount >= header->capacity || header->freeSpace <= spaceWithBuffer) { + oldestMsgId = header->oldestMsgId; + err = DeleteMsg(mboxType, oldestMsgId, 0); + if (err != NWC24_OK) { + return err; + } + + err = GetCachedMBCHeader(mboxType, &header); + if (err != NWC24_OK) { + return err; + } + } + } else { + return NWC24_ERR_INVALID_VALUE; + } + return NWC24_OK; +} + +static NWC24Err GetMBoxFilePath(char* buffer, s32 mboxType, u32 msgId) { + const char* workDir; + NWC24Err err; + + switch (mboxType) { + case 0: + Mail_sprintf(buffer, "@24:/mb/s%07d.msg", msgId); + break; + case 1: + Mail_sprintf(buffer, "@24:/mb/r%07d.msg", msgId); + break; + default: + return NWC24_ERR_INVALID_VALUE; + } + return NWC24_OK; +} + +static NWC24Err MakeMailPath(char* buffer, s32 mboxType) { + const char* mboxDir = NWC24GetMBoxDir(); + s32 pathLen = STD_strnlen(mboxDir, 0x40); + + if (pathLen + 14 > 0x100) { + return NWC24_ERR_NOMEM; + } else { + switch (mboxType) { + case 0: + Mail_sprintf(buffer, "%s%s", mboxDir, "/wc24send.ctl"); + break; + case 1: + Mail_sprintf(buffer, "%s%s", mboxDir, "/wc24recv.ctl"); + break; + default: + return NWC24_ERR_INVALID_VALUE; + } + return NWC24_OK; + } +} + +static BOOL IsFileThere(const char* path) { + NWC24File file; + NWC24Err err; + + err = NWC24FOpen(&file, path, NWC24_SEEK_END); + if (err == NWC24_OK) { + return NWC24FClose(&file); + } + return err; +} + +NWC24Err DeleteMsg(s32 mboxType, u32 msgId, u32 flags) { + NWC24Err err; + const char* mboxDir; + MBoxControlHeader* header; + u32 entryOffsetToDelete = 0; + MBoxControlHeader* header3; + NWC24File file; + s32 pathLen; + u32 offset; + char* pathWorkCopy; + MBoxControlHeader* header2; + int cmp; + u32 checkedCount = 0; + u32 oldestMsgId = 0; + MBoxControlEntry* entry; + NWC24Err mountErr = NWC24_OK; + NWC24Err writeErr = NWC24_OK; + NWC24Err deleteErr = NWC24_OK; + char* pathWork; + + if (!NWC24IsMsgLibOpened() && !NWC24IsMsgLibOpenedByTool()) { + return NWC24_ERR_LIB_NOT_OPENED; + } + entry = (MBoxControlEntry*)((char*)NWC24WorkP + 0x1200); + + err = GetCachedMBCHeader(mboxType, &header); + if (err != NWC24_OK) { + return err; + } + + pathWorkCopy = NWC24WorkP->pathWork; + pathWork = pathWorkCopy; + + err = MakeMailPath(pathWork, mboxType); + if (err != NWC24_OK) { + return err; + } + + err = NWC24FOpen(&file, pathWork, 4); + if (err != NWC24_OK) { + return err; + } + + err = NWC24_ERR_NOT_FOUND; + + for (offset = sizeof(MBoxControlHeader); offset < header->mailDataOffset; offset += sizeof(MBoxControlHeader)) { + NWC24Err readErr; + NWC24FSeek(&file, offset, NWC24_SEEK_BEG); + readErr = NWC24FRead(entry, sizeof(MBoxControlHeader), &file); + if (readErr != NWC24_OK) { + err = readErr; + break; + } + + if (entry->id == 0) { + continue; + } + + entryOffsetToDelete++; + + if (entry->id == msgId) { + if (flags != 0) { + if ((entry->flags & 0x2) != 0) { + if (NWC24GetAppId() - 0x48410000 != 0x4541) { + err = NWC24_ERR_PROTECTED; + break; + } + } else { + if ((entry->appId & 0xFFFFFF00) != (NWC24GetAppId() & 0xFFFFFF00)) { + if ((entry->flags & 0x8) != 0) { + if (NWC24GetAppId() - 0x48410000 == 0x4541) { + goto permission_ok; + } + } + err = NWC24_ERR_PROTECTED; + break; + } + } + } + permission_ok: + + checkedCount = offset; + err = NWC24_OK; + continue; + } + + if (oldestMsgId != 0) { + if (CompareMsgId(oldestMsgId, entry->id) <= 0) { + continue; + } + } + oldestMsgId = entry->id; + } + + if (err == NWC24_ERR_NOT_FOUND) { + if (header->msgCount != entryOffsetToDelete) { + header->msgCount = entryOffsetToDelete; + + // TODO(Alex9303) Fakematch: (possibly) Missing inline function + header3 = header; + NWC24FSeek(&file, 0, NWC24_SEEK_BEG); + NWC24FWrite(header3, sizeof(MBoxControlHeader), &file); + } + } + + if (err != NWC24_OK) { + NWC24FClose(&file); + + if (msgId == 0) { + err = NWC24_ERR_INVALID_VALUE; + } + + return err; + } + + header->oldestMsgId = oldestMsgId; + + writeErr = ClearMBCEntry(header, checkedCount, &file); + + mountErr = MountVFMBox(mboxType); + switch (mountErr) { + case NWC24_OK: + pathWork = NWC24WorkP->pathWork; + mountErr = GetMBoxFilePath(pathWork, mboxType, msgId); + switch (mountErr) { + case NWC24_OK: + deleteErr = NWC24FDeleteVF(pathWork); + mountErr = UnmountVFMBox(); + if (deleteErr != NWC24_OK) { + mountErr = deleteErr; + } + break; + } + break; + } + + // TODO(Alex9303) Fakematch: Reusing arguments msgId/mboxType as error variables to match r23/r22 + msgId = mountErr; + if (writeErr != NWC24_OK) { + msgId = writeErr; + } + + // TODO(Alex9303) Fakematch: (possibly) Missing inline function + header2 = header; + NWC24FSeek(&file, 0, NWC24_SEEK_BEG); + + mboxType = NWC24FWrite(header2, sizeof(MBoxControlHeader), &file); + if (msgId != NWC24_OK) { + mboxType = msgId; + } + + err = NWC24FClose(&file); + if (mboxType != NWC24_OK) { + err = mboxType; + } + + return err; +} + +NWC24Err DuplicationCheck(MBoxControlHeader* header, const NWC24MsgObj* msg, NWC24File* file, s32 mboxType) { + NWC24Err err; + u32 offset; + u32 checkedCount; + u32 bestMsgId; + u32 bestOffset; + u32 oldestMsgId; + MBoxControlEntry* entry; + char* pathWork; + char** pathWorkPtr; + + err = NWC24_OK; + checkedCount = 0; + bestMsgId = 0; + bestOffset = 0; + oldestMsgId = 0; + + pathWorkPtr = &pathWork; + + if (mboxType == 0) { + return NWC24_OK; + } + + entry = (MBoxControlEntry*)((char*)NWC24WorkP + 0x1200); + + for (offset = sizeof(MBoxControlHeader); offset < header->mailDataOffset; offset += sizeof(MBoxControlHeader)) { + int isDuplicate; + int cmp; + + if (checkedCount >= header->msgCount) { + break; + } + + NWC24FSeek(file, offset, NWC24_SEEK_BEG); + err = NWC24FRead(entry, sizeof(MBoxControlHeader), file); + if (err != NWC24_OK) { + break; + } + + if (entry->id == 0) { + continue; + } + + checkedCount++; + isDuplicate = 1; + + if (entry->id == msg->id) { + isDuplicate = 0; + } + + if ((entry->flags & msg->flags & 1) != 0) { + if (entry->appId != msg->appId) { + isDuplicate = 0; + } + if (entry->fromId != msg->fromId) { + isDuplicate = 0; + } + } + + if (isDuplicate) { + u32 msgTag = msg->tag & 0xFFFF; + u32 msgCreationMs; + + if (msgTag != 0) { + if ((entry->tag & 0xFFFF) == msgTag) { + bestMsgId = entry->id; + bestOffset = offset; + continue; + } + } + + msgCreationMs = ((const u32*)msg)[7]; + if (msgCreationMs != 0 && entry->nextFreeOrCreationMs == msgCreationMs && entry->flags == msg->flags && entry->length == msg->length && entry->UNK_0x10 == ((const u32*)msg)[4] && (s32)entry->createTime == (s32)((const u32*)msg)[10]) { + bestMsgId = entry->id; + bestOffset = offset; + continue; + } + } + + if (oldestMsgId != 0) { + if (CompareMsgId(oldestMsgId, entry->id) <= 0) { + continue; + } + } + oldestMsgId = entry->id; + } + + if (err != NWC24_OK) { + return err; + } + + header->oldestMsgId = oldestMsgId; + + if (bestMsgId != 0) { + NWC24Err writeErr; + NWC24Err result; + + entry = (MBoxControlEntry*)(((char*)NWC24WorkP) + 0x1200); + pathWork = NWC24WorkP->WORK_0x1200; + + err = NWC24FSeek(file, bestOffset, NWC24_SEEK_BEG); + err = NWC24FRead(*pathWorkPtr, sizeof(MBoxControlHeader), file); + + header->msgCount--; + header->totalMsgSize -= entry->length; + + memset(entry, 0, sizeof(MBoxControlHeader)); + entry->appId = header->nextFreeEntry; + header->nextFreeEntry = bestOffset; + + err = NWC24FSeek(file, bestOffset, NWC24_SEEK_BEG); + writeErr = NWC24FWrite(entry, sizeof(MBoxControlHeader), file); + + err = MountVFMBox(mboxType); + if (err != NWC24_OK) { + result = err; + } else { + pathWork = NWC24WorkP->pathWork; + err = GetMBoxFilePath(pathWork, mboxType, bestMsgId); + if (err != NWC24_OK) { + result = err; + } else { + NWC24Err deleteErr = NWC24FDeleteVF(pathWork); + + result = UnmountVFMBox(); + if (deleteErr != NWC24_OK) { + result = deleteErr; + } + } + } + + if (writeErr != NWC24_OK) { + result = writeErr; + } + + return result; + } + + return err; +} + +NWC24Err GetCachedMBCHeader(s32 mboxType, MBoxControlHeader** headerOut) { + NWC24Err err; + const char* mboxDir; + NWC24File file; + NWC24Err result = NWC24_OK; + char* pathWork; + + if (mboxType == 0) { + *headerOut = (MBoxControlHeader*)NWC24WorkP->WORK_0x1100; + } else if (mboxType == 1) { + *headerOut = (MBoxControlHeader*)NWC24WorkP->WORK_0x1180; + } else { + *headerOut = 0; + return NWC24_ERR_INVALID_VALUE; + } + + if ((*headerOut)->magic != 0x57635466) { + pathWork = NWC24WorkP->pathWork; + err = MakeMailPath(pathWork, mboxType); + if (err != NWC24_OK) { + return err; + } + + err = NWC24FOpen(&file, pathWork, 2); + if (err != NWC24_OK) { + return err; + } + + result = LoadMBCHeader(&file, *headerOut); + err = NWC24FClose(&file); + if (result == NWC24_OK) { + if (err != NWC24_OK) { + result = err; + } + } + } + + if ((*headerOut)->version != 4) { + result = NWC24_ERR_VER_MISMATCH; + } + return result; +} + +static NWC24Err LoadMBCHeader(NWC24File* file, MBoxControlHeader* header) { + NWC24Err err; + + NWC24FSeek(file, 0, NWC24_SEEK_BEG); + err = NWC24FRead(header, sizeof(MBoxControlHeader), file); + if (err != NWC24_OK) { + return err; + } + + if (header->magic != 0x57635466) { + return NWC24_ERR_BROKEN; + } + + return NWC24_OK; +} + +static NWC24Err SaveMBCHeader(NWC24File* file, MBoxControlHeader* header) { + NWC24FSeek(file, 0, NWC24_SEEK_BEG); + return NWC24FWrite(header, sizeof(MBoxControlHeader), file); +} + +NWC24Err AddMBCEntry(MBoxControlHeader* header, const NWC24MsgObj* msg, NWC24File* file) { + s32 entryOffset = header->nextFreeEntry; + MBoxControlEntry* entryBuffer; + s32 nextEntryOffset; + NWC24Err err; + + entryBuffer = (MBoxControlEntry*)NWC24WorkP->WORK_0x1200; + + if (entryOffset == 0) { + return NWC24_ERR_FULL; + } + + if ((u32)entryOffset >= header->mailDataOffset || ((entryOffset - sizeof(MBoxControlHeader)) & 0x7F) != 0) { + return NWC24_ERR_BROKEN; + } + + NWC24FSeek(file, entryOffset, NWC24_SEEK_BEG); + + err = NWC24FRead(entryBuffer, sizeof(MBoxControlEntry), file); + if (err != NWC24_OK) { + return err; + } + + nextEntryOffset = entryBuffer->appId; + + if ((u32)nextEntryOffset >= header->mailDataOffset || ((nextEntryOffset - sizeof(MBoxControlHeader)) & 0x7F) != 0) { + return NWC24_ERR_BROKEN; + } + + header->nextFreeEntry = nextEntryOffset; + CopyMsgObjToMBCFmt(msg, entryBuffer); + + NWC24FSeek(file, entryOffset, NWC24_SEEK_BEG); + + err = NWC24FWrite(entryBuffer, sizeof(MBoxControlEntry), file); + if (err != NWC24_OK) { + return err; + } + + if (header->msgCount == 0) { + header->oldestMsgId = msg->id; + } + + header->msgCount++; + header->totalMsgSize += msg->length; + return NWC24_OK; +} + +static NWC24Err ClearMBCEntry(MBoxControlHeader* header, u32 offset, NWC24File* file) { + MBoxControlEntry* entryBuffer; + + entryBuffer = (MBoxControlEntry*)NWC24WorkP->WORK_0x1200; + NWC24FSeek(file, offset, NWC24_SEEK_BEG); + NWC24FRead(entryBuffer, sizeof(MBoxControlEntry), file); + + header->msgCount--; + header->totalMsgSize -= entryBuffer->length; + + memset(entryBuffer, 0, sizeof(MBoxControlEntry)); + entryBuffer->appId = header->nextFreeEntry; + header->nextFreeEntry = offset; + + NWC24FSeek(file, offset, NWC24_SEEK_BEG); + return NWC24FWrite(entryBuffer, sizeof(MBoxControlEntry), file); +} + +NWC24Err GetNewMsgId(MBoxControlHeader* header, u32* msgIdOut) { + if (header->magic != 0x57635466) { + return NWC24_ERR_INVALID_VALUE; + } + + if (header->nextMsgId > 1000000) { + header->nextMsgId = 1; + } + + *msgIdOut = header->nextMsgId; + header->nextMsgId++; + + return NWC24_OK; +} + +static s32 CompareMsgId(u32 id1, u32 id2) { + if (id1 == id2) { + return 0; + } else if (id1 > 900000 && id2 < 100000) { + return -1; + } else if (id1 < 100000 && id2 > 900000) { + return 1; + } else if (id1 > id2) { + return 1; + } else { + return -1; + } +} + +NWC24Err MountVFMBox(s32 mboxType) { + s32 result; + NWC24Err err; + const char* mboxDir; + char* pathWork; + + if (MountInfo.count != 0 && MountInfo.type != mboxType) { + MountInfo.count = 1; + UnmountVFMBox(); + return NWC24_ERR_FATAL; + } + + if ((s32)(++MountInfo.count) > 1) { + return NWC24_OK; + } + + pathWork = NWC24WorkP->pathWork; + result = MakeMailPath(pathWork, mboxType); + if (result != NWC24_OK) { + return result; + } + + result = NWC24_OK; + err = NWC24MountVF("@24", pathWork); + if (err != result) { + return err; + } else { + err = result; + MountInfo.type = mboxType; + } + + return err; +} + +NWC24Err CopyMsgObjToMBCFmt(const NWC24MsgObj* src, MBoxControlEntry* dst) { + u32 packData30 = (((u32)src->DATA_0x30.ptr) & 0xFFFFF) | (src->DATA_0x30.size << 20); + u32 packData38 = (((u32)src->DATA_0x38.ptr) & 0xFFFFF) | (src->DATA_0x38.size << 20); + u32 packSubject = (((u32)src->subject.ptr) & 0xFFFFF) | (src->subject.size << 20); + u32 packContent = (((u32)src->contentType.ptr) & 0xFFFFF) | (src->contentType.size << 20); + u32 packTransfer = (((u32)src->transferEncoding.ptr) & 0xFFFFF) | (src->transferEncoding.size << 20); + + dst->id = src->id; + dst->flags = src->flags; + dst->length = src->length; + dst->appId = src->appId; + *(u32*)&dst->UNK_0x10 = src->UNK_0x10; + dst->tag = src->tag; + dst->ledPattern = src->ledPattern; + dst->nextFreeOrCreationMs = *(const u32*)((const u8*)src + 0x1C); + ((u32*)&dst->fromId)[0] = ((u32*)&src->fromId)[0]; + + ((u32*)&dst->fromId)[1] = ((u32*)&src->fromId)[1]; + + dst->createTime = src->createTime; + dst->UNK_0x2C = src->WORD_0x2C; + + *(u8*)&dst->numTo = src->numTo; + *(u8*)((u8*)dst + 0x31) = src->numAttached; + *(u16*)((u8*)dst + 0x32) = src->groupId; + + dst->packedSubjectText = packData30; + dst->packedTextSubjectSize = packData38; + dst->packedSubjectTextSize = packSubject; + dst->packedTextSizeContentType = packContent; + dst->packedContentTypeTransferEnc = packTransfer; + + dst->textPtr = (u32)src->text.ptr; + dst->textSize = src->text.size; + dst->textOrigSize = *(const u32*)((const u8*)src + 0xE8); + dst->UNK_0x74 = *(const u32*)((const u8*)src + 0xEC); + + dst->attached0Ptr = (u32)src->attached[0].ptr; + dst->attached0Size = src->attached[0].size; + dst->attached0OrigSize = src->attachedSize[0]; + dst->attached0_type = src->attachedType[0]; + + dst->attached1Ptr = (u32)src->attached[1].ptr; + dst->attached1Size = src->attached[1].size; + dst->attached1OrigSize = src->attachedSize[1]; + dst->attached1_type = src->attachedType[1]; + + return NWC24_OK; +} diff --git a/src/revolution/NWC24/NWC24Mime.c b/src/revolution/NWC24/NWC24Mime.c new file mode 100644 index 00000000..36bc8c6a --- /dev/null +++ b/src/revolution/NWC24/NWC24Mime.c @@ -0,0 +1,191 @@ +#include + +static char *MIMEEncStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +NWC24Err Base64Encode(char *inputBuffer, s32 inputLength, u32 *inBytesConsumed, char *outBuffer, s32 outBufferSize, u32 *outBytesWritten) { + s32 result = NWC24_OK; + s32 outputIndex; + s32 inputIndex = 0; + BOOL hasThird; + BOOL hasSecond; + u32 combined; + + for (outputIndex = 0; inputIndex < inputLength; inputIndex += 3) { + if ((outputIndex + 3) >= outBufferSize) { + result = NWC24_ERR_OVERFLOW; + break; + } + + hasThird = FALSE; + hasSecond = FALSE; + + combined = ((unsigned char)inputBuffer[inputIndex]); + combined <<= 8; + + if ((inputIndex + 1) < inputLength) { + hasSecond = TRUE; + combined |= ((unsigned char)inputBuffer[inputIndex + 1]); + } + + combined <<= 8; + + if ((inputIndex + 2) < inputLength) { + hasThird = TRUE; + combined |= ((unsigned char)inputBuffer[inputIndex + 2]); + } + + outBuffer[outputIndex + 0] = MIMEEncStr[(combined >> 18) & 0x3F]; + outBuffer[outputIndex + 1] = MIMEEncStr[(combined >> 12) & 0x3F]; + outBuffer[outputIndex + 2] = MIMEEncStr[(hasSecond) ? ((combined >> 6) & 0x3F) : (0x40)]; + outBuffer[outputIndex + 3] = MIMEEncStr[(hasThird) ? (combined & 0x3F) : (0x40)]; + + outputIndex += 4; + } + + *outBytesWritten = outputIndex; + + if (inBytesConsumed != 0) { + outputIndex = inputLength; + if (inputLength < inputIndex) { + inputIndex = inputLength; + } + *inBytesConsumed = inputIndex; + } + return result; +} + +void NWC24Base64Encode(char *inputBuffer, s32 inputLength, char *outBuffer, s32 outBufferSize, u32 *outBytesWritten) { + Base64Encode(inputBuffer, inputLength, 0, outBuffer, outBufferSize, outBytesWritten); +} + +void NWC24InitBase64Table(signed char *codes) { + int i; + for (i = 0; i < 256; i++) { + codes[i] = (char)0xFF; + } + + for (i = 'A'; i <= 'Z'; i++) { + codes[i] = (char)(i - 'A'); + } + + for (i = 'a'; i <= 'z'; i++) { + codes[i] = (char)(26 + i - 'a'); + } + + for (i = '0'; i <= '9'; i++) { + codes[i] = (char)(52 + i - '0'); + } + + codes['+'] = 62; + codes['/'] = 63; + + return; +} + +NWC24Err QEncode(u8 *outBuffer, u32 outBufferSize, u32 *outBytesWritten, u8 *inputBuffer, u32 inputLength, u32 *inBytesConsumed, s32 maxLineLength) { + u8 *outputPtr; + u32 outBytesUsed; + u32 inputIndex; + s32 result; + s32 lineLength; + u8 encodeBuffer[8]; + s32 bufferIndex; + BOOL needsSoftBreak; + s32 emitCount; + s32 bytesRead; + s16 currentByte; + + result = NWC24_OK; + + if (outBuffer == 0) { + return NWC24_ERR_INVALID_VALUE; + } + if (inputBuffer == 0) { + return NWC24_ERR_INVALID_VALUE; + } + if (inputLength == 0) { + return NWC24_ERR_OVERFLOW; + } + if (inBytesConsumed) { + *inBytesConsumed = 0; + } + if (outBytesWritten) { + *outBytesWritten = 0; + } + outputPtr = outBuffer; + lineLength = 0; + outBytesUsed = 0; + inputIndex = 0; + while (inputIndex < inputLength) { + currentByte = *inputBuffer; + needsSoftBreak = FALSE; + + if (((((char)currentByte) >= '!') && (((char)currentByte) <= '~')) && (((char)currentByte) != '=')) { + bytesRead = 1; + emitCount = 1; + encodeBuffer[0] = *inputBuffer; + } else { + if ((((inputIndex + 1) < inputLength) && (((char)currentByte) == '\r')) && (((long)inputBuffer[1]) == '\n')) { + bytesRead = 2; + encodeBuffer[0] = *inputBuffer; + emitCount = 1; + encodeBuffer[1] = inputBuffer[1]; + } else { + encodeBuffer[0] = '='; + if (((*inputBuffer) >> 4) >= 10) { + encodeBuffer[1] = (((*inputBuffer) >> 4) % 10) + 'A'; + } else { + encodeBuffer[1] = (((*inputBuffer) >> 4) % 10) + '0'; + } + if (((*inputBuffer) & 0xf) >= 10) { + encodeBuffer[2] = (((*inputBuffer) & 0xf) % 10) + 'A'; + } else { + encodeBuffer[2] = (((*inputBuffer) & 0xf) % 10) + '0'; + } + + bytesRead = 1; + emitCount = 3; + } + } + if ((maxLineLength == 0) && ((lineLength + emitCount) >= 75)) { + needsSoftBreak = TRUE; + emitCount = emitCount + 3; + } + if (((u32)(outBytesUsed + emitCount)) >= outBufferSize) { + result = NWC24_ERR_OVERFLOW; + break; + } + outBytesUsed = emitCount + outBytesUsed; + if (needsSoftBreak) { + *outputPtr = '='; + outputPtr[1] = '\r'; + outputPtr[2] = '\n'; + outputPtr = outputPtr + 3; + lineLength = 0; + emitCount = emitCount - 3; + } + lineLength = emitCount + lineLength; + bufferIndex = 0; + while (bufferIndex < emitCount) { + *(outputPtr++) = encodeBuffer[bufferIndex]; + bufferIndex++; + } + + inputIndex = inputIndex + bytesRead; + inputBuffer = inputBuffer + bytesRead; + } + + if (inBytesConsumed) { + *inBytesConsumed = inputIndex; + } + + if (outBytesWritten) { + *outBytesWritten = outBytesUsed; + } + + return result; +} + +s32 NWC24EncodeQuotedPrintable(u8 *outBuffer, u32 outBufferSize, u32 *outBytesWritten, u8 *inputBuffer, u32 inputLength, u32 *inBytesConsumed) { + return QEncode(outBuffer, outBufferSize, outBytesWritten, inputBuffer, inputLength, inBytesConsumed, 0); +} diff --git a/src/revolution/NWC24/NWC24MsgCommit.c b/src/revolution/NWC24/NWC24MsgCommit.c new file mode 100644 index 00000000..96b2bc4d --- /dev/null +++ b/src/revolution/NWC24/NWC24MsgCommit.c @@ -0,0 +1,1357 @@ +#include + +extern char MultiPartDivider[40]; +static const char* LoopBackEnable = (const char[]){0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}; +static char* MonthStr[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +extern NWC24Work* NWC24WorkP; + +extern void* m_pFile; +extern const char* ContentTypeA; +extern const char* ContentTxEncA; +extern const char* ContentDispA; +extern const char* ContentTypeTP; +extern const char* ContentTxEncT; + +// STD API +u32 STD_strnlen(const char* str, u32 maxLen); +int Mail_sprintf(char *buffer, const char *format, ...); + +// Other NWC24 functions from other files +void NWC24Data_Init(NWC24Data* data); +NWC24Err NWC24FClose(NWC24File* file); +NWC24Err NWC24FWrite(const void* src, s32 size, NWC24File* file); +NWC24Err NWC24GetMyUserId(u64* idOut); +NWC24Err NWC24iMBoxCloseMsg(NWC24File* file); +NWC24Err NWC24iMBoxAddMsgObj(s32 mboxType, NWC24MsgObj* msg); +NWC24Err NWC24iMBoxCancelMsg(NWC24File* file, s32 mboxType, u32 msgId); +NWC24Err NWC24iMBoxOpenNewMsg(s32 mboxType, NWC24File* file, u32* msgId); +NWC24Err NWC24iMBoxFlushHeader(s32 mboxType); +NWC24Err NWC24iSetNewMsgArrived(u32 flags); +const char* NWC24GetAccountDomain(void); + +// Functions not present in RevoYawarakaD.MAP +static NWC24Err WriteXWiiAltField(NWC24MsgObj* msg); +static NWC24Err WriteTextBody(NWC24MsgObj* msg); +static NWC24Err WriteMIMEPartBoundaryEnd(NWC24MsgObj* msg); + +NWC24Err NWC24CommitMsg(NWC24MsgObj *msg); +NWC24Err NWC24CommitMsgInternal(NWC24MsgObj* msg, s32 flagLocal); +NWC24Err CheckMsgObject(const NWC24MsgObj* msg); +NWC24Err CheckMsgBoxSpace(NWC24MsgObj* msg, u32 requiredSize); +NWC24Err SynthesizeAddrStr(NWC24AddrId* addrId, u32 flags, char* outBuffer, int bufferSize, u32* outLength); +NWC24Err WriteSMTP_MAILFROM(NWC24MsgObj* msg); +NWC24Err WriteSMTP_RCPTTO(NWC24MsgObj* msg); +static NWC24Err WriteSMTP_DATA(NWC24MsgObj* msg); +NWC24Err WriteFromField(NWC24MsgObj* msg); +NWC24Err WriteToField(NWC24MsgObj* msg); +static NWC24Err WriteSubjectField(NWC24MsgObj* msg); +NWC24Err WriteDateField(NWC24MsgObj* msg); +static NWC24Err WriteMessageIdField(NWC24MsgObj* msg); +NWC24Err WriteXWiiAppIdField(NWC24MsgObj* msg); +static NWC24Err WriteXWiiTagField(NWC24MsgObj* msg); +static NWC24Err WriteXWiiCmdField(NWC24MsgObj* msg); +NWC24Err WriteXWiiFaceField(NWC24MsgObj* msg); +NWC24Err WriteXWiiAltNameField(NWC24MsgObj* msg); +static NWC24Err WriteXWiiMBNoReplyField(NWC24MsgObj* msg); +static NWC24Err WriteXWiiMBRegDateField(NWC24MsgObj* msg); +static NWC24Err WriteXWiiMBDelayField(NWC24MsgObj* msg); +static NWC24Err WriteMIMETopHeader(NWC24MsgObj* msg); +static NWC24Err WriteMIMEPartBoundary(NWC24MsgObj* msg); +NWC24Err WriteMIMEAttachHeader(NWC24MsgObj* msg, u32 attachIndex); +NWC24Err WriteContentTypeField(NWC24MsgObj* msg); +static NWC24Err WriteUserField(NWC24MsgObj* msg); +NWC24Err WritePlainText(NWC24MsgObj* msg); +static NWC24Err WriteAttachData(NWC24MsgObj* msg, s32 i); +NWC24Err WriteBase64Data(const void* data, u32 size, u32* bytesWritten); +NWC24Err WriteQPData(const void* data, u32 size, u32* bytesWritten); + +static NWC24Err WriteXWiiAltField(NWC24MsgObj* msg) { + // TODO(Alex9303) Function missing from RevoYawarakaD.MAP. Keeping so NWC24CommitMsgInternal can be somewhat readable. + NWC24Err result; + u32 writeSize; + if (!(msg->flags & 0x2000)) { + return NWC24_OK; + } else { + char* stringWork = NWC24WorkP->stringWork; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "X-Wii-Alt: %08x\r\n", (u32)msg->altMeta.size); + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + } + return result; +} + +static NWC24Err WriteTextBody(NWC24MsgObj* msg) { + // TODO(Alex9303) Function missing from RevoYawarakaD.MAP. Keeping so NWC24CommitMsgInternal can be somewhat readable. + NWC24Err result; + result = WritePlainText(msg); + if (result == NWC24_ERR_NULL) { + return NWC24_OK; + } + return result; +} + +static NWC24Err WriteMIMEPartBoundaryEnd(NWC24MsgObj* msg) { + // TODO(Alex9303) Function missing from RevoYawarakaD.MAP. Keeping so NWC24CommitMsgInternal can be somewhat readable. + char* stringWork = NWC24WorkP->stringWork; + NWC24Err result; + u32 writeSize; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "\r\n--%s", MultiPartDivider); + Mail_strcat(stringWork, "--\r\n"); + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + return result; +} + +NWC24Err NWC24CommitMsg(NWC24MsgObj *msg) { + s32 flagLocal; + u32 flags; + u64 myUserId; + u64 targetUserId; + + flagLocal = 0; + + if (NWC24IsMsgLibOpened() == 0 && NWC24IsMsgLibOpenedByTool() == 0) { + return NWC24_ERR_LIB_NOT_OPENED; + } + + flags = msg->flags; + + if ((flags & 0x100) == 0 || (flags & 0x200) != 0) { + return NWC24_ERR_PROTECTED; + } + + if (LoopBackEnable != 0 && (flags & 0x1) != 0) { + NWC24GetMyUserId(&myUserId); + if (msg->numTo == 1) { + targetUserId = msg->toIds[0]; + if ((targetUserId ^ myUserId) == 0) { + flagLocal = 1; + } + } + } + + return NWC24CommitMsgInternal(msg, flagLocal); +} + +NWC24Err NWC24CommitMsgInternal(NWC24MsgObj* msg, s32 mboxType) { + NWC24File file; + s32 i = 0; + u32 msgId; + NWC24Data data1; + NWC24Data data2; + NWC24Data attachedData[2]; + NWC24Err result; + NWC24Err err; + + NWC24Data_Init(&data1); + NWC24Data_Init(&data2); + + for (i = 0; i < 2; i++) { + NWC24Data_Init(&attachedData[i]); + } + + NWC24iSetErrorCode(NWC24_OK); + err = CheckMsgObject(msg); + if (err != NWC24_OK) { + goto exit_error; + } + err = CheckMsgBoxSpace(msg, mboxType); + if (err != NWC24_OK) { + goto exit_error; + } + result = NWC24iMBoxOpenNewMsg(mboxType, &file, &msgId); + if (result != NWC24_OK) { + err = result; + goto error_break; + } + msg->id = msgId; + msg->length = 0; + m_pFile = &file; + + if (mboxType == 0) { + // Regswap happens inside of this code block. + result = WriteSMTP_MAILFROM(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + result = WriteSMTP_RCPTTO(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + result = WriteSMTP_DATA(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + } + + result = WriteDateField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + result = WriteFromField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + result = WriteToField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteMessageIdField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + data1.ptr = (void*)(msg->length + 9); + result = WriteSubjectField(msg); + if (result == NWC24_OK) { + data1.size = (msg->length - ((u32)data1.ptr)) - 2; + } else if (result == NWC24_ERR_NULL) { + result = NWC24_OK; + data1.ptr = 0; + } + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + if (msg->flags & 1) { + result = WriteXWiiAppIdField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiCmdField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiTagField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiAltField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiAltNameField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiFaceField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + if (msg->mb.raw != 0) { + result = WriteXWiiMBNoReplyField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiMBRegDateField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteXWiiMBDelayField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + } + } + + result = WriteUserField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + Mail_memset(MultiPartDivider, 0, 40); + Mail_sprintf(MultiPartDivider, "----_%08x%08x__________", msg->createTime, msg->id); + + result = WriteMIMETopHeader(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + if (msg->flags & 0x10000ULL) { + msg->UNK_0x10 = msg->length; + result = WriteMIMEPartBoundary(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + } + + result = WriteContentTypeField(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + if (!(msg->flags & 0x10000)) { + msg->UNK_0x10 = msg->length; + } + data2.ptr = (void*)msg->length; + result = WriteTextBody(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + data2.size = msg->length - ((u32)data2.ptr); + + for (i = 0; i < ((u32)msg->numAttached); i++) { + result = WriteMIMEPartBoundary(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + result = WriteMIMEAttachHeader(msg, i); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + attachedData[i].ptr = (void*)msg->length; + result = WriteAttachData(msg, i); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + + attachedData[i].size = msg->length - ((u32)attachedData[i].ptr); + + if (i == (msg->numAttached - 1)) { + result = WriteMIMEPartBoundaryEnd(msg); + if (result != NWC24_OK) { + NWC24FClose(&file); + err = result; + goto error_break; + } + } + } + +error_break: + if (err == NWC24_OK) { + NWC24iMBoxCloseMsg(&file); + } else { + NWC24iMBoxCancelMsg(&file, mboxType, msgId); + NWC24iMBoxFlushHeader(mboxType); + goto exit_error; + } + + msg->subject = data1; + msg->text = data2; + msg->attached[0] = attachedData[0]; + msg->attached[1] = *(attachedData + 1); + msg->flags |= 0x200; + + if (mboxType == 1) { + msg->flags |= 0x20; + } else if (mboxType == 0) { + msg->flags |= 0x10; + } + + result = NWC24iMBoxAddMsgObj(mboxType, msg); + if (err != NWC24_OK) { + result = err; + } + err = result; + + if (mboxType == 1) { + int arrivedFlags = 1; + if (msg->flags & 8) { + arrivedFlags |= 2; + } + NWC24iSetNewMsgArrived(arrivedFlags); + } + +exit_error: + if ((((((err == (NWC24_ERR_FULL)) || ((err <= (NWC24_ERR_FILE_OPEN)) && (err >= (NWC24_ERR_FILE_OTHER)))) || (err == (NWC24_ERR_NAND_CORRUPT))) || (err == (NWC24_ERR_FILE_EXISTS))) || (err == (NWC24_ERR_INTERNAL_VF))) || (err == (NWC24_ERR_FILE_BROKEN))) { + NWC24iSetErrorCode((NWC24Err)((err - 0x20000) + 21772)); + } + + return err; +} + +NWC24Err CheckMsgObject(const NWC24MsgObj* msg) { + const char* subject; + + u8 numTo = msg->numTo; + if (numTo == 0) { + return NWC24_ERR_NULL; + } + if (numTo > NWC24_MSG_RECIPIENT_MAX) { + return NWC24_ERR_INVALID_VALUE; + } + + subject = (const char*)msg->subject.ptr; + if (subject != NULL) { + const char* p = subject; + + while (*p != '\0') { + if ((p[0] == '\r' && p[1] != '\n')) { + return NWC24_ERR_FORMAT; + } + + if (p[0] == '\n' && p[1] != ' ') { + return NWC24_ERR_FORMAT; + } + + p++; + } + } + + if (msg->numAttached > NWC24_MSG_ATTACHMENT_MAX) { + return NWC24_ERR_INVALID_VALUE; + } + + return NWC24_OK; +} + +NWC24Err CheckMsgBoxSpace(NWC24MsgObj* msg, u32 requiredSize) { + u32 textSize; + u32 totalSize = 0; + int i; + NWC24Err result; + NWC24Err err; + + for (i = 0; i < (int)msg->numAttached; i++) { + totalSize += ((((msg->attachedSize[i] * 4) + 2) / 3) + ((msg->attachedSize[i] / 57) * 2)) + 4; + } + + switch (msg->encoding) { + case NWC24_ENC_7BIT: + case NWC24_ENC_8BIT: + textSize = msg->text.size; + break; + + case NWC24_ENC_BASE64: + textSize = ((((msg->text.size * 4) + 2) / 3) + ((msg->text.size / 57) * 2)) + 4; + break; + + case NWC24_ENC_QUOTED_PRINTABLE: + textSize = (msg->text.size * 4) / 3; + break; + + default: + textSize = 0; + break; + } + + totalSize += textSize; + + if (totalSize >= 0x31c00) { + return NWC24_ERR_OVERFLOW; + } + + err = NWC24iMBoxCheck(requiredSize, totalSize + NWC24_WORK_BUFFER_SIZE); + + result = 0; + if (err != 0) { + result = err; + } + + return result; +} + +NWC24Err SynthesizeAddrStr(NWC24AddrId* addrId, u32 flags, char* outBuffer, int bufferSize, u32* outLength) { + s32 domainLength; + char addrIdString[20]; + NWC24Err err = NWC24_OK; + const char* accountDomain; + + if ((flags & 1) != 0) { + accountDomain = NWC24GetAccountDomain(); + domainLength = Mail_strlen(accountDomain); + + if (domainLength <= 0) { + err = NWC24_ERR_CONFIG; + goto cleanup; + } + + if ((domainLength + 0x12) >= bufferSize) { + err = NWC24_ERR_NOMEM; + goto cleanup; + } + + NWC24iConvIdToStr(addrId->id, addrIdString); + domainLength = Mail_sprintf(outBuffer, "%c%s%s", 0x77, addrIdString, accountDomain); + + if (domainLength == 0) { + err = NWC24_ERR_FATAL; + goto cleanup; + } + + *outLength = domainLength; + + } else if ((flags & 2) != 0) { + if ((addrId->data.size + 3) >= bufferSize) { + err = NWC24_ERR_NOMEM; + goto cleanup; + } + + domainLength = Mail_sprintf(outBuffer, "%s", addrId->data.ptr); + + if (domainLength == 0) { + err = NWC24_ERR_FATAL; + goto cleanup; + } + + *outLength = domainLength; + + } else { + err = NWC24_ERR_INVALID_VALUE; + } + +cleanup: + return err; +} + +NWC24Err WriteSMTP_MAILFROM(NWC24MsgObj* msg) { + NWC24Err err; + char* workp; + u32 addrType; + u32 addrLen; + int totalLen; + + workp = NWC24WorkP->stringWork; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strncat(workp, "MAIL FROM:<", 0x3fe); + + workp += 11; + + addrType = 1; + if (msg->flags & 0x100000ULL) { + addrType = 2; + } + + err = SynthesizeAddrStr((NWC24AddrId*)&msg->fromId, addrType, workp, 0x3fe, &addrLen); + if (err != NWC24_OK) { + return err; + } + + workp += addrLen; + *workp++ = '\r'; + *workp++ = '\n'; + + totalLen = addrLen + 13; + + if ((0x3fe - totalLen) <= 0) { + return NWC24_ERR_NOMEM; + } + + if (err == NWC24_OK) { + err = NWC24FWrite(NWC24WorkP->stringWork, totalLen, m_pFile); + if (err == NWC24_OK) { + msg->length += totalLen; + } + } + + return err; +} + +NWC24Err WriteSMTP_RCPTTO(NWC24MsgObj* msg) { + char* workp; + u32 i; + u32 totalLen; + int remainSize; + NWC24Err err; + + workp = NWC24WorkP->stringWork; + totalLen = 0; + err = NWC24_OK; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + remainSize = 0x3fe; + + for (i = 0; i < msg->numTo; i++) { + u32 addrLen; + + Mail_strncat(workp, "RCPT TO:<", remainSize); + workp += 9; + + err = SynthesizeAddrStr((NWC24AddrId*)&msg->toIds[i], msg->flags, workp, remainSize, &addrLen); + if (err != NWC24_OK) { + break; + } + + workp += addrLen; + *workp++ = '\r'; + *workp++ = '\n'; + + remainSize -= (addrLen + 11); + totalLen += (addrLen + 11); + + if (remainSize <= 0) { + err = NWC24_ERR_NOMEM; + break; + } + } + + if (err == NWC24_OK) { + err = NWC24FWrite(NWC24WorkP->stringWork, totalLen, m_pFile); + if (err == NWC24_OK) { + msg->length += totalLen; + } + } + + return err; +} + +static NWC24Err WriteSMTP_DATA(NWC24MsgObj* msg) { + NWC24Err result; + char* stringWork = NWC24WorkP->stringWork; + Mail_strcpy(stringWork, "DATA\r\n"); + result = NWC24FWrite(stringWork, Mail_strlen(stringWork), m_pFile); + if (result == NWC24_OK) { + msg->length += 6; + } + return result; +} + +NWC24Err WriteFromField(NWC24MsgObj* msg) { + char* workp; + u32 addrType; + u32 addrLen; + NWC24Err err; + int totalLen; + + workp = NWC24WorkP->stringWork; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strncat(workp, "From: ", 0x3fe); + + workp += 6; + msg->DATA_0x30.ptr = (void*)(msg->length + 6); + + addrType = 1; + if (msg->flags & 0x100000ULL) { + addrType = 2; + } + + err = SynthesizeAddrStr((NWC24AddrId*)&msg->fromId, addrType, workp, 0x3fe, &addrLen); + if (err != NWC24_OK) { + return err; + } + + workp += addrLen; + *workp++ = '\r'; + *workp++ = '\n'; + + totalLen = addrLen + 8; + + if (0x3fe - totalLen <= 0) { + return NWC24_ERR_NOMEM; + } + + if (err == NWC24_OK) { + err = NWC24FWrite(NWC24WorkP->stringWork, totalLen, m_pFile); + if (err == NWC24_OK) { + msg->length += totalLen; + msg->DATA_0x30.size = ((int)(msg->length - (u32)msg->DATA_0x30.ptr)) - 2; + } + } + + return err; +} + +NWC24Err WriteToField(NWC24MsgObj* msg) { + NWC24AddrId* toId; + char* workp; + int i; + int totalLen; + int remainSize; + u32 len; + NWC24Err err; + + workp = NWC24WorkP->stringWork; + err = NWC24_OK; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strncat(workp, "To: ", NWC24_WORK_BUFFER_SIZE); + + workp += 4; + toId = (NWC24AddrId*)&msg->toIds[0]; + i = 0; + totalLen = 4; + remainSize = 0x3FC; + + *(u32*)&msg->DATA_0x38.ptr = msg->length + 4; + + while (i < msg->numTo) { + err = SynthesizeAddrStr(toId, msg->flags, workp, remainSize, &len); + if (err != NWC24_OK) { + break; + } + + remainSize -= len; + workp += len; + totalLen += len; + + if (remainSize <= 4) { + err = -11; + break; + } + + if (i < msg->numTo - 1) { + *workp++ = ','; + *workp++ = '\r'; + *workp++ = '\n'; + *workp++ = ' '; + totalLen += 4; + remainSize -= 4; + } + + toId++; + i++; + } + + *workp++ = '\r'; + *workp++ = '\n'; + totalLen += 2; + + if (err == NWC24_OK) { + err = NWC24FWrite(NWC24WorkP, totalLen, m_pFile); + if (err == NWC24_OK) { + msg->length += totalLen; + msg->DATA_0x38.size = ((int) (msg->length - (*((u32 *) (&msg->DATA_0x38.ptr))))) - 2; + } + } + + return err; +} + +static NWC24Err WriteSubjectField(NWC24MsgObj* msg) { + char* stringWork = NWC24WorkP->stringWork; + NWC24Err result; + u32 writeSize; + if (msg->subject.size == 0) { + result = NWC24_ERR_NULL; + } else { + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(stringWork, "Subject: "); + Mail_strncat(stringWork, (const char*)msg->subject.ptr, 1021); + Mail_strncat(stringWork, "\r\n", NWC24_WORK_BUFFER_SIZE); + writeSize = STD_strnlen(stringWork, NWC24_WORK_BUFFER_SIZE); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + } + return result; +} + +NWC24Err WriteDateField(NWC24MsgObj* msg) { + char* workp; + NWC24Date date; + u32 len; + NWC24Err err; + NWC24Calendar cal; + + NWC24Date_Init(&date); + NETGetUniversalCalendar((u8*)&cal); + + date.year = cal.year; + date.month = cal.month + 1; + date.day = cal.day; + date.hour = cal.hour; + date.min = cal.min; + date.sec = cal.sec; + + if (date.month > 12) { + return NWC24_ERR_FORMAT; + } + + workp = NWC24WorkP->stringWork; + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + + len = Mail_sprintf(workp, "Date: %02d %s %d %02d:%02d:%02d +0000\r\n", date.day, MonthStr[date.month - 1], date.year, date.hour, date.min, date.sec); + + NWC24iDateToMinutes(&msg->createTime, &date); + len = Mail_strlen(workp); + + err = NWC24FWrite(workp, len, m_pFile); + if (err == NWC24_OK) { + msg->length += len; + } + + return err; +} + +static NWC24Err WriteMessageIdField(NWC24MsgObj* msg) { + u64 myUserId; + char stackBuffer[32]; + NWC24Err result; + u32 writeSize; + char* stringWork = NWC24WorkP->stringWork; + const char* domain = NWC24GetAccountDomain(); + NWC24GetMyUserId(&myUserId); + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(stringWork, "Message-ID: <"); + Mail_memset(stackBuffer, 0, 32); + Mail_sprintf(stackBuffer, "%08x%08x%08x%08x", msg->id, (u32)(myUserId >> 32), (u32)myUserId, msg->createTime); + Mail_strcat(stringWork, stackBuffer); + Mail_strcat(stringWork, domain); + Mail_strcat(stringWork, ">\r\n"); + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + return result; +} + +NWC24Err WriteXWiiAppIdField(NWC24MsgObj* msg) { + char* workp = NWC24WorkP->stringWork; + char tempbuf[16]; + u32 flags = 0; + u32 len; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(workp, "X-Wii-AppId: "); + + if (NWC24IsMsgLibOpenedByTool() == 0) { + if (NWC24GetAppId() != 0x48414541) { + msg->appId = NWC24GetAppId(); + } + } + + if (msg->flags & 0x4) { + flags |= 1; + } + if (msg->flags & 0x8) { + flags |= 2; + } + + Mail_memset(tempbuf, 0, 0x10); + Mail_sprintf(tempbuf, "%d-%08x-%04x", flags, msg->appId, msg->groupId); + Mail_strcat(workp, tempbuf); + + len = Mail_strlen(workp); + if (NWC24FWrite(workp, len, m_pFile) == NWC24_OK) { + msg->length += len; + } +} + +static NWC24Err WriteXWiiTagField(NWC24MsgObj* msg) { + NWC24Err result; + u32 writeSize; + if (msg->tag == 0) { + return NWC24_OK; + } else { + char* stringWork = NWC24WorkP->stringWork; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "X-Wii-Tag: %08x\r\n", msg->tag); + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + } + return result; +} + +static NWC24Err WriteXWiiCmdField(NWC24MsgObj* msg) { + NWC24Err result; + u32 writeSize; + if (msg->ledPattern == 0) { + return NWC24_OK; + } else { + char* stringWork = NWC24WorkP->stringWork; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "X-Wii-Led: %08x\r\n", msg->ledPattern); + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + } + return result; +} + +NWC24Err WriteXWiiFaceField(NWC24MsgObj* msg) { + char* workp; + const u8* dataPtr; + u32 currentOffset = 0; + NWC24Err err = NWC24_OK; + u32 encodedLen; + s32 remainSize; + s32 chunkSize; + + if (msg->face.size == 0) { + return NWC24_OK; + } + + workp = NWC24WorkP->stringWork; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(workp, "X-Wii-Face:"); + currentOffset = 10; + dataPtr = (const u8*)msg->face.ptr; + remainSize = msg->face.size; + + while (remainSize > 0) { + workp[currentOffset++] = ' '; + chunkSize = remainSize; + if (remainSize > 42) { + chunkSize = 42; + } + + err = NWC24Base64Encode((char*)dataPtr, chunkSize, workp + currentOffset, 76, &encodedLen); + + dataPtr += chunkSize; + remainSize -= chunkSize; + currentOffset += encodedLen; + + workp[currentOffset++] = '\r'; + workp[currentOffset++] = '\n'; + } + + err = NWC24FWrite(workp, currentOffset, m_pFile); + if (err == NWC24_OK) { + msg->length += currentOffset; + } + + return err; +} + +NWC24Err WriteXWiiAltNameField(NWC24MsgObj* msg) { + char* workp; + const u8* dataPtr; + u32 currentOffset = 0; + NWC24Err err = NWC24_OK; + u32 encodedLen; + s32 remainSize; + s32 chunkSize; + + if (msg->alt.size == 0) { + return NWC24_OK; + } + + workp = NWC24WorkP->stringWork; + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(workp, "X-Wii-AltName:"); + currentOffset = 14; + dataPtr = (const u8*)msg->alt.ptr; + remainSize = msg->alt.size; + + while (remainSize > 0) { + workp[currentOffset++] = ' '; + chunkSize = remainSize; + if (remainSize > 42) { + chunkSize = 42; + } + + err = NWC24Base64Encode((char*)dataPtr, chunkSize, workp + currentOffset, 76, &encodedLen); + + dataPtr += chunkSize; + remainSize -= chunkSize; + currentOffset += encodedLen; + + workp[currentOffset++] = '\r'; + workp[currentOffset++] = '\n'; + } + + err = NWC24FWrite(workp, currentOffset, m_pFile); + if (err == NWC24_OK) { + msg->length += currentOffset; + } + + return err; +} + +static NWC24Err WriteXWiiMBNoReplyField(NWC24MsgObj* msg) { + NWC24Err result; + if (!(msg->mb.raw & 0x80000000)) { + return NWC24_OK; + } else { + char* stringWork = NWC24WorkP->stringWork; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(stringWork, "X-Wii-MB-Parameter:\r\n"); + result = NWC24FWrite(stringWork, 21, m_pFile); + if (result == NWC24_OK) { + msg->length += 21; + } + } + return result; +} + +static NWC24Err WriteXWiiMBRegDateField(NWC24MsgObj* msg) { + u32 writeSize; + NWC24Err result; + u32 regdate = msg->mb.regdate; + if (regdate == 0) { + return NWC24_OK; + } else { + char* stringWork = NWC24WorkP->stringWork; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "X-Wii-MB-RegDate: %04x\r\n", regdate); + writeSize = STD_strnlen(stringWork, NWC24_WORK_BUFFER_SIZE); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + } + return result; +} + +static NWC24Err WriteXWiiMBDelayField(NWC24MsgObj* msg) { + NWC24Err result; + u32 writeSize; + u32 delay = msg->mb.raw & 0x00FF0000; + if (delay == 0) { + return NWC24_OK; + } else { + char* stringWork = NWC24WorkP->stringWork; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "X-Wii-MB-Delay: %02x\r\n", delay >> 16); + writeSize = STD_strnlen(stringWork, NWC24_WORK_BUFFER_SIZE); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + } + return result; +} + +static NWC24Err WriteMIMETopHeader(NWC24MsgObj* msg) { + char* stringWork = NWC24WorkP->stringWork; + NWC24Err result; + u32 writeSize; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_strcpy(stringWork, "MIME-Version: 1.0\r\n"); + if (msg->flags & 0x10000) { + Mail_strcat(stringWork, "Content-Type: multipart/mixed; boundary=\""); + Mail_strcat(stringWork, MultiPartDivider); + Mail_strcat(stringWork, "\"\r\n"); + } + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + return result; +} + +static NWC24Err WriteMIMEPartBoundary(NWC24MsgObj* msg) { + char* stringWork = NWC24WorkP->stringWork; + NWC24Err result; + u32 writeSize; + Mail_memset(stringWork, 0, NWC24_WORK_BUFFER_SIZE); + Mail_sprintf(stringWork, "\r\n--%s", MultiPartDivider); + Mail_strcat(stringWork, "\r\n"); + writeSize = Mail_strlen(stringWork); + result = NWC24FWrite(stringWork, writeSize, m_pFile); + if (result == NWC24_OK) { + msg->length += writeSize; + } + return result; +} + +NWC24Err WriteMIMEAttachHeader(NWC24MsgObj* msg, u32 attachIndex) { + char* workp; + const char* mimeTypeStr; + const char* mimeSuffix; + s32 written; + s32 totalWritten = 0; + NWC24Err err; + + Mail_memset(NWC24WorkP->stringWork, 0, NWC24_WORK_BUFFER_SIZE); + workp = NWC24WorkP->stringWork; + + mimeTypeStr = NWC24GetMIMETypeStr(msg->attachedType[attachIndex]); + mimeSuffix = NWC24iGetMIMETypeSuffix(msg->attachedType[attachIndex]); + + written = Mail_strlen(ContentTypeA) + Mail_strlen(ContentTxEncA) + Mail_strlen(ContentDispA) + Mail_strlen(mimeTypeStr) + 4; + + if (written >= NWC24_WORK_BUFFER_SIZE) { + return NWC24_ERR_NOMEM; + } + + written = Mail_sprintf(workp, ContentTypeA, mimeTypeStr, (char)('a' + attachIndex), msg->id, mimeSuffix);; + if (written <= 0) { + return NWC24_ERR_FAILED; + } + workp += written; + totalWritten += written; + + written = Mail_sprintf(workp, ContentTxEncA); + if (written <= 0) { + return NWC24_ERR_FAILED; + } + workp += written; + totalWritten += written; + + written = Mail_sprintf(workp, ContentDispA, (char)('a' + attachIndex), msg->id, mimeSuffix); + if (written <= 0) { + return NWC24_ERR_FAILED; + } + workp += written; + totalWritten += written; + + err = NWC24FWrite(NWC24WorkP->stringWork, totalWritten, m_pFile); + if (err == NWC24_OK) { + msg->length += totalWritten; + } + + return err; +} + +NWC24Err WriteContentTypeField(NWC24MsgObj* msg) { + char* work = NWC24WorkP->stringWork; + const char* charset; + const char* encoding; + u32 total = 0; + NWC24Err err = NWC24_OK; + + Mail_memset(work, 0, NWC24_WORK_BUFFER_SIZE); + + charset = NWC24GetCharsetStr(msg->charset); + if (charset != NULL) { + int clen = 0; + + Mail_sprintf(work, ContentTypeTP, charset); + + msg->contentType.ptr = (void*)(msg->length + 0x22); + + clen = Mail_strlen(charset); + msg->contentType.size = clen + 1; + + clen = Mail_strlen(work); + total += clen; + work += clen; + } + + encoding = NWC24GetEncodingStr(msg->encoding); + if (encoding != NULL) { + int elen = 0; + + Mail_sprintf(work, ContentTxEncT, encoding); + + msg->transferEncoding.ptr = (void*)(msg->length + total + 0x1b); + + elen = Mail_strlen(encoding); + msg->transferEncoding.size = elen + 1; + + elen = Mail_strlen(work); + total += elen; + } + + if (total == 0) { + err = NWC24_OK; + } else { + err = NWC24FWrite(NWC24WorkP->stringWork, total, m_pFile); + if (err == NWC24_OK) { + msg->length += total; + } + } + + return err; +} + +static NWC24Err WriteUserField(NWC24MsgObj* msg) { + NWC24Err result; + if (msg->DATA_0xD0.size == 0) { + return NWC24_OK; + } else { + result = NWC24FWrite(msg->DATA_0xD0.ptr, msg->DATA_0xD0.size, m_pFile); + if (result == NWC24_OK) { + msg->length += msg->DATA_0xD0.size; + } + } + return result; +} + +NWC24Err WritePlainText(NWC24MsgObj* msg) { + NWC24Err err = NWC24_OK; + u32 bytesWritten; + + if (msg->text.size == 0) { + return NWC24_ERR_NULL; + } + + switch (msg->encoding) { + case NWC24_ENC_7BIT: + case NWC24_ENC_8BIT: + err = NWC24FWrite(msg->text.ptr, msg->text.size, m_pFile); + bytesWritten = msg->text.size; + break; + case NWC24_ENC_BASE64: + err = WriteBase64Data(msg->text.ptr, msg->text.size, &bytesWritten); + break; + case NWC24_ENC_QUOTED_PRINTABLE: + err = WriteQPData(msg->text.ptr, msg->text.size, &bytesWritten); + break; + default: + err = NWC24_ERR_INVALID_VALUE; + break; + } + + if (err == NWC24_OK) { + msg->altMeta.ptr = (const void *)msg->text.size; + msg->length += bytesWritten; + } + + return err; +} + +static NWC24Err WriteAttachData(NWC24MsgObj* msg, s32 i) { + NWC24Err result; + u32 writeSize; + result = WriteBase64Data(msg->attached[i].ptr, msg->attached[i].size, &writeSize); + if (result == NWC24_OK) { + msg->length += writeSize; + } + return result; +} + +NWC24Err WriteBase64Data(const void* data, u32 size, u32* bytesWritten) { + char* workp = NWC24WorkP->stringWork; + const u8* dataPtr = (const u8*)data; + int remainSize = size; + int totalWritten = 0; + NWC24Err err = NWC24_OK; + u32 encodedLen; + + while (remainSize >= 57) { + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + err = NWC24Base64Encode((char*)dataPtr, 57, workp, 76, &encodedLen); + if (err != NWC24_OK) { + break; + } + + workp[encodedLen++] = '\r'; + workp[encodedLen++] = '\n'; + workp[encodedLen] = '\0'; + + err = NWC24FWrite(workp, encodedLen, m_pFile); + if (err != NWC24_OK) { + break; + } + + dataPtr += 57; + remainSize -= 57; + totalWritten += encodedLen; + } + + if (err == NWC24_OK) { + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + err = NWC24Base64Encode((char*)dataPtr, remainSize, workp, 76, &encodedLen); + if (err == NWC24_OK) { + workp[encodedLen++] = '\r'; + workp[encodedLen++] = '\n'; + workp[encodedLen++] = '\r'; + workp[encodedLen++] = '\n'; + workp[encodedLen] = '\0'; + + err = NWC24FWrite(workp, encodedLen, m_pFile); + if (err == NWC24_OK) { + totalWritten += encodedLen; + } + } + } + + *bytesWritten = totalWritten; + return err; +} + +NWC24Err WriteQPData(const void* data, u32 size, u32* bytesWritten) { + char* workp = NWC24WorkP->stringWork; + const u8* dataPtr = (const u8*)data; + int remainSize = size; + int totalWritten = 0; + NWC24Err err = NWC24_OK; + + while (remainSize > 0) { + u32 consumed; + u32 written; + + if (totalWritten > 0) { + Mail_memcpy(workp, "=\r\n", 3); + err = NWC24FWrite(workp, 3, m_pFile); + if (err != NWC24_OK) { + break; + } + totalWritten += 3; + } + + Mail_memset(workp, 0, NWC24_WORK_BUFFER_SIZE); + err = NWC24EncodeQuotedPrintable((u8*)workp, NWC24_WORK_BUFFER_SIZE, &written, (u8*)dataPtr, remainSize, &consumed); + + if (err != NWC24_OK) { + if (err != NWC24_ERR_OVERFLOW) { + break; + } + } + + err = NWC24FWrite(workp, written, m_pFile); + if (err != NWC24_OK) { + break; + } + + dataPtr += consumed; + remainSize -= consumed; + totalWritten += written; + } + + *bytesWritten = totalWritten; + return err; +} diff --git a/src/revolution/NWC24/NWC24MsgObj.c b/src/revolution/NWC24/NWC24MsgObj.c index 95810c88..b2b78ada 100644 --- a/src/revolution/NWC24/NWC24MsgObj.c +++ b/src/revolution/NWC24/NWC24MsgObj.c @@ -52,7 +52,7 @@ NWC24Err NWC24InitMsgObj(NWC24MsgObj* msg, NWC24MsgType type) { return NWC24_ERR_INVALID_VALUE; } - _msg->WORD_0x28 = 0; + _msg->createTime = 0; _msg->WORD_0x2C = 0; NWC24GetMyUserId(&_msg->fromId); _msg->numTo = 0; @@ -79,8 +79,8 @@ NWC24Err NWC24InitMsgObj(NWC24MsgObj* msg, NWC24MsgType type) { NWC24Data_Init(&_msg->DATA_0x30); NWC24Data_Init(&_msg->DATA_0x38); - NWC24Data_Init(&_msg->DATA_0x50); - NWC24Data_Init(&_msg->DATA_0x58); + NWC24Data_Init(&_msg->contentType); + NWC24Data_Init(&_msg->transferEncoding); NWC24Data_Init(&_msg->DATA_0xD0); NWC24Data_Init(&_msg->face); NWC24Data_Init(&_msg->alt); diff --git a/src/revolution/NWC24/NWC24StdApi.c b/src/revolution/NWC24/NWC24StdApi.c new file mode 100644 index 00000000..2c3fc908 --- /dev/null +++ b/src/revolution/NWC24/NWC24StdApi.c @@ -0,0 +1,311 @@ +#include + +char* Mail_strcpy(char* dst, const char* src); +size_t Mail_strlen(const char* str); +size_t STD_strnlen(const char* str, size_t n); +void* Mail_memcpy(void* dst, const void* src, size_t n); +void* Mail_memset(void* dst, int ch, size_t n); +char* Mail_strcat(char* dst, const char* src); +char* Mail_strncat(char* dst, const char* src, size_t n); +static int Mail_toupper(int c); +static void set_to_head(char *str, char c); +static void set_to_tail(char *str, char c); +int convNum(char *dst, int number, int numberBase, char charBase, int signedFlag, int width, char specifierChar, char justifyChar); +int Mail_sprintf(char *buffer, char *format, ...); +int Mail_vsprintf(char *str, char *format, va_list arg); + +char* Mail_strcpy(char* dst, const char* src) { + char* backup = dst; + + while (*src != '\0') { + *dst++ = *src++; + }; + + *dst = '\0'; + return backup; +} + +size_t Mail_strlen(const char* str) { + size_t len = 0; + + while (str[len] != '\0') { + len++; + } + + return len; +} + +size_t STD_strnlen(const char* str, size_t n) { + size_t len = 0; + int i; + + for (i = 0; i < n; i++) { + if (str[len] == '\0') { + break; + } + len++; + } + + return len; +} + +void* Mail_memcpy(void* dst, const void* src, size_t n) { + size_t i; + + for (i = 0; i < n; i++) { + ((u8*)dst)[i] = ((u8*)src)[i]; + } + + return dst; +} + +void* Mail_memset(void* dst, int ch, size_t n) { + size_t i; + for (i = 0; i < n; i++) { + ((u8*)dst)[i] = ch; + } + + return dst; +} + +char* Mail_strcat(char* dst, const char* src) { + Mail_strcpy(dst + Mail_strlen(dst), src); + return dst; +} + +char* Mail_strncat(char* dst, const char* src, size_t n) { + const size_t len = Mail_strlen(dst); + size_t i; + + for (i = 0; i < n && src[i] != '\0'; i++) { + dst[i + len] = src[i]; + }; + + dst[(int)len + (int)i] = '\0'; + return dst; +} + +static int Mail_toupper(int c) { + return c & 0xDF; +} + +// Not really sure how get non-inlined Mail_strlen() to inline naturally. +inline static int Mail_strlen_inline(const char *str) { + int len = 0; + while (str[len] != '\0') { + len++; + } + return len; +} + +static void set_to_head(char *str, char c) { + // TODO(Alex9303) Figure out how to get Mail_strlen to inline naturally here. + int len = Mail_strlen_inline(str); + int i; + for (i = len; i >= 0; i--) { + str[i + 1] = str[i]; + } + *str = c; +} + +static void set_to_tail(char *str, char c) { + int len; + len = Mail_strlen(str); + str[len++] = c; + str[len] = '\0'; +} + +int convNum(char *dst, int number, int numberBase, char charBase, int signedFlag, int width, char specifierChar, char justifyChar) { + unsigned int workingValue; + int charsWritten; + int digitcharsWritten; + char remainder; + char digitCharBase; + char finalPadChar; + BOOL isNegative; + + if ((signedFlag != 0) && (number & 0x80000000)) { + isNegative = 1; + workingValue = -number; + } else { + isNegative = 0; + workingValue = number; + } + + digitcharsWritten = 0; + charsWritten = 0; + + while (workingValue != 0) { + remainder = workingValue % numberBase; + + workingValue /= numberBase; + digitCharBase = charBase; + set_to_head(dst, remainder + ((char)((remainder <= 9) ? (0x30) : (digitCharBase)))); + digitcharsWritten++; + charsWritten++; + } + + if ((*dst) == '\0') { + set_to_head(dst, '0'); + digitcharsWritten++; + charsWritten++; + } + + finalPadChar = specifierChar; + if (finalPadChar != '0') { + finalPadChar = ' '; + } + + digitcharsWritten += isNegative; + + while (digitcharsWritten < width) { + if (justifyChar == 'L') { + set_to_tail(dst, ' '); + charsWritten++; + } else { + set_to_head(dst, finalPadChar); + charsWritten++; + } + digitcharsWritten++; + } + + if (isNegative) { + set_to_head(dst, '-'); + } + + return charsWritten; +} + +int Mail_sprintf(char *buffer, char *format, ...) { + va_list args; + int ret; + + va_start(args, format); + ret = Mail_vsprintf(buffer, format, args); + va_end(args); + + return ret; +} + +int Mail_vsprintf(char *str, char *format, va_list arg) { + s32 charsWritten; + BOOL isNumberFormat; + BOOL signedFlag; + s32 stringLength; + s32 numberBase; + s32 width; + char formatChar; + char justifyChar; + char specifierChar; + char charBase; + char *stringArg; + char longFlag; + u32 number; + + *str = 0; + charsWritten = 0; + + while ((formatChar = *format++) != 0) { + while (*str) { + str++; + } + + if (formatChar == '%') { + (formatChar = *format++); + if (formatChar == '%') { + set_to_tail(str++, formatChar); + charsWritten++; + } else { + justifyChar = formatChar; + if (formatChar == '-') { + formatChar = *format++; + } + + specifierChar = formatChar; + signedFlag = FALSE; + numberBase = 10; + charBase = '0'; + isNumberFormat = FALSE; + + if (formatChar == '*') { + width = va_arg(arg, int); + formatChar = *format++; + } else { + width = 0; + while ((formatChar >= '0') && (formatChar <= '9')) { + width = width * 10 + formatChar - '0'; + formatChar = *format++; + } + } + + longFlag = Mail_toupper(formatChar); + if (longFlag == 'L') { + formatChar = *format++; + } + + switch (formatChar) { + case 'd': + isNumberFormat = TRUE; + signedFlag = TRUE; + break; + case 'o': + isNumberFormat = TRUE; + numberBase = 8; + break; + case 'u': + isNumberFormat = TRUE; + break; + case 'x': + isNumberFormat = TRUE; + numberBase = 16; + charBase = 'a' - 10; + break; + case 'X': + isNumberFormat = TRUE; + numberBase = 16; + charBase = 'A' - 10; + break; + case 'c': + formatChar = va_arg(arg, s32); + set_to_tail(str++, formatChar); + charsWritten++; + break; + case 's': + stringArg = va_arg(arg, char *); + if (stringArg) { + stringLength = Mail_strlen(stringArg); + Mail_strcat(str, stringArg); + } else { + stringLength = 0; + } + charsWritten += stringLength; + while (stringLength < width) { + charsWritten++; + if (justifyChar == '-') { + set_to_tail(str, ' '); + } else { + set_to_head(str, ' '); + } + stringLength++; + } + break; + } + + if (isNumberFormat) { + if (longFlag == 'L') { + number = va_arg(arg, u32); + } else if (signedFlag) { + number = va_arg(arg, s32); + } else { + number = va_arg(arg, u32); + } + charsWritten += convNum(str, number, numberBase, charBase, signedFlag, width, specifierChar, justifyChar); + } + } + } else { + set_to_tail(str++, formatChar); + charsWritten++; + } + } + return charsWritten; +}