#include "Atlas.h" // https://github.com/uyjulian/AtlasTranslate wchar_t AtlasPath[2 * MAX_PATH]; static int atlasVersion = 0; int GetAtlasVersion() { return atlasVersion; } static int atlasTransDirection = 0; int GetAtlasTransDirection() { return atlasTransDirection; } // dir is 1 for jap to eng, 2 for eng to jap. typedef int __cdecl CreateEngineType(int x, int dir, int x3, char *x4); static CreateEngineType *CreateEngine = 0; typedef int __cdecl DestroyEngineType(); static DestroyEngineType *DestroyEngine = 0; typedef int __cdecl TranslatePairType(char *in, char **out, void **dunno, unsigned int *maybeSize); static TranslatePairType *TranslatePair = 0; typedef int __cdecl AtlInitEngineDataType(int x1, int x2, int *x3, int x4, int *x5); static AtlInitEngineDataType *AtlInitEngineData = 0; // No clue what this does. Doesn't set direction. typedef int __cdecl SetTransStateType(int dunno); static SetTransStateType *SetTransState = 0; static FreeAtlasDataType *FreeAtlasData = 0; AwuWordDelType *AwuWordDel = 0; // static TextRuleSet *ruleSet = 0; // typedef int __cdecl AwuDlgAtlasPopupEnvDetailSetType(void *, char *type, void *x3, char *word); // AwuDlgAtlasPopupEnvDetailSetType *AwuDlgAtlasPopupEnvDetailSet = 0; HMODULE atlecont = 0; HMODULE awdict = 0; HMODULE awuenv = 0; int atlasHappy = 0; void UninitAtlas() { atlasTransDirection = 0; atlasVersion = 0; if (atlasHappy) { DestroyEngine(); atlasHappy = 0; } // if (ruleSet) // { // delete ruleSet; // ruleSet = 0; // } if (atlecont) { FreeLibrary(atlecont); atlecont = 0; } if (awdict) { FreeLibrary(awdict); awdict = 0; } if (awuenv) { FreeLibrary(awuenv); awuenv = 0; } } // ATLAS_CAN_MODIFY lets me modify the original string, otherwise // make and use a copy. // ATLAS_NO_FREE means to return the atlas results directly // rather than copy them over to a standard malloc-ed string. char *AtlasTransSJIS(char *jis, int flags) { // Don't bother removing extra bytes, since it doesn't last long. char *outjis = 0; void *unsure = 0; unsigned int maybeSize = 0; char *temp = jis; if (!(flags & ATLAS_CAN_MODIFY)) temp = (char *)malloc(1 + strlen(jis)); int p1 = 0; int p2 = 0; int lastJis = 1; while (jis[p1]) { if (jis[p1] == ' ' || jis[p1] == '\t' || jis[p1] == '\n' || jis[p1] == '\r' || (jis[p1] == -127 && jis[p1 + 1] == 64)) { if (jis[p1] == -127) p1++; p1++; if (!lastJis) temp[p2++] = ' '; } else { lastJis = (jis[p1] < 0 && ((unsigned char)jis[p1] != 0x82 || (unsigned char)jis[p1 + 1] >= 0x9E || jis[p1 + 1] < 0x4F)); if (lastJis) while (p2 > 0 && temp[p2 - 1] == ' ') p2--; unsigned char u = jis[p1]; // double-byte JIS. if ((u >= 0x81 && u <= 0x9F) || (u >= 0xE0 && u <= 0xEF)) temp[p2++] = jis[p1++]; temp[p2++] = jis[p1++]; } } temp[p2] = 0; // I completely ignore res. Not sure if it matters. int res = TranslatePair(temp, &outjis, &unsure, &maybeSize); if (!(flags & ATLAS_CAN_MODIFY)) free(temp); if (unsure) FreeAtlasData(unsure, 0, 0, 0); if ((flags & ATLAS_NO_FREE)) return outjis; if (outjis) { char *out = strdup(outjis); FreeAtlasData(outjis, 0, 0, 0); return out; } return 0; } wchar_t *AtlasTrans(const wchar_t *text, int len) { // return original string if no Japanese characters. // if (atlasTransDirection == ATLAS_JAP_TO_ENG && !HasJap(text)) return 0; // Don't bother removing extra bytes, since it doesn't last long. if (len < 0) len = (int)wcslen(text); int len2 = 4 * len + 1; char *jis = (char *)malloc(len2); len2 = WideCharToMultiByte(932, 0, text, -1, jis, len2, 0, 0); char *outjis; if (!len2 || !(outjis = AtlasTransSJIS(jis, ATLAS_CAN_MODIFY | ATLAS_NO_FREE))) { free(jis); return 0; } free(jis); // Extra is so I can add on punctuation if I want. len2 = (int)(3 + strlen(outjis)); wchar_t *out = (wchar_t *)malloc(sizeof(wchar_t) * len2); len2 = (int)MultiByteToWideChar(932, 0, outjis, -1, out, len2); FreeAtlasData(outjis, 0, 0, 0); if (len2) return out; free(out); return 0; } int LoadAtlasDlls() { if (atlecont && awdict && awuenv) return 1; wchar_t newPath[MAX_PATH * 2]; for (int i = 0; i < 2; i++) { for (int v = 14; v >= 13; v--) { if (i == 0) { wchar_t temp[MAX_PATH]; wsprintfW(temp, L"Software\\Fujitsu\\ATLAS\\V%i.0\\EJ", v); HKEY hKey = 0; if (ERROR_SUCCESS != RegOpenKey(HKEY_CURRENT_USER, temp, &hKey)) continue; DWORD type; DWORD size = sizeof(newPath) - 2; wchar_t *name; int res = RegQueryValueExW(hKey, L"TRENV EJ", 0, &type, (BYTE *)newPath, &size); RegCloseKey(hKey); if (ERROR_SUCCESS != res || type != REG_SZ || !(name = wcsrchr(newPath, '\\'))) continue; name[1] = 0; } #ifndef NO_SHELL32 else { if (S_OK != SHGetFolderPathW(0, CSIDL_PROGRAM_FILES, 0, SHGFP_TYPE_CURRENT, newPath)) continue; wsprintfW(wcschr(newPath, 0), L"\\ATLAS V%i\\", v); } #endif wchar_t *w = wcschr(newPath, 0); wcscpy(w, L"AtleCont.dll"); atlecont = LoadLibraryEx(newPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); wcscpy(w, L"awdict.dll"); awdict = LoadLibraryEx(newPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); wcscpy(w, L"awuenv.dll"); awuenv = LoadLibraryEx(newPath, 0, LOAD_WITH_ALTERED_SEARCH_PATH); if (atlecont && awdict && awuenv) { *w = 0; wcscpy(AtlasPath, newPath); atlasVersion = v; return 1; } UninitAtlas(); } } return 0; } int AtlasIsLoaded() { return atlasHappy; } int InitAtlas(AtlasConfig &cfg, int transDirection) { if (atlasHappy) { if (transDirection == atlasTransDirection) return 1; UninitAtlas(); } if (!LoadAtlasDlls()) return 0; if (atlecont && awdict && (CreateEngine = (CreateEngineType *)GetProcAddress(atlecont, "CreateEngine")) && (DestroyEngine = (DestroyEngineType *)GetProcAddress(atlecont, "DestroyEngine")) && (TranslatePair = (TranslatePairType *)GetProcAddress(atlecont, "TranslatePair")) && (FreeAtlasData = (FreeAtlasDataType *)GetProcAddress(atlecont, "FreeAtlasData")) && (AtlInitEngineData = (AtlInitEngineDataType *)GetProcAddress(atlecont, "AtlInitEngineData")) && (SetTransState = (SetTransStateType *)GetProcAddress(atlecont, "SetTransState")) && //(AwuDlgAtlasPopupEnvDetailSet = (AwuDlgAtlasPopupEnvDetailSetType*)(awuenv, "AwuDlgAtlasPopupEnvDetailSet")) && (AwuWordDel = (AwuWordDelType *)GetProcAddress(awdict, "AwuWordDel"))) { union { char temp[MAX_PATH * 4]; wchar_t path[MAX_PATH * 2]; }; wcscpy(path, cfg.trsPath); wchar_t *name = wcsrchr(path, '\\'); if (name) { name[0] = 0; name++; } else { name = cfg.trsPath; wcscpy(path, L"Rule Sets\\"); } if (1) { if (WideCharToMultiByte(932, 0, cfg.environment, -1, temp, 4 * MAX_PATH, 0, 0)) { // SetTransState(1); // Needed? No clue. static int dunno[1000] = {0}; static int dunno2[1000] = {0}; if (0 == AtlInitEngineData(0, 2, dunno, 0, dunno2) && 1 == CreateEngine(1, transDirection, 0, temp)) { atlasTransDirection = transDirection; atlasHappy = 1; return 1; //} } } } } UninitAtlas(); return 0; } // Basically just AtlasThreadProc, with some stuff removed. wchar_t *TranslateFull(wchar_t *otext, int freeText, int NeedAbort(int line, int lines, void *data), void *data) { if (!otext) //|| !ruleSet) return 0; if (!otext) return 0; int len1 = wcslen(otext); auto needFree = AtlasTrans(otext, len1); return needFree; int memLen = wcslen(otext); int len = memLen; int count = 1; wchar_t *snipped = otext; // ruleSet->ParseText(otext, &len, &count); wchar_t *text = otext; if (!freeText) text = (wchar_t *)malloc(sizeof(wchar_t) * memLen); text[0] = 0; len = 0; int pos = 0; // Doesn't work, sadly. Looks like atlas isn't threadsafe. /* wchar_t **strings = (wchar_t**) malloc(sizeof(wchar_t*) * count); wchar_t *p = snipped; for (int i=0; i 0 && len2 > 1) { wchar_t e1 = trans[len1 - 1]; wchar_t *e2 = &needFree[len2]; if (e2[-1] == ' ') e2--; if (e2[-1] == '.' || e2[-1] == ',' || e2[-1] == '?' || e2[-1] == '!') e2--; // desired punctuation. wchar_t p = 0; if (e1 == L'!') p = '!'; if (e1 == L'?') p = '!'; if (e1 == L'。') p = '.'; if (p) { if (*e2 == L'.' || *e2 == L'?' || *e2 == L'!') { // First part of handling for "!?" and shouted questions without '?'. // second part further down. if (*e2 == '?') { if (e1 == L'!') wcscpy(e2, L"!? "); } else if (*e2 == '.') *e2 = p; } else { e2[0] = p; e2[1] = ' '; e2[2] = 0; } } } } len += wcslen(trans); } len += wcslen(prefix); if (len + 20 >= memLen) { memLen = len + len / 2 + 1000; text = (wchar_t *)realloc(text, sizeof(wchar_t) * memLen); } if (prefix) { if (prefix[0] == ',') { if (oldLen && text[oldLen - 1] == ' ') oldLen--; if (oldLen && text[oldLen - 1] == '.' || text[oldLen - 1] == ',') oldLen--; } else if (prefix[0] == '.') { if (oldLen && text[oldLen - 1] == ' ') oldLen--; if (oldLen > 1 && text[oldLen - 1] == '.' && (text[oldLen - 2] == '.' || prefix[0] == '.')) oldLen--; // Fix !..., ?..., and ?...? if (oldLen && (text[oldLen - 1] == '?' || text[oldLen - 1] == '!')) { int s = oldLen - 1; while (s && text[s - 1] == '!' || text[s - 1] == '?') s--; int count1 = oldLen - s; int count2 = 1; while (prefix[count2] == '.') count2++; memmove(text + oldLen + count2 - count1, text + s, (count1 + 1) * sizeof(wchar_t)); memcpy(text + s, prefix, sizeof(wchar_t) * count2); prefix += count2; oldLen += count2; // handle the extra ? in ?...? if (prefix[0] == text[oldLen - 1]) prefix++; } } else if (prefix[0] == '?' || prefix[0] == '!') { if (oldLen && text[oldLen - 1] == ' ') oldLen--; if (oldLen > 1 && text[oldLen - 1] == '?') if (oldLen > 2 && text[oldLen - 2] == '!') oldLen--; } wcscpy(text + oldLen, prefix); oldLen += wcslen(prefix); } if (trans) { if (oldLen && (text[oldLen - 1] == ',' || text[oldLen - 1] == '.' || text[oldLen - 1] == '!' || text[oldLen - 1] == '?')) text[oldLen++] = ' '; if (oldLen > 1 && (text[oldLen - 2] == '.' || text[oldLen - 2] == '!' || text[oldLen - 2] == '?')) text[oldLen++] = ' '; wcscpy(text + oldLen, trans); oldLen += wcslen(trans); } len = oldLen; if (needFree) free(needFree); } free(snipped); if (len && text[len - 1] == ' ') text[len - 1] = 0; return text; } char *TranslateFull(char *otext, int freeText, int NeedAbort(int line, int lines, void *data), void *data) { int len = -1; auto ws = StringToWideString(otext, 932); if (!ws.size()) return 0; wchar_t *outw = TranslateFull(ws.data(), freeText, NeedAbort, data); if (!outw) return 0; len = -1; auto s = WideStringToString(outw, 932); char *out = new char[s.size() + 1]; strcpy(out, s.c_str()); free(outw); return out; } static wchar_t *logFile = 0; void SetLogFile(wchar_t *file) { logFile = file; } wchar_t *TranslateFullLog(wchar_t *otext) { if (logFile && logFile[0]) { HANDLE hMutex = CreateMutex(0, 0, L"TRAG Logging Super Mutext +10 from Outer Space"); DWORD res; if (hMutex) res = WaitForSingleObject(hMutex, 2000); HANDLE hFile = CreateFile(logFile, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { DWORD junk; if (GetLastError() == ERROR_ALREADY_EXISTS) SetFilePointer(hFile, 0, 0, FILE_END); else { wchar_t bom = 0xFEFF; WriteFile(hFile, &bom, sizeof(wchar_t), &junk, 0); } WriteFile(hFile, otext, sizeof(wchar_t) * wcslen(otext), &junk, 0); WriteFile(hFile, L"\r\n", sizeof(wchar_t) * 2, &junk, 0); CloseHandle(hFile); } if (hMutex) { // Prolly not needed. if (WAIT_OBJECT_0 == res) ReleaseMutex(hMutex); CloseHandle(hMutex); } } return TranslateFull(otext); } struct AtlasConfig atlcfg; HANDLE mutex = NULL; int atlaswmain(int argc, wchar_t *argv[]) { HANDLE hPipe = CreateNamedPipe(argv[1], PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, 65535, 65535, NMPWAIT_WAIT_FOREVER, 0); SetEvent(CreateEvent(&allAccess, FALSE, FALSE, argv[2])); if (ConnectNamedPipe(hPipe, NULL) != NULL) { DWORD len = 0; } while (true) { wchar_t src[4096] = {0}; DWORD _; if (!ReadFile(hPipe, src, 4096 * 2, &_, NULL)) break; if (!mutex) { mutex = CreateMutex(NULL, FALSE, NULL); if (!mutex) { return false; } } bool waitingForMutex = true; while (waitingForMutex) { switch (WaitForSingleObject(mutex, INFINITE)) { case WAIT_OBJECT_0: { waitingForMutex = false; break; } case WAIT_ABANDONED: { return false; } default: { } } } if (!AtlasIsLoaded()) { // atlcfg.flags = ~BREAK_ON_SINGLE_LINE_BREAKS; wcscpy(atlcfg.environment, L"Entertainment"); wcscpy(atlcfg.trsPath, L""); InitAtlas(atlcfg, ATLAS_JAP_TO_ENG); if (!AtlasIsLoaded()) { ReleaseMutex(mutex); auto text = L"Atlas Load Failed"; WriteFile(hPipe, text, wcslen(text) * 2, &_, NULL); return false; } } wchar_t *text = TranslateFull(src, 0, NULL, NULL); WriteFile(hPipe, text, wcslen(text) * 2, &_, NULL); free(text); ReleaseMutex(mutex); } return 0; }