", html_id) << std::endl;
314 | // htmlLog << fmt::format("
");
315 | std::string code = decompilation_code(decomp);
316 | encode_html(code);
317 |
318 | std::stringstream ss(code);
319 | std::string to;
320 |
321 | uint32_t lineIndex = 0;
322 | std::vector usedLines;
323 | while (std::getline(ss, to, '\n')) {
324 | if (lineIndex != 0) {
325 | bool found = false;
326 | bool last = call.children.size() && *lineNumbers == call.children[call.children.size() - 1].line;
327 | FuncCall * foundChild = nullptr;
328 | for (auto &child : call.children) {
329 | if (*lineNumbers == child.line) {
330 | found = true;
331 | foundChild = &child;
332 | break;
333 | }
334 | }
335 | std::string lineNumber;
336 | auto firstInstanceOfLineNumber = false;
337 | if (std::find(usedLines.begin(), usedLines.end(), *lineNumbers) != usedLines.end()) {
338 | lineNumber = fmt::format("{:>5}", "");
339 | } else {
340 | lineNumber = fmt::format("{:>5}", *lineNumbers);
341 | usedLines.push_back(*lineNumbers);
342 | firstInstanceOfLineNumber = true;
343 | }
344 | htmlLog << fmt::format("{} {} {}
", last ? " class='last-line'" : "", lineNumber, found ? (last ? ">" : "*") : "|", to) << std::endl;
345 | if (found && !foundChild->get_func()->flags.isNative && firstInstanceOfLineNumber) {
346 | PrintCall(htmlLog, *foundChild);
347 | }
348 | }
349 | lineNumbers++;
350 | lineIndex++;
351 | }
352 |
353 | // htmlLog << code;
354 | // htmlLog << fmt::format("
") << std::endl;
355 | htmlLog << fmt::format("
") << std::endl;
356 | decompilation_free(decomp);
357 | } else {
358 | htmlLog << "" << std::endl;
376 | if (std::filesystem::exists(path)) {
377 | if (!files.contains(path)) {
378 | std::ifstream file(path);
379 | std::string line;
380 | while (std::getline(file, line)) {
381 | files[path].emplace_back(line);
382 | }
383 | file.close();
384 | }
385 | if (is_red) {
386 | for (int idx = 0; idx < files[path].size(); ++idx) {
387 | if (files[path][idx].find(func.c_str()) != std::string::npos) {
388 | line_idx = idx;
389 | break;
390 | }
391 | }
392 | }
393 | auto line_index = line_idx;
394 | htmlLog << fmt::format("
{}:{}
", path.string().c_str(), rel_path.string().c_str(), line_idx) << std::endl;
395 | if (files[path].size() > line_index) {
396 | htmlLog << fmt::format("
", line_idx - LINES_BEFORE_TO_PRINT);
397 | for (int i = -LINES_BEFORE_TO_PRINT; i <= LINES_AFTER_TO_PRINT; i++) {
398 | if (files[path].size() > (line_index + i)) {
399 | auto code = files[path][line_index + i];
400 | encode_html(code);
401 | htmlLog << code << std::endl;
402 | }
403 | }
404 | htmlLog << fmt::format("
") << std::endl;
405 | } else {
406 | spdlog::warn("Line number exceded file: {}:{}", path.string().c_str(), line_idx + 1);
407 | }
408 | } else {
409 | htmlLog << fmt::format("
{}:{}
", path.string().c_str(), rel_path.string().c_str(), line_idx) << std::endl;
410 | spdlog::warn("Could not locate file: {}", path.string().c_str());
411 | }
412 | htmlLog << "
" << std::endl;
413 | }
414 | }
415 |
416 | FuncCall * FindFunc(std::vector& map, RED4ext::CName key) {
417 | if (auto it = find_if(map.begin(), map.end(), [&key](FuncCall& obj) {
418 | return obj.get_func()->fullName == key;
419 | }); it != map.end()) {
420 | return it._Ptr;
421 | } else {
422 | for (auto& value : map) {
423 | if (auto func = FindFunc(value.children, key); func != nullptr) {
424 | return func;
425 | }
426 | }
427 | return nullptr;
428 | }
429 | }
430 |
431 | void PrintCall(std::ofstream& htmlLog, FuncCall& call) {
432 | auto rtti = RED4ext::CRTTISystem::Get();
433 | htmlLog << "" << std::endl;
434 | auto func = call.get_func();
435 | if (call.contextString.Length()) {
436 | htmlLog << "
\n";
437 | }
438 | htmlLog << fmt::format("", call.get_func()->fullName.ToString());
439 | if (call.callType == CallType::Static) {
440 | htmlLog << "static ";
441 | }
442 | // if (func->flags.isNative) {
443 | // htmlLog << "native ";
444 | // }
445 | if (call.type) {
446 | htmlLog << fmt::format("{}::", rtti->ConvertNativeToScriptName(call.type->GetName()).ToString());
447 | }
448 | htmlLog << fmt::format("{}", call.GetFuncName());
449 | // if (func) {
450 | // htmlLog << func->GetType()
451 | // }
452 | uint32_t line;
453 | if (call.line) {
454 | line = call.line;
455 | } else {
456 | line = call.get_func()->bytecode.unk04;
457 | }
458 | // htmlLog << fmt::format(" {}", line);
459 | htmlLog << "" << std::endl;
460 | if (call.contextString.Length()) {
461 | htmlLog << "
" << std::endl;
462 | htmlLog << fmt::format("{}
", call.contextString.c_str()) << std::endl;
463 | htmlLog << " " << std::endl;
464 | }
465 | // if (call.get_func()->bytecode.fileIndex != 0 || line != 0) {
466 | print_redscript_source(htmlLog, call);
467 | // print_source(htmlLog, call.get_func()->bytecode.fileIndex, line, call.GetFuncName());
468 | // }
469 | // for (auto& child : call.children) {
470 | // PrintCall(htmlLog, child);
471 | // }
472 |
473 | auto scriptFile = *ScriptHost::Get()->interface.files.Get(call.get_func()->bytecode.fileIndex);
474 | if (scriptFile) {
475 | auto path = std::filesystem::path(scriptFile->filename.c_str());
476 | htmlLog << fmt::format("
{}", path.string().c_str());
477 | }
478 |
479 | htmlLog << "
" << std::endl;
480 | }
481 |
482 | std::wstring currentLogFile;
483 |
484 | // static INT_PTR CALLBACK dlg_proc(HWND dlg, UINT msg, WPARAM wp, LPARAM lp) {
485 | // switch(msg) {
486 | // case WM_COMMAND:
487 | // switch(LOWORD(wp))
488 | // {
489 | // case CTD_HELPER_OPEN:
490 | // ShellExecute(0, 0, currentLogFile.c_str(), 0, 0 , SW_SHOW );
491 | // break;
492 | // }
493 | // break;
494 | // case WM_CLOSE:
495 | // PostQuitMessage(0);
496 | // break;
497 | // default:
498 | // return FALSE;
499 | // }
500 | // return TRUE;
501 | // }
502 |
503 | // 1.6 RVA: 0x2FFC6F0 / 50317040
504 | /// @pattern 40 53 48 83 EC 40 48 83 3D ? ? ? 01 00 48 8B D9 75 62 48 83 3D ? ? ? 01 00 75 58 33 C0 48
505 | // void __fastcall SetHWND(HWND hWnd);
506 | //
507 | // REGISTER_HOOK(void __fastcall, SetHWND, HWND aHwnd) {
508 | // hWnd = aHwnd;
509 | // SetHWND_Original(aHwnd);
510 | // }
511 |
512 | void print_log(std::ofstream& stream, std::string name, std::filesystem::path path) {
513 | if (std::filesystem::exists(path)) {
514 | std::ifstream log_file(path);
515 | std::stringstream log_buffer;
516 | log_buffer << log_file.rdbuf();
517 | stream << fmt::format("{} log
\n ", name, log_buffer.str()) << std::endl;
518 | }
519 | }
520 |
521 | // struct ErrorReporter
522 | // {
523 | // uint64_t unk00[9];
524 | // HANDLE handle;
525 | // uint32_t unk50;
526 | // uint8_t success;
527 | // };
528 |
529 | // 1.6 RVA: 0x2BCF150 / 45936976
530 | /// @pattern 0F B6 05 ? ? ? 02 C3
531 | /// @nth 6/12
532 | // bool __fastcall IsDebug();
533 |
534 | // /// @pattern 40 53 48 81 EC 30 02 00 00 80 3D ? ? ? 02 00 48 8B D9 0F 85 B2 00 00 00 41 B8 04 01 00 00 48
535 | // bool __fastcall LaunchErrorReporter(ErrorReporter* errorReporter);
536 |
537 | // REGISTER_HOOK(bool __fastcall, LaunchErrorReporter, ErrorReporter* errorReporter) {
538 | // auto result = LaunchErrorReporter_Original(errorReporter);
539 | // if (result) {
540 | // }
541 | // return result;
542 | // }
543 |
544 | // 48 8D 68 A1 48 81 EC A0 00 00 00 0F B6 F1
545 | // 1.6 RVA: 0x2B93EF0 / 45694704
546 | // 1.61 RVA: 0x2B99290
547 | // 1.61hf RVA: 0x2B9BC70
548 | /// @pattern 4C 8B DC 49 89 5B 08 49 89 73 10 57 48 83 EC 20 48 8B 05 E1 C2 02 01 48 8B FA 40 8A F1 48 83 F8
549 | void __fastcall CrashFunc(uint8_t a1, uintptr_t a2);
550 |
551 | REGISTER_HOOK(void __fastcall, CrashFunc, uint8_t a1, uintptr_t a2) {
552 |
553 | time_t now = time(0);
554 | struct tm tstruct;
555 | char log_filename[80];
556 | char niceTimestamp[80];
557 | tstruct = *localtime(&now);
558 | strftime(log_filename, sizeof(log_filename), "%Y-%m-%d_%H-%M-%S.html", &tstruct);
559 | strftime(niceTimestamp, sizeof(niceTimestamp), "%Y-%m-%d %H:%M:%S", &tstruct);
560 |
561 | auto ctd_helper_dir = Utils::GetRootDir() / "red4ext" / "logs" / "ctd_helper";
562 | auto currentLogFilePath = ctd_helper_dir / log_filename;
563 | currentLogFile = currentLogFilePath.wstring();
564 |
565 | spdlog::error(L"Crash! Check {} for details", currentLogFile);
566 |
567 | std::filesystem::create_directories(ctd_helper_dir);
568 |
569 | std::ofstream htmlLog;
570 | htmlLog.open(currentLogFilePath);
571 | htmlLog << CTD_HELPER_HEADER;
572 | htmlLog << fmt::format("CTD Helper Report for Crash on {}\n", niceTimestamp);
573 | htmlLog << "\n";
574 | // auto dlg = CreateDialog(pluginHandle, MAKEINTRESOURCE(CTD_HELPER_DIALOG), hWnd, dlg_proc);
575 | // auto dlg = CreateDialog(pluginHandle, MAKEINTRESOURCE(CTD_HELPER_DIALOG), GetConsoleWindow() , dlg_proc);
576 | // ShowWindow(dlg, SW_SHOW);
577 |
578 | htmlLog << fmt::format("CTD Helper Report for Crash on {}
\n", niceTimestamp);
579 | htmlLog << "Generated by CTD Helper. All code is decompiled redscript from the blob used in the game.
\n";
580 |
581 | print_log(htmlLog, "RED4ext", Utils::GetRootDir() / "red4ext" / "logs" / "red4ext.log");
582 | print_log(htmlLog, "Redscript", Utils::GetRootDir() / "r6" / "logs" / "redscript_rCURRENT.log");
583 | print_log(htmlLog, "Input Loader", Utils::GetRootDir() / "red4ext" / "logs" / "input_loader.log");
584 |
585 | if (scriptLinkingError) {
586 | std::wstring werror(errorMessage);
587 | std::string error(werror.begin(), werror.end());
588 | htmlLog << fmt::format("Script Linking Error
\n ", error);
589 | }
590 |
591 | std::map> orgd;
592 |
593 | for (auto &queue : funcCallQueues) {
594 | auto thread = queue.first;
595 | for (auto i = 0; queue.second.size(); i++) {
596 | auto call = queue.second.front();
597 | // if (call.context.instance && call.context.refCount) {
598 | // call.self.type->ToString(call.context.instance, call.self.contextString);
599 | // call.parent.contextString = call.self.contextString;
600 | // }
601 | call.self.callType = call.isStatic ? CallType::Static : CallType::Method;
602 | call.self.line = call.line;
603 | if (orgd[thread].empty()) {
604 | call.self.callType = call.isStatic ? CallType::Static : CallType::Method;
605 | auto child = call.parent.children.emplace_back(call.self);
606 | auto parent = orgd[thread].emplace_back(call.parent);
607 | child.parent = &parent;
608 | } else {
609 | if (auto func = FindFunc(orgd[thread], call.parent.get_func()->fullName); func != nullptr) {
610 | auto child = func->children.emplace_back(call.self);
611 | child.parent = func;
612 | } else {
613 | auto child = call.parent.children.emplace_back(call.self);
614 | auto parent = orgd[thread].emplace_back(call.parent);
615 | child.parent = &parent;
616 | }
617 | }
618 | queue.second.pop();
619 | }
620 | }
621 |
622 | for (auto &queue : orgd) {
623 | auto level = 0;
624 | std::queue stack;
625 | auto crashing = lastThread == queue.first;
626 | htmlLog << fmt::format("{0}{1}
", queue.first, crashing ? " LAST EXECUTED":"") << std::endl;
627 | uint64_t last = 0;
628 | for (auto& call : queue.second) {
629 | PrintCall(htmlLog, call);
630 | }
631 | htmlLog << "" << std::endl;
632 | }
633 |
634 | htmlLog << R"(
635 |