Repository: markdwags/Razor Branch: master Commit: d1c7b2404da9 Files: 378 Total size: 5.9 MB Directory structure: gitextract_9zwc60cb/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ └── workflows/ │ ├── build-only.yml │ ├── build-site.yml │ └── build.yml ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Crypt/ │ ├── Crypt.cpp │ ├── Crypt.def │ ├── Crypt.h │ ├── Crypt.rc │ ├── Crypt.vcxproj │ ├── Crypt.vcxproj.filters │ ├── LoginEncryption.cpp │ ├── LoginEncryption.h │ ├── MemFinder.cpp │ ├── MemFinder.h │ ├── OSIEncryption.cpp │ ├── OSIEncryption.h │ ├── OldStatusBar.cpp │ ├── PacketInfo.cpp │ ├── PacketInfo.h │ ├── UOArt.cpp │ ├── debug.h │ ├── platform.h │ ├── resource.h │ ├── stdafx.cpp │ ├── stdafx.h │ ├── table.h │ ├── trees.cpp │ ├── twofish.c │ ├── twofish.h │ ├── uo_huffman.cpp │ └── uo_huffman.h ├── FastColoredTextBox/ │ ├── AutocompleteItem.cs │ ├── AutocompleteMenu.cs │ ├── Bookmarks.cs │ ├── Char.cs │ ├── CommandManager.cs │ ├── Commands.cs │ ├── DocumentMap.cs │ ├── EncodingDetector.cs │ ├── ExportToHTML.cs │ ├── ExportToRTF.cs │ ├── FCTB_key.snk │ ├── FastColoredTextBox.cs │ ├── FastColoredTextBox.csproj │ ├── FastColoredTextBox.resx │ ├── FastColoredTextBox.xml │ ├── FileTextSource.cs │ ├── FindForm.Designer.cs │ ├── FindForm.cs │ ├── FindForm.resx │ ├── GoToForm.Designer.cs │ ├── GoToForm.cs │ ├── GoToForm.resx │ ├── Hints.cs │ ├── Hotkeys.cs │ ├── HotkeysEditorForm.Designer.cs │ ├── HotkeysEditorForm.cs │ ├── HotkeysEditorForm.resx │ ├── LimitedStack.cs │ ├── Line.cs │ ├── LinesAccessor.cs │ ├── MacrosManager.cs │ ├── NativeMethods.cs │ ├── Place.cs │ ├── PlatformType.cs │ ├── Properties/ │ │ └── AssemblyInfo.cs │ ├── Range.cs │ ├── ReplaceForm.Designer.cs │ ├── ReplaceForm.cs │ ├── ReplaceForm.resx │ ├── Ruler.Designer.cs │ ├── Ruler.cs │ ├── Style.cs │ ├── SyntaxDescriptor.cs │ ├── SyntaxHighlighter.cs │ ├── TextSource.cs │ ├── TypeDescriptor.cs │ ├── UnfocusablePanel.cs │ └── VisualMarker.cs ├── INSTALL.md ├── LICENSE.md ├── Loader/ │ ├── Loader.cpp │ ├── Loader.def │ ├── Loader.h │ ├── Loader.rc │ ├── Loader.vcxproj │ ├── Loader.vcxproj.filters │ ├── resource.h │ ├── stdafx.cpp │ └── stdafx.h ├── Notepad++UDL.xml ├── Platform/ │ ├── Platform.cpp │ ├── Platform.def │ ├── Platform.h │ ├── Platform.vcxproj │ └── Platform.vcxproj.filters ├── README.md ├── Razor/ │ ├── Agents/ │ │ ├── Agents.cs │ │ ├── BuyAgent.cs │ │ ├── IgnoreAgent.cs │ │ ├── OrganizerAgent.cs │ │ ├── RestockAgent.cs │ │ ├── ScavengerAgent.cs │ │ ├── SearchExemptionAgent.cs │ │ ├── SellAgent.cs │ │ └── UseOnceAgent.cs │ ├── Boat/ │ │ ├── BoatWindow.Designer.cs │ │ ├── BoatWindow.cs │ │ └── BoatWindow.resx │ ├── Client/ │ │ ├── ClassicUO.cs │ │ ├── Client.cs │ │ ├── OSI.cs │ │ └── UOAssist.cs │ ├── Core/ │ │ ├── ActionQueue.cs │ │ ├── BandageTimer.cs │ │ ├── BodCapture.cs │ │ ├── BuffDebuffManager.cs │ │ ├── ClassicUOManager.cs │ │ ├── Commands.cs │ │ ├── ContainerLabels.cs │ │ ├── CooldownManager.cs │ │ ├── Counters.cs │ │ ├── DamageTracker.cs │ │ ├── Dress.cs │ │ ├── DressList.cs │ │ ├── EncodedSpeech.cs │ │ ├── FriendsManager.cs │ │ ├── GateTimer.cs │ │ ├── Geometry.cs │ │ ├── GoldPerHourTimer.cs │ │ ├── Item.cs │ │ ├── ItemID.cs │ │ ├── Main.cs │ │ ├── Map.cs │ │ ├── MemHelper.cs │ │ ├── MessageInBottleCapture.cs │ │ ├── MessageManager.cs │ │ ├── Mobile.cs │ │ ├── MsgQueue.cs │ │ ├── ObjectPropertyList.cs │ │ ├── OverheadManager.cs │ │ ├── Overrides.cs │ │ ├── PasswordMemory.cs │ │ ├── Ping.cs │ │ ├── Player.cs │ │ ├── ScreenCapture.cs │ │ ├── Serial.cs │ │ ├── SkillTimer.cs │ │ ├── Spells.cs │ │ ├── StaffToolsManager.cs │ │ ├── StealthSteps.cs │ │ ├── SystemMessages.cs │ │ ├── Targeting.cs │ │ ├── TargetingClosest.cs │ │ ├── TargetingNextPrevious.cs │ │ ├── TargetingRandom.cs │ │ ├── TextFilterManager.cs │ │ ├── Timer.cs │ │ ├── UOEntity.cs │ │ ├── Utility.cs │ │ ├── WaypointManager.cs │ │ ├── World.cs │ │ └── ZLib.cs │ ├── Filters/ │ │ ├── Death.cs │ │ ├── Filter.cs │ │ ├── Light.cs │ │ ├── MessageFilter.cs │ │ ├── MobileFilter.cs │ │ ├── SoundFilters.cs │ │ ├── SoundMusicManager.cs │ │ ├── StaffItems.cs │ │ ├── TargetFilterManager.cs │ │ ├── VetRewardGump.cs │ │ ├── WallStaticFilter.cs │ │ └── Weather.cs │ ├── Gumps/ │ │ ├── Gump.cs │ │ ├── GumpButtonType.cs │ │ ├── GumpCollection.cs │ │ ├── GumpElement.cs │ │ ├── GumpPage.cs │ │ ├── GumpType.cs │ │ └── Internal/ │ │ ├── AgentsGump.cs │ │ ├── BoatControlGump.cs │ │ ├── BuffGump.cs │ │ ├── CooldownGump.cs │ │ ├── DamageTrackerGump.cs │ │ ├── DamageTrackerListGump.cs │ │ ├── HotKeyGump.cs │ │ ├── InputDialogGump.cs │ │ ├── ItemInfoGump.cs │ │ ├── MobileInfoGump.cs │ │ ├── SystemMessagesGump.cs │ │ └── TestMessageGump.cs │ ├── HotKeys/ │ │ ├── Counters.cs │ │ ├── HotKeys.cs │ │ ├── Misc.cs │ │ ├── SkillHotKeys.cs │ │ ├── SpecialMoves.cs │ │ └── Undress.cs │ ├── Macros/ │ │ ├── Actions.cs │ │ ├── Actions.resx │ │ ├── Macro.cs │ │ ├── MacroManager.cs │ │ └── MacroVariables.cs │ ├── Map/ │ │ ├── MapWindow.cs │ │ ├── MapWindow.resx │ │ ├── Region.cs │ │ ├── UOMapControl.cs │ │ ├── UOMapControl.resx │ │ └── UOMapRuneButton.cs │ ├── Network/ │ │ ├── Handlers.cs │ │ ├── Loader.cs │ │ ├── Packet.cs │ │ ├── PacketHandler.cs │ │ ├── PacketTable.cs │ │ ├── PacketWriter.cs │ │ └── Packets.cs │ ├── Platform.cs │ ├── Properties/ │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── Razor.csproj │ ├── Razor.manifest │ ├── Scripts/ │ │ ├── AgentCommands.cs │ │ ├── Aliases.cs │ │ ├── Commands.cs │ │ ├── Engine/ │ │ │ ├── Interpreter.cs │ │ │ ├── Lexer.cs │ │ │ └── TextParser.cs │ │ ├── Expressions.cs │ │ ├── Helpers/ │ │ │ └── CommandHelper.cs │ │ ├── RazorScript.cs │ │ ├── ScriptManager.cs │ │ ├── ScriptVariables.cs │ │ ├── SpeechCommands.cs │ │ └── TargetCommands.cs │ ├── UI/ │ │ ├── AddCounter.cs │ │ ├── AddCounter.resx │ │ ├── ArtViewer.Designer.cs │ │ ├── ArtViewer.cs │ │ ├── BuffDebuffOptions.Designer.cs │ │ ├── BuffDebuffOptions.cs │ │ ├── BuffDebuffOptions.resx │ │ ├── Config.cs │ │ ├── ContainerLabels.Designer.cs │ │ ├── ContainerLabels.cs │ │ ├── ContainerLabels.resx │ │ ├── Ext.cs │ │ ├── HueEntry.cs │ │ ├── HueEntry.resx │ │ ├── InputBox.cs │ │ ├── InputBox.resx │ │ ├── InputDropdown.cs │ │ ├── InputDropdown.resx │ │ ├── Languages.cs │ │ ├── MacroInsertDoWhile.cs │ │ ├── MacroInsertDoWhile.resx │ │ ├── MacroInsertIf.cs │ │ ├── MacroInsertIf.resx │ │ ├── MacroInsertWait.cs │ │ ├── MacroInsertWait.resx │ │ ├── MacroInsertWhile.cs │ │ ├── MacroInsertWhile.resx │ │ ├── MessageDialog.cs │ │ ├── MessageDialog.resx │ │ ├── Razor.Designer.cs │ │ ├── Razor.cs │ │ ├── Razor.resx │ │ ├── SoundEntry.cs │ │ ├── SoundEntry.resx │ │ ├── SplashScreen.cs │ │ ├── SplashScreen.resx │ │ ├── WelcomeForm.cs │ │ └── WelcomeForm.resx │ ├── UltimaSDK/ │ │ ├── ASCIIFont.cs │ │ ├── AnimationEdit.cs │ │ ├── Animations.cs │ │ ├── Animdata.cs │ │ ├── Art.cs │ │ ├── BwtDecompress.cs │ │ ├── CalibrationInfo.cs │ │ ├── Client.cs │ │ ├── ClientHandles.cs │ │ ├── FileIndex.cs │ │ ├── Files.cs │ │ ├── Gumps.cs │ │ ├── Hues.cs │ │ ├── Light.cs │ │ ├── LocationPointer.cs │ │ ├── Map.cs │ │ ├── MultiMap.cs │ │ ├── Multis.cs │ │ ├── NativeMethods.cs │ │ ├── ProcessStream.cs │ │ ├── RadarCol.cs │ │ ├── SkillGroups.cs │ │ ├── Skills.cs │ │ ├── Sound.cs │ │ ├── SpeechList.cs │ │ ├── StackDataReader.cs │ │ ├── StringEntry.cs │ │ ├── StringHelper.cs │ │ ├── StringList.cs │ │ ├── Textures.cs │ │ ├── TileData.cs │ │ ├── TileList.cs │ │ ├── TileMatrix.cs │ │ ├── TileMatrixPatch.cs │ │ ├── UnicodeFont.cs │ │ ├── ValueStringBuilder.cs │ │ ├── Verdata.cs │ │ └── WindowProcessStream.cs │ ├── app.config │ └── packages.config ├── Razor.sln ├── etc/ │ ├── Language/ │ │ ├── Razor_lang.CHS │ │ ├── Razor_lang.PTB │ │ ├── Razor_lang.bg │ │ ├── Razor_lang.cht │ │ ├── Razor_lang.deu │ │ ├── Razor_lang.enu │ │ ├── Razor_lang.esp │ │ ├── Razor_lang.ita │ │ ├── Razor_lang.pl │ │ ├── Razor_lang.rus │ │ ├── Razor_lang.swe │ │ └── Razor_lang.tur │ ├── Razor.nsi │ ├── animdata.csv │ ├── counters.xml │ ├── doors.xml │ ├── dotNet.nsh │ ├── guardlines.def │ ├── items.xml │ ├── license.txt │ ├── overrides.def │ ├── spells.def │ └── uorguardlines.def └── help/ ├── docs/ │ ├── download.md │ ├── faq.md │ ├── guide/ │ │ ├── commands.md │ │ ├── comments.md │ │ ├── expressions.md │ │ ├── index.md │ │ ├── keywords.md │ │ ├── layers.md │ │ └── variables.md │ ├── help/ │ │ ├── advanced.md │ │ ├── agents.md │ │ ├── armdress.md │ │ ├── displaycounters.md │ │ ├── filters.md │ │ ├── friends.md │ │ ├── general.md │ │ ├── hotkeys.md │ │ ├── index.md │ │ ├── macros.md │ │ ├── options.md │ │ ├── screenshots.md │ │ ├── scripts.md │ │ └── skills.md │ ├── history.md │ ├── index.md │ ├── install/ │ │ ├── linux.md │ │ └── windows.md │ ├── releasenotes.md │ ├── stylesheets/ │ │ └── extra.css │ └── uoaapi.md ├── mkdocs.yml ├── overrides/ │ └── main.html ├── pygments.lexers.razor ├── pygments.mapping.razor └── pygments.styles.razor ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help Razor improve title: "[BUG]" labels: bug assignees: '' --- **Please include if you are using the standard OSI client or using ClassicUO.** Remember, **Great Bug Reports** tend to have: - A quick summary and/or background of the issue - Steps to reproduce - Be specific! This can be challenging at times in a game like UO, but the more specific you are, the easier it is to fix - Include screenshots, gifs, videos if possible. Free apps like ShareX and Greenshot make it easy. - What you expected would happen - What actually happens - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for Razor title: "[FEATURE]" labels: enhancement assignees: '' --- All feature requests will be considered, but not all will be implemented but please don't let that discourage you from submitting more. **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/workflows/build-only.yml ================================================ name: Build Only on: pull_request: branches: - master paths-ignore: - 'help/**' - '**/build-site.yml' jobs: build: strategy: matrix: configuration: [Release] runs-on: windows-2022 env: Solution_Name: Razor.sln steps: - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: Install .NET uses: actions/setup-dotnet@v1 with: dotnet-version: 6.0.x - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.1 - name: Restore Razor run: msbuild $env:Solution_Name /t:Restore /p:Configuration=$env:Configuration env: Configuration: ${{ matrix.configuration }} - name: Build Razor run: msbuild $env:Solution_Name /t:Build /p:Configuration=$env:Configuration env: Configuration: ${{ matrix.configuration }} ================================================ FILE: .github/workflows/build-site.yml ================================================ name: Build Site on: push: branches: - master paths: - 'help/**' - '**/build-site.yml' pull_request: branches: - master paths: - 'help/**' - '**/build-site.yml' jobs: build: runs-on: ubuntu-latest defaults: run: working-directory: ./help strategy: matrix: python-version: [3.10.9] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install mkdocs pip install mkdocs-material pip install mkdocs-git-revision-date-localized-plugin pip install pillow pip install cairosvg sudo apt-get install -y libcairo2-dev libfreetype6-dev libffi-dev libjpeg-dev libpng-dev libz-dev - name: Install and modify Pygments run: | git clone https://github.com/pygments/pygments.git # Replace with Razor files rm ./pygments/pygments/lexers/_mapping.py cp pygments.mapping.razor ./pygments/pygments/lexers/_mapping.py cp pygments.lexers.razor ./pygments/pygments/lexers/razor.py cp pygments.styles.razor ./pygments/pygments/styles/razor.py pip install -e ./pygments/ # Runs a single command using the runners shell - name: Build site run: mkdocs build - name: Generate robots.txt run: | cat > ./site/robots.txt <DataPath, path, MAX_PATH); ReleaseMutex( CommMutex ); } DLLFUNCTION int InstallLibrary( HWND PostWindow, DWORD pid, int flags ) { DWORD UOTId = 0; Log( "Initialize library..." ); HWND hWnd = NULL; if ( pid != 0 ) { hWnd = FindWindow( "Ultima Online", NULL ); while ( hWnd != NULL ) { UOTId = GetWindowThreadProcessId( hWnd, &UOProcId ); if ( UOProcId == pid ) break; hWnd = FindWindowEx( NULL, hWnd, "Ultima Online", NULL ); } if ( UOProcId != pid || hWnd == NULL ) { hWnd = FindWindow( "Ultima Online Third Dawn", NULL ); while ( hWnd != NULL ) { UOTId = GetWindowThreadProcessId( hWnd, &UOProcId ); if (UOProcId == pid) break; hWnd = FindWindowEx( NULL, hWnd, "Ultima Online Third Dawn", NULL ); } } if ( UOProcId != pid ) return NO_TID; } else { hWnd = FindUOWindow(); if ( hWnd != NULL ) UOTId = GetWindowThreadProcessId( hWnd, &UOProcId ); } hUOWindow = hWnd; hRazorWnd = PostWindow; if ( hUOWindow == NULL ) return NO_UOWND; if ( !UOTId || !UOProcId ) return NO_TID; if ( !CreateSharedMemory() ) return NO_SHAREMEM; pShared->Reserved0 = false; hWndProcRetHook = SetWindowsHookEx( WH_CALLWNDPROCRET, WndProcRetHookFunc, hInstance, UOTId ); if ( !hWndProcRetHook ) return NO_HOOK; hGetMsgHook = SetWindowsHookEx( WH_GETMESSAGE, GetMsgHookFunc, hInstance, UOTId ); if ( !hGetMsgHook ) return NO_HOOK; WNDCLASS wc; wc.style = 0; wc.lpfnWndProc = (WNDPROC)UOAWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, IDI_WINLOGO); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = NULL; wc.lpszMenuName = NULL; wc.lpszClassName = "UOASSIST-TP-MSG-WND"; RegisterClass(&wc); DWORD error = GetLastError(); hUOAWnd = CreateWindow("UOASSIST-TP-MSG-WND", "UOASSIST-TP-MSG-WND", WS_OVERLAPPEDWINDOW, 0, 0, 50, 50, NULL, NULL, hInstance, 0); if (hUOAWnd) ShowWindow(hUOAWnd, FALSE); ServerEncrypted = (flags&0x10) != 0; ClientEncrypted = (flags&0x08) != 0; PostMessage( hUOWindow, WM_PROCREADY, (WPARAM)flags, (LPARAM)hRazorWnd ); return SUCCESS; } DLLFUNCTION void WaitForWindow( DWORD pid ) { DWORD UOTId = 0; DWORD exitCode; HANDLE hProc = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, pid ); UOProcId = 0; do { Sleep( 10 ); HWND hWnd = FindWindow( "Ultima Online", NULL ); while ( hWnd != NULL ) { UOTId = GetWindowThreadProcessId( hWnd, &UOProcId ); if ( UOProcId == pid ) break; hWnd = FindWindowEx( NULL, hWnd, "Ultima Online", NULL ); } if ( UOProcId != pid || hWnd == NULL ) { hWnd = FindWindow( "Ultima Online Third Dawn", NULL ); while ( hWnd != NULL ) { UOTId = GetWindowThreadProcessId( hWnd, &UOProcId ); if (UOProcId == pid) break; hWnd = FindWindowEx( NULL, hWnd, "Ultima Online Third Dawn", NULL ); } } GetExitCodeProcess( hProc, &exitCode ); } while ( UOProcId != pid && exitCode == STILL_ACTIVE ); CloseHandle( hProc ); } DLLFUNCTION void Shutdown( bool close ) { Log( "Shutdown" ); if (hUOAWnd && IsWindow(hUOAWnd)) { UnregisterClass("UOASSIST-TP-MSG-WND", hInstance); SendMessage(hUOAWnd, WM_CLOSE, 0, 0); hUOAWnd = NULL; } if ( hUOWindow && IsWindow( hUOWindow ) ) PostMessage( hUOWindow, WM_QUIT, 0, 0 ); } DLLFUNCTION int GetUOProcId() { return UOProcId; } DLLFUNCTION HANDLE GetCommMutex() { return CommMutex; } // totalsend and totalrecv are NOT mutex sync'd DLLFUNCTION unsigned int TotalOut() { if ( pShared ) return pShared->TotalSend; else return 0; } DLLFUNCTION unsigned int TotalIn() { if ( pShared ) return pShared->TotalRecv; else return 0; } DLLFUNCTION void CalibratePosition( uint16_t x, uint16_t y, uint16_t z ) { Position pos; COPYDATASTRUCT copydata; pos.x = x; pos.y = y; pos.z = z; copydata.dwData = (ULONG_PTR)UONET_MESSAGE_COPYDATA::POSITION; copydata.cbData = sizeof(pos); copydata.lpData = &pos; SendMessage(hUOWindow, WM_COPYDATA, (WPARAM)hRazorWnd, (LPARAM)©data); } /* These variables are used in the UO client process address space */ /* 7.0.15.1 Client, Modified for Outlands Located at 0x00A72BD8 */ #pragma pack(1) struct PositionOutlands { uint16_t x; uint16_t y; uint16_t z; }; static_assert(sizeof(struct PositionOutlands) == 6, "Incorrect size\n"); /* 5.0.8.3 Client, UO Renaissance Located at address 0x007D7C58 */ #pragma pack(1) struct PositionUOR { uint32_t z; uint32_t y; uint32_t x; }; static_assert(sizeof(struct PositionUOR) == 12, "Incorrect size\n"); // UOR client this is at 0x007D7C60 (x is) // Fall back search. Not always reliable. #pragma pack(1) struct PositionGeneric { uint32_t z; uint32_t y; uint32_t x; }; static_assert(sizeof(struct PositionGeneric) == 12, "Incorrect size\n"); Position g_TestPosition = {}; Position g_LastPosition = {}; void *g_ClientMem = nullptr; static void CheckPosition() { if (g_ClientMem == nullptr) { if (strncmp(pShared->UOVersion, "5.0.8.3", sizeof(pShared->UOVersion)) == 0) { /* On UOR, we know exactly where the position is. */ g_ClientMem = (void *)(0x007D7C60); } else if (strncmp(pShared->UOVersion, "7.0.15.1", sizeof(pShared->UOVersion)) == 0) { /* Similarly on Outlands, we know where the position is. */ g_ClientMem = (void *)(0x00A72BD8); } else { /* Scan the region of memory in the client known to hold the player's position */ for (uintptr_t addr = 0x00500000; addr < 0x00C00000; addr += 2) { if (IsBadReadPtr((void*)addr, sizeof(PositionGeneric))) { break; } PositionGeneric* mem = (PositionGeneric*)addr; if (mem->x == g_TestPosition.x && mem->y == g_TestPosition.y && mem->z == g_TestPosition.z) { g_ClientMem = mem; break; } } } } if (g_ClientMem != nullptr) { Position pos; if (strncmp(pShared->UOVersion, "5.0.8.3", sizeof(pShared->UOVersion)) == 0) { PositionUOR *mem = (PositionUOR*)g_ClientMem; pos.x = mem->x; pos.y = mem->y; pos.z = mem->z; } else if (strncmp(pShared->UOVersion, "7.0.15.1", sizeof(pShared->UOVersion)) == 0) { /* Similarly on Outlands, we know where the position is. */ PositionOutlands *mem = (PositionOutlands*)g_ClientMem; pos.x = mem->x; pos.y = mem->y; pos.z = mem->z; } else { PositionGeneric *mem = (PositionGeneric*)g_ClientMem; pos.x = mem->x; pos.y = mem->y; pos.z = mem->z; } if (pos.x != g_LastPosition.x || pos.y != g_LastPosition.y || pos.z != g_LastPosition.z) { /* Inform Razor of a position change */ COPYDATASTRUCT copydata; copydata.dwData = (ULONG_PTR)UONET_MESSAGE_COPYDATA::POSITION; copydata.cbData = sizeof(pos); copydata.lpData = &pos; SendMessage(hRazorWnd, WM_COPYDATA, (WPARAM)hUOWindow, (LPARAM)©data); } g_LastPosition = pos; } } VOID CALLBACK OnTick(HWND hwnd, UINT Message, UINT TimerId, DWORD dwTime) { /* Scan client memory for position updates */ CheckPosition(); /* Post a message to Razor to indicate a game loop tick.*/ PostMessage(hRazorWnd, WM_UONETEVENT, ON_TICK, 0); } SIZE *SizePtr = NULL; void OnSetUOWindowSize( int width ) { if ( width != 800 && width != 600 ) // in case it actually the height for some reason { SizePtr->cx = 640; SizePtr->cy = 480; } else { *SizePtr = DesiredSize; } } DLLFUNCTION void OnAttach( void *params, int paramsLen ) { int count = 0; DWORD addr = 0, oldProt; MemFinder mf; UOProcId = GetCurrentProcessId(); if ( !CreateSharedMemory() ) return; CopyFailed = false; mf.AddEntry("UoClientApp", 12, 0x00500000); mf.AddEntry("report\0", 8, 0x00500000); mf.AddEntry("Another copy of ", 16, 0x00500000); mf.AddEntry("\x00\x68\x88\x13\x00\x00\x56\xE8", 8); mf.AddEntry("\x68\x88\x13\x00\x00", 5, 16, 0x00400000); // (end of a push offset), push 5000, push esi mf.AddEntry("Electronic Arts Inc.", 20); mf.AddEntry("intro.bik", 10); mf.AddEntry("osilogo.bik", 12); mf.AddEntry("\x80\x02\x00\x00\xE0\x01\x00\x00", 8, 0x00500000); // current screen size mf.AddEntry("\x8B\x44\x24\x04\xBA\x80\x02\x00\x00\x3B\xC2\xB9\xE0\x01\x00\x00", 16); // resize screen function mf.AddEntry("\x57\x56\x6A\x00\x6A\x00\xE8", 7); // redraw screen/edge function mf.AddEntry(PACKET_TBL_STR, PACKET_TS_LEN, 10, 0x00500000); mf.AddEntry(CRYPT_KEY_STR, CRYPT_KEY_LEN); mf.AddEntry(CRYPT_KEY_STR_3D, CRYPT_KEY_3D_LEN); mf.AddEntry(CRYPT_KEY_STR_NEW, CRYPT_KEY_NEW_LEN); mf.AddEntry(CRYPT_KEY_STR_MORE_NEW, CRYPT_KEY_MORE_NEW_LEN); mf.AddEntry("UO Version %s", 12); mf.AddEntry("Multiple Instances Running", 26, 0x00500000); memcpy( pShared->PacketTable, StaticPacketTable, 256*sizeof(short) ); mf.Execute(); SizePtr = (SIZE*)mf.GetAddress("\x80\x02\x00\x00\xE0\x01\x00\x00", 8); if (SizePtr) { addr = mf.GetAddress("\x8B\x44\x24\x04\xBA\x80\x02\x00\x00\x3B\xC2\xB9\xE0\x01\x00\x00", 16); if (addr) { int i; DWORD origAddr = addr; VirtualProtect((void*)origAddr, 128, PAGE_EXECUTE_READWRITE, &oldProt); for (i = 16; i < 128; i++) { if (*((BYTE*)(addr + i)) == 0xE9) // find the first jmp { memset((void*)addr, 0x90, i); // nop // mov eax, dword [esp+4] *((BYTE*)(addr + 0)) = 0x8B; // mov *((BYTE*)(addr + 1)) = 0x44; // eax *((BYTE*)(addr + 2)) = 0x24; // [esp *((BYTE*)(addr + 3)) = 0x04; // +4] addr += 4; *((BYTE*)addr) = 0x50; // push eax addr++; // call OnSetUOWindowSize *((BYTE*)addr) = 0xE8; *((DWORD*)(addr + 1)) = ((DWORD)OnSetUOWindowSize) - (addr + 5); addr += 5; break; } } VirtualProtect((void*)origAddr, 128, oldProt, &oldProt); } } int i = 0; while ((addr = mf.GetAddress(PACKET_TBL_STR, PACKET_TS_LEN, i++)) != 0) { memset(pShared->PacketTable, 0xFF, 512); addr += PACKET_TBL_OFFSET; if (IsBadReadPtr((void*)addr, sizeof(ClientPacketInfo) * 128)) continue; ClientPacketInfo *tbl = (ClientPacketInfo*)addr; if (tbl[0].Id == 1 || tbl[0].Id == 2 || tbl[0].Id >= 256) continue; // this one isnt in order because OSI are idiots (0xF8) pShared->PacketTable[tbl[0].Id] = tbl[0].Length; int idx = 1; bool got1 = false, got2 = false; for (int prev = 0; prev < 255 && idx < 256; idx++) { if (IsBadReadPtr((void*)(tbl + idx), sizeof(ClientPacketInfo)) || tbl[idx].Id <= prev || tbl[idx].Id >= 256) { break; } got1 |= tbl[idx].Id == 1 && tbl[idx].Length == StaticPacketTable[1]; got2 |= tbl[idx].Id == 2 && tbl[idx].Length == StaticPacketTable[2]; prev = tbl[idx].Id; if (pShared->PacketTable[prev] == 0xFFFF) pShared->PacketTable[prev] = tbl[idx].Length; } if (idx < 128 || !got1 || !got2) continue; else break; } if (!addr) CopyFailed = true; addr = mf.GetAddress(CRYPT_KEY_STR, CRYPT_KEY_LEN); if (!addr) { addr = mf.GetAddress(CRYPT_KEY_STR_NEW, CRYPT_KEY_NEW_LEN); if (!addr) { addr = mf.GetAddress(CRYPT_KEY_STR_MORE_NEW, CRYPT_KEY_MORE_NEW_LEN); if (!addr) { addr = mf.GetAddress(CRYPT_KEY_STR_3D, CRYPT_KEY_3D_LEN); if (addr) { LoginEncryption::SetKeys((const DWORD*)(addr + CRYPT_KEY_3D_LEN), (const DWORD*)(addr + CRYPT_KEY_3D_LEN + 19)); ClientType = THREED; } else { CopyFailed = true; } } else { addr += CRYPT_KEY_MORE_NEW_LEN; const DWORD *pKey1 = *((DWORD**)addr); const DWORD *pKey2 = pKey1 + 1; if (IsBadReadPtr(pKey2, 4) || IsBadReadPtr(pKey1, 4)) CopyFailed = true; else LoginEncryption::SetKeys(pKey1, pKey2); } } else { addr += CRYPT_KEY_NEW_LEN; const DWORD *pKey1 = *((DWORD**)addr); const DWORD *pKey2 = pKey1 - 1; if (IsBadReadPtr(pKey2, 4) || IsBadReadPtr(pKey1, 4)) CopyFailed = true; else LoginEncryption::SetKeys(pKey1, pKey2); } } else { LoginEncryption::SetKeys((const DWORD*)(addr + CRYPT_KEY_LEN), (const DWORD*)(addr + CRYPT_KEY_LEN + 6)); } // Multi UO addr = mf.GetAddress("UoClientApp", 12); if (addr) { VirtualProtect((void*)addr, 12, PAGE_READWRITE, &oldProt); _snprintf((char*)addr, 12, "UoApp%d", UOProcId); VirtualProtect((void*)addr, 12, oldProt, &oldProt); } addr = mf.GetAddress("Another copy of ", 16); if (addr) { char buff[5]; buff[0] = 0x68; // push *((DWORD*)(&buff[1])) = addr; addr = 0x00400000; do { addr = MemFinder::Find(buff, 5, addr, 0x00600000); if (addr) { if ((*((unsigned char*)(addr - 5))) == 0x74) // jz? MemoryPatch(addr - 5, 0xEB, 1); // change to jmp addr += 5; // skip ahead to find the next instance } } while (addr > 0 && addr < 0x00600000); } addr = mf.GetAddress("Multiple Instances Running", 26); if (addr) { char buff[5]; buff[0] = 0x68; // push *((DWORD*)(&buff[1])) = addr; addr = 0x00400000; do { addr = MemFinder::Find(buff, 5, addr, 0x00600000); if (addr) { char in = (*((unsigned char*)(addr - 4))); if (in == 0x74 || in == 0x75) { // jz or jnz MemoryPatch(addr - 4, 0xEB, 1); // change to jmp } addr += 5; // skip ahead to find the next instance } } while (addr > 0 && addr < 0x00600000); } addr = mf.GetAddress("report\0", 8); if (addr) { VirtualProtect((void*)addr, 12, PAGE_READWRITE, &oldProt); _snprintf((char*)addr, 8, "r%d", UOProcId); VirtualProtect((void*)addr, 12, oldProt, &oldProt); } // Splash screen crap: for (int i = 0; i < 16; i++) { addr = mf.GetAddress("\x68\x88\x13\x00\x00", 5, i); if (!addr) break; for (int e = 5; e < 24; e++) { if (*((BYTE*)(addr + e)) == 0x8B && *((BYTE*)(addr + e + 1)) == 0x3D) { MemoryPatch(addr + 1, 0x00000001); // change 5000ms to 1ms i = 99; break; } } } addr = mf.GetAddress("intro.bik", 10); if (addr) MemoryPatch(addr, "intro.SUX", 10); addr = mf.GetAddress("ostlogo.bik", 12); if (addr) MemoryPatch(addr, "osilogo.SUX", 12); addr = mf.GetAddress("Electronic Arts Inc.", 20); if (addr) { addr -= 7; VirtualProtect((void*)addr, 52, PAGE_EXECUTE_READWRITE, &oldProt); strncpy((char*)addr, "[Powered by Razor - The cutting edge UO Assistant]\0", 52); VirtualProtect((void*)addr, 52, oldProt, &oldProt); } NativeGetUOVersion = NULL; if (ClientType == TWOD) { addr = mf.GetAddress("UO Version %s", 12); if (addr) { char temp[8]; temp[0] = 0x68; *((DWORD*)&temp[1]) = addr; addr = MemFinder::Find(temp, 5); if (addr) { count = 0; while (*((BYTE*)addr) != 0xE8 && count < 128) { addr--; count++; } if (*((BYTE*)addr) == 0xE8) NativeGetUOVersion = (GetUOVersionFunc)((addr + 5) + *((DWORD*)(addr + 1))); } } } } DLLFUNCTION void SetServer( unsigned int addr, unsigned short port ) { if (pShared) { pShared->ServerIP = addr; pShared->ServerPort = port; } } DLLFUNCTION const char *GetUOVersion() { if ( pShared ) return pShared->UOVersion; else return ""; } bool CreateSharedMemory() { if (pShared) return true; char name[256]; CommMutex = NULL; hFileMap = NULL; pShared = NULL; Log( "Creating shared mem, proc: %x", UOProcId ); sprintf( name, "UONetSharedCOMM_%x", UOProcId ); CommMutex = CreateMutex( NULL, FALSE, name ); if ( !CommMutex ) return false; for (int i = 0; i < 30; i++) { sprintf(name, "UONetSharedFM_%x%d", UOProcId, i); hFileMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(SharedMemory), name); if (!hFileMap || hFileMap == INVALID_HANDLE_VALUE) { auto err = GetLastError(); continue; } pShared = (SharedMemory*)MapViewOfFile(hFileMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(SharedMemory)); if (!pShared) { auto err = GetLastError(); continue; } break; } if (!pShared) { return false; } //memset( pShared, 0, sizeof(SharedMemory) ); return true; } void CloseSharedMemory() { Log( "Close shared memory" ); if ( hWndProcRetHook ) UnhookWindowsHookEx( hWndProcRetHook ); if ( hGetMsgHook ) UnhookWindowsHookEx( hGetMsgHook ); if ( CommMutex ) CloseHandle( CommMutex ); CommMutex = NULL; if ( pShared ) UnmapViewOfFile( pShared ); pShared = NULL; if ( hFileMap ) CloseHandle( hFileMap ); hFileMap = NULL; //these are shared vars hWndProcRetHook = NULL; hGetMsgHook = NULL; FreeArt(); delete ClientCrypt; delete ClientLogin; delete ServerCrypt; delete ServerLogin; ClientCrypt = NULL; ClientLogin = NULL; ServerCrypt = NULL; ServerLogin = NULL; } void CreateEncryption() { delete ClientCrypt; delete ClientLogin; delete ServerCrypt; delete ServerLogin; if ( ClientEncrypted ) { ClientCrypt = new OSIEncryption(); ClientLogin = new LoginEncryption(); } if ( ServerEncrypted ) { ServerCrypt = new OSIEncryption(); ServerLogin = new LoginEncryption(); } } inline void Maintenance( Buffer &buff ) { if ( buff.Length <= 0 ) { buff.Start = 0; buff.Length = 0; } else if ( buff.Start > SHARED_BUFF_SIZE / 2 ) { //shift all the data to the begining of the buffer memmove( buff.Buff, &buff.Buff[buff.Start], buff.Length ); buff.Start = 0; } } int RecvData() { int len = SHARED_BUFF_SIZE; char buff[SHARED_BUFF_SIZE]; int ackLen = (*(NetIOFunc)OldRecv)(CurrentConnection,buff,len,0); if ( ackLen == SOCKET_ERROR ) { if ( WSAGetLastError() != WSAEWOULDBLOCK ) { WaitForSingleObject( CommMutex, INFINITE ); pShared->ForceDisconn = true; ReleaseMutex( CommMutex ); } else { WSASetLastError( WSAEWOULDBLOCK ); } ackLen = -1; } else if ( ackLen > 0 ) { if ( FirstRecv ) { Compression::Reset(); FirstRecv = false; } WaitForSingleObject( CommMutex, INFINITE ); pShared->TotalRecv += ackLen; if ( LoginServer ) { memcpy( &pShared->InRecv.Buff[pShared->InRecv.Start+pShared->InRecv.Length], buff, ackLen ); pShared->InRecv.Length += ackLen; } else { if ( ServerEncrypted ) ServerCrypt->DecryptFromServer( (BYTE*)buff, (BYTE*)buff, ackLen ); int blen = Compression::Decompress( (char*)&pShared->InRecv.Buff[pShared->InRecv.Start+pShared->InRecv.Length], buff, ackLen ); pShared->InRecv.Length += blen; if ( !ServerNegotiated && !InGame && pShared && pShared->AllowNegotiate ) { int pos = pShared->InRecv.Start; unsigned char *p_buff = &pShared->InRecv.Buff[pos]; while ( pos < pShared->InRecv.Length ) { int left = pShared->InRecv.Length - pos; int p_len = GetPacketLength( p_buff, left ); if ( *p_buff == 0xA9 && p_len >= 1+2+1+30+30 && p_len <= left ) { // character list unsigned char hash[16], test[16]; memcpy( pShared->AuthBits, p_buff + 1+2+1+30+1, 8 ); if ( p_buff[3] > 1 ) memcpy( hash, p_buff + 1+2+1+30+30+30+1, 16 ); else memcpy( hash, p_buff + 1+2+1+30+1+8, 16 ); for ( int i = 0; i < p_buff[3]; i++ ) memset( p_buff + 1+2+1+ 30 + 60*i, 0, 30 ); if ( memcmp( hash, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16 ) != 0 ) { OSIEncryption::MD5( p_buff, p_len, test ); ServerNegotiated = memcmp( hash, test, 16 ) == 0; } if ( !ServerNegotiated ) memset( pShared->AuthBits, 0, 8 ); Forwarding = Forwarded = false; break; } if ( p_len <= 0 ) { break; } else { pos += p_len; p_buff += p_len; } } } } ReleaseMutex( CommMutex ); SendMessage( hRazorWnd, WM_UONETEVENT, RECV, 0 ); } return ackLen; } int HookRecv( SOCKET sock, char *buff, int len, int flags ) { int ackLen; if ( sock == CurrentConnection && CurrentConnection ) { WaitForSingleObject( CommMutex, INFINITE ); if ( pShared->ForceDisconn && pShared->OutRecv.Length <= 0 ) { ReleaseMutex( CommMutex ); WSASetLastError( WSAECONNRESET ); return -1; } if ( LoginServer ) { if ( pShared->OutRecv.Length > 0 ) { ackLen = pShared->OutRecv.Length; memcpy( buff, &pShared->OutRecv.Buff[pShared->OutRecv.Start], ackLen ); if ( ((BYTE)buff[0]) == 0x8C ) LoginServer = false; if ( Forwarding ) Seeded = Forwarded = true; pShared->OutRecv.Start += ackLen; pShared->OutRecv.Length -= ackLen; } else { ackLen = 0; } } else { ackLen = 0; while ( pShared->OutRecv.Length > 0 ) { int blen = GetPacketLength( &pShared->OutRecv.Buff[pShared->OutRecv.Start], pShared->OutRecv.Length ); if ( blen <= 0 || blen > pShared->OutRecv.Length || ackLen+blen > len ) break; ackLen += Compression::Compress( &buff[ackLen], (char*)&pShared->OutRecv.Buff[pShared->OutRecv.Start], blen ); pShared->OutRecv.Start += blen; pShared->OutRecv.Length -= blen; } if ( ClientEncrypted && ackLen > 0 ) ClientCrypt->EncryptForClient( (BYTE*)buff, (BYTE*)buff, ackLen ); } Maintenance( pShared->InRecv ); Maintenance( pShared->OutRecv ); ReleaseMutex( CommMutex ); if ( ackLen == 0 ) { WSASetLastError( WSAEWOULDBLOCK ); return -1; } else { return ackLen; } } else { return (*(NetIOFunc)OldRecv)(sock,buff,len,flags); } } int SkipSendData = 0; int HookSend( SOCKET sock, char *buff, int len, int flags ) { int ackLen; if ( sock == CurrentConnection && CurrentConnection ) { if ( !Seeded ) { if ( len > 0 && ((BYTE)*buff) == ((BYTE)0xEF) ) SkipSendData = 16; if ( len >= 4 ) { Seeded = true; CryptSeed = *((DWORD*)buff); if ( ServerEncrypted ) { ServerCrypt->Initialize( CryptSeed ); ServerLogin->Initialize( (BYTE*)&CryptSeed ); } if ( ClientEncrypted ) { ClientCrypt->Initialize( CryptSeed ); ClientLogin->Initialize( (BYTE*)&CryptSeed ); } Compression::Reset(); } ackLen = (*(NetIOFunc)OldSend)(sock,buff,len,flags); pShared->TotalSend += len; } else if ( SkipSendData < len ) { SkipSendData = 0; if ( FirstSend ) { FirstSend = false; if ( ClientEncrypted ) LoginServer = ClientLogin->TestForLogin( (BYTE)buff[0] ); else LoginServer = LoginEncryption::IsLoginByte( (BYTE)buff[0] ); if ( LoginServer ) Forwarding = Forwarded = false; } WaitForSingleObject( CommMutex, INFINITE ); if (ClientEncrypted) { if ( Forwarded ) { CryptSeed = LoginEncryption::GenerateBadSeed( CryptSeed ); ClientCrypt->Initialize( CryptSeed ); ClientCrypt->DecryptFromClient( (BYTE*)buff, (BYTE*)(&pShared->InSend.Buff[pShared->InSend.Start+pShared->InSend.Length]), len ); ClientLogin->Decrypt( (BYTE*)(&pShared->InSend.Buff[pShared->InSend.Start+pShared->InSend.Length]), (BYTE*)(&pShared->InSend.Buff[pShared->InSend.Start+pShared->InSend.Length]), len ); LoginServer = Forwarding = Forwarded = false; } else { if ( LoginServer ) { ClientLogin->Decrypt( (BYTE*)(buff), (BYTE*)(&pShared->InSend.Buff[pShared->InSend.Start+pShared->InSend.Length]), len ); if ( ((BYTE)pShared->InSend.Buff[pShared->InSend.Start+pShared->InSend.Length]) == 0xA0 ) Forwarding = true; } else { ClientCrypt->DecryptFromClient((BYTE*)(buff), (BYTE*)(&pShared->InSend.Buff[pShared->InSend.Start + pShared->InSend.Length]), len); } } } else memcpy( &pShared->InSend.Buff[pShared->InSend.Start+pShared->InSend.Length], buff, len ); pShared->InSend.Length += len; ReleaseMutex( CommMutex ); SendMessage( hRazorWnd, WM_UONETEVENT, SEND, 0 ); WaitForSingleObject( CommMutex, INFINITE ); FlushSendData(); Maintenance( pShared->InSend ); ReleaseMutex( CommMutex ); ackLen = len;//lie and say we sent it all -- or should we tell the truth? (probably not since then it could try to send it again..) } else { ackLen = (*(NetIOFunc)OldSend)(sock,buff,len,flags); pShared->TotalSend += len; SkipSendData -= len; } } else { ackLen = (*(NetIOFunc)OldSend)(sock,buff,len,flags); } return ackLen; } #define RAZOR_ID_KEY "\x9\x11\x83+\x4\x17\x83\x5\x24\x85\x7\x17\x87\x6\x19\x88" #define RAZOR_ID_KEY_LEN 16 void FlushSendData() { WaitForSingleObject( CommMutex, INFINITE ); if ( pShared->OutSend.Length > 0 && CurrentConnection ) { int ackLen = 0; int outLen = pShared->OutSend.Length; if ( !InGame && !LoginServer ) { int pos = pShared->OutSend.Start; unsigned char *buff = &pShared->OutSend.Buff[pos]; while ( pos < outLen ) { int left = pShared->OutSend.Length - pos; int len = GetPacketLength( buff, left ); if ( *buff == 0x5D && len >= 1+4+30+30 && len <= left ) { // play character if ( pShared->AllowNegotiate && ServerNegotiated ) { // the first 2 bytes are 0 // the next 4 bytes are "flags" which say the user's client type (lbr,t2a,aos,etc) // the rest are ignored, so we can use them for auth memcpy( buff + 1 + 4 + 30 + 2 + 4, pShared->AuthBits, 8 ); memcpy( buff + 1 + 4 + 30 + 2 + 4 + 8, RAZOR_ID_KEY, RAZOR_ID_KEY_LEN ); } InGame = true; break; } else if ( *buff == 0x00 && (*((DWORD*)&buff[1])) == 0xEDEDEDED && len >= 1+4+4+1+30+30 && len <= left ) { // char creation if ( pShared->AllowNegotiate && ServerNegotiated ) { memcpy( buff + 1 + 4 + 4 + 1 + 30 + 15, pShared->AuthBits, 8 ); memcpy( buff + 1 + 4 + 4 + 1 + 30 + 15 + 8, RAZOR_ID_KEY, min( 7, RAZOR_ID_KEY_LEN ) ); } InGame = true; break; } if ( len <= 0 ) { break; } else { pos += len; buff += len; } } // END while } // END if ( !InGame && !LoginServer if ( ServerEncrypted ) { if ( tempBuff == NULL ) tempBuff = new char[SHARED_BUFF_SIZE]; if ( LoginServer ) ServerLogin->Encrypt( (BYTE*)&pShared->OutSend.Buff[pShared->OutSend.Start], (BYTE*)tempBuff, outLen ); else ServerCrypt->EncryptForServer( (BYTE*)&pShared->OutSend.Buff[pShared->OutSend.Start], (BYTE*)tempBuff, outLen ); ackLen = (*(NetIOFunc)OldSend)(CurrentConnection,tempBuff,outLen,0); } else { ackLen = (*(NetIOFunc)OldSend)(CurrentConnection,(char*)&pShared->OutSend.Buff[pShared->OutSend.Start],outLen,0); } if ( ackLen == SOCKET_ERROR ) { if ( WSAGetLastError() != WSAEWOULDBLOCK ) pShared->ForceDisconn = true; } else //if ( ackLen >= 0 ) { pShared->TotalSend += ackLen; pShared->OutSend.Start += ackLen; pShared->OutSend.Length -= ackLen; } } Maintenance( pShared->OutSend ); ReleaseMutex( CommMutex ); } int HookConnect( SOCKET sock, const sockaddr *addr, int addrlen ) { int retVal; if ( addr && addrlen >= sizeof(sockaddr_in) ) { const sockaddr_in *old_addr = (const sockaddr_in *)addr; sockaddr_in useAddr; memcpy( &useAddr, old_addr, sizeof(sockaddr_in) ); if ( !Forwarded && pShared->ServerIP != 0 ) { useAddr.sin_addr.S_un.S_addr = pShared->ServerIP; useAddr.sin_port = htons( pShared->ServerPort ); } retVal = (*(ConnFunc)OldConnect)( sock, (sockaddr*)&useAddr, sizeof(sockaddr_in) ); if ( retVal != SOCKET_ERROR ) { Log( "Connecting to %i", sock ); CreateEncryption(); Seeded = false; LoginServer = false; FirstRecv = true; FirstSend = true; Forwarding = Forwarded = false; WaitForSingleObject( CommMutex, INFINITE ); CurrentConnection = sock; pShared->OutRecv.Length = pShared->InRecv.Length = pShared->OutSend.Length = pShared->InSend.Length = 0; pShared->ForceDisconn = false; ReleaseMutex( CommMutex ); PostMessage( hRazorWnd, WM_UONETEVENT, CONNECT, useAddr.sin_addr.S_un.S_addr ); } } else { retVal = (*(ConnFunc)OldConnect)( sock, addr, addrlen ); } return retVal; } int HookCloseSocket( SOCKET sock ) { int retVal = (*(CLSFunc)OldCloseSocket)( sock ); if ( sock == CurrentConnection && sock != 0 ) { Log( "Closing socket %i", sock ); CurrentConnection = 0; WaitForSingleObject( CommMutex, INFINITE ); pShared->OutRecv.Length = pShared->InRecv.Length = pShared->OutSend.Length = pShared->InSend.Length = 0; pShared->TotalSend = pShared->TotalRecv = 0; pShared->ForceDisconn = false; ReleaseMutex( CommMutex ); ServerNegotiated = false; InGame = false; memset( pShared->AuthBits, 0, 8 ); PostMessage( hRazorWnd, WM_UONETEVENT, DISCONNECT, 0 ); } return retVal; } int HookSelect( int ndfs, fd_set *readfd, fd_set *writefd, fd_set *exceptfd, const struct timeval *timeout ) { bool checkRecv = false; bool checkErr = false; bool modified = false; int realRet = 0; int myRet = 0; if ( CurrentConnection ) { if ( readfd != NULL ) checkRecv = FD_ISSET( CurrentConnection, readfd ); if ( exceptfd != NULL ) checkErr = FD_ISSET( CurrentConnection, exceptfd ); } timeval myTimeout; if ( SmartCPU ) { int length = 0; if ( Active ) { LARGE_INTEGER end; QueryPerformanceCounter( &end ); length = int( 1000000.0 * ((end.QuadPart-Counter.QuadPart)/double(PerfFreq.QuadPart)) ); } if ( length < 33333 ) { myTimeout.tv_sec = 0; myTimeout.tv_usec = 33333 - length; timeout = &myTimeout; } } realRet = (*(SelectFunc)OldSelect)( ndfs, readfd, writefd, exceptfd, timeout ); if ( SmartCPU ) QueryPerformanceCounter( &Counter ); if ( checkRecv ) { if ( FD_ISSET( CurrentConnection, readfd ) ) { FD_CLR( CurrentConnection, readfd ); RecvData(); realRet--; } WaitForSingleObject( CommMutex, INFINITE ); if ( pShared->OutRecv.Length > 0 || ( pShared->ForceDisconn ) ) { FD_SET( CurrentConnection, readfd ); myRet++; } ReleaseMutex( CommMutex ); } if ( checkErr && !FD_ISSET( CurrentConnection, exceptfd ) ) { WaitForSingleObject( CommMutex, INFINITE ); if ( pShared->ForceDisconn && pShared->OutRecv.Length <= 0 ) { FD_SET( CurrentConnection, exceptfd ); myRet++; } ReleaseMutex( CommMutex ); } if ( realRet < 0 ) { return myRet; } else { return realRet + myRet; } } bool HookFunction( const char *Dll, const char *FuncName, int Ordinal, unsigned long NewAddr, unsigned long *OldAddr, unsigned long *PatchAddr ) { DWORD baseAddr = (DWORD)GetModuleHandle(NULL); if ( !baseAddr ) return false; IMAGE_DOS_HEADER *idh = (IMAGE_DOS_HEADER *)baseAddr; IMAGE_FILE_HEADER *ifh = (IMAGE_FILE_HEADER *)(baseAddr + idh->e_lfanew + sizeof(DWORD)); IMAGE_OPTIONAL_HEADER *ioh = (IMAGE_OPTIONAL_HEADER *)((DWORD)(ifh) + sizeof(IMAGE_FILE_HEADER)); IMAGE_IMPORT_DESCRIPTOR *iid = (IMAGE_IMPORT_DESCRIPTOR *)(baseAddr + ioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while( iid->Name ) { if( _stricmp( Dll, (char *)(baseAddr + iid->Name) ) == 0 ) { IMAGE_THUNK_DATA * pThunk = (IMAGE_THUNK_DATA *)((DWORD)iid->OriginalFirstThunk + baseAddr); IMAGE_THUNK_DATA * pThunk2 = (IMAGE_THUNK_DATA *)((DWORD)iid->FirstThunk + baseAddr); while( pThunk->u1.AddressOfData ) { char *name = NULL; int ord; if( pThunk->u1.Ordinal & 0x80000000 ) { // Imported by ordinal only: ord = pThunk->u1.Ordinal & 0xFFFF; } else { // Imported by name (with ordinal hint) IMAGE_IMPORT_BY_NAME * pName = (IMAGE_IMPORT_BY_NAME *)((DWORD)pThunk->u1.AddressOfData + baseAddr); ord = pName->Hint; name = (char *)pName->Name; } if ( ord == Ordinal || ( name && FuncName && !strcmp( name, FuncName ) ) ) { *OldAddr = (unsigned long)pThunk2->u1.Function; *PatchAddr = (unsigned long)(&pThunk2->u1.Function); MemoryPatch( *PatchAddr, NewAddr ); return true; } pThunk++; pThunk2++; } } iid++; } return false; } bool FindFunction( const char *Dll, const char *FuncName, int Ordinal, unsigned long *ImpAddr, unsigned long *CallAddr ) { DWORD baseAddr = (DWORD)GetModuleHandle(NULL); if ( !baseAddr ) return false; IMAGE_DOS_HEADER *idh = (IMAGE_DOS_HEADER *)baseAddr; IMAGE_FILE_HEADER *ifh = (IMAGE_FILE_HEADER *)(baseAddr + idh->e_lfanew + sizeof(DWORD)); IMAGE_OPTIONAL_HEADER *ioh = (IMAGE_OPTIONAL_HEADER *)((DWORD)(ifh) + sizeof(IMAGE_FILE_HEADER)); IMAGE_IMPORT_DESCRIPTOR *iid = (IMAGE_IMPORT_DESCRIPTOR *)(baseAddr + ioh->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); while( iid->Name ) { if( _stricmp( Dll, (char *)(baseAddr + iid->Name) ) == 0 ) { IMAGE_THUNK_DATA * pThunk = (IMAGE_THUNK_DATA *)((DWORD)iid->OriginalFirstThunk + baseAddr); IMAGE_THUNK_DATA * pThunk2 = (IMAGE_THUNK_DATA *)((DWORD)iid->FirstThunk + baseAddr); while( pThunk->u1.AddressOfData ) { char *name = NULL; int ord; if( pThunk->u1.Ordinal & 0x80000000 ) { // Imported by ordinal only: ord = pThunk->u1.Ordinal & 0xFFFF; } else { // Imported by name (with ordinal hint) IMAGE_IMPORT_BY_NAME * pName = (IMAGE_IMPORT_BY_NAME *)((DWORD)pThunk->u1.AddressOfData + baseAddr); ord = pName->Hint; name = (char *)pName->Name; } if ( ord == Ordinal || ( name && FuncName && !strcmp( name, FuncName ) ) ) { *ImpAddr = (unsigned long)pThunk2->u1.Function; *CallAddr = (unsigned long)(&pThunk2->u1.Function); return true; } pThunk++; pThunk2++; } } iid++; } return false; } #define NOTO_HUE_STR "\0\0\0\0\x59\0\0\0\x3F\0\0\0\xb2\x03\0\0" #define NOTO_HUE_LEN 16 DWORD NotoLoc = 0; void SetCustomNotoHue( int hue ) { if ( !NotoLoc ) { NotoLoc = MemFinder::Find( NOTO_HUE_STR, NOTO_HUE_LEN ); if ( !NotoLoc ) NotoLoc = 0xFFFFFFFF; } if ( NotoLoc != 0xFFFFFFFF ) *((int*)(NotoLoc + 8*4)) = hue; } bool PatchMemory( void ) { Log( "Patching client functions." ); return HookFunction( "wsock32.dll", "closesocket", 3, (unsigned long)HookCloseSocket, &OldCloseSocket, &CloseSocketAddress ) && HookFunction( "wsock32.dll", "connect", 4, (unsigned long)HookConnect, &OldConnect, &ConnectAddress ) && HookFunction( "wsock32.dll", "recv", 16, (unsigned long)HookRecv, &OldRecv, &RecvAddress ) && HookFunction( "wsock32.dll", "select", 18, (unsigned long)HookSelect, &OldSelect, &SelectAddress ) && HookFunction( "wsock32.dll", "send", 19, (unsigned long)HookSend, &OldSend, &SendAddress ) ; } void MemoryPatch( unsigned long Address, unsigned long value ) { MemoryPatch( Address, &value, 4 ); // sizeof(int) } void MemoryPatch( unsigned long Address, int value, int numBytes ) { MemoryPatch( Address, &value, numBytes ); } void MemoryPatch( unsigned long Address, const void *value, int length ) { DWORD OldProtect; if ( !VirtualProtect( (void *)Address, length, PAGE_READWRITE, &OldProtect ) ) return; memcpy( (void *)Address, value, length ); VirtualProtect( (void *)Address, length, OldProtect, &OldProtect ); } bool CheckParent( HWND hCheck, HWND hComp ) { hCheck = GetParent( hCheck ); while ( hCheck != hComp && hCheck != NULL ) hCheck = GetParent( hCheck ); return ( hCheck == hComp ); } void MessageProc( HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam, MSG *pMsg ) { HWND hFore; switch ( nMsg ) { // Custom messages case WM_PROCREADY: hRazorWnd = (HWND)lParam; UOProcId = GetCurrentProcessId(); hUOWindow = hWnd; if (!pShared) // If this failed the first time or was not run at all, try it once more before panicing OnAttach(NULL, 0); ClientEncrypted = (wParam & 0x08) != 0; ServerEncrypted = (wParam & 0x10) != 0; InitThemes(); if (pShared) { pShared->AllowNegotiate = (wParam & 0x04) != 0; pShared->UOVersion[0] = 0; if (NativeGetUOVersion != NULL) strncpy(pShared->UOVersion, NativeGetUOVersion(), 16); } if ( !pShared ) PostMessage( hRazorWnd, WM_UONETEVENT, NOT_READY, NO_SHAREMEM ); else if ( CopyFailed ) PostMessage( hRazorWnd, WM_UONETEVENT, NOT_READY, NO_COPY ); else if ( !PatchMemory() ) PostMessage( hRazorWnd, WM_UONETEVENT, NOT_READY, NO_PATCH ); else PostMessage( hRazorWnd, WM_UONETEVENT, READY, SUCCESS ); /* Start a timer that will call a callback each tick. We use this to implement * timers as well as scan client memory for position updates. */ SetTimer(hUOWindow, (UINT_PTR)0xAA, 25, OnTick); break; case WM_UONETEVENT: switch ( LOWORD(wParam) ) { case SEND: FlushSendData(); break; case STAT_BAR: PatchStatusBar( (BOOL)lParam ); break; case NOTO_HUE: SetCustomNotoHue( (int)lParam ); break; case SETWNDSIZE: DesiredSize.cx = LOWORD(lParam); DesiredSize.cy = HIWORD(lParam); break; case SMART_CPU: SmartCPU = lParam; break; case NEGOTIATE: if ( pShared ) pShared->AllowNegotiate = (lParam != 0); break; case SET_MAP_HWND: hMapWnd = (HWND)lParam; break; } break; case WM_COPYDATA: { COPYDATASTRUCT *copydata = (COPYDATASTRUCT *)lParam; switch ((UONET_MESSAGE_COPYDATA)copydata->dwData) { case UONET_MESSAGE_COPYDATA::POSITION: g_TestPosition = *(Position *)copydata->lpData; break; } break; } // Macro stuff case WM_SYSKEYDOWN: case WM_KEYDOWN: if ( pMsg && !SendMessage( hRazorWnd, WM_UONETEVENT, KEYDOWN, wParam ) ) { // dont give the key to the client pMsg->message = WM_NULL; pMsg->lParam = 0; pMsg->wParam = 0; } break; case WM_SYSKEYUP: case WM_KEYUP: if ( pMsg && wParam == VK_SNAPSHOT ) // VK_SNAPSHOT (Print Screen) Doesn't seem to send a KeyDown message SendMessage( hRazorWnd, WM_UONETEVENT, KEYDOWN, wParam ); break; case WM_MOUSEWHEEL: PostMessage( hRazorWnd, WM_UONETEVENT, MOUSE, MAKELONG( 0, (((short)HIWORD(wParam)) < 0 ? -1 : 1) ) ); break; case WM_MBUTTONDOWN: PostMessage( hRazorWnd, WM_UONETEVENT, MOUSE, MAKELONG( 1, 0 ) ); break; case WM_XBUTTONDOWN: PostMessage( hRazorWnd, WM_UONETEVENT, MOUSE, MAKELONG( HIWORD(wParam) + 1, 0 ) ); break; //Activation tracking : case WM_ACTIVATE: Active = wParam; PostMessage( hRazorWnd, WM_UONETEVENT, ACTIVATE, wParam ); break; case WM_KILLFOCUS: hFore = GetForegroundWindow(); if ( ((HWND)wParam) != hRazorWnd && hFore != hRazorWnd && ((HWND)wParam) != hMapWnd && hFore != hMapWnd && !CheckParent( hFore, hRazorWnd ) ) { PostMessage( hRazorWnd, WM_UONETEVENT, FOCUS, FALSE ); } break; case WM_SETFOCUS: PostMessage( hRazorWnd, WM_UONETEVENT, FOCUS, TRUE ); break; //Custom title bar: case WM_NCACTIVATE: Active = wParam; //fallthrough case WM_NCPAINT: case WM_GETICON: case WM_SETTEXT: case WM_CUSTOMTITLE: CheckTitlebarAttr(hWnd); RedrawTitleBar( hWnd, Active ); break; } } LRESULT CALLBACK GetMsgHookFunc( int Code, WPARAM Flag, LPARAM pMsg ) { if ( Code >= 0 && Flag != PM_NOREMOVE ) //dont process messages until they are removed from the queue { MSG *Msg = (MSG*)pMsg; /* Msg->message ^= 0x11; Msg->message ^= Disabled * 101; Msg->message *= !(Disabled * 020); Msg->message ^= 0x11; */ if ( Msg->hwnd == hUOWindow || ( hUOWindow == NULL && Msg->message == WM_PROCREADY ) ) MessageProc( Msg->hwnd, Msg->message, Msg->wParam, Msg->lParam, Msg ); } return CallNextHookEx( NULL, Code, Flag, pMsg ); } LRESULT CALLBACK WndProcRetHookFunc( int Code, WPARAM Flag, LPARAM pMsg ) { if ( Code >= 0 ) { CWPRETSTRUCT *Msg = (CWPRETSTRUCT *)(pMsg); /* Msg->message ^= 0x11; Msg->message ^= Disabled * 101; Msg->message *= !(Disabled * 020); Msg->message ^= 0x11; */ if ( Msg->hwnd == hUOWindow || ( hUOWindow == NULL && Msg->message == WM_PROCREADY ) ) MessageProc( Msg->hwnd, Msg->message, Msg->wParam, Msg->lParam, NULL ); } return CallNextHookEx( NULL, Code, Flag, pMsg ); } LRESULT CALLBACK UOAWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam) { if (nMsg >= WM_USER + 200 && nMsg < WM_USER + 315) return SendMessage(hRazorWnd, nMsg, wParam, lParam); else return DefWindowProc(hWnd, nMsg, wParam, lParam); } void Log( const char *format, ... ) { #ifdef _DEBUG #ifdef LOGGING FILE *log = fopen( "Crypt.log", "a" ); if ( log ) { char timeStr[256]; struct tm *newtime; time_t aclock; time( &aclock ); newtime = localtime( &aclock ); strncpy( timeStr, asctime( newtime ), 256 ); int len = (int)strlen( timeStr ); if ( timeStr[len-1] == '\n' ) timeStr[len-1] = 0; char OutTxt[512]; va_list argList; va_start( argList, format ); _vsnprintf( OutTxt, 512, format, argList ); va_end( argList ); fprintf( log, "%s: %s\n", timeStr, OutTxt ); fclose( log ); } #endif #endif } ================================================ FILE: Crypt/Crypt.def ================================================ LIBRARY Crypt EXPORTS InstallLibrary Shutdown FindUOWindow GetSharedAddress GetPacketLength IsDynLength GetUOProcId GetCommMutex TotalIn TotalOut WaitForWindow SetDataPath CalibratePosition OnAttach SetServer GetUOVersion ================================================ FILE: Crypt/Crypt.h ================================================ #pragma once #pragma pack(1) #include #define DLL_VERSION "1.4.1" #define DLLFUNCTION __declspec(dllexport) #define DLLVAR DLLFUNCTION #ifdef _DEBUG //#define LOGGING #endif enum IError { SUCCESS, NO_UOWND, NO_TID, NO_HOOK, NO_SHAREMEM, LIB_DISABLED, NO_PATCH, NO_COPY, INVALID_PARAMS, UNKNOWN, }; enum UONET_MESSAGE { SEND = 1, RECV = 2, READY = 3, NOT_READY = 4, CONNECT = 5, DISCONNECT = 6, KEYDOWN = 7, MOUSE = 8, ACTIVATE = 9, FOCUS = 10, CLOSE = 11, STAT_BAR = 12, NOTO_HUE = 13, DLL_ERROR = 14, SETWNDSIZE = 19, SMART_CPU = 21, NEGOTIATE = 22, SET_MAP_HWND = 23, ON_TICK = 24, }; enum class UONET_MESSAGE_COPYDATA { POSITION = 1, }; //#define SHARED_BUFF_SIZE 0x80000 // Client's buffers are 500k #define SHARED_BUFF_SIZE 524288 // 262144 // 250k struct Buffer { int Length; int Start; BYTE Buff[SHARED_BUFF_SIZE]; }; #pragma pack(1) struct Position { uint16_t x; uint16_t y; uint16_t z; }; static_assert(sizeof(struct Position) == 6, "Incorrect size\n"); struct SharedMemory { // Do *not* mess with this struct. Really. I mean it. Buffer InRecv; Buffer OutRecv; Buffer InSend; Buffer OutSend; char TitleBar[1024]; bool ForceDisconn; unsigned int TotalSend; unsigned int TotalRecv; unsigned short PacketTable[256]; char DataPath[256]; bool AllowNegotiate; unsigned char AuthBits[16]; bool Reserved0; unsigned int ServerIP; unsigned short ServerPort; char UOVersion[16]; }; class PatchInfo { public: PatchInfo( DWORD addr, int len ) { Address = addr; Length = len; Data = new char[Length]; memcpy( Data, (const void*)Address, Length ); } ~PatchInfo() { delete[] Data; } DWORD Address; int Length; char *Data; }; #define WM_PROCREADY WM_USER #define WM_UONETEVENT WM_USER+1 #define WM_CUSTOMTITLE WM_USER+2 #ifndef WM_XBUTTONDOWN #define WM_XBUTTONDOWN 0x020B #endif extern HWND hUOWindow; extern HINSTANCE hInstance; extern SharedMemory *pShared; extern HANDLE CommMutex; DLLFUNCTION int InstallLibrary( HWND PostWindow, DWORD pId ); DLLFUNCTION void Shutdown( bool closeClient ); DLLFUNCTION HWND FindUOWindow(); DLLFUNCTION void *GetSharedAddress(); DLLFUNCTION int GetPacketLength( unsigned char *data, int len ); DLLFUNCTION bool IsDynLength( unsigned char packet ); DLLFUNCTION int GetUOProcId(); DLLFUNCTION HANDLE GetCommMutex(); DLLFUNCTION unsigned int TotalIn(); DLLFUNCTION unsigned int TotalOut(); DLLFUNCTION void CalibratePosition(uint16_t x, uint16_t y, uint16_t z); DLLFUNCTION void OnAttach(void *params, int paramsLen); DLLFUNCTION void SetServer(unsigned int addr, unsigned short port); DLLFUNCTION const char *GetUOVersion(); LRESULT CALLBACK UOAWndProc( HWND, UINT, WPARAM, LPARAM ); void Log( const char *format, ... ); void MemoryPatch( unsigned long, unsigned long ); void MemoryPatch( unsigned long, int, int ); void MemoryPatch( unsigned long, const void *, int ); void RedrawTitleBar( HWND, bool ); void CheckTitlebarAttr(HWND); void FreeArt(); void InitThemes(); bool PatchStatusBar( BOOL preAOS ); //#define PACKET_TBL_STR "Got Logout OK packet!\0\0\0" //#define PACKET_TS_LEN 24 #define PACKET_TBL_STR "\x07\0\0\0\x03\0\0\0" #define PACKET_TS_LEN 8 #define PACKET_TBL_OFFSET (0-(8+12+12)) //search disassembly for //static key1 C1 E2 1F D1 E8 D1 E9 0B C6 0B CA 35 static key2 81 F1 dynamic key 4D #define CRYPT_KEY_STR "\xC1\xE2\x1F\xD1\xE8\xD1\xE9\x0B\xC6\x0B\xCA\x35" #define CRYPT_KEY_LEN 12 //static key1 D1 E8 0B C6 C1 E2 1F 35 static key2 D1 E9 89 83 F0 00 42 00 8B 45 08 0B CA 81 F1 dynamic key 48 #define CRYPT_KEY_STR_3D "\xD1\xE8\x0B\xC6\xC1\xE2\x1F\x35" #define CRYPT_KEY_3D_LEN 8 /* To calculate login keys: key1 = ( Major << 23 ) | ( Minor << 14 ) | ( Revision << 4 ); key1 ^= ( Revision * Revision ) << 9; key1 ^= ( Minor * Minor ); key1 ^= ( Minor * 11 ) << 24; key1 ^= ( Revision * 7 ) << 19; key1 ^= 0x2C13A5FD; key2 = ( Major << 22 ) | ( Revision << 13 ) | ( Minor << 3 ); key2 ^= ( Revision * Revision * 3 ) << 10; key2 ^= ( Minor * Minor ); key2 ^= ( Minor * 13 ) << 23; key2 ^= ( Revision * 7 ) << 18; key2 ^= 0xA31D527F; */ /* .text:0041AA2F C1 E6 1F shl esi, 31 .text:0041AA32 D1 E8 shr eax, 1 .text:0041AA34 0B C6 or eax, esi .text:0041AA36 47 inc edi .text:0041AA37 33 05 BC 29 6B 00 xor eax, LoginKey_2 .text:0041AA3D C1 E2 1F shl edx, 31 .text:0041AA40 89 83 F8 00 0A 00 mov [ebx+0A00F8h], eax .text:0041AA46 D1 E8 shr eax, 1 .text:0041AA48 0B C6 or eax, esi .text:0041AA4A 8B 35 BC 29 6B 00 mov esi, LoginKey_2 .text:0041AA50 33 C6 xor eax, esi .text:0041AA52 D1 E9 shr ecx, 1 .text:0041AA54 89 83 F8 00 0A 00 mov [ebx+0A00F8h], eax .text:0041AA5A 0B CA or ecx, edx .text:0041AA5C 8B 15 B8 29 6B 00 mov edx, LoginKey_1 */ // -- -- -- -- -- -- // 1F D1 E8 0B C6 47 33 05 memoryloc_2 C1 E2 1F 89 83 F8 00 0A 00 D1 E8 0B C6 8B 35 memoryloc_2 33 C6 D1 E9 89 83 F8 00 0A 00 0B Ca 8b 15 memoryloc_1 33 CA #define CRYPT_KEY_STR_NEW "\x1F\xD1\xE8\x0B\xC6\x47\x33\x05" #define CRYPT_KEY_NEW_LEN 8 /* .text:0041C599 8B F0 mov esi, eax .text:0041C59B 8B FA mov edi, edx .text:0041C59D D1 E8 shr eax, 1 .text:0041C59F C1 E7 1F shl edi, 31 .text:0041C5A2 0B C7 or eax, edi .text:0041C5A4 33 05 FC AB 6C 00 xor eax, dword_6CABFC .text:0041C5AA D1 EA shr edx, 1 .text:0041C5AC D1 E8 shr eax, 1 .text:0041C5AE C1 E6 1F shl esi, 31 .text:0041C5B1 0B C7 or eax, edi .text:0041C5B3 33 05 FC AB 6C 00 xor eax, dword_6CABFC .text:0041C5B9 0B D6 or edx, esi .text:0041C5BB 33 15 00 AC 6C 00 xor edx, dword_6CAC00 .text:0041C5C1 83 EB 01 sub ebx, 1 .text:0041C5C4 83 C5 01 add ebp, 1 .text:0041C5C7 85 DB test ebx, ebx */ // E8 C1 E7 1F 0B C7 33 05 memoryloc_2 #define CRYPT_KEY_STR_MORE_NEW "\xE8\xC1\xE7\x1F\x0B\xC7\x33\x05" #define CRYPT_KEY_MORE_NEW_LEN 8 ================================================ FILE: Crypt/Crypt.vcxproj ================================================  Debug Win32 Release Win32 {B32A4D9E-3AF1-4C75-989D-D62C4E8F08B1} Crypt Win32Proj 10.0 DynamicLibrary v143 MultiByte <_ProjectFileVersion>10.0.30319.1 $(SolutionDir)bin\$(Platform)\$(Configuration)\ WIN32;_DEBUG;_WINDOWS;_USRDLL;CRYPT_EXPORTS;%(PreprocessorDefinitions) Level3 Disabled MultiThreadedDebug StdCall ws2_32.lib;%(AdditionalDependencies) Crypt.def true Windows MachineX86 _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_WINDOWS;_USRDLL;CRYPT_EXPORTS;%(PreprocessorDefinitions) Level3 MultiThreaded StdCall ws2_32.lib;%(AdditionalDependencies) Crypt.def false Windows true MachineX86 {b24e4fb1-4936-4544-9e88-f3ff9b04fdba} false ================================================ FILE: Crypt/Crypt.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Header Files Source Files Resource Files Resource Files ================================================ FILE: Crypt/LoginEncryption.cpp ================================================ #include "stdafx.h" #include "Crypt.h" #include "LoginEncryption.h" #define N2L(C, LL) \ LL = ((unsigned int)(*((C)++))) << 24, \ LL |= ((unsigned int)(*((C)++))) << 16, \ LL |= ((unsigned int)(*((C)++))) << 8, \ LL |= ((unsigned int)(*((C)++))) DWORD zero = 0; const DWORD *LoginEncryption::Key1 = &zero; const DWORD *LoginEncryption::Key2 = &zero; LoginEncryption::LoginEncryption() { m_Table[0] = m_Table[1] = 0; } void LoginEncryption::SetKeys( const DWORD *k1, const DWORD *k2 ) { //LoginEncryption::StaticKey1 = *((DWORD*)sk1); LoginEncryption::Key1 = k1; LoginEncryption::Key2 = k2; } void LoginEncryption::Initialize( const BYTE *pSeed ) { DWORD seed; N2L(pSeed, seed); /*char temp[256]; sprintf( temp, "Key1 = %08x, Key2 = %08x", *Key1, *Key2 ); MessageBox( NULL, temp, "Keys", MB_OK );*/ m_Table[0] = (((~seed) ^ 0x00001357) << 16) | ((seed ^ 0xffffaaaa) & 0x0000ffff); m_Table[1] = ((seed ^ 0x43210000) >> 16) | (((~seed) ^ 0xabcdffff) & 0xffff0000); } void LoginEncryption::Encrypt( const BYTE *in, BYTE *out, int len ) { for (int i=0;i> 1) | (pt0 << 31)) ^ LOGIN_KEY_1) >> 1) | (pt0<<31)) ^ LOGIN_KEY_1; //m_Table[0] = ((pt0 >> 1) | (pt1 << 31)) ^ LOGIN_KEY_2; BYTE out = in ^ ((unsigned char)m_Table[0]); DWORD OldT1 = m_Table[1]; m_Table[1] = ((((m_Table[1] >> 1) | (m_Table[0] << 31)) ^ ((*Key1)-1)) >> 1 | (m_Table[0] << 31)) ^ (*Key1); m_Table[0] = ((m_Table[0]>>1) | (OldT1<<31)) ^ (*Key2); return out; } bool LoginEncryption::TestForLogin( BYTE encrypted ) { return IsLoginByte( (BYTE)( encrypted ^ ((BYTE)m_Table[0]) ) ); } bool LoginEncryption::IsLoginByte( BYTE unencrypted ) { return unencrypted == ((BYTE)0x80) || unencrypted == ((BYTE)0x48); } DWORD LoginEncryption::GenerateBadSeed( DWORD oldSeed ) { DWORD newSeed = (*Key1) ^ (*Key2); newSeed = ((newSeed >> 24) & 0xFF) | ((newSeed >> 8) & 0xFF00) | ((newSeed << 8) & 0xFF0000) | ((newSeed << 24) & 0xFF000000); return htonl( newSeed ^ htonl( oldSeed ) ); } ================================================ FILE: Crypt/LoginEncryption.h ================================================ #pragma once class LoginEncryption { public: LoginEncryption(); void Initialize( const BYTE *pSeed ); static void SetKeys( const DWORD *k1, const DWORD *k2 ); void Encrypt( const BYTE *in, BYTE *out, int len ); void Decrypt( const BYTE *in, BYTE *out, int len ); bool TestForLogin( BYTE encrypted ); static DWORD GenerateBadSeed( DWORD oldSeed ); static bool IsLoginByte( BYTE unencrypted ); private: static const DWORD *Key1, *Key2; BYTE Crypt( BYTE ); DWORD m_Table[2]; }; ================================================ FILE: Crypt/MemFinder.cpp ================================================ #include "stdafx.h" #include "MemFinder.h" DWORD MemFinder::Find(const void *data, int length, DWORD addressHint, DWORD addressMax) { for (DWORD addr = addressHint; addr < addressMax; addr++) { if (IsBadReadPtr((void*)addr, length)) { continue; } __try { if (memcmp((const void*)addr, data, length) == 0) return addr; } __except (EXCEPTION_ACCESS_VIOLATION) { continue; } } return 0; } MemFinder::MemFinder() { Clear(); } MemFinder::~MemFinder() { Clear(); } void MemFinder::Clear() { _Executed = false; _StartPos = 0xFFFFFFFF; for (unsigned int i = 0; i<_Entries.size(); i++) delete[] _Entries[i].Data; _Entries.clear(); } void MemFinder::AddEntry(const void *data, int length, unsigned int maxResults, DWORD hint) { Entry ent; ent.Data = new char[length]; memcpy(ent.Data, data, length); ent.Length = length; ent.MaxResults = maxResults; ent.PositionHint = hint; if (hint < _StartPos) _StartPos = hint; _Entries.push_back(ent); } DWORD MemFinder::GetAddress(const void *data, int length, unsigned int idx) { if (!_Executed) return 0; for (unsigned int i = 0; i < _Entries.size(); i++) { if (_Entries[i].Length != length) continue; if (!memcmp(data, _Entries[i].Data, length)) { if (idx < _Entries[i].Results.size()) return _Entries[i].Results[idx]; else return 0; } } return 0; } void MemFinder::Execute() { bool allDone = false; for (DWORD pos = _StartPos; pos < 0x02000000 && !allDone; pos++) { __try { allDone = true; for (unsigned int i = 0; i < _Entries.size(); i++) { Entry &e = _Entries[i]; if (e.Results.size() >= e.MaxResults) { /* Already found this entry */ continue; } if (IsBadReadPtr((void*)pos, e.Length)) { continue; } allDone = false; if (e.PositionHint > pos) { /* This entry always appears in memory after this position */ continue; } if (!memcmp((void*)pos, e.Data, e.Length)) { e.Results.push_back(pos); } } } __except (EXCEPTION_ACCESS_VIOLATION) { break; } } _Executed = true; } ================================================ FILE: Crypt/MemFinder.h ================================================ #pragma pack(1) #pragma once #include using std::vector; class MemFinder { public: static DWORD Find( const void *data, int length, DWORD hint = 0x00400000, DWORD addressMax = 0x01000000 ); MemFinder(); ~MemFinder(); void AddEntry( const void *data, int length, unsigned int maxResults, DWORD hint ); void AddEntry( const void *data, int length, DWORD hint = 0x00400000 ) { AddEntry( data, length, 1, hint ); } DWORD GetAddress( const void *data, int length, unsigned int idx = 0 ); void Execute(); void Clear(); private: struct Entry { char *Data; int Length; DWORD PositionHint; unsigned int MaxResults; vector Results; }; vector _Entries; bool _Executed; DWORD _StartPos; }; ================================================ FILE: Crypt/OSIEncryption.cpp ================================================ #include "stdafx.h" #include "Crypt.h" #include "OSIEncryption.h" #include "TwoFish.h" OSIEncryption::OSIEncryption() { memset( &m_Key, 0, sizeof(m_Key) ); memset( &m_Cipher, 0, sizeof(m_Cipher) ); memset( m_TFTable, 0, 256 ); memset( m_XORTable, 0, 16 ); Log( "XT: %X", m_XORTable ); m_XORPos = m_TFPos = 0; } void OSIEncryption::Initialize( DWORD Seed ) { memset( &m_Key, 0, sizeof(m_Key) ); memset( &m_Cipher, 0, sizeof(m_Cipher) ); memset( m_TFTable, 0, 256 ); makeKey( &m_Key, DIR_DECRYPT, 0x80, NULL ); m_Key.key32[0] = m_Key.key32[1] = m_Key.key32[2] = m_Key.key32[3] = Seed; reKey( &m_Key ); cipherInit( &m_Cipher, MODE_ECB, NULL ); for(int i=0;i<256;i++) m_TFTable[i] = i; ReinitTFTable(); MD5( m_TFTable, 256, m_XORTable ); m_XORPos = 0; } void OSIEncryption::ReinitTFTable() { unsigned char tmpBuff[256]; blockEncrypt( &m_Cipher, &m_Key, m_TFTable, 256*8, tmpBuff ); memcpy( m_TFTable, tmpBuff, 256 ); m_TFPos = 0; } void OSIEncryption::Hash( BYTE *field, const BYTE *param ) { unsigned int a = *((unsigned int *)(field + 0)); unsigned int b = *((unsigned int *)(field + 4)); unsigned int c = *((unsigned int *)(field + 8)); unsigned int d = *((unsigned int *)(field + 12)); a = ((b & c) | (~b & d)) + *((unsigned int *)(param + 0x00)) + a - 0x28955B88; a = ((a << 0x07) | (a >> 0x19)) + b; d = ((a & b) | (~a & c)) + *((unsigned int *)(param + 0x04)) + d - 0x173848AA; d = ((d << 0x0c) | (d >> 0x14)) + a; c = ((d & a) | (~d & b)) + *((unsigned int *)(param + 0x08)) + c + 0x242070DB; c = ((c << 0x11) | (c >> 0x0f)) + d; b = ((c & d) | (~c & a)) + *((unsigned int *)(param + 0x0c)) + b - 0x3E423112; b = ((b << 0x16) | (b >> 0x0a)) + c; a = ((b & c) | (~b & d)) + *((unsigned int *)(param + 0x10)) + a - 0x0A83F051; a = ((a << 0x07) | (a >> 0x19)) + b; d = ((a & b) | (~a & c)) + *((unsigned int *)(param + 0x14)) + d + 0x4787C62A; d = ((d << 0x0c) | (d >> 0x14)) + a; c = ((d & a) | (~d & b)) + *((unsigned int *)(param + 0x18)) + c - 0x57CFB9ED; c = ((c << 0x11) | (c >> 0x0f)) + d; b = ((c & d) | (~c & a)) + *((unsigned int *)(param + 0x1c)) + b - 0x02B96AFF; b = ((b << 0x16) | (b >> 0x0a)) + c; a = ((b & c) | (~b & d)) + *((unsigned int *)(param + 0x20)) + a + 0x698098D8; a = ((a << 0x07) | (a >> 0x19)) + b; d = ((a & b) | (~a & c)) + *((unsigned int *)(param + 0x24)) + d - 0x74BB0851; d = ((d << 0x0c) | (d >> 0x14)) + a; c = ((d & a) | (~d & b)) + *((unsigned int *)(param + 0x28)) + c - 0x0000A44F; c = ((c << 0x11) | (c >> 0x0f)) + d; b = ((c & d) | (~c & a)) + *((unsigned int *)(param + 0x2C)) + b - 0x76A32842; b = ((b << 0x16) | (b >> 0x0a)) + c; a = ((b & c) | (~b & d)) + *((unsigned int *)(param + 0x30)) + a + 0x6B901122; a = ((a << 0x07) | (a >> 0x19)) + b; d = ((a & b) | (~a & c)) + *((unsigned int *)(param + 0x34)) + d - 0x02678E6D; d = ((d << 0x0c) | (d >> 0x14)) + a; c = ((d & a) | (~d & b)) + *((unsigned int *)(param + 0x38)) + c - 0x5986BC72; c = ((c << 0x11) | (c >> 0x0f)) + d; b = ((c & d) | (~c & a)) + *((unsigned int *)(param + 0x3c)) + b + 0x49B40821; b = ((b << 0x16) | (b >> 0x0a)) + c; a = ((b & d) | (~d & c)) + *((unsigned int *)(param + 0x04)) + a - 0x09E1DA9E; a = ((a << 0x05) | (a >> 0x1b)) + b; d = ((a & c) | (~c & b)) + *((unsigned int *)(param + 0x18)) + d - 0x3FBF4CC0; d = ((d << 0x09) | (d >> 0x17)) + a; c = ((d & b) | (~b & a)) + *((unsigned int *)(param + 0x2c)) + c + 0x265E5A51; c = ((c << 0x0e) | (c >> 0x12)) + d; b = ((c & a) | (~a & d)) + *((unsigned int *)(param + 0x00)) + b - 0x16493856; b = ((b << 0x14) | (b >> 0x0c)) + c; a = ((b & d) | (~d & c)) + *((unsigned int *)(param + 0x14)) + a - 0x29D0EFA3; a = ((a << 0x05) | (a >> 0x1b)) + b; d = ((a & c) | (~c & b)) + *((unsigned int *)(param + 0x28)) + d + 0x02441453; d = ((d << 0x09) | (d >> 0x17)) + a; c = ((d & b) | (~b & a)) + *((unsigned int *)(param + 0x3c)) + c - 0x275E197F; c = ((c << 0x0e) | (c >> 0x12)) + d; b = ((c & a) | (~a & d)) + *((unsigned int *)(param + 0x10)) + b - 0x182C0438; b = ((b << 0x14) | (b >> 0x0c)) + c; a = ((b & d) | (~d & c)) + *((unsigned int *)(param + 0x24)) + a + 0x21E1CDE6; a = ((a << 0x05) | (a >> 0x1b)) + b; d = ((a & c) | (~c & b)) + *((unsigned int *)(param + 0x38)) + d - 0x3CC8F82A; d = ((d << 0x09) | (d >> 0x17)) + a; c = ((d & b) | (~b & a)) + *((unsigned int *)(param + 0x0c)) + c - 0x0B2AF279; c = ((c << 0x0e) | (c >> 0x12)) + d; b = ((c & a) | (~a & d)) + *((unsigned int *)(param + 0x20)) + b + 0x455A14ED; b = ((b << 0x14) | (b >> 0x0c)) + c; a = ((b & d) | (~d & c)) + *((unsigned int *)(param + 0x34)) + a - 0x561C16FB; a = ((a << 0x05) | (a >> 0x1b)) + b; d = ((a & c) | (~c & b)) + *((unsigned int *)(param + 0x08)) + d - 0x03105C08; d = ((d << 0x09) | (d >> 0x17)) + a; c = ((d & b) | (~b & a)) + *((unsigned int *)(param + 0x1c)) + c + 0x676F02D9; c = ((c << 0x0e) | (c >> 0x12)) + d; b = ((c & a) | (~a & d)) + *((unsigned int *)(param + 0x30)) + b - 0x72D5B376; b = ((b << 0x14) | (b >> 0x0c)) + c; a = (b ^ c ^ d) + *((unsigned int *)(param + 0x14)) + a - 0x0005C6BE; a = ((a << 0x04) | (a >> 0x1c)) + b; d = (a ^ b ^ c) + *((unsigned int *)(param + 0x20)) + d - 0x788E097F; d = ((d << 0x0b) | (d >> 0x15)) + a; c = (d ^ a ^ b) + *((unsigned int *)(param + 0x2c)) + c + 0x6D9D6122; c = ((c << 0x10) | (c >> 0x10)) + d; b = (c ^ d ^ a) + *((unsigned int *)(param + 0x38)) + b - 0x021AC7F4; b = ((b << 0x17) | (b >> 0x09)) + c; a = (b ^ c ^ d) + *((unsigned int *)(param + 0x04)) + a - 0x5B4115BC; a = ((a << 0x04) | (a >> 0x1c)) + b; d = (a ^ b ^ c) + *((unsigned int *)(param + 0x10)) + d + 0x4BDECFA9; d = ((d << 0x0b) | (d >> 0x15)) + a; c = (d ^ a ^ b) + *((unsigned int *)(param + 0x1c)) + c - 0x0944B4A0; c = ((c << 0x10) | (c >> 0x10)) + d; b = (c ^ d ^ a) + *((unsigned int *)(param + 0x28)) + b - 0x41404390; b = ((b << 0x17) | (b >> 0x09)) + c; a = (b ^ c ^ d) + *((unsigned int *)(param + 0x34)) + a + 0x289B7EC6; a = ((a << 0x04) | (a >> 0x1c)) + b; d = (a ^ b ^ c) + *((unsigned int *)(param + 0x00)) + d - 0x155ED806; d = ((d << 0x0b) | (d >> 0x15)) + a; c = (d ^ a ^ b) + *((unsigned int *)(param + 0x0c)) + c - 0x2B10CF7B; c = ((c << 0x10) | (c >> 0x10)) + d; b = (c ^ d ^ a) + *((unsigned int *)(param + 0x18)) + b + 0x04881D05; b = ((b << 0x17) | (b >> 0x09)) + c; a = (b ^ c ^ d) + *((unsigned int *)(param + 0x24)) + a - 0x262B2FC7; a = ((a << 0x04) | (a >> 0x1c)) + b; d = (a ^ b ^ c) + *((unsigned int *)(param + 0x30)) + d - 0x1924661B; d = ((d << 0x0b) | (d >> 0x15)) + a; c = (d ^ a ^ b) + *((unsigned int *)(param + 0x3c)) + c + 0x1fa27cf8; c = ((c << 0x10) | (c >> 0x10)) + d; b = (c ^ d ^ a) + *((unsigned int *)(param + 0x08)) + b - 0x3B53A99B; b = ((b << 0x17) | (b >> 0x09)) + c; a = ((~d | b) ^ c) + *((unsigned int *)(param + 0x00)) + a - 0x0BD6DDBC; a = ((a << 0x06) | (a >> 0x1a)) + b; d = ((~c | a) ^ b) + *((unsigned int *)(param + 0x1c)) + d + 0x432AFF97; d = ((d << 0x0a) | (d >> 0x16)) + a; c = ((~b | d) ^ a) + *((unsigned int *)(param + 0x38)) + c - 0x546BDC59; c = ((c << 0x0f) | (c >> 0x11)) + d; b = ((~a | c) ^ d) + *((unsigned int *)(param + 0x14)) + b - 0x036C5FC7; b = ((b << 0x15) | (b >> 0x0b)) + c; a = ((~d | b) ^ c) + *((unsigned int *)(param + 0x30)) + a + 0x655B59C3; a = ((a << 0x06) | (a >> 0x1a)) + b; d = ((~c | a) ^ b) + *((unsigned int *)(param + 0x0C)) + d - 0x70F3336E; d = ((d << 0x0a) | (d >> 0x16)) + a; c = ((~b | d) ^ a) + *((unsigned int *)(param + 0x28)) + c - 0x00100B83; c = ((c << 0x0f) | (c >> 0x11)) + d; b = ((~a | c) ^ d) + *((unsigned int *)(param + 0x04)) + b - 0x7A7BA22F; b = ((b << 0x15) | (b >> 0x0b)) + c; a = ((~d | b) ^ c) + *((unsigned int *)(param + 0x20)) + a + 0x6FA87E4F; a = ((a << 0x06) | (a >> 0x1a)) + b; d = ((~c | a) ^ b) + *((unsigned int *)(param + 0x3c)) + d - 0x01D31920; d = ((d << 0x0a) | (d >> 0x16)) + a; c = ((~b | d) ^ a) + *((unsigned int *)(param + 0x18)) + c - 0x5CFEBCEC; c = ((c << 0x0f) | (c >> 0x11)) + d; b = ((~a | c) ^ d) + *((unsigned int *)(param + 0x34)) + b + 0x4E0811A1; b = ((b << 0x15) | (b >> 0x0b)) + c; a = ((~d | b) ^ c) + *((unsigned int *)(param + 0x10)) + a - 0x08AC817E; a = ((a << 0x06) | (a >> 0x1a)) + b; d = ((~c | a) ^ b) + *((unsigned int *)(param + 0x2c)) + d - 0x42C50DCB; d = ((d << 0x0a) | (d >> 0x16)) + a; c = ((~b | d) ^ a) + *((unsigned int *)(param + 0x08)) + c + 0x2AD7D2BB; c = ((c << 0x0f) | (c >> 0x11)) + d; b = ((~a | c) ^ d) + *((unsigned int *)(param + 0x24)) + b - 0x14792C6F; b = ((b << 0x15) | (b >> 0x0b)) + c; *((unsigned int *)(field + 0)) += a; *((unsigned int *)(field + 4)) += b; *((unsigned int *)(field + 8)) += c; *((unsigned int *)(field + 12)) += d; } void OSIEncryption::CallHash( BYTE *key, const BYTE *challenge, unsigned int len ) { unsigned int *ptr1, *ptr2; unsigned int a, b, c; ptr1 = (unsigned int *)(key + 16); ptr2 = (unsigned int *)(key + 20); a = *ptr1; b = (a >> 3) & 0x3f; a += len * 8; *ptr1 = a; if ( a < (len << 3) ) ptr2 += 4; *ptr2 = *ptr2 + (len >> 0x1d); a = 64 - b; c = 0; if ( a <= len ) { memcpy( key + b + 24, challenge, a ); Hash( key, key + 24 ); c = a; a += 0x3F; while ( a < len ) { Hash( key, challenge + a - 0x3f ); a += 64; c += 64; } b = 0; } memcpy( key + b + 24, challenge + c, len - c ); } void OSIEncryption::CalcResponse( BYTE *result, BYTE *field ) { BYTE buf1[0x80]; BYTE buf2[0x80]; int i; memset( buf1, 0, 0x80 ); *buf1 = 0x80; memcpy( buf2, field + 16, 8 ); i = ( int(*((unsigned int *)(buf2))) >> 3 ) & 0x3f; if ( i < 56 ) i = 56 - i; else i = 120 - i; CallHash( field, buf1, i ); CallHash( field, buf2, 8 ); memcpy( result, field, 16 ); } void OSIEncryption::MD5( const BYTE *input, int inLen, BYTE *output ) { BYTE field[0x80]; memset( field, 0, 0x80 ); field[0x0] = 0x01; field[0x1] = 0x23; field[0x2] = 0x45; field[0x3] = 0x67; field[0x4] = 0x89; field[0x5] = 0xAB; field[0x6] = 0xCD; field[0x7] = 0xEF; field[0x8] = 0xFE; field[0x9] = 0xDC; field[0xA] = 0xBA; field[0xB] = 0x98; field[0xC] = 0x76; field[0xD] = 0x54; field[0xE] = 0x32; field[0xF] = 0x10; CallHash( field, input, inLen ); CalcResponse( output, field ); } //going TO the client uses rotating XOR, going FROM the client uses TwoFish void OSIEncryption::XORCrypt( const BYTE *in, BYTE *out, int len ) { for (int i=0;i= 256 ) ReinitTFTable(); out[i] = in[i] ^ m_TFTable[m_TFPos++]; } } //server -> us void OSIEncryption::DecryptFromServer( const BYTE *in, BYTE *out, int len ) { XORCrypt( in, out, len ); } //us -> client void OSIEncryption::EncryptForClient( const BYTE *in, BYTE *out, int len ) { XORCrypt( in, out, len ); } //us -> server void OSIEncryption::EncryptForServer( const BYTE *in, BYTE *out, int len ) { TwoFishCrypt( in, out, len ); } //client -> us void OSIEncryption::DecryptFromClient( const BYTE *in, BYTE *out, int len ) { TwoFishCrypt( in, out, len ); } ================================================ FILE: Crypt/OSIEncryption.h ================================================ #pragma once #include "TwoFish.h" class OSIEncryption { public: OSIEncryption(); void Initialize( DWORD dwSeed ); void DecryptFromServer( const BYTE *in, BYTE *out, int len ); void DecryptFromClient( const BYTE *in, BYTE *out, int len ); void EncryptForServer( const BYTE *in, BYTE *out, int len ); void EncryptForClient( const BYTE *in, BYTE *out, int len ); static void MD5( const BYTE *input, int inLen, BYTE *output ); private: void TwoFishCrypt( const BYTE *in, BYTE *out, int len ); void ReinitTFTable(); void XORCrypt( const BYTE *in, BYTE *out, int len ); void InitializeXORTable( const BYTE* data, int dataLen ); static void Hash( BYTE *field, const BYTE *param ); static void CallHash( BYTE *key, const BYTE *challenge, unsigned int len ); static void CalcResponse( BYTE *result, BYTE *field ); keyInstance m_Key; cipherInstance m_Cipher; unsigned int m_TFPos; unsigned char m_XORPos; BYTE m_TFTable[256]; BYTE m_XORTable[16]; }; ================================================ FILE: Crypt/OldStatusBar.cpp ================================================ #include "StdAfx.h" #include "Crypt.h" #include "MemFinder.h" DWORD GumpOutV28( DWORD OutFuncAddr, int esiOff, char TwoFourOff, DWORD CurAddr, int x, int y ) { int count = 0; memcpy( (void*)(CurAddr+count), "\x8D\x44\x24\x20", 4 ); // lea eax, [esp+70h+var_50] count += 4; memcpy( (void*)(CurAddr+count), "\x50", 1 ); // push eax count ++; memcpy( (void*)(CurAddr+count), "\x8B\x46", 2 ); // mov eax, [esi+24h] count += 2; *((unsigned char*)(CurAddr+count)) = 0x24+TwoFourOff;//+esiOff; count ++; memcpy( (void*)(CurAddr+count), "\x50", 1 ); // push eax count ++; memcpy( (void*)(CurAddr+count), "\x8B\x56", 2 ); // mov edx, [esi+44h] count += 2; *((unsigned char*)(CurAddr+count)) = 0x44+esiOff; count ++; memcpy( (void*)(CurAddr+count), "\x8B\x42\x14", 3 ); // mov eax, [edx+14h] count += 3; memcpy( (void*)(CurAddr+count), "\x83\xC0\x04", 3 ); // add eax, 4 count += 3; memcpy( (void*)(CurAddr+count), "\x50", 1 ); // push eax count ++; memcpy( (void*)(CurAddr+count), "\x8D\x44\x24\x54", 4 ); // lea eax, [esp+7Ch+var_28] count += 4; memcpy( (void*)(CurAddr+count), "\x50", 1 ); // push eax count ++; memcpy( (void*)(CurAddr+count), "\x68\x86\x03\x00\x00", 5 ); // push 386h count += 5; memcpy( (void*)(CurAddr+count), "\x6A\x01", 2 ); // push 1 count += 2; // push y if ( y < 0x80 ) { *((unsigned char*)(CurAddr+count)) = 0x6A; count ++; *((unsigned char*)(CurAddr+count)) = (unsigned char)y; count ++; } else { *((unsigned char*)(CurAddr+count)) = 0x68; count ++; *((int*)(CurAddr+count)) = y; count += 4; } // push x if ( x < 0x80 ) { *((unsigned char*)(CurAddr+count)) = 0x6A; count ++; *((unsigned char*)(CurAddr+count)) = (unsigned char)x; count ++; } else { *((unsigned char*)(CurAddr+count)) = 0x68; count ++; *((int*)(CurAddr+count)) = x; count += 4; } // call theFunction *((unsigned char*)(CurAddr+count)) = 0xE8; count ++; *((int*)(CurAddr+count)) = OutFuncAddr - (CurAddr+count+4); count += 4; memcpy( (void*)(CurAddr+count), "\x83\xC4\x20", 3 ); // add esp, 20h count += 3; return count; } DWORD Sprintf1UV28( DWORD sprintfAddr, DWORD fmtAddr, DWORD CurAddr, int esiOffset ) { int count = 0; memcpy( (void*)(CurAddr+count), "\x33\xC9", 2 ); // xor ecx, ecx count += 2; memcpy( (void*)(CurAddr+count), "\x8D\x54\x24\x48", 4 ); // lea edx, [esp+70h+var_28] count += 4; memcpy( (void*)(CurAddr+count), "\x66\x8B\x8E", 3 ); // mov cx, [esi+___] count += 3; *((int*)(CurAddr+count)) = esiOffset; count += 4; memcpy( (void*)(CurAddr+count), "\x51", 1 ); // push ecx count ++; // push offset fmtAddr *((unsigned char*)(CurAddr+count)) = 0x68; count ++; *((int*)(CurAddr+count)) = fmtAddr; count += 4; memcpy( (void*)(CurAddr+count), "\x52", 1 ); // push edx count ++; // call _sprintf *((unsigned char*)(CurAddr+count)) = 0xE8; count ++; *((int*)(CurAddr+count)) = sprintfAddr - (CurAddr+count+4); count += 4; memcpy( (void*)(CurAddr+count), "\x83\xC4\x0C", 3 ); // add esp, 0Ch count += 3; return count; } DWORD Sprintf2UV28_Byte( DWORD sprintfAddr, DWORD fmtAddr, DWORD CurAddr, int offset1, int offset2 ) { int count = 0; memcpy( (void*)(CurAddr+count), "\x33\xC0", 2 ); // xor eax, eax count += 2; memcpy( (void*)(CurAddr+count), "\x8A\x86", 2 ); // mov al, [esi+___] count += 2; *((int*)(CurAddr+count)) = offset2; count += 4; memcpy( (void*)(CurAddr+count), "\x50", 1 ); // push eax count ++; memcpy( (void*)(CurAddr+count), "\x33\xC9", 2 ); // xor ecx, ecx count += 2; memcpy( (void*)(CurAddr+count), "\x8D\x54\x24\x4C", 4 ); // lea edx, [esp+70h+var_28] count += 4; memcpy( (void*)(CurAddr+count), "\x8A\x8E", 2 ); // mov cl, [esi+___] count += 2; *((int*)(CurAddr+count)) = offset1; count += 4; memcpy( (void*)(CurAddr+count), "\x51", 1 ); // push ecx count ++; // push offset fmtAddr *((unsigned char*)(CurAddr+count)) = 0x68; count ++; *((int*)(CurAddr+count)) = fmtAddr; count += 4; memcpy( (void*)(CurAddr+count), "\x52", 1 ); // push edx count ++; // call _sprintf *((unsigned char*)(CurAddr+count)) = 0xE8; count ++; *((int*)(CurAddr+count)) = sprintfAddr - (CurAddr+count+4); count += 4; memcpy( (void*)(CurAddr+count), "\x83\xC4\x10", 3 ); // add esp, 10h count += 3; return count; } DWORD Sprintf2UV28( DWORD sprintfAddr, DWORD fmtAddr, DWORD CurAddr, int offset1, int offset2 ) { int count = 0; memcpy( (void*)(CurAddr+count), "\x33\xC0", 2 ); // xor eax, eax count += 2; memcpy( (void*)(CurAddr+count), "\x66\x8B\x86", 3 ); // mov ax, [esi+___] count += 3; *((int*)(CurAddr+count)) = offset2; count += 4; memcpy( (void*)(CurAddr+count), "\x50", 1 ); // push eax count ++; memcpy( (void*)(CurAddr+count), "\x33\xC9", 2 ); // xor ecx, ecx count += 2; memcpy( (void*)(CurAddr+count), "\x8D\x54\x24\x4C", 4 ); // lea edx, [esp+70h+var_28] count += 4; memcpy( (void*)(CurAddr+count), "\x66\x8B\x8E", 3 ); // mov cx, [esi+___] count += 3; *((int*)(CurAddr+count)) = offset1; count += 4; memcpy( (void*)(CurAddr+count), "\x51", 1 ); // push ecx count ++; // push offset fmtAddr *((unsigned char*)(CurAddr+count)) = 0x68; count ++; *((int*)(CurAddr+count)) = fmtAddr; count += 4; memcpy( (void*)(CurAddr+count), "\x52", 1 ); // push edx count ++; // call _sprintf *((unsigned char*)(CurAddr+count)) = 0xE8; count ++; *((int*)(CurAddr+count)) = sprintfAddr - (CurAddr+count+4); count += 4; memcpy( (void*)(CurAddr+count), "\x83\xC4\x10", 3 ); // add esp, 10h count += 3; return count; } /*#define STATCODE_BEG "\x8D\x54\x24\x48\x8B\xC3\x2B\xD3\x8A\x08\x88\x0C\x02\x40\x84\xC9\x75\xF6" #define STATCODE_B_LEN 18 #define STATCODE_START_OFF (STATCODE_B_LEN+0x15) #define STACK_OFFSET -3 #define STACK_BASE 0x28 */ #define STATCODE_BEG "\x68\x86\x03\x00\x00\x6A\x01\x6A\x32\x6A\x28\xE8" #define STATCODE_B_LEN 12 #define STATCODE_START_OFF (-0x1E) #define STACK_OFFSET 6 #define STACK_BASE 0x28 #define STATCODE_END "\x51\x52\x68\x86\x03\x00\x00\x6A\x01\x68\x8F\x00\x00\x00\x00" #define STATCODE_E_LEN 14 #define STATCODE_END_OFFSET 9 #define GUMP_STRING1 "\x53\x55\x8B\x6C\x24\x1C\x56\x57\x8B\xFD\x83\xC9\xFF\x33\xC0\xF2" #define GS1_LEN 16 #define GS1_OFFSET 0x29 #define GID_REPL "\x6C\x2A\x00\x00" #define GID_R_LEN 4 #define SPRINTF_STR "\x55\x8B\xEC\x83\xEC\x20\x8B\x45\x08\x56\x89\x45\xE8\x89\x45\xE0\x8D\x45\x10\xC7\x45\xEC\x42\x00\x00\x00\x50\x8D\x45\xE0\xFF\x75\x0C\xC7\x45\xE4\xFF\xFF\xFF\x7F\x50\xE8" #define SPRINTF_LEN 42 #define LOCK_STR_1 "\x6A\x4E\x6A\x28" #define LOCK_STR_2 "\x6A\x69\x6A\x28" #define LOCK_STR_3 "\x68\x85\x00\x00\x00\x6A\x28" #define LOCK_LEN_1 4 #define LOCK_LEN_2 4 #define LOCK_LEN_3 7 #define MIN_STR "\x68\xA8\x00\x00\x00\x68\x98\x01\x00\x00\x68\x92\x00\x00\x00\x68\x81\x01\x00\x00" #define MIN_STR_LEN 20 vector *PatchVec = NULL; bool PatchStatusBar( BOOL preAOS ) { if ( !preAOS ) { if ( PatchVec == NULL ) return true; for(unsigned int i=0;isize();i++) { PatchInfo *patch = (*PatchVec)[i]; MemoryPatch( patch->Address, patch->Data, patch->Length ); delete patch; } delete PatchVec; PatchVec = NULL; return true; } if ( PatchVec != NULL ) return true; const unsigned int NewGumpID = 0x00000802; MemFinder mf; mf.AddEntry( GUMP_STRING1, GS1_LEN ); mf.AddEntry( "%u/%u", 6 ); mf.AddEntry( MIN_STR, MIN_STR_LEN ); mf.AddEntry( STATCODE_BEG, STATCODE_B_LEN ); mf.AddEntry( SPRINTF_STR, SPRINTF_LEN ); mf.AddEntry( LOCK_STR_1, LOCK_LEN_1 ); mf.AddEntry( GID_REPL, GID_R_LEN ); mf.Execute(); DWORD GumpString1 = mf.GetAddress( GUMP_STRING1, GS1_LEN ); if ( !GumpString1 ) return false; GumpString1 -= GS1_OFFSET; DWORD uuFmt = mf.GetAddress( "%u/%u", 6 ); if ( !uuFmt ) return false; DWORD uFmt = uuFmt + 3; DWORD MinLoc = mf.GetAddress( MIN_STR, MIN_STR_LEN ); if ( !MinLoc ) return false; MinLoc += 1; DWORD Start = mf.GetAddress( STATCODE_BEG, STATCODE_B_LEN ); if ( !Start ) return false; Start += STATCODE_START_OFF; DWORD End = Start; do { End = MemFinder::Find( STATCODE_END, STATCODE_E_LEN, End + 1 ); } while ( End && *((unsigned char*)(End+STATCODE_E_LEN+1)) != 0xE8 ); if ( !End ) return false; End += STATCODE_E_LEN + STATCODE_END_OFFSET; DWORD sprintfAddr = mf.GetAddress( SPRINTF_STR, SPRINTF_LEN ); DWORD LockLoc = mf.GetAddress( LOCK_STR_1, LOCK_LEN_1 ); DWORD CurPos = mf.GetAddress( GID_REPL, GID_R_LEN ); if ( !( sprintfAddr && LockLoc && CurPos ) ) return false; PatchVec = new vector(); PatchVec->push_back( new PatchInfo( CurPos, 4 ) ); MemoryPatch( CurPos, NewGumpID ); CurPos = MemFinder::Find( GID_REPL, GID_R_LEN, CurPos+4 ); if ( CurPos ) { PatchVec->push_back( new PatchInfo( CurPos, 4 ) ); MemoryPatch( CurPos, NewGumpID ); } PatchVec->push_back( new PatchInfo( LockLoc+0x01, 1 ) ); PatchVec->push_back( new PatchInfo( LockLoc+0x03, 1 ) ); MemoryPatch( LockLoc+0x01, 0x3E, 1 ); MemoryPatch( LockLoc+0x03, 0x26, 1 ); LockLoc = MemFinder::Find( LOCK_STR_2, LOCK_LEN_2, LockLoc ); if ( LockLoc ) { PatchVec->push_back( new PatchInfo( LockLoc+0x01, 1 ) ); PatchVec->push_back( new PatchInfo( LockLoc+0x03, 1 ) ); MemoryPatch( LockLoc+0x01, 0x4A, 1 ); MemoryPatch( LockLoc+0x03, 0x26, 1 ); LockLoc = MemFinder::Find( LOCK_STR_3, LOCK_LEN_3, LockLoc ); if ( LockLoc ) { PatchVec->push_back( new PatchInfo( LockLoc+0x01, 4 ) ); PatchVec->push_back( new PatchInfo( LockLoc+0x06, 1 ) ); MemoryPatch( LockLoc+0x01, 0x56, 4 ); MemoryPatch( LockLoc+0x06, 0x26, 1 ); } } PatchVec->push_back( new PatchInfo( MinLoc+ 0, 4 ) ); PatchVec->push_back( new PatchInfo( MinLoc+ 5, 4 ) ); PatchVec->push_back( new PatchInfo( MinLoc+10, 4 ) ); PatchVec->push_back( new PatchInfo( MinLoc+15, 4 ) ); MemoryPatch( MinLoc+ 0, 137 ); MemoryPatch( MinLoc+ 5, 263 ); MemoryPatch( MinLoc+10, 111 ); MemoryPatch( MinLoc+15, 239 ); int len = End - Start; PatchVec->push_back( new PatchInfo( Start, len ) ); DWORD OldProtect; if ( !VirtualProtect( (void *)(Start), len, PAGE_EXECUTE_READWRITE, &OldProtect ) ) return false; CurPos = Start; int statOffset = *((unsigned char*)(Start + 0x31 + 0x18 + 3)); int esiOffset = statOffset - 0xD4; int stackChange = STACK_BASE - *((unsigned char*)(Start + STACK_OFFSET)); int twoFourOff = *((unsigned char*)(Start + 0x31 + 2)) - 0x24; bool client5 = false; char uuPush[5]; uuPush[0] = 0x68; *((DWORD*)&uuPush[1]) = uuFmt; DWORD uuSP = MemFinder::Find( uuPush, 5, Start ); if ( uuSP ) client5 = ( *((int*)(uuSP - 10)) != (statOffset + 0x1F) ); if ( stackChange != 0 ) { memcpy( (void*)CurPos, "\x83\xC4", 2 ); // add esp, __h CurPos += 2; *((char*)CurPos) = (char)stackChange; CurPos++; } if ( *((unsigned char*)(Start + 11)) != 0x46 ) // was reg is it in? { memcpy( (void*)CurPos, "\x8B\xF5", 2 ); // mov esi, ebp CurPos += 2; } // name CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 53 ); if ( stackChange != 0 ) { memcpy( (void*)CurPos, "\x83\xC4", 2 ); // add esp, __h CurPos += 2; *((char*)CurPos) = -stackChange; CurPos++; } memset( (void*)CurPos, 0x90, (Start+0x31) - CurPos ); CurPos = Start + 0x31 + 0x12; if ( stackChange != 0 ) { memcpy( (void*)CurPos, "\x83\xC4", 2 ); // add esp, __h CurPos += 2; *((char*)CurPos) = (char)stackChange; CurPos++; } if ( *((unsigned char*)(Start + 11)) != 0x46 ) // was reg is it in? { memcpy( (void*)CurPos, "\x8B\xF5", 2 ); // mov esi, ebp CurPos += 2; } // str CurPos += Sprintf1UV28( sprintfAddr, uFmt, CurPos, statOffset+0x00 ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 72 ); // dex CurPos += Sprintf1UV28( sprintfAddr, uFmt, CurPos, statOffset+0x02 ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 84 ); // int CurPos += Sprintf1UV28( sprintfAddr, uFmt, CurPos, statOffset+0x04 ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 96 ); // sex? //memcpy( (void*)(CurPos), "\xC7\x44\x24\x48Yes", 8 ); // mov [esp+70h+var_28], __ //CurPos += 8; //CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 108 ); // followers: if ( client5 ) { CurPos += Sprintf2UV28_Byte( sprintfAddr, uuFmt, CurPos, statOffset+2+0x1E, statOffset+2+0x1F ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 108 ); } else { CurPos += Sprintf2UV28_Byte( sprintfAddr, uuFmt, CurPos, statOffset+0x1E, statOffset+0x1F ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 108 ); } // AR if ( client5 ) CurPos += Sprintf1UV28( sprintfAddr, uFmt, CurPos, statOffset+0x1C ); else CurPos += Sprintf1UV28( sprintfAddr, uFmt, CurPos, statOffset+0x1A ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 86, 120 ); // hits CurPos += Sprintf2UV28( sprintfAddr, uuFmt, CurPos, statOffset+0x06, statOffset+0x08 ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 174, 72 ); // stam CurPos += Sprintf2UV28( sprintfAddr, uuFmt, CurPos, statOffset+0x0A, statOffset+0x0C ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 174, 96 ); // mana CurPos += Sprintf2UV28( sprintfAddr, uuFmt, CurPos, statOffset+0x0E, statOffset+0x10 ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 174, 84 ); // gold CurPos += Sprintf1UV28( sprintfAddr, uFmt, CurPos, statOffset+0x14 ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 174, 108 ); if ( client5 ) { CurPos += Sprintf2UV28( sprintfAddr, uuFmt, CurPos, statOffset+0x18, statOffset+0x1A ); CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 174, 120 ); } else { // Weight: memcpy( (void*)CurPos, "\x33\xC9", 2 ); // xor ecx, ecx CurPos += 2; memcpy( (void*)CurPos, "\x66\x8B\x8E", 3 ); // mov cx, [esi+___] CurPos += 3; *((int*)CurPos) = statOffset+0x00; // str CurPos += 4; memcpy( (void*)CurPos, "\x8D\x04\xCD\x00\x00\x00\x00\x2B\xC1\x99\x2B\xC2\xD1\xF8\x83\xC0\x28", 17 ); // eax = ecx*3.5 + 40 CurPos += 17; memcpy( (void*)CurPos, "\x50", 1 ); // push eax CurPos ++; memcpy( (void*)CurPos, "\x33\xC9", 2 ); // xor ecx, ecx CurPos += 2; memcpy( (void*)CurPos, "\x8D\x54\x24\x4C", 4 ); // lea edx, [esp+74h+var_28] CurPos += 4; memcpy( (void*)CurPos, "\x66\x8B\x8E", 3 ); // mov cx, [esi+___] CurPos += 3; *((int*)CurPos) = statOffset+0x18; // weight CurPos += 4; memcpy( (void*)CurPos, "\x51", 1 ); // push ecx CurPos ++; // push offset fmtAddr *((unsigned char*)CurPos) = 0x68; CurPos ++; *((int*)CurPos) = uuFmt; CurPos += 4; memcpy( (void*)CurPos, "\x52", 1 ); // push edx CurPos ++; // call _sprintf *((unsigned char*)CurPos) = 0xE8; CurPos ++; *((int*)CurPos) = sprintfAddr - (CurPos+4); CurPos += 4; memcpy( (void*)CurPos, "\x83\xC4\x10", 3 ); // add esp, 10h CurPos += 3; CurPos += GumpOutV28( GumpString1, esiOffset, twoFourOff, CurPos, 174, 120 ); } if ( stackChange != 0 ) { memcpy( (void*)CurPos, "\x83\xC4", 2 ); // add esp, __h CurPos += 2; *((char*)CurPos) = -stackChange; CurPos++; } memset( (void*)CurPos, 0x90, End-CurPos ); VirtualProtect( (void *)(Start), len, OldProtect, &OldProtect ); return true; } ================================================ FILE: Crypt/PacketInfo.cpp ================================================ #include "stdafx.h" #include "Crypt.h" #include "PacketInfo.h" DLLFUNCTION int GetPacketLength( unsigned char *packet, int len ) { if ( pShared != NULL ) { int packetLen = pShared->PacketTable[(unsigned char)packet[0]]; if ( packetLen >= 0x4000 ) { if ( len < 3 ) return 0; else return packet[1]<<8 | packet[2]; } else { return packetLen; } } else if ( len >= 3 ) { //attempt to auto detect an undefined packet return packet[1]<<8 | packet[2]; } else { return -1; } } DLLFUNCTION bool IsDynLength( BYTE packet ) { if ( pShared != NULL ) return pShared->PacketTable[packet] >= 0x8000; else return true; } ================================================ FILE: Crypt/PacketInfo.h ================================================ #pragma once #pragma pack(1) struct ClientPacketInfo // 12 bytes { int Id; int Unk; unsigned short Length; short Unk0000; }; const unsigned short StaticPacketTable[] = { 0x0068, // 0x00 0x0005, // 0x01 0x0007, // 0x02 0x8000, // 0x03 0x0002, // 0x04 0x0005, // 0x05 0x0005, // 0x06 0x0007, // 0x07 0x000F, // 0x08 0x0005, // 0x09 0x000B, // 0x0A 0x0007, // 0x0B 0x8000, // 0x0C 0x0003, // 0x0D 0x8000, // 0x0E 0x003D, // 0x0F 0x00D7, // 0x10 0x8000, // 0x11 0x8000, // 0x12 0x000A, // 0x13 0x0006, // 0x14 0x0009, // 0x15 0x8000, // 0x16 0x8000, // 0x17 0x8000, // 0x18 0x8000, // 0x19 0x8000, // 0x1A 0x0025, // 0x1B 0x8000, // 0x1C 0x0005, // 0x1D 0x0004, // 0x1E 0x0008, // 0x1F 0x0013, // 0x20 0x0008, // 0x21 0x0003, // 0x22 0x001A, // 0x23 0x0007, // 0x24 0x0015, // 0x25 0x0005, // 0x26 0x0002, // 0x27 0x0005, // 0x28 0x0001, // 0x29 0x0005, // 0x2A 0x0002, // 0x2B 0x0002, // 0x2C 0x0011, // 0x2D 0x000F, // 0x2E 0x000A, // 0x2F 0x0005, // 0x30 0x8000, // 0x31 0x0002, // 0x32 0x0002, // 0x33 0x000A, // 0x34 0x028D, // 0x35 0x8000, // 0x36 0x0008, // 0x37 0x0007, // 0x38 0x0009, // 0x39 0x8000, // 0x3A 0x8000, // 0x3B 0x8000, // 0x3C 0x0002, // 0x3D 0x0025, // 0x3E 0x8000, // 0x3F 0x00C9, // 0x40 0x8000, // 0x41 0x8000, // 0x42 0x0229, // 0x43 0x02C9, // 0x44 0x0005, // 0x45 0x8000, // 0x46 0x000B, // 0x47 0x0049, // 0x48 0x005D, // 0x49 0x0005, // 0x4A 0x0009, // 0x4B 0x8000, // 0x4C 0x8000, // 0x4D 0x0006, // 0x4E 0x0002, // 0x4F 0x8000, // 0x50 0x8000, // 0x51 0x8000, // 0x52 0x0002, // 0x53 0x000C, // 0x54 0x0001, // 0x55 0x000B, // 0x56 0x006E, // 0x57 0x006A, // 0x58 0x8000, // 0x59 0x8000, // 0x5A 0x0004, // 0x5B 0x0002, // 0x5C 0x0049, // 0x5D 0x8000, // 0x5E 0x0031, // 0x5F 0x0005, // 0x60 0x0009, // 0x61 0x000F, // 0x62 0x000D, // 0x63 0x0001, // 0x64 0x0004, // 0x65 0x8000, // 0x66 0x0015, // 0x67 0x8000, // 0x68 0x8000, // 0x69 0x0003, // 0x6A 0x0009, // 0x6B 0x0013, // 0x6C 0x0003, // 0x6D 0x000E, // 0x6E 0x8000, // 0x6F 0x001C, // 0x70 0x8000, // 0x71 0x0005, // 0x72 0x0002, // 0x73 0x8000, // 0x74 0x0023, // 0x75 0x0010, // 0x76 0x0011, // 0x77 0x8000, // 0x78 0x0009, // 0x79 0x8000, // 0x7A 0x0002, // 0x7B 0x8000, // 0x7C 0x000D, // 0x7D 0x0002, // 0x7E 0x8000, // 0x7F 0x003E, // 0x80 0x8000, // 0x81 0x0002, // 0x82 0x0027, // 0x83 0x0045, // 0x84 0x0002, // 0x85 0x8000, // 0x86 0x8000, // 0x87 0x0042, // 0x88 0x8000, // 0x89 0x8000, // 0x8A 0x8000, // 0x8B 0x000B, // 0x8C 0x8000, // 0x8D 0x8000, // 0x8E 0x8000, // 0x8F 0x0013, // 0x90 0x0041, // 0x91 0x8000, // 0x92 0x0063, // 0x93 0x8000, // 0x94 0x0009, // 0x95 0x8000, // 0x96 0x0002, // 0x97 0x8000, // 0x98 0x001A, // 0x99 0x8000, // 0x9A 0x0102, // 0x9B 0x0135, // 0x9C 0x0033, // 0x9D 0x8000, // 0x9E 0x8000, // 0x9F 0x0003, // 0xA0 0x0009, // 0xA1 0x0009, // 0xA2 0x0009, // 0xA3 0x0095, // 0xA4 0x8000, // 0xA5 0x8000, // 0xA6 0x0004, // 0xA7 0x8000, // 0xA8 0x8000, // 0xA9 0x0005, // 0xAA 0x8000, // 0xAB 0x8000, // 0xAC 0x8000, // 0xAD 0x8000, // 0xAE 0x000D, // 0xAF 0x8000, // 0xB0 0x8000, // 0xB1 0x8000, // 0xB2 0x8000, // 0xB3 0x8000, // 0xB4 0x0040, // 0xB5 0x0009, // 0xB6 0x8000, // 0xB7 0x8000, // 0xB8 0x0005, // 0xB9 0x0006, // 0xBA 0x0009, // 0xBB 0x0003, // 0xBC 0x8000, // 0xBD 0x8000, // 0xBE 0x8000, // 0xBF 0x0024, // 0xC0 0x8000, // 0xC1 0x8000, // 0xC2 0x8000, // 0xC3 0x0006, // 0xC4 0x00CB, // 0xC5 0x0001, // 0xC6 0x0031, // 0xC7 0x0002, // 0xC8 0x0006, // 0xC9 0x0006, // 0xCA 0x0007, // 0xCB 0x8000, // 0xCC 0x0001, // 0xCD 0x8000, // 0xCE 0x004E, // 0xCF 0x8000, // 0xD0 0x0002, // 0xD1 0x0019, // 0xD2 0x8000, // 0xD3 0x8000, // 0xD4 0x8000, // 0xD5 0x8000, // 0xD6 0x8000, // 0xD7 0x8000, // 0xD8 0x010C, // 0xD9 0x8000, // 0xDA 0x8000, // 0xDB 0x0009, // 0xDC 0x8000, // 0xDD 0x8000, // 0xDE 0x8000, // 0xDF 0x8000, // 0xE0 0x8000, // 0xE1 0x000A, // 0xE2 0x8000, // 0xE3 0x8000, // 0xE4 0x8000, // 0xE5 0x0005, // 0xE6 0x000C, // 0xE7 0x000D, // 0xE8 0x004B, // 0xE9 0x0003, // 0xEA 0x8000, // 0xEB 0x8000, // 0xEC 0x8000, // 0xED 0x8000, // 0xEE 0x0015, // 0xEF 0x8000, // 0xF0 0x8000, // 0xF1 0x8000, // 0xF2 0x0018, // 0xF3 0x8000, // 0xF4 0x0015, // 0xF5 0x8000, // 0xF6 0x8000, // 0xF7 0x006A, // 0xF8 0x8000, // 0xF9 0x8000, // 0xFA 0x8000, // 0xFB 0x8000, // 0xFC 0x8000, // 0xFD 0x0008, // 0xFE 0x8000, // 0xFF }; ================================================ FILE: Crypt/UOArt.cpp ================================================ #include "stdafx.h" #include "Crypt.h" #include "Resource.h" #include #include #include #define Color16to32(c16) (((c16) & 0x7C00) >> 7) | (((c16) & 0x3E0) << 6) | (((c16) & 0x1F) << 19) struct UOItem { int RealWidth; int RealHeight; int Left; int Top; int Bottom; int Right; int GetWidth() const { return Right - Left; } int GetHeight() const { return Bottom - Top; } unsigned short **Data; // [ReadlWidth][RealHeight] 32bit color data int ItemID; UOItem *pNext; }; struct ArtIdx { long FilePos; long Length; long Unused; }; struct ArtHeader { long Unknown; short Width; short Height; //followed by short LookupTable }; UOItem *ArtCache = NULL; unsigned short **Hues = NULL; int NumHues = 0; inline int Round(float n) { int i = (int)n; return i + (n - i >= 0.5 ? 1 : 0); } unsigned short *GetHue(int index) { if (Hues == NULL) { if (!pShared) return NULL; char str[512]; int length, blockCount, index; FILE *huesMul = NULL; WaitForSingleObject(CommMutex, INFINITE); sprintf(str, "%s/hues.mul", pShared->DataPath); ReleaseMutex(CommMutex); huesMul = fopen(str, "rb"); if (!huesMul) { Hues = new unsigned short *[1]; Hues[0] = new unsigned short[34]; memset(Hues[0], 0, 34 * 2); NumHues = 1; return NULL; } fseek(huesMul, 0, SEEK_END); length = (int)ftell(huesMul); fseek(huesMul, 0, SEEK_SET); blockCount = length / 708; if (blockCount > 375) blockCount = 375; NumHues = blockCount * 8; Hues = new unsigned short *[NumHues]; index = 0; for (int b = 0; b < blockCount; b++) { fseek(huesMul, 4, SEEK_CUR); for (int i = 0; i < 8; i++, index++) { Hues[index] = new unsigned short[34]; for (int c = 0; c < 34; c++) { unsigned short color; fread(&color, 2, 1, huesMul); Hues[index][c] = color | 0x8000; } fseek(huesMul, 20, SEEK_CUR); // ignore name } } fclose(huesMul); } if (index > 0 && index <= NumHues) return Hues[index - 1]; else return NULL; } unsigned short ApplyHueToPixel(unsigned short *hue, unsigned short pix) { if (hue) return hue[(pix >> 10) & 31]; else return pix; } UOItem *ReadUOItem(int item, int bh) { if (item == 0 || item >= 0xFFFF || !pShared) return NULL; char str[512]; short *Lookup; unsigned short *Run; FILE *idxMul, *artMul; ArtIdx idx; ArtHeader header; memset(&header, 0, sizeof(ArtHeader)); WaitForSingleObject(CommMutex, INFINITE); sprintf(str, "%s/artidx.mul", pShared->DataPath); ReleaseMutex(CommMutex); idxMul = fopen(str, "rb"); if (!idxMul) return NULL; fseek(idxMul, item * sizeof(ArtIdx), SEEK_SET); fread(&idx, sizeof(ArtIdx), 1, idxMul); fclose(idxMul); if (idx.FilePos == -1 || idx.Length == -1) return NULL; WaitForSingleObject(CommMutex, INFINITE); sprintf(str, "%s/art.mul", pShared->DataPath); ReleaseMutex(CommMutex); artMul = fopen(str, "rb"); if (!artMul) { fclose(idxMul); return NULL; } fseek(artMul, idx.FilePos, SEEK_SET); fread(&header, sizeof(ArtHeader), 1, artMul); if (header.Height <= 0 || header.Width <= 0 || header.Height >= 1024 || header.Width >= 1024 || header.Unknown > 0xFFFF || header.Unknown == 0) { fclose(artMul); return NULL; } Run = new unsigned short[header.Width]; // it should never be wider than the whole image! Lookup = new short[header.Height]; fread(Lookup, header.Height * 2, 1, artMul); long dataStart = ftell(artMul); UOItem *pNew = new UOItem; pNew->ItemID = item; pNew->pNext = ArtCache; ArtCache = pNew; unsigned short **Image = new unsigned short *[header.Width]; for (int i = 0; i < header.Width; i++) { Image[i] = new unsigned short[header.Height]; memset(Image[i], 0, header.Height * 2); } pNew->Left = pNew->Top = 0x7FFFFFFF; pNew->Right = pNew->Bottom = 0; for (int y = 0; y < header.Height; y++) { int x = 0; fseek(artMul, dataStart + Lookup[y] * 2, SEEK_SET); do { short RunOffset = 0, RunLength = 0; fread(&RunOffset, 2, 1, artMul); fread(&RunLength, 2, 1, artMul); if (RunLength <= 0 || RunOffset < 0 || RunOffset + RunLength >= 2048 || RunLength > header.Width) break; if (y > pNew->Bottom) pNew->Bottom = y; if (y < pNew->Top) pNew->Top = y; x += RunOffset; if (x < pNew->Left) pNew->Left = x; fread(Run, RunLength * 2, 1, artMul); for (int o = 0; o < RunLength; o++, x++) Image[x][y] = Run[o]; if (x > pNew->Right) pNew->Right = x; } while (true); } fclose(artMul); delete[] Run; delete[] Lookup; float scale = float(bh) / float(pNew->GetHeight()); if (scale > 1 || scale <= 0) scale = 1; pNew->RealHeight = (int)(header.Height * scale + 1); pNew->RealWidth = (int)(header.Width * scale + 1); pNew->Data = new unsigned short *[pNew->RealWidth]; for (int x = 0; x < pNew->RealWidth; x++) { pNew->Data[x] = new unsigned short[pNew->RealHeight]; memset(pNew->Data[x], 0, 2 * pNew->RealHeight); } for (int x = 0; x < header.Width; x++) { for (int y = 0; y < header.Height; y++) pNew->Data[(int)(x * scale)][(int)(y * scale)] |= Image[x][y]; } pNew->Top = (int)(pNew->Top * scale); pNew->Left = (int)(pNew->Left * scale); pNew->Bottom = (int)(pNew->Bottom * scale); pNew->Right = (int)(pNew->Right * scale); for (int x = 0; x < header.Width; x++) delete[] Image[x]; delete[] Image; return pNew; } UOItem *FindItem(int item) { UOItem *node = ArtCache; while (node != NULL) { if (node->ItemID == item) return node; else node = node->pNext; } return NULL; } inline COLORREF Brightness(int shift, COLORREF c) { return RGB(min(255, GetRValue(c) + shift), min(255, GetGValue(c) + shift), min(255, GetBValue(c) + shift)); } int DrawUOItem(HDC hDC, RECT rect, int item, int hueIdx) { item |= 0x4000; rect.top++; rect.bottom--; int maxHeight = rect.bottom - rect.top; UOItem *i = FindItem(item); if (i == NULL) i = ReadUOItem(item, maxHeight); if (i == NULL) return 0; if (i->GetHeight() < maxHeight) rect.top += (maxHeight - i->GetHeight()) / 2; unsigned short *hue = GetHue(hueIdx); for (int x = i->Left; x <= i->Right; x++) { for (int y = i->Top; y <= i->Bottom; y++) { if (i->Data[x][y] != 0) SetPixel(hDC, rect.left + x - i->Left, rect.top + y - i->Top, Brightness(0x30, Color16to32(ApplyHueToPixel(hue, i->Data[x][y])))); } } return i->GetWidth() + 3; } void FreeItem(UOItem *node) { if (node != NULL) { FreeItem(node->pNext); for (int i = 0; i < node->RealWidth; i++) delete[] node->Data[i]; delete[] node->Data; delete node; } } void FreeArt() { FreeItem(ArtCache); if (Hues && NumHues > 0) { for (int i = 0; i < NumHues; i++) delete[] Hues[i]; delete[] Hues; } } int DrawUOItem(HDC, RECT, int, int); typedef HTHEME(__stdcall *OPENTHEMEDATA)(HWND, LPCWSTR); OPENTHEMEDATA zOpenThemeData = NULL; typedef HRESULT(__stdcall *DRAWTHEMEBACKGROUND)(HTHEME, HDC, int, int, const RECT *, OPTIONAL const RECT *); DRAWTHEMEBACKGROUND zDrawThemeBackground = NULL; typedef HRESULT(__stdcall *CLOSETHEMEDATA)(HTHEME); CLOSETHEMEDATA zCloseThemeData = NULL; typedef HRESULT(__stdcall *GETTHEMESYSFONT)(HTHEME, int, OUT LOGFONTW *); GETTHEMESYSFONT zGetThemeSysFont = NULL; typedef COLORREF(__stdcall *GETTHEMESYSCOLOR)(HTHEME, int); GETTHEMESYSCOLOR zGetThemeSysColor = NULL; typedef HRESULT(__stdcall *GETTHEMEMETRIC)(HTHEME, HDC, int, int, int, int *); GETTHEMEMETRIC zGetThemeMetric = NULL; typedef HRESULT(__stdcall *DWMSETWINDOWATTRIBUTE)(HWND, DWORD, LPCVOID, DWORD); DWMSETWINDOWATTRIBUTE zDwmSetWindowAttribute = NULL; HMODULE hThemes = NULL; HMODULE hDwmApi = NULL; void InitThemes() { hThemes = LoadLibrary("UXTHEME.DLL"); if (hThemes) { zOpenThemeData = (OPENTHEMEDATA)GetProcAddress(hThemes, "OpenThemeData"); zDrawThemeBackground = (DRAWTHEMEBACKGROUND)GetProcAddress(hThemes, "DrawThemeBackground"); zCloseThemeData = (CLOSETHEMEDATA)GetProcAddress(hThemes, "CloseThemeData"); zGetThemeSysColor = (GETTHEMESYSCOLOR)GetProcAddress(hThemes, "GetThemeSysColor"); zGetThemeSysFont = (GETTHEMESYSFONT)GetProcAddress(hThemes, "GetThemeSysFont"); zGetThemeMetric = (GETTHEMEMETRIC)GetProcAddress(hThemes, "GetThemeMetric"); } hDwmApi = LoadLibrary("DWMAPI.DLL"); if (hDwmApi) { zDwmSetWindowAttribute = (DWMSETWINDOWATTRIBUTE)GetProcAddress(hDwmApi, "DwmSetWindowAttribute"); } } inline int GetHex2(LPCSTR hex) { int num = 0; if (!isxdigit(hex[0]) || !isxdigit(hex[1])) return -1; num = isdigit(hex[1]) ? (hex[1] - '0') : (tolower(hex[1]) - 'a' + 10); num += (isdigit(hex[0]) ? (hex[0] - '0') : (tolower(hex[0]) - 'a' + 10)) * 16; return num; } inline int GetHex4(LPCSTR hex) { return (GetHex2(hex) << 8) | GetHex2(&hex[2]); } void DoStat(HDC hDC, int v, int t, int l, int h, int w) { if (w <= 0) return; RECT fill; fill.top = t; fill.left = l; fill.bottom = t + h; fill.right = l + w; HBRUSH hBr = NULL; if (v == 0xFF) //poisoned hBr = CreateSolidBrush(RGB(255, 128, 0)); // orange else if (v <= 25) hBr = CreateSolidBrush(RGB(255, 0, 0)); // red else if (v <= 75) hBr = CreateSolidBrush(RGB(255, 255, 0)); // yellow else hBr = CreateSolidBrush(RGB(0, 255, 0)); // green FillRect(hDC, &fill, hBr); DeleteObject(hBr); } int DrawStatBar(HDC hDC, RECT rect, int width, int status, int hp, int mn, int st) { HGDIOBJ hOld = NULL; POINT pt[2]; rect.right = rect.left + width + 2; int o = (rect.bottom - rect.top - 20) / 2; if (o > 0) rect.top += o; rect.bottom = rect.top + 6 + 6 + 6 + 1; hOld = SelectObject(hDC, GetStockObject(BLACK_PEN)); FrameRect(hDC, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH)); pt[0].x = rect.left; pt[1].x = rect.right; pt[0].y = rect.top + 6; pt[1].y = rect.top + 6; Polyline(hDC, pt, 2); pt[0].y = rect.top + 6 + 6; pt[1].y = rect.top + 6 + 6; Polyline(hDC, pt, 2); if (status == 1) // poisoned DoStat(hDC, 0xFF, rect.top + 1, rect.left + 1, 5, (int)((double(hp + 1) / 100.0) * width)); else DoStat(hDC, hp, rect.top + 1, rect.left + 1, 5, (int)((double(hp + 1) / 100.0) * width)); DoStat(hDC, mn, rect.top + 1 + 6, rect.left + 1, 5, (int)((double(mn + 1) / 100) * width)); DoStat(hDC, st, rect.top + 2 + 6 + 5, rect.left + 1, 5, (int)((double(st + 1) / 100) * width)); SelectObject(hDC, hOld); return width + 2; } void CheckTitlebarAttr(HWND hWnd) { static bool curNCRP = true; bool newNCRP = !pShared || pShared->TitleBar[0] == '\0'; if (curNCRP != newNCRP && zDwmSetWindowAttribute) { DWMNCRENDERINGPOLICY policy = newNCRP ? DWMNCRP_ENABLED : DWMNCRP_DISABLED; zDwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, &policy, sizeof(policy)); curNCRP = newNCRP; } } void DrawColorTitleBar(HTHEME hTheme, HWND hWnd, HDC hOutDC, bool active, bool maximized, LPCSTR str, int len, RECT orig) { const COLORREF def = GetSysColor(active ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT); COLORREF color = def; RECT rect; RECT window; GetWindowRect(hWnd, &window); rect.left = rect.top = 0; rect.right = window.right - window.left; rect.bottom = (orig.bottom - orig.top); HDC hDC = CreateCompatibleDC(hOutDC); HBITMAP hBmp = CreateCompatibleBitmap(hOutDC, rect.right, rect.bottom); SelectObject(hDC, hBmp); bool needRegFill = true; HFONT hFont = NULL; if (hThemes) { LOGFONTW lf; if (maximized) { needRegFill = zDrawThemeBackground(hTheme, hDC, WP_MAXCAPTION, active ? MXCS_ACTIVE : MXCS_INACTIVE, &rect, NULL) != S_OK; } else { int modTop = GetSystemMetrics(SM_CYFRAME); rect.top -= modTop; needRegFill = zDrawThemeBackground(hTheme, hDC, WP_CAPTION, active ? CS_ACTIVE : CS_INACTIVE, &rect, NULL) != S_OK; rect.top += modTop; } if (zGetThemeSysFont(hTheme, TMT_CAPTIONFONT, &lf) == S_OK) { hFont = CreateFontIndirectW(&lf); SelectObject(hDC, hFont); } } if (needRegFill) FillRect(hDC, &rect, GetSysColorBrush(active ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION)); if (hFont == NULL) SelectObject(hDC, GetStockObject(ANSI_VAR_FONT)); rect.left = orig.left; int start = 0; int dlen = 0; int t; SetBkMode(hDC, TRANSPARENT); for (int i = 0; i < len; i++) { if (rect.left >= rect.right) break; if (str[i] == '~') { switch (str[i + 1]) { case '#': if (dlen > 0) { SetTextColor(hDC, color); DrawText(hDC, &str[start], dlen, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); SIZE ext; GetTextExtentPoint32(hDC, &str[start], dlen, &ext); rect.left += ext.cx; } dlen = 0; if (str[i + 2] == '~') { color = def; SetBkMode(hDC, TRANSPARENT); start = i + 3; i += 2; } else { color = RGB(GetHex2(&str[i + 2]), GetHex2(&str[i + 4]), GetHex2(&str[i + 6])); start = i + 8; i += 7; } break; case 'S': case 's': // ~SSFRRGGBB if (dlen > 0) { SetTextColor(hDC, color); DrawText(hDC, &str[start], dlen, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); SIZE ext; GetTextExtentPoint32(hDC, &str[start], dlen, &ext); rect.left += ext.cx; } dlen = 0; t = 30; switch (toupper(str[i + 2])) { case 'S': t = 15; break; //case 'R': w = 30; break; case 'M': t = 45; break; case 'L': t = 60; break; case 'X': t = 75; break; } rect.left += 1; rect.left += DrawStatBar(hDC, rect, t, str[i + 3] - '0', GetHex2(&str[i + 4]), GetHex2(&str[i + 6]), GetHex2(&str[i + 8])); rect.left += 1; start = i + 10; i += 9; break; case 'I': case 'i': if (dlen > 0) { SetTextColor(hDC, color); DrawText(hDC, &str[start], dlen, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); SIZE ext; GetTextExtentPoint32(hDC, &str[start], dlen, &ext); rect.left += ext.cx; } dlen = 0; if (str[i + 6] == '~') { rect.left += DrawUOItem(hDC, rect, GetHex4(&str[i + 2]), 0); start = i + 7; i += 6; } else { rect.left += DrawUOItem(hDC, rect, GetHex4(&str[i + 2]), GetHex4(&str[i + 6])); start = i + 10; i += 9; } break; case '^': if (dlen > 0) { SetTextColor(hDC, color); DrawText(hDC, &str[start], dlen, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); SIZE ext; GetTextExtentPoint32(hDC, &str[start], dlen, &ext); rect.left += ext.cx; } dlen = 0; color = RGB(0, 0, 0); SetBkMode(hDC, OPAQUE); SetBkColor(hDC, RGB(GetHex2(&str[i + 2]), GetHex2(&str[i + 4]), GetHex2(&str[i + 6]))); start = i + 8; i += 7; break; case 'B': case 'b': // skip bold identifier i++; break; } } else { dlen++; } } if (dlen > 0) { SetTextColor(hDC, color); DrawText(hDC, &str[start], dlen, &rect, DT_LEFT | DT_VCENTER | DT_SINGLELINE); } BitBlt(hOutDC, orig.left, orig.top, (orig.right - orig.left), (orig.bottom - orig.top) - 1, hDC, orig.left, 0, SRCCOPY); DeleteDC(hDC); DeleteObject(hBmp); if (hFont) DeleteObject(hFont); } void RedrawTitleBar(HWND hWnd, bool active) { if (!pShared) return; WaitForSingleObject(CommMutex, INFINITE); if (pShared->TitleBar[0] == 0) { ReleaseMutex(CommMutex); return; } int len = (int)strlen(pShared->TitleBar); if (len >= 1024) len = 1023; WINDOWPLACEMENT place; RECT rect; HDC hDC = GetWindowDC(hWnd); //WINDOW dc allows us to draw on the non client area GetWindowPlacement(hWnd, &place); GetWindowRect(hWnd, &rect); // Change the coords (believe me, okay?) rect.top = GetSystemMetrics(SM_CYFRAME); rect.bottom = rect.top + GetSystemMetrics(SM_CYCAPTION); rect.right = (rect.right - rect.left) - (4 * GetSystemMetrics(SM_CXSIZE) + GetSystemMetrics(SM_CXFRAME)); rect.left = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXSMICON) + 5; if (hThemes) { HTHEME hTheme = zOpenThemeData(hWnd, L"WINDOW"); DrawColorTitleBar(hTheme, hWnd, hDC, active, place.showCmd == SW_MAXIMIZE, pShared->TitleBar, len, rect); zCloseThemeData(hTheme); } else { rect.left += GetSystemMetrics(SM_CXFRAME); DrawColorTitleBar(NULL, hWnd, hDC, active, place.showCmd == SW_MAXIMIZE, pShared->TitleBar, len, rect); } ReleaseDC(hWnd, hDC); ReleaseMutex(CommMutex); } ================================================ FILE: Crypt/debug.h ================================================ #ifdef DEBUG /* keep these macros common so they are same for both versions */ CONST int debugCompile = 1; extern int debug; extern void DebugIO(CONST char *s); /* display the debug output */ #define DebugDump(x,s,R,XOR,doRot,showT,needBswap) \ { if (debug) _Dump(x,s,R,XOR,doRot,showT,needBswap,t0,t1); } #define DebugDumpKey(key) { if (debug) _DumpKey(key); } #define IV_ROUND -100 void _Dump(CONST void *p,CONST char *s,int R,int XOR,int doRot,int showT,int needBswap, DWORD t0,DWORD t1) { char line[512]; /* build output here */ int i,n; DWORD q[4]; if (R == IV_ROUND) sprintf(line,"%sIV: ",s); else sprintf(line,"%sR[%2d]: ",s,R); for (n=0;line[n];n++) ; for (i=0;i<4;i++) { q[i]=((CONST DWORD *)p)[i^(XOR)]; if (needBswap) q[i]=Bswap(q[i]); } sprintf(line+n,"x= %08lX %08lX %08lX %08lX.", ROR(q[0],doRot*(R )/2), ROL(q[1],doRot*(R )/2), ROR(q[2],doRot*(R+1)/2), ROL(q[3],doRot*(R+1)/2)); for (;line[n];n++) ; if (showT) sprintf(line+n," t0=%08lX. t1=%08lX.",t0,t1); for (;line[n];n++) ; sprintf(line+n,"\n"); DebugIO(line); } void _DumpKey(CONST keyInstance *key) { char line[512]; int i; int k64Cnt=(key->keyLen+63)/64; /* round up to next multiple of 64 bits */ int subkeyCnt = ROUND_SUBKEYS + 2*key->numRounds; sprintf(line,";\n;makeKey: Input key --> S-box key [%s]\n", (key->direction == DIR_ENCRYPT) ? "Encrypt" : "Decrypt"); DebugIO(line); for (i=0;i %08lX\n","", key->key32[2*i+1],key->key32[2*i],key->sboxKeys[k64Cnt-1-i]); DebugIO(line); } sprintf(line,";%11sSubkeys\n",""); DebugIO(line); for (i=0;isubKeys[2*i],key->subKeys[2*i+1], (2*i == INPUT_WHITEN) ? " Input whiten" : (2*i == OUTPUT_WHITEN) ? " Output whiten" : (2*i == ROUND_SUBKEYS) ? " Round subkeys" : ""); DebugIO(line); } DebugIO(";\n"); } #else CONST int debugCompile = 0; #define DebugDump(x,s,R,XOR,doRot,showT,needBswap) #define DebugDumpKey(key) #endif ================================================ FILE: Crypt/platform.h ================================================ /*************************************************************************** PLATFORM.H -- Platform-specific defines for TWOFISH code Submitters: Bruce Schneier, Counterpane Systems Doug Whiting, Hi/fn John Kelsey, Counterpane Systems Chris Hall, Counterpane Systems David Wagner, UC Berkeley Code Author: Doug Whiting, Hi/fn Version 1.00 April 1998 Copyright 1998, Hi/fn and Counterpane Systems. All rights reserved. Notes: * Tab size is set to 4 characters in this file ***************************************************************************/ /* use intrinsic rotate if possible */ #ifndef ROL #define ROL(x,n) (((x) << ((n) & 0x1F)) | ((x) >> (32-((n) & 0x1F)))) #define ROR(x,n) (((x) >> ((n) & 0x1F)) | ((x) << (32-((n) & 0x1F)))) #endif #if (0) && defined(__BORLANDC__) && (__BORLANDC__ >= 0x462) #error "!!!This does not work for some reason!!!" #include /* get prototype for _lrotl() , _lrotr() */ #pragma inline __lrotl__ #pragma inline __lrotr__ #undef ROL /* get rid of inefficient definitions */ #undef ROR #define ROL(x,n) __lrotl__(x,n) /* use compiler intrinsic rotations */ #define ROR(x,n) __lrotr__(x,n) #endif #ifdef _MSC_VER #include /* get prototypes for rotation functions */ #undef ROL #undef ROR #pragma intrinsic(_lrotl,_lrotr) /* use intrinsic compiler rotations */ #define ROL(x,n) _lrotl(x,n) #define ROR(x,n) _lrotr(x,n) #endif #ifndef _M_IX86 #ifdef __BORLANDC__ #define _M_IX86 300 /* make sure this is defined for Intel CPUs */ #endif #endif #ifdef _M_IX86 #define LittleEndian 1 /* e.g., 1 for Pentium, 0 for 68K */ #define ALIGN32 0 /* need dword alignment? (no for Pentium) */ #else /* non-Intel platforms */ #define LittleEndian 0 /* (assume big endian */ #define ALIGN32 1 /* (assume need alignment for non-Intel) */ #endif #if LittleEndian #define Bswap(x) (x) /* NOP for little-endian machines */ #define ADDR_XOR 0 /* NOP for little-endian machines */ #else #define Bswap(x) ((ROR(x,8) & 0xFF00FF00) | (ROL(x,8) & 0x00FF00FF)) #define ADDR_XOR 3 /* convert byte address in dword */ #endif /* Macros for extracting bytes from dwords (correct for endianness) */ #define _b(x,N) (((BYTE *)&x)[((N) & 3) ^ ADDR_XOR]) /* pick bytes out of a dword */ #define b0(x) _b(x,0) /* extract LSB of DWORD */ #define b1(x) _b(x,1) #define b2(x) _b(x,2) #define b3(x) _b(x,3) /* extract MSB of DWORD */ ================================================ FILE: Crypt/resource.h ================================================ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. // Used by Crypt.rc // #define IDI_ICON1 101 #define IDI_RAZOR 101 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1001 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: Crypt/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // Crypt.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file ================================================ FILE: Crypt/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #pragma warning(disable: 4309) #pragma warning(disable: 4311) #pragma warning(disable: 4312) #pragma warning(disable: 4800) #pragma warning(disable: 4530) #pragma warning(disable: 4996) #pragma comment(lib, "ws2_32.lib") #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include #pragma pack(1) #include #include #include #include #include #include //#include //#include //using std::vector; //using std::string; ================================================ FILE: Crypt/table.h ================================================ /*************************************************************************** TABLE.H -- Tables, macros, constants for Twofish S-boxes and MDS matrix Submitters: Bruce Schneier, Counterpane Systems Doug Whiting, Hi/fn John Kelsey, Counterpane Systems Chris Hall, Counterpane Systems David Wagner, UC Berkeley Code Author: Doug Whiting, Hi/fn Version 1.00 April 1998 Copyright 1998, Hi/fn and Counterpane Systems. All rights reserved. Notes: * Tab size is set to 4 characters in this file * These definitions should be used in optimized and unoptimized versions to insure consistency. ***************************************************************************/ /* for computing subkeys */ #define SK_STEP 0x02020202u #define SK_BUMP 0x01010101u #define SK_ROTL 9 /* Reed-Solomon code parameters: (12,8) reversible code g(x) = x**4 + (a + 1/a) x**3 + a x**2 + (a + 1/a) x + 1 where a = primitive root of field generator 0x14D */ #define RS_GF_FDBK 0x14D /* field generator */ #define RS_rem(x) \ { BYTE b = (BYTE) (x >> 24); \ DWORD g2 = ((b << 1) ^ ((b & 0x80) ? RS_GF_FDBK : 0 )) & 0xFF; \ DWORD g3 = ((b >> 1) & 0x7F) ^ ((b & 1) ? RS_GF_FDBK >> 1 : 0 ) ^ g2 ; \ x = (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b; \ } /* Macros for the MDS matrix * The MDS matrix is (using primitive polynomial 169): * 01 EF 5B 5B * 5B EF EF 01 * EF 5B 01 EF * EF 01 EF 5B *---------------------------------------------------------------- * More statistical properties of this matrix (from MDS.EXE output): * * Min Hamming weight (one byte difference) = 8. Max=26. Total = 1020. * Prob[8]: 7 23 42 20 52 95 88 94 121 128 91 * 102 76 41 24 8 4 1 3 0 0 0 * Runs[8]: 2 4 5 6 7 8 9 11 * MSBs[8]: 1 4 15 8 18 38 40 43 * HW= 8: 05040705 0A080E0A 14101C14 28203828 50407050 01499101 A080E0A0 * HW= 9: 04050707 080A0E0E 10141C1C 20283838 40507070 80A0E0E0 C6432020 07070504 * 0E0E0A08 1C1C1410 38382820 70705040 E0E0A080 202043C6 05070407 0A0E080E * 141C101C 28382038 50704070 A0E080E0 4320C620 02924B02 089A4508 * Min Hamming weight (two byte difference) = 3. Max=28. Total = 390150. * Prob[3]: 7 18 55 149 270 914 2185 5761 11363 20719 32079 * 43492 51612 53851 52098 42015 31117 20854 11538 6223 2492 1033 * MDS OK, ROR: 6+ 7+ 8+ 9+ 10+ 11+ 12+ 13+ 14+ 15+ 16+ * 17+ 18+ 19+ 20+ 21+ 22+ 23+ 24+ 25+ 26+ */ #define MDS_GF_FDBK 0x169 /* primitive polynomial for GF(256)*/ #define LFSR1(x) ( ((x) >> 1) ^ (((x) & 0x01) ? MDS_GF_FDBK/2 : 0)) #define LFSR2(x) ( ((x) >> 2) ^ (((x) & 0x02) ? MDS_GF_FDBK/2 : 0) \ ^ (((x) & 0x01) ? MDS_GF_FDBK/4 : 0)) #define Mx_1(x) ((DWORD) (x)) /* force result to dword so << will work */ #define Mx_X(x) ((DWORD) ((x) ^ LFSR2(x))) /* 5B */ #define Mx_Y(x) ((DWORD) ((x) ^ LFSR1(x) ^ LFSR2(x))) /* EF */ #define M00 Mul_1 #define M01 Mul_Y #define M02 Mul_X #define M03 Mul_X #define M10 Mul_X #define M11 Mul_Y #define M12 Mul_Y #define M13 Mul_1 #define M20 Mul_Y #define M21 Mul_X #define M22 Mul_1 #define M23 Mul_Y #define M30 Mul_Y #define M31 Mul_1 #define M32 Mul_Y #define M33 Mul_X #define Mul_1 Mx_1 #define Mul_X Mx_X #define Mul_Y Mx_Y /* Define the fixed p0/p1 permutations used in keyed S-box lookup. By changing the following constant definitions for P_ij, the S-boxes will automatically get changed in all the Twofish source code. Note that P_i0 is the "outermost" 8x8 permutation applied. See the f32() function to see how these constants are to be used. */ #define P_00 1 /* "outermost" permutation */ #define P_01 0 #define P_02 0 #define P_03 (P_01^1) /* "extend" to larger key sizes */ #define P_04 1 #define P_10 0 #define P_11 0 #define P_12 1 #define P_13 (P_11^1) #define P_14 0 #define P_20 1 #define P_21 1 #define P_22 0 #define P_23 (P_21^1) #define P_24 0 #define P_30 0 #define P_31 1 #define P_32 1 #define P_33 (P_31^1) #define P_34 1 #define p8(N) P8x8[P_##N] /* some syntax shorthand */ /* fixed 8x8 permutation S-boxes */ /*********************************************************************** * 07:07:14 05/30/98 [4x4] TestCnt=256. keySize=128. CRC=4BD14D9E. * maxKeyed: dpMax = 18. lpMax =100. fixPt = 8. skXor = 0. skDup = 6. * log2(dpMax[ 6..18])= --- 15.42 1.33 0.89 4.05 7.98 12.05 * log2(lpMax[ 7..12])= 9.32 1.01 1.16 4.23 8.02 12.45 * log2(fixPt[ 0.. 8])= 1.44 1.44 2.44 4.06 6.01 8.21 11.07 14.09 17.00 * log2(skXor[ 0.. 0]) * log2(skDup[ 0.. 6])= --- 2.37 0.44 3.94 8.36 13.04 17.99 ***********************************************************************/ CONST BYTE P8x8[2][256]= { /* p0: */ /* dpMax = 10. lpMax = 64. cycleCnt= 1 1 1 0. */ /* 817D6F320B59ECA4.ECB81235F4A6709D.BA5E6D90C8F32471.D7F4126E9B3085CA. */ /* Karnaugh maps: * 0111 0001 0011 1010. 0001 1001 1100 1111. 1001 1110 0011 1110. 1101 0101 1111 1001. * 0101 1111 1100 0100. 1011 0101 0010 0000. 0101 1000 1100 0101. 1000 0111 0011 0010. * 0000 1001 1110 1101. 1011 1000 1010 0011. 0011 1001 0101 0000. 0100 0010 0101 1011. * 0111 0100 0001 0110. 1000 1011 1110 1001. 0011 0011 1001 1101. 1101 0101 0000 1100. */ { 0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0 }, /* p1: */ /* dpMax = 10. lpMax = 64. cycleCnt= 2 0 0 1. */ /* 28BDF76E31940AC5.1E2B4C376DA5F908.4C75169A0ED82B3F.B951C3DE647F208A. */ /* Karnaugh maps: * 0011 1001 0010 0111. 1010 0111 0100 0110. 0011 0001 1111 0100. 1111 1000 0001 1100. * 1100 1111 1111 1010. 0011 0011 1110 0100. 1001 0110 0100 0011. 0101 0110 1011 1011. * 0010 0100 0011 0101. 1100 1000 1000 1110. 0111 1111 0010 0110. 0000 1010 0000 0011. * 1101 1000 0010 0001. 0110 1001 1110 0101. 0001 0100 0101 0111. 0011 1011 1111 0010. */ { 0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91 } }; ================================================ FILE: Crypt/trees.cpp ================================================ int Trees[] = { 0x4CCA, 0x4CCB, 0x4CCC, 0x4CCD, 0x4CD0, 0x4CD3, 0x4CD6, 0x4CD8, 0x4CDA, 0x4CDD, 0x4CE0, 0x4CE3, 0x4CE6, 0x4CF8, 0x4CFB, 0x4CFE, 0x4D01, 0x4D41, 0x4D42, 0x4D43, 0x4D44, 0x4D57, 0x4D58, 0x4D59, 0x4D5A, 0x4D5B, 0x4D6E, 0x4D6F, 0x4D70, 0x4D71, 0x4D72, 0x4D84, 0x4D85, 0x4D86, 0x52B5, 0x52B6, 0x52B7, 0x52B8, 0x52B9, 0x52BA, 0x52BB, 0x52BC, 0x52BD, }; int Leaves[] = { 0x4CCE, 0x4CCF, 0x4CD1, 0x4CD2, 0x4CD4, 0x4CD5, 0x4CD7, 0x4CD9, 0x4CDB, 0x4CDC, 0x4CDE, 0x4CDF, 0x4CE1, 0x4CE2, 0x4CE4, 0x4CE5, 0x4CE7, 0x4CE8, 0x4CF9, 0x4CFA, 0x4CFC, 0x4CFD, 0x4CFF, 0x4D00, 0x4D02, 0x4D03, 0x4D45, 0x4D46, 0x4D47, 0x4D48, 0x4D49, 0x4D4A, 0x4D4B, 0x4D4C, 0x4D4D, 0x4D4E, 0x4D4F, 0x4D50, 0x4D51, 0x4D52, 0x4D53, 0x4D5C, 0x4D5D, 0x4D5E, 0x4D5F, 0x4D60, 0x4D61, 0x4D62, 0x4D63, 0x4D64, 0x4D65, 0x4D66, 0x4D67, 0x4D68, 0x4D69, 0x4D73, 0x4D74, 0x4D75, 0x4D76, 0x4D77, 0x4D78, 0x4D79, 0x4D7A, 0x4D7B, 0x4D7C, 0x4D7D, 0x4D7E, 0x4D7F, 0x4D87, 0x4D88, 0x4D89, 0x4D8A, 0x4D8B, 0x4D8C, 0x4D8D, 0x4D8E, 0x4D8F, 0x4D90, 0x4D95, 0x4D96, 0x4D97, 0x4D99, 0x4D9A, 0x4D9B, 0x4D9D, 0x4D9E, 0x4D9F, 0x4DA1, 0x4DA2, 0x4DA3, 0x4DA5, 0x4DA6, 0x4DA7, 0x4DA9, 0x4DAA, 0x4DAB, 0x52BE, 0x52BF, 0x52C0, 0x52C1, 0x52C2, 0x52C3, 0x52C4, 0x52C5, 0x52C6, 0x52C7 }; HANDLE WINAPI NewCreateFile( LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { strlwr( (char*)lpFileName ); if ( strstr( lpFileName, "artidx.mul" ) && (dwDesiredAccess & GENERIC_READ) != 0 ) { char path[MAX_PATH]; GetTempPath( MAX_PATH, path ); strcat( path, "/razor_art.tmp" ); CopyFile( lpFileName, path, FALSE ); int stump[3], none[3]; FILE *f = fopen( path, "rb" ); fseek( f, 12*0xE57, SEEK_SET ); fread( stump, 12, 1, f ); fseek( f, 12*0x115E, SEEK_SET ); fread( none, 12, 1, f ); fclose( f ); f = fopen( path, "wb" ); for(int i=0;Trees[i] != 0;i++) { fseek( f, 12*Trees[i], SEEK_SET ); fwrite( stump, 12, 1, f ); } for(int i=0;Leaves[i] != 0;i++) { fseek( f, 12*Leaves[i], SEEK_SET ); fwrite( none, 12, 1, f ); } fclose( f ); return CreateFile( path, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile ); } else { return CreateFile( lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile ); } } #define CREATE_FILE_LOC "\x68\x00\x00\x00\xC0\x50\xFF\x15\xF8\x71\x56\x00\x83\xF8\xFF" #define CFL_LEN 15 void PatchCreateFile() { DWORD CurPos = FindInMemory( 0x00400000, CREATE_FILE_LOC, CFL_LEN ); if ( !CurPos ) return; CurPos += 6; *((unsigned char*)(CurPos)) = 0xE8; // call *((unsigned int*)(CurPos+1)) = int(NewCreateFile) - (CurPos+4); *((unsigned char*)(CurPos+5)) = 0x90; // nop } void PatchStartup( DWORD ProcID ) { HANDLE hProc = OpenProcess( PROCESS_ALL_ACCESS, FALSE, ProcID ); if ( !hProc ) return; DWORD image_base = ???; IMAGE_DOS_HEADER *idh = (IMAGE_DOS_HEADER *)image_base; IMAGE_FILE_HEADER *ifh = (IMAGE_FILE_HEADER *)(image_base + idh->e_lfanew + sizeof(DWORD)); IMAGE_OPTIONAL_HEADER *ioh = (IMAGE_OPTIONAL_HEADER *)((DWORD)(ifh) + sizeof(IMAGE_FILE_HEADER)); DWORD acc = 0; void *addr = VirtualAllocEx( hProc, NULL, 512, MEM_COMMIT, PAGE_EXECUTE_READWRITE ); DWORD Pos = (DWORD)addr; char file[256]; acc = GetModuleFileName( GetModuleHandle(NULL), file, 256 ); WriteProcessMemory( hProc, (void*)Pos, file, acc, &acc ); DWORD FileLoc = Pos; Pos += acc; strcpy( file, "AttachedStart" ); WriteProcessMemory( hProc, (void*)Pos, file, strlen(file)+1, &acc ); DWORD ProcLoc = Pos; Pos += acc; unsigned char LoadFunc[] = { 0x55, // push ebp 0x8B, 0xEC, // mov ebp, esp 0x53, // push ebx 0x56, // push esi 0x57, // push edi 0x68, 0, 0, 0, 0, // push _address_ 0xFF,0x15,0xB8,0x71,0x56,0x00, // call ds:LoadLibraryA 0x68, 0, 0, 0, 0, // push _address_ 0x50, // push eax 0x8B, 0xF0, // mov esi, eax 0xFF,0x15,0xBC,0x71,0x56,0x00, // call ds:GetProcAddress 0xFF, 0xD0, // call eax //0x56, // push esi ; hLibModule //0xFF,0x15,0xC0,0x71,0x56,0x00, // call ds:FreeLibrary 0x5F, // pop edi 0x5E, // pop esi 0x5B, // pop ebx 0x5D, // pop ebp 0xE9, 0, 0, 0, 0, // jmp _addressoffset_ 0x90, // nop 0x90, // nop }; const int len = 44; *((int*)(LoadFunc+7)) = FileLoc - (Pos+7+4); *((int*)(LoadFunc+18)) = ProcLoc - (Pos+18+4); *((int*)(LoadFunc+38)) = (ioh->ImageBase + ioh->AddressOfEntryPoint) - (Pos+38+4); WriteProcessMemory( hProc, (void*)Pos, LoadFunc, len, &acc ); ioh->AddressOfEntryPoint = Pos; CloseHandle( hProc ); } ================================================ FILE: Crypt/twofish.c ================================================ /*************************************************************************** TWOFISH2.C -- Optimized C API calls for TWOFISH AES submission Submitters: Bruce Schneier, Counterpane Systems Doug Whiting, Hi/fn John Kelsey, Counterpane Systems Chris Hall, Counterpane Systems David Wagner, UC Berkeley Code Author: Doug Whiting, Hi/fn Version 1.00 April 1998 Copyright 1998, Hi/fn and Counterpane Systems. All rights reserved. Notes: * Optimized version * Tab size is set to 4 characters in this file ***************************************************************************/ #pragma pack(1) #include #include "twofish.h" #include "table.h" #pragma warning(disable: 4005)//ignore macro re-definitions #if defined(min_key) && !defined(MIN_KEY) #define MIN_KEY 1 /* toupper() */ #elif defined(part_key) && !defined(PART_KEY) #define PART_KEY 1 #elif defined(zero_key) && !defined(ZERO_KEY) #define ZERO_KEY 1 #endif #ifdef USE_ASM extern int useAsm; /* ok to use ASM code? */ typedef int cdecl CipherProc (cipherInstance *cipher, keyInstance *key,BYTE *input,int inputLen,BYTE *outBuffer); typedef int cdecl KeySetupProc(keyInstance *key); extern CipherProc *blockEncrypt_86; /* ptr to ASM functions */ extern CipherProc *blockDecrypt_86; extern KeySetupProc *reKey_86; extern DWORD cdecl TwofishAsmCodeSize(void); #endif /* +***************************************************************************** * Constants/Macros/Tables -****************************************************************************/ #define CONST /* help syntax from C++, NOP here */ CONST fullSbox MDStab; /* not actually const. Initialized ONE time */ int needToBuildMDS=1; /* is MDStab initialized yet? */ #define BIG_TAB 0 #if BIG_TAB BYTE bigTab[4][256][256]; /* pre-computed S-box */ #endif /* number of rounds for various key sizes: 128, 192, 256 */ /* (ignored for now in optimized code!) */ CONST int numRounds[4]= {0,ROUNDS_128,ROUNDS_192,ROUNDS_256}; #if REENTRANT #define _sBox_ key->sBox8x32 #else static fullSbox _sBox_; /* permuted MDStab based on keys */ #endif #define _sBox8_(N) (((BYTE *) _sBox_) + (N)*256) /*------- see what level of S-box precomputation we need to do -----*/ #if defined(ZERO_KEY) #define MOD_STRING "(Zero S-box keying)" #define Fe32_128(x,R) \ ( MDStab[0][p8(01)[p8(02)[_b(x,R )]^b0(SKEY[1])]^b0(SKEY[0])] ^ \ MDStab[1][p8(11)[p8(12)[_b(x,R+1)]^b1(SKEY[1])]^b1(SKEY[0])] ^ \ MDStab[2][p8(21)[p8(22)[_b(x,R+2)]^b2(SKEY[1])]^b2(SKEY[0])] ^ \ MDStab[3][p8(31)[p8(32)[_b(x,R+3)]^b3(SKEY[1])]^b3(SKEY[0])] ) #define Fe32_192(x,R) \ ( MDStab[0][p8(01)[p8(02)[p8(03)[_b(x,R )]^b0(SKEY[2])]^b0(SKEY[1])]^b0(SKEY[0])] ^ \ MDStab[1][p8(11)[p8(12)[p8(13)[_b(x,R+1)]^b1(SKEY[2])]^b1(SKEY[1])]^b1(SKEY[0])] ^ \ MDStab[2][p8(21)[p8(22)[p8(23)[_b(x,R+2)]^b2(SKEY[2])]^b2(SKEY[1])]^b2(SKEY[0])] ^ \ MDStab[3][p8(31)[p8(32)[p8(33)[_b(x,R+3)]^b3(SKEY[2])]^b3(SKEY[1])]^b3(SKEY[0])] ) #define Fe32_256(x,R) \ ( MDStab[0][p8(01)[p8(02)[p8(03)[p8(04)[_b(x,R )]^b0(SKEY[3])]^b0(SKEY[2])]^b0(SKEY[1])]^b0(SKEY[0])] ^ \ MDStab[1][p8(11)[p8(12)[p8(13)[p8(14)[_b(x,R+1)]^b1(SKEY[3])]^b1(SKEY[2])]^b1(SKEY[1])]^b1(SKEY[0])] ^ \ MDStab[2][p8(21)[p8(22)[p8(23)[p8(24)[_b(x,R+2)]^b2(SKEY[3])]^b2(SKEY[2])]^b2(SKEY[1])]^b2(SKEY[0])] ^ \ MDStab[3][p8(31)[p8(32)[p8(33)[p8(34)[_b(x,R+3)]^b3(SKEY[3])]^b3(SKEY[2])]^b3(SKEY[1])]^b3(SKEY[0])] ) #define GetSboxKey DWORD SKEY[4]; /* local copy */ \ memcpy(SKEY,key->sboxKeys,sizeof(SKEY)); /*----------------------------------------------------------------*/ #elif defined(MIN_KEY) #define MOD_STRING "(Minimal keying)" #define Fe32_(x,R)(MDStab[0][p8(01)[_sBox8_(0)[_b(x,R )]] ^ b0(SKEY0)] ^ \ MDStab[1][p8(11)[_sBox8_(1)[_b(x,R+1)]] ^ b1(SKEY0)] ^ \ MDStab[2][p8(21)[_sBox8_(2)[_b(x,R+2)]] ^ b2(SKEY0)] ^ \ MDStab[3][p8(31)[_sBox8_(3)[_b(x,R+3)]] ^ b3(SKEY0)]) #define sbSet(N,i,J,v) { _sBox8_(N)[i+J] = v; } #define GetSboxKey DWORD SKEY0 = key->sboxKeys[0] /* local copy */ /*----------------------------------------------------------------*/ #elif defined(PART_KEY) #define MOD_STRING "(Partial keying)" #define Fe32_(x,R)(MDStab[0][_sBox8_(0)[_b(x,R )]] ^ \ MDStab[1][_sBox8_(1)[_b(x,R+1)]] ^ \ MDStab[2][_sBox8_(2)[_b(x,R+2)]] ^ \ MDStab[3][_sBox8_(3)[_b(x,R+3)]]) #define sbSet(N,i,J,v) { _sBox8_(N)[i+J] = v; } #define GetSboxKey /*----------------------------------------------------------------*/ #else /* default is FULL_KEY */ #ifndef FULL_KEY #define FULL_KEY 1 #endif #if BIG_TAB #define TAB_STR " (Big table)" #else #define TAB_STR #endif #ifdef COMPILE_KEY #define MOD_STRING "(Compiled subkeys)" TAB_STR #else #define MOD_STRING "(Full keying)" TAB_STR #endif /* Fe32_ does a full S-box + MDS lookup. Need to #define _sBox_ before use. Note that we "interleave" 0,1, and 2,3 to avoid cache bank collisions in optimized assembly language. */ #define Fe32_(x,R) (_sBox_[0][2*_b(x,R )] ^ _sBox_[0][2*_b(x,R+1)+1] ^ \ _sBox_[2][2*_b(x,R+2)] ^ _sBox_[2][2*_b(x,R+3)+1]) /* set a single S-box value, given the input byte */ #define sbSet(N,i,J,v) { _sBox_[N&2][2*i+(N&1)+2*J]=MDStab[N][v]; } #define GetSboxKey #endif CONST char *moduleDescription ="Optimized C "; CONST char *modeString =MOD_STRING; /* macro(s) for debugging help */ #define CHECK_TABLE 0 /* nonzero --> compare against "slow" table */ #define VALIDATE_PARMS 0 /* disable for full speed */ #include "debug.h" /* debug display macros */ /* end of debug macros */ #ifdef GetCodeSize extern DWORD Here(DWORD x); /* return caller's address! */ DWORD TwofishCodeStart(void) { return Here(0); } #endif /* +***************************************************************************** * * Function Name: TableOp * * Function: Handle table use checking * * Arguments: op = what to do (see TAB_* defns in AES.H) * * Return: TRUE --> done (for TAB_QUERY) * * Notes: This routine is for use in generating the tables KAT file. * For this optimized version, we don't actually track table usage, * since it would make the macros incredibly ugly. Instead we just * run for a fixed number of queries and then say we're done. * -****************************************************************************/ int TableOp(int op) { static int queryCnt=0; switch (op) { case TAB_DISABLE: break; case TAB_ENABLE: break; case TAB_RESET: queryCnt=0; break; case TAB_QUERY: queryCnt++; if (queryCnt < TAB_MIN_QUERY) return FALSE; } return TRUE; } /* +***************************************************************************** * * Function Name: ParseHexDword * * Function: Parse ASCII hex nibbles and fill in key/iv dwords * * Arguments: bit = # bits to read * srcTxt = ASCII source * d = ptr to dwords to fill in * dstTxt = where to make a copy of ASCII source * (NULL ok) * * Return: Zero if no error. Nonzero --> invalid hex or length * * Notes: Note that the parameter d is a DWORD array, not a byte array. * This routine is coded to work both for little-endian and big-endian * architectures. The character stream is interpreted as a LITTLE-ENDIAN * byte stream, since that is how the Pentium works, but the conversion * happens automatically below. * -****************************************************************************/ int ParseHexDword(int bits,CONST char *srcTxt,DWORD *d,char *dstTxt) { int i; char c; DWORD b; union /* make sure LittleEndian is defined correctly */ { BYTE b[4]; DWORD d[1]; } v; v.d[0]=1; if (v.b[0 ^ ADDR_XOR] != 1) return BAD_ENDIAN; /* make sure compile-time switch is set ok */ #if VALIDATE_PARMS #if ALIGN32 if (((int)d) & 3) return BAD_ALIGN32; #endif #endif for (i=0;i*32= '0') && (c <= '9')) b=c-'0'; else if ((c >= 'a') && (c <= 'f')) b=c-'a'+10; else if ((c >= 'A') && (c <= 'F')) b=c-'A'+10; else return BAD_KEY_MAT; /* invalid hex character */ /* works for big and little endian! */ d[i/8] |= b << (4*((i^1)&7)); } return 0; /* no error */ } #if CHECK_TABLE /* +***************************************************************************** * * Function Name: f32 * * Function: Run four bytes through keyed S-boxes and apply MDS matrix * * Arguments: x = input to f function * k32 = pointer to key dwords * keyLen = total key length (k32 --> keyLey/2 bits) * * Return: The output of the keyed permutation applied to x. * * Notes: * This function is a keyed 32-bit permutation. It is the major building * block for the Twofish round function, including the four keyed 8x8 * permutations and the 4x4 MDS matrix multiply. This function is used * both for generating round subkeys and within the round function on the * block being encrypted. * * This version is fairly slow and pedagogical, although a smartcard would * probably perform the operation exactly this way in firmware. For * ultimate performance, the entire operation can be completed with four * lookups into four 256x32-bit tables, with three dword xors. * * The MDS matrix is defined in TABLE.H. To multiply by Mij, just use the * macro Mij(x). * -****************************************************************************/ DWORD f32(DWORD x,CONST DWORD *k32,int keyLen) { BYTE b[4]; /* Run each byte thru 8x8 S-boxes, xoring with key byte at each stage. */ /* Note that each byte goes through a different combination of S-boxes.*/ *((DWORD *)b) = Bswap(x); /* make b[0] = LSB, b[3] = MSB */ switch (((keyLen + 63)/64) & 3) { case 0: /* 256 bits of key */ b[0] = p8(04)[b[0]] ^ b0(k32[3]); b[1] = p8(14)[b[1]] ^ b1(k32[3]); b[2] = p8(24)[b[2]] ^ b2(k32[3]); b[3] = p8(34)[b[3]] ^ b3(k32[3]); /* fall thru, having pre-processed b[0]..b[3] with k32[3] */ case 3: /* 192 bits of key */ b[0] = p8(03)[b[0]] ^ b0(k32[2]); b[1] = p8(13)[b[1]] ^ b1(k32[2]); b[2] = p8(23)[b[2]] ^ b2(k32[2]); b[3] = p8(33)[b[3]] ^ b3(k32[2]); /* fall thru, having pre-processed b[0]..b[3] with k32[2] */ case 2: /* 128 bits of key */ b[0] = p8(00)[p8(01)[p8(02)[b[0]] ^ b0(k32[1])] ^ b0(k32[0])]; b[1] = p8(10)[p8(11)[p8(12)[b[1]] ^ b1(k32[1])] ^ b1(k32[0])]; b[2] = p8(20)[p8(21)[p8(22)[b[2]] ^ b2(k32[1])] ^ b2(k32[0])]; b[3] = p8(30)[p8(31)[p8(32)[b[3]] ^ b3(k32[1])] ^ b3(k32[0])]; } /* Now perform the MDS matrix multiply inline. */ return ((M00(b[0]) ^ M01(b[1]) ^ M02(b[2]) ^ M03(b[3])) ) ^ ((M10(b[0]) ^ M11(b[1]) ^ M12(b[2]) ^ M13(b[3])) << 8) ^ ((M20(b[0]) ^ M21(b[1]) ^ M22(b[2]) ^ M23(b[3])) << 16) ^ ((M30(b[0]) ^ M31(b[1]) ^ M32(b[2]) ^ M33(b[3])) << 24) ; } #endif /* CHECK_TABLE */ /* +***************************************************************************** * * Function Name: RS_MDS_encode * * Function: Use (12,8) Reed-Solomon code over GF(256) to produce * a key S-box dword from two key material dwords. * * Arguments: k0 = 1st dword * k1 = 2nd dword * * Return: Remainder polynomial generated using RS code * * Notes: * Since this computation is done only once per reKey per 64 bits of key, * the performance impact of this routine is imperceptible. The RS code * chosen has "simple" coefficients to allow smartcard/hardware implementation * without lookup tables. * -****************************************************************************/ DWORD RS_MDS_Encode(DWORD k0,DWORD k1) { int i,j; DWORD r; for (i=r=0;i<2;i++) { r ^= (i) ? k0 : k1; /* merge in 32 more key bits */ for (j=0;j<4;j++) /* shift one byte at a time */ RS_rem(r); } return r; } /* +***************************************************************************** * * Function Name: BuildMDS * * Function: Initialize the MDStab array * * Arguments: None. * * Return: None. * * Notes: * Here we precompute all the fixed MDS table. This only needs to be done * one time at initialization, after which the table is "CONST". * -****************************************************************************/ void BuildMDS(void) { int i; DWORD d; BYTE m1[2],mX[2],mY[4]; for (i=0;i<256;i++) { m1[0]=P8x8[0][i]; /* compute all the matrix elements */ mX[0]=(BYTE) Mul_X(m1[0]); mY[0]=(BYTE) Mul_Y(m1[0]); m1[1]=P8x8[1][i]; mX[1]=(BYTE) Mul_X(m1[1]); mY[1]=(BYTE) Mul_Y(m1[1]); #undef Mul_1 /* change what the pre-processor does with Mij */ #undef Mul_X #undef Mul_Y #define Mul_1 m1 /* It will now access m01[], m5B[], and mEF[] */ #define Mul_X mX #define Mul_Y mY #define SetMDS(N) \ b0(d) = M0##N[P_##N##0]; \ b1(d) = M1##N[P_##N##0]; \ b2(d) = M2##N[P_##N##0]; \ b3(d) = M3##N[P_##N##0]; \ MDStab[N][i] = d; SetMDS(0); /* fill in the matrix with elements computed above */ SetMDS(1); SetMDS(2); SetMDS(3); } #undef Mul_1 #undef Mul_X #undef Mul_Y #define Mul_1 Mx_1 /* re-enable true multiply */ #define Mul_X Mx_X #define Mul_Y Mx_Y #if BIG_TAB { int j,k; BYTE *q0,*q1; for (i=0;i<4;i++) { switch (i) { case 0: q0=p8(01); q1=p8(02); break; case 1: q0=p8(11); q1=p8(12); break; case 2: q0=p8(21); q1=p8(22); break; case 3: q0=p8(31); q1=p8(32); break; } for (j=0;j<256;j++) for (k=0;k<256;k++) bigTab[i][j][k]=q0[q1[k]^j]; } } #endif needToBuildMDS=0; /* NEVER modify the table again! */ } /* +***************************************************************************** * * Function Name: ReverseRoundSubkeys * * Function: Reverse order of round subkeys to switch between encrypt/decrypt * * Arguments: key = ptr to keyInstance to be reversed * newDir = new direction value * * Return: None. * * Notes: * This optimization allows both blockEncrypt and blockDecrypt to use the same * "fallthru" switch statement based on the number of rounds. * Note that key->numRounds must be even and >= 2 here. * -****************************************************************************/ void ReverseRoundSubkeys(keyInstance *key,BYTE newDir) { DWORD t0,t1; register DWORD *r0=key->subKeys+ROUND_SUBKEYS; register DWORD *r1=r0 + 2*key->numRounds - 2; for (;r0 < r1;r0+=2,r1-=2) { t0=r0[0]; /* swap the order */ t1=r0[1]; r0[0]=r1[0]; /* but keep relative order within pairs */ r0[1]=r1[1]; r1[0]=t0; r1[1]=t1; } key->direction=newDir; } /* +***************************************************************************** * * Function Name: Xor256 * * Function: Copy an 8-bit permutation (256 bytes), xoring with a byte * * Arguments: dst = where to put result * src = where to get data (can be same asa dst) * b = byte to xor * * Return: None * * Notes: * BorlandC's optimization is terrible! When we put the code inline, * it generates fairly good code in the *following* segment (not in the Xor256 * code itself). If the call is made, the code following the call is awful! * The penalty is nearly 50%! So we take the code size hit for inlining for * Borland, while Microsoft happily works with a call. * -****************************************************************************/ #if defined(__BORLANDC__) /* do it inline */ #define Xor32(dst,src,i) { ((DWORD *)dst)[i] = ((DWORD *)src)[i] ^ tmpX; } #define Xor256(dst,src,b) \ { \ register DWORD tmpX=0x01010101u * b;\ for (i=0;i<64;i+=4) \ { Xor32(dst,src,i ); Xor32(dst,src,i+1); Xor32(dst,src,i+2); Xor32(dst,src,i+3); } \ } #else /* do it as a function call */ void Xor256(void *dst,void *src,BYTE b) { register DWORD x=b*0x01010101u; /* replicate byte to all four bytes */ register DWORD *d=(DWORD *)dst; register DWORD *s=(DWORD *)src; #define X_8(N) { d[N]=s[N] ^ x; d[N+1]=s[N+1] ^ x; } #define X_32(N) { X_8(N); X_8(N+2); X_8(N+4); X_8(N+6); } X_32(0 ); X_32( 8); X_32(16); X_32(24); /* all inline */ d+=32; /* keep offsets small! */ s+=32; X_32(0 ); X_32( 8); X_32(16); X_32(24); /* all inline */ } #endif /* +***************************************************************************** * * Function Name: reKey * * Function: Initialize the Twofish key schedule from key32 * * Arguments: key = ptr to keyInstance to be initialized * * Return: TRUE on success * * Notes: * Here we precompute all the round subkeys, although that is not actually * required. For example, on a smartcard, the round subkeys can * be generated on-the-fly using f32() * -****************************************************************************/ int reKey(keyInstance *key) { int i,j,k64Cnt,keyLen; int subkeyCnt; DWORD A=0,B=0,q; DWORD sKey[MAX_KEY_BITS/64],k32e[MAX_KEY_BITS/64],k32o[MAX_KEY_BITS/64]; BYTE L0[256],L1[256]; /* small local 8-bit permutations */ #if VALIDATE_PARMS #if ALIGN32 if (((int)key) & 3) return BAD_ALIGN32; if ((key->keyLen % 64) || (key->keyLen < MIN_KEY_BITS)) return BAD_KEY_INSTANCE; #endif #endif if (needToBuildMDS) /* do this one time only */ BuildMDS(); #define F32(res,x,k32) \ { \ DWORD t=x; \ switch (k64Cnt & 3) \ { \ case 0: /* same as 4 */ \ b0(t) = p8(04)[b0(t)] ^ b0(k32[3]); \ b1(t) = p8(14)[b1(t)] ^ b1(k32[3]); \ b2(t) = p8(24)[b2(t)] ^ b2(k32[3]); \ b3(t) = p8(34)[b3(t)] ^ b3(k32[3]); \ /* fall thru, having pre-processed t */ \ case 3: b0(t) = p8(03)[b0(t)] ^ b0(k32[2]); \ b1(t) = p8(13)[b1(t)] ^ b1(k32[2]); \ b2(t) = p8(23)[b2(t)] ^ b2(k32[2]); \ b3(t) = p8(33)[b3(t)] ^ b3(k32[2]); \ /* fall thru, having pre-processed t */ \ case 2: /* 128-bit keys (optimize for this case) */ \ res= MDStab[0][p8(01)[p8(02)[b0(t)] ^ b0(k32[1])] ^ b0(k32[0])] ^ \ MDStab[1][p8(11)[p8(12)[b1(t)] ^ b1(k32[1])] ^ b1(k32[0])] ^ \ MDStab[2][p8(21)[p8(22)[b2(t)] ^ b2(k32[1])] ^ b2(k32[0])] ^ \ MDStab[3][p8(31)[p8(32)[b3(t)] ^ b3(k32[1])] ^ b3(k32[0])] ; \ } \ } #if !CHECK_TABLE #if defined(USE_ASM) /* only do this if not using assember */ if (!(useAsm & 4)) #endif #endif { subkeyCnt = ROUND_SUBKEYS + 2*key->numRounds; keyLen=key->keyLen; k64Cnt=(keyLen+63)/64; /* number of 64-bit key words */ for (i=0,j=k64Cnt-1;ikey32[2*i ]; k32o[i]=key->key32[2*i+1]; /* compute S-box keys using (12,8) Reed-Solomon code over GF(256) */ sKey[j]=key->sboxKeys[j]=RS_MDS_Encode(k32e[i],k32o[i]); /* reverse order */ } } #ifdef USE_ASM if (useAsm & 4) { #if defined(COMPILE_KEY) && defined(USE_ASM) key->keySig = VALID_SIG; /* show that we are initialized */ key->codeSize = sizeof(key->compiledCode); /* set size */ #endif reKey_86(key); } else #endif { for (i=q=0;isubKeys[2*i ] = A+B; /* combine with a PHT */ B = A + 2*B; key->subKeys[2*i+1] = ROL(B,SK_ROTL); } #if !defined(ZERO_KEY) switch (keyLen) /* case out key length for speed in generating S-boxes */ { case 128: #if defined(FULL_KEY) || defined(PART_KEY) #if BIG_TAB #define one128(N,J) sbSet(N,i,J,L0[i+J]) #define sb128(N) { \ BYTE *qq=bigTab[N][b##N(sKey[1])]; \ Xor256(L0,qq,b##N(sKey[0])); \ for (i=0;i<256;i+=2) { one128(N,0); one128(N,1); } } #else #define one128(N,J) sbSet(N,i,J,p8(N##1)[L0[i+J]]^k0) #define sb128(N) { \ Xor256(L0,p8(N##2),b##N(sKey[1])); \ { register DWORD k0=b##N(sKey[0]); \ for (i=0;i<256;i+=2) { one128(N,0); one128(N,1); } } } #endif #elif defined(MIN_KEY) #define sb128(N) Xor256(_sBox8_(N),p8(N##2),b##N(sKey[1])) #endif sb128(0); sb128(1); sb128(2); sb128(3); break; case 192: #if defined(FULL_KEY) || defined(PART_KEY) #define one192(N,J) sbSet(N,i,J,p8(N##1)[p8(N##2)[L0[i+J]]^k1]^k0) #define sb192(N) { \ Xor256(L0,p8(N##3),b##N(sKey[2])); \ { register DWORD k0=b##N(sKey[0]); \ register DWORD k1=b##N(sKey[1]); \ for (i=0;i<256;i+=2) { one192(N,0); one192(N,1); } } } #elif defined(MIN_KEY) #define one192(N,J) sbSet(N,i,J,p8(N##2)[L0[i+J]]^k1) #define sb192(N) { \ Xor256(L0,p8(N##3),b##N(sKey[2])); \ { register DWORD k1=b##N(sKey[1]); \ for (i=0;i<256;i+=2) { one192(N,0); one192(N,1); } } } #endif sb192(0); sb192(1); sb192(2); sb192(3); break; case 256: #if defined(FULL_KEY) || defined(PART_KEY) #define one256(N,J) sbSet(N,i,J,p8(N##1)[p8(N##2)[L0[i+J]]^k1]^k0) #define sb256(N) { \ Xor256(L1,p8(N##4),b##N(sKey[3])); \ for (i=0;i<256;i+=2) {L0[i ]=p8(N##3)[L1[i]]; \ L0[i+1]=p8(N##3)[L1[i+1]]; } \ Xor256(L0,L0,b##N(sKey[2])); \ { register DWORD k0=b##N(sKey[0]); \ register DWORD k1=b##N(sKey[1]); \ for (i=0;i<256;i+=2) { one256(N,0); one256(N,1); } } } #elif defined(MIN_KEY) #define one256(N,J) sbSet(N,i,J,p8(N##2)[L0[i+J]]^k1) #define sb256(N) { \ Xor256(L1,p8(N##4),b##N(sKey[3])); \ for (i=0;i<256;i+=2) {L0[i ]=p8(N##3)[L1[i]]; \ L0[i+1]=p8(N##3)[L1[i+1]]; } \ Xor256(L0,L0,b##N(sKey[2])); \ { register DWORD k1=b##N(sKey[1]); \ for (i=0;i<256;i+=2) { one256(N,0); one256(N,1); } } } #endif sb256(0); sb256(1); sb256(2); sb256(3); break; } #endif } #if CHECK_TABLE /* sanity check vs. pedagogical code*/ { GetSboxKey; for (i=0;isubKeys[2*i ] == A+ B); assert(key->subKeys[2*i+1] == ROL(A+2*B,SK_ROTL)); } #if !defined(ZERO_KEY) /* any S-boxes to check? */ for (i=q=0;i<256;i++,q+=0x01010101) assert(f32(q,key->sboxKeys,keyLen) == Fe32_(q,0)); #endif } #endif /* CHECK_TABLE */ DebugDumpKey(key); if (key->direction == DIR_ENCRYPT) ReverseRoundSubkeys(key,DIR_ENCRYPT); /* reverse the round subkey order */ return TRUE; } /* +***************************************************************************** * * Function Name: makeKey * * Function: Initialize the Twofish key schedule * * Arguments: key = ptr to keyInstance to be initialized * direction = DIR_ENCRYPT or DIR_DECRYPT * keyLen = # bits of key text at *keyMaterial * keyMaterial = ptr to hex ASCII chars representing key bits * * Return: TRUE on success * else error code (e.g., BAD_KEY_DIR) * * Notes: This parses the key bits from keyMaterial. Zeroes out unused key bits * -****************************************************************************/ int makeKey(keyInstance *key, BYTE direction, int keyLen,CONST char *keyMaterial) { #if VALIDATE_PARMS /* first, sanity check on parameters */ if (key == NULL) return BAD_KEY_INSTANCE;/* must have a keyInstance to initialize */ if ((direction != DIR_ENCRYPT) && (direction != DIR_DECRYPT)) return BAD_KEY_DIR; /* must have valid direction */ if ((keyLen > MAX_KEY_BITS) || (keyLen < 8) || (keyLen & 0x3F)) return BAD_KEY_MAT; /* length must be valid */ key->keySig = VALID_SIG; /* show that we are initialized */ #if ALIGN32 if ((((int)key) & 3) || (((int)key->key32) & 3)) return BAD_ALIGN32; #endif #endif key->direction = direction;/* set our cipher direction */ key->keyLen = (keyLen+63) & ~63; /* round up to multiple of 64 */ key->numRounds = numRounds[(keyLen-1)/64]; memset(key->key32,0,sizeof(key->key32)); /* zero unused bits */ key->keyMaterial[MAX_KEY_SIZE]=0; /* terminate ASCII string */ if ((keyMaterial == NULL) || (keyMaterial[0]==0)) return TRUE; /* allow a "dummy" call */ if (ParseHexDword(keyLen,keyMaterial,key->key32,key->keyMaterial)) return BAD_KEY_MAT; return reKey(key); /* generate round subkeys */ } /* +***************************************************************************** * * Function Name: cipherInit * * Function: Initialize the Twofish cipher in a given mode * * Arguments: cipher = ptr to cipherInstance to be initialized * mode = MODE_ECB, MODE_CBC, or MODE_CFB1 * IV = ptr to hex ASCII test representing IV bytes * * Return: TRUE on success * else error code (e.g., BAD_CIPHER_MODE) * -****************************************************************************/ int cipherInit(cipherInstance *cipher, BYTE mode,CONST char *IV) { int i; #if VALIDATE_PARMS /* first, sanity check on parameters */ if (cipher == NULL) return BAD_PARAMS; /* must have a cipherInstance to initialize */ if ((mode != MODE_ECB) && (mode != MODE_CBC) && (mode != MODE_CFB1)) return BAD_CIPHER_MODE; /* must have valid cipher mode */ cipher->cipherSig = VALID_SIG; #if ALIGN32 if ((((int)cipher) & 3) || (((int)cipher->IV) & 3) || (((int)cipher->iv32) & 3)) return BAD_ALIGN32; #endif #endif if ((mode != MODE_ECB) && (IV)) /* parse the IV */ { if (ParseHexDword(BLOCK_SIZE,IV,cipher->iv32,NULL)) return BAD_IV_MAT; for (i=0;iIV)[i] = Bswap(cipher->iv32[i]); } cipher->mode = mode; return TRUE; } /* +***************************************************************************** * * Function Name: blockEncrypt * * Function: Encrypt block(s) of data using Twofish * * Arguments: cipher = ptr to already initialized cipherInstance * key = ptr to already initialized keyInstance * input = ptr to data blocks to be encrypted * inputLen = # bits to encrypt (multiple of blockSize) * outBuffer = ptr to where to put encrypted blocks * * Return: # bits ciphered (>= 0) * else error code (e.g., BAD_CIPHER_STATE, BAD_KEY_MATERIAL) * * Notes: The only supported block size for ECB/CBC modes is BLOCK_SIZE bits. * If inputLen is not a multiple of BLOCK_SIZE bits in those modes, * an error BAD_INPUT_LEN is returned. In CFB1 mode, all block * sizes can be supported. * -****************************************************************************/ int blockEncrypt(cipherInstance *cipher, keyInstance *key,CONST BYTE *input, int inputLen, BYTE *outBuffer) { int i,n; /* loop counters */ DWORD x[BLOCK_SIZE/32]; /* block being encrypted */ DWORD t0,t1; /* temp variables */ int rounds=key->numRounds; /* number of rounds */ BYTE bit,bit0,ctBit,carry; /* temps for CFB */ /* make local copies of things for faster access */ int mode = cipher->mode; DWORD sk[TOTAL_SUBKEYS]; DWORD IV[BLOCK_SIZE/32]; GetSboxKey; #if VALIDATE_PARMS if ((cipher == NULL) || (cipher->cipherSig != VALID_SIG)) return BAD_CIPHER_STATE; if ((key == NULL) || (key->keySig != VALID_SIG)) return BAD_KEY_INSTANCE; if ((rounds < 2) || (rounds > MAX_ROUNDS) || (rounds&1)) return BAD_KEY_INSTANCE; if ((mode != MODE_CFB1) && (inputLen % BLOCK_SIZE)) return BAD_INPUT_LEN; #if ALIGN32 if ( (((int)cipher) & 3) || (((int)key ) & 3) || (((int)input ) & 3) || (((int)outBuffer) & 3)) return BAD_ALIGN32; #endif #endif if (mode == MODE_CFB1) { /* use recursion here to handle CFB, one block at a time */ cipher->mode = MODE_ECB; /* do encryption in ECB */ for (n=0;nIV,BLOCK_SIZE,(BYTE *)x); bit0 = 0x80 >> (n & 7);/* which bit position in byte */ ctBit = (input[n/8] & bit0) ^ ((((BYTE *) x)[0] & 0x80) >> (n&7)); outBuffer[n/8] = (outBuffer[n/8] & ~ bit0) | ctBit; carry = ctBit >> (7 - (n&7)); for (i=BLOCK_SIZE/8-1;i>=0;i--) { bit = cipher->IV[i] >> 7; /* save next "carry" from shift */ cipher->IV[i] = (cipher->IV[i] << 1) ^ carry; carry = bit; } } cipher->mode = MODE_CFB1; /* restore mode for next time */ return inputLen; } /* here for ECB, CBC modes */ if (key->direction != DIR_ENCRYPT) ReverseRoundSubkeys(key,DIR_ENCRYPT); /* reverse the round subkey order */ #ifdef USE_ASM if ((useAsm & 1) && (inputLen)) #ifdef COMPILE_KEY if (key->keySig == VALID_SIG) return ((CipherProc *)(key->encryptFuncPtr))(cipher,key,input,inputLen,outBuffer); #else return (*blockEncrypt_86)(cipher,key,input,inputLen,outBuffer); #endif #endif /* make local copy of subkeys for speed */ memcpy(sk,key->subKeys,sizeof(DWORD)*(ROUND_SUBKEYS+2*rounds)); if (mode == MODE_CBC) BlockCopy(IV,cipher->iv32) else IV[0]=IV[1]=IV[2]=IV[3]=0; for (n=0;nmode == MODE_CBC) DebugDump(cipher->iv32,"",IV_ROUND,0,0,0,0); #endif #define LoadBlockE(N) x[N]=Bswap(((DWORD *)input)[N]) ^ sk[INPUT_WHITEN+N] ^ IV[N] LoadBlockE(0); LoadBlockE(1); LoadBlockE(2); LoadBlockE(3); DebugDump(x,"",0,0,0,0,0); #define EncryptRound(K,R,id) \ t0 = Fe32##id(x[K ],0); \ t1 = Fe32##id(x[K^1],3); \ x[K^3] = ROL(x[K^3],1); \ x[K^2]^= t0 + t1 + sk[ROUND_SUBKEYS+2*(R) ]; \ x[K^3]^= t0 + 2*t1 + sk[ROUND_SUBKEYS+2*(R)+1]; \ x[K^2] = ROR(x[K^2],1); \ DebugDump(x,"",rounds-(R),0,0,1,0); #define Encrypt2(R,id) { EncryptRound(0,R+1,id); EncryptRound(2,R,id); } #if defined(ZERO_KEY) switch (key->keyLen) { case 128: for (i=rounds-2;i>=0;i-=2) Encrypt2(i,_128); break; case 192: for (i=rounds-2;i>=0;i-=2) Encrypt2(i,_192); break; case 256: for (i=rounds-2;i>=0;i-=2) Encrypt2(i,_256); break; } #else Encrypt2(14,_); Encrypt2(12,_); Encrypt2(10,_); Encrypt2( 8,_); Encrypt2( 6,_); Encrypt2( 4,_); Encrypt2( 2,_); Encrypt2( 0,_); #endif /* need to do (or undo, depending on your point of view) final swap */ #if LittleEndian #define StoreBlockE(N) ((DWORD *)outBuffer)[N]=x[N^2] ^ sk[OUTPUT_WHITEN+N] #else #define StoreBlockE(N) { t0=x[N^2] ^ sk[OUTPUT_WHITEN+N]; ((DWORD *)outBuffer)[N]=Bswap(t0); } #endif StoreBlockE(0); StoreBlockE(1); StoreBlockE(2); StoreBlockE(3); if (mode == MODE_CBC) { IV[0]=Bswap(((DWORD *)outBuffer)[0]); IV[1]=Bswap(((DWORD *)outBuffer)[1]); IV[2]=Bswap(((DWORD *)outBuffer)[2]); IV[3]=Bswap(((DWORD *)outBuffer)[3]); } #ifdef DEBUG DebugDump(outBuffer,"",rounds+1,0,0,0,1); if (cipher->mode == MODE_CBC) DebugDump(cipher->iv32,"",IV_ROUND,0,0,0,0); #endif } if (mode == MODE_CBC) BlockCopy(cipher->iv32,IV); return inputLen; } /* +***************************************************************************** * * Function Name: blockDecrypt * * Function: Decrypt block(s) of data using Twofish * * Arguments: cipher = ptr to already initialized cipherInstance * key = ptr to already initialized keyInstance * input = ptr to data blocks to be decrypted * inputLen = # bits to encrypt (multiple of blockSize) * outBuffer = ptr to where to put decrypted blocks * * Return: # bits ciphered (>= 0) * else error code (e.g., BAD_CIPHER_STATE, BAD_KEY_MATERIAL) * * Notes: The only supported block size for ECB/CBC modes is BLOCK_SIZE bits. * If inputLen is not a multiple of BLOCK_SIZE bits in those modes, * an error BAD_INPUT_LEN is returned. In CFB1 mode, all block * sizes can be supported. * -****************************************************************************/ int blockDecrypt(cipherInstance *cipher, keyInstance *key,CONST BYTE *input, int inputLen, BYTE *outBuffer) { int i,n; /* loop counters */ DWORD x[BLOCK_SIZE/32]; /* block being encrypted */ DWORD t0,t1; /* temp variables */ int rounds=key->numRounds; /* number of rounds */ BYTE bit,bit0,ctBit,carry; /* temps for CFB */ /* make local copies of things for faster access */ int mode = cipher->mode; DWORD sk[TOTAL_SUBKEYS]; DWORD IV[BLOCK_SIZE/32]; GetSboxKey; #if VALIDATE_PARMS if ((cipher == NULL) || (cipher->cipherSig != VALID_SIG)) return BAD_CIPHER_STATE; if ((key == NULL) || (key->keySig != VALID_SIG)) return BAD_KEY_INSTANCE; if ((rounds < 2) || (rounds > MAX_ROUNDS) || (rounds&1)) return BAD_KEY_INSTANCE; if ((cipher->mode != MODE_CFB1) && (inputLen % BLOCK_SIZE)) return BAD_INPUT_LEN; #if ALIGN32 if ( (((int)cipher) & 3) || (((int)key ) & 3) || (((int)input) & 3) || (((int)outBuffer) & 3)) return BAD_ALIGN32; #endif #endif if (cipher->mode == MODE_CFB1) { /* use blockEncrypt here to handle CFB, one block at a time */ cipher->mode = MODE_ECB; /* do encryption in ECB */ for (n=0;nIV,BLOCK_SIZE,(BYTE *)x); bit0 = 0x80 >> (n & 7); ctBit = input[n/8] & bit0; outBuffer[n/8] = (outBuffer[n/8] & ~ bit0) | (ctBit ^ ((((BYTE *) x)[0] & 0x80) >> (n&7))); carry = ctBit >> (7 - (n&7)); for (i=BLOCK_SIZE/8-1;i>=0;i--) { bit = cipher->IV[i] >> 7; /* save next "carry" from shift */ cipher->IV[i] = (cipher->IV[i] << 1) ^ carry; carry = bit; } } cipher->mode = MODE_CFB1; /* restore mode for next time */ return inputLen; } /* here for ECB, CBC modes */ if (key->direction != DIR_DECRYPT) ReverseRoundSubkeys(key,DIR_DECRYPT); /* reverse the round subkey order */ #ifdef USE_ASM if ((useAsm & 2) && (inputLen)) #ifdef COMPILE_KEY if (key->keySig == VALID_SIG) return ((CipherProc *)(key->decryptFuncPtr))(cipher,key,input,inputLen,outBuffer); #else return (*blockDecrypt_86)(cipher,key,input,inputLen,outBuffer); #endif #endif /* make local copy of subkeys for speed */ memcpy(sk,key->subKeys,sizeof(DWORD)*(ROUND_SUBKEYS+2*rounds)); if (mode == MODE_CBC) BlockCopy(IV,cipher->iv32) else IV[0]=IV[1]=IV[2]=IV[3]=0; for (n=0;nkeyLen) { case 128: for (i=rounds-2;i>=0;i-=2) Decrypt2(i,_128); break; case 192: for (i=rounds-2;i>=0;i-=2) Decrypt2(i,_192); break; case 256: for (i=rounds-2;i>=0;i-=2) Decrypt2(i,_256); break; } #else { Decrypt2(14,_); Decrypt2(12,_); Decrypt2(10,_); Decrypt2( 8,_); Decrypt2( 6,_); Decrypt2( 4,_); Decrypt2( 2,_); Decrypt2( 0,_); } #endif DebugDump(x,"",0,0,0,0,0); if (cipher->mode == MODE_ECB) { #if LittleEndian #define StoreBlockD(N) ((DWORD *)outBuffer)[N] = x[N] ^ sk[INPUT_WHITEN+N] #else #define StoreBlockD(N) { t0=x[N]^sk[INPUT_WHITEN+N]; ((DWORD *)outBuffer)[N] = Bswap(t0); } #endif StoreBlockD(0); StoreBlockD(1); StoreBlockD(2); StoreBlockD(3); #undef StoreBlockD DebugDump(outBuffer,"",-1,0,0,0,1); continue; } else { #define StoreBlockD(N) x[N] ^= sk[INPUT_WHITEN+N] ^ IV[N]; \ IV[N] = Bswap(((DWORD *)input)[N]); \ ((DWORD *)outBuffer)[N] = Bswap(x[N]); StoreBlockD(0); StoreBlockD(1); StoreBlockD(2); StoreBlockD(3); #undef StoreBlockD DebugDump(outBuffer,"",-1,0,0,0,1); } } if (mode == MODE_CBC) /* restore iv32 to cipher */ BlockCopy(cipher->iv32,IV) return inputLen; } #ifdef GetCodeSize DWORD TwofishCodeSize(void) { DWORD x= Here(0); #ifdef USE_ASM if (useAsm & 3) return TwofishAsmCodeSize(); #endif return x - TwofishCodeStart(); }; #endif ================================================ FILE: Crypt/twofish.h ================================================ /* aes.h */ /* ---------- See examples at end of this file for typical usage -------- */ /* AES Cipher header file for ANSI C Submissions Lawrence E. Bassham III Computer Security Division National Institute of Standards and Technology This sample is to assist implementers developing to the Cryptographic API Profile for AES Candidate Algorithm Submissions. Please consult this document as a cross-reference. ANY CHANGES, WHERE APPROPRIATE, TO INFORMATION PROVIDED IN THIS FILE MUST BE DOCUMENTED. CHANGES ARE ONLY APPROPRIATE WHERE SPECIFIED WITH THE STRING "CHANGE POSSIBLE". FUNCTION CALLS AND THEIR PARAMETERS CANNOT BE CHANGED. STRUCTURES CAN BE ALTERED TO ALLOW IMPLEMENTERS TO INCLUDE IMPLEMENTATION SPECIFIC INFORMATION. */ /* Includes: Standard include files */ #pragma once #pragma pack(1) #include #include "platform.h" /* platform-specific defines */ /* Defines: Add any additional defines you need */ #define DIR_ENCRYPT 0 /* Are we encrpyting? */ #define DIR_DECRYPT 1 /* Are we decrpyting? */ #define MODE_ECB 1 /* Are we ciphering in ECB mode? */ #define MODE_CBC 2 /* Are we ciphering in CBC mode? */ #define MODE_CFB1 3 /* Are we ciphering in 1-bit CFB mode? */ #define TRUE 1 #define FALSE 0 #define BAD_KEY_DIR -1 /* Key direction is invalid (unknown value) */ #define BAD_KEY_MAT -2 /* Key material not of correct length */ #define BAD_KEY_INSTANCE -3 /* Key passed is not valid */ #define BAD_CIPHER_MODE -4 /* Params struct passed to cipherInit invalid */ #define BAD_CIPHER_STATE -5 /* Cipher in wrong state (e.g., not initialized) */ /* CHANGE POSSIBLE: inclusion of algorithm specific defines */ /* TWOFISH specific definitions */ #define MAX_KEY_SIZE 64 /* # of ASCII chars needed to represent a key */ #define MAX_IV_SIZE 16 /* # of bytes needed to represent an IV */ #define BAD_INPUT_LEN -6 /* inputLen not a multiple of block size */ #define BAD_PARAMS -7 /* invalid parameters */ #define BAD_IV_MAT -8 /* invalid IV text */ #define BAD_ENDIAN -9 /* incorrect endianness define */ #define BAD_ALIGN32 -10 /* incorrect 32-bit alignment */ #define BLOCK_SIZE 128 /* number of bits per block */ #define MAX_ROUNDS 16 /* max # rounds (for allocating subkey array) */ #define ROUNDS_128 16 /* default number of rounds for 128-bit keys*/ #define ROUNDS_192 16 /* default number of rounds for 192-bit keys*/ #define ROUNDS_256 16 /* default number of rounds for 256-bit keys*/ #define MAX_KEY_BITS 256 /* max number of bits of key */ #define MIN_KEY_BITS 128 /* min number of bits of key (zero pad) */ #define VALID_SIG 0x48534946 /* initialization signature ('FISH') */ #define MCT_OUTER 400 /* MCT outer loop */ #define MCT_INNER 10000 /* MCT inner loop */ #define REENTRANT 1 /* nonzero forces reentrant code (slightly slower) */ #define INPUT_WHITEN 0 /* subkey array indices */ #define OUTPUT_WHITEN ( INPUT_WHITEN + BLOCK_SIZE/32) #define ROUND_SUBKEYS (OUTPUT_WHITEN + BLOCK_SIZE/32) /* use 2 * (# rounds) */ #define TOTAL_SUBKEYS (ROUND_SUBKEYS + 2*MAX_ROUNDS) /* Typedefs: Typedef'ed data storage elements. Add any algorithm specific parameters at the bottom of the structs as appropriate. */ typedef unsigned char BYTE; typedef unsigned long DWORD; /* 32-bit unsigned quantity */ typedef DWORD fullSbox[4][256]; /* The structure for key information */ typedef struct { BYTE direction; /* Key used for encrypting or decrypting? */ #if ALIGN32 BYTE dummyAlign[3]; /* keep 32-bit alignment */ #endif int keyLen; /* Length of the key */ char keyMaterial[MAX_KEY_SIZE+4];/* Raw key data in ASCII */ /* Twofish-specific parameters: */ DWORD keySig; /* set to VALID_SIG by makeKey() */ int numRounds; /* number of rounds in cipher */ DWORD key32[MAX_KEY_BITS/32]; /* actual key bits, in dwords */ DWORD sboxKeys[MAX_KEY_BITS/64];/* key bits used for S-boxes */ DWORD subKeys[TOTAL_SUBKEYS]; /* round subkeys, input/output whitening bits */ #if REENTRANT fullSbox sBox8x32; /* fully expanded S-box */ #if defined(COMPILE_KEY) && defined(USE_ASM) #undef VALID_SIG #define VALID_SIG 0x504D4F43 /* 'COMP': C is compiled with -DCOMPILE_KEY */ DWORD cSig1; /* set after first "compile" (zero at "init") */ void *encryptFuncPtr; /* ptr to asm encrypt function */ void *decryptFuncPtr; /* ptr to asm decrypt function */ DWORD codeSize; /* size of compiledCode */ DWORD cSig2; /* set after first "compile" */ BYTE compiledCode[5000]; /* make room for the code itself */ #endif #endif } keyInstance; /* The structure for cipher information */ typedef struct { BYTE mode; /* MODE_ECB, MODE_CBC, or MODE_CFB1 */ #if ALIGN32 BYTE dummyAlign[3]; /* keep 32-bit alignment */ #endif BYTE IV[MAX_IV_SIZE]; /* CFB1 iv bytes (CBC uses iv32) */ /* Twofish-specific parameters: */ DWORD cipherSig; /* set to VALID_SIG by cipherInit() */ DWORD iv32[BLOCK_SIZE/32]; /* CBC IV bytes arranged as dwords */ } cipherInstance; /* Function protoypes */ #ifdef __cplusplus extern "C"{ #endif int makeKey(keyInstance *key, BYTE direction, int keyLen, char *keyMaterial); int cipherInit(cipherInstance *cipher, BYTE mode, char *IV); int blockEncrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer); int blockDecrypt(cipherInstance *cipher, keyInstance *key, BYTE *input, int inputLen, BYTE *outBuffer); int reKey(keyInstance *key); /* do key schedule using modified key.keyDwords */ #ifdef __cplusplus } #endif /* API to check table usage, for use in ECB_TBL KAT */ #define TAB_DISABLE 0 #define TAB_ENABLE 1 #define TAB_RESET 2 #define TAB_QUERY 3 #define TAB_MIN_QUERY 50 int TableOp(int op); #ifndef CONST #define CONST /* helpful C++ syntax sugar, NOP for ANSI C */ #endif #if BLOCK_SIZE == 128 /* optimize block copies */ #define Copy1(d,s,N) ((DWORD *)(d))[N] = ((DWORD *)(s))[N] #define BlockCopy(d,s) { Copy1(d,s,0);Copy1(d,s,1);Copy1(d,s,2);Copy1(d,s,3); } #else #define BlockCopy(d,s) { memcpy(d,s,BLOCK_SIZE/8); } #endif ================================================ FILE: Crypt/uo_huffman.cpp ================================================ //////////////////////////////////////////////////////////////////////////////// // // uo_huffman.cpp // // // Decompression code based on: // Ultimate Melange. Client for UO-Emulators // Copyright (C) 2000 Axel Kittenberger // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // //////////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "uo_huffman.h" /* Decompression Table This is a static huffman tree that is walked down for decompression, negative nodes (and 0) are final values, positive nodes point to other nodes. If drawn this tree is sorted from up to down (layer by layer) and left to right. */ int Compression::DecompressionTree[] = { /* 0*/ 1, 2, /* 1*/ 3, 4, /* 2*/ 5, 0, /* 3*/ 6, 7, /* 4*/ 8, 9, /* 5*/ 10, 11, /* 6*/ 12, 13, /* 7*/ -256, 14, /* 8*/ 15, 16, /* 9*/ 17, 18, /* 10*/ 19, 20, /* 11*/ 21, 22, /* 12*/ -1, 23, /* 13*/ 24, 25, /* 14*/ 26, 27, /* 15*/ 28, 29, /* 16*/ 30, 31, /* 17*/ 32, 33, /* 18*/ 34, 35, /* 19*/ 36, 37, /* 20*/ 38, 39, /* 21*/ 40, -64, /* 22*/ 41, 42, /* 23*/ 43, 44, /* 24*/ -6, 45, /* 25*/ 46, 47, /* 26*/ 48, 49, /* 27*/ 50, 51, /* 28*/ -119, 52, /* 29*/ -32, 53, /* 30*/ 54, -14, /* 31*/ 55, -5, /* 32*/ 56, 57, /* 33*/ 58, 59, /* 34*/ 60, -2, /* 35*/ 61, 62, /* 36*/ 63, 64, /* 37*/ 65, 66, /* 38*/ 67, 68, /* 39*/ 69, 70, /* 40*/ 71, 72, /* 41*/ -51, 73, /* 42*/ 74, 75, /* 43*/ 76, 77, /* 44*/ -101, -111, /* 45*/ -4, -97, /* 46*/ 78, 79, /* 47*/ -110, 80, /* 48*/ 81, -116, /* 49*/ 82, 83, /* 50*/ 84, -255, /* 51*/ 85, 86, /* 52*/ 87, 88, /* 53*/ 89, 90, /* 54*/ -15, -10, /* 55*/ 91, 92, /* 56*/ -21, 93, /* 57*/ -117, 94, /* 58*/ 95, 96, /* 59*/ 97, 98, /* 60*/ 99, 100, /* 61*/ -114, 101, /* 62*/ -105, 102, /* 63*/ -26, 103, /* 64*/ 104, 105, /* 65*/ 106, 107, /* 66*/ 108, 109, /* 67*/ 110, 111, /* 68*/ 112, -3, /* 69*/ 113, -7, /* 70*/ 114, -131, /* 71*/ 115, -144, /* 72*/ 116, 117, /* 73*/ -20, 118, /* 74*/ 119, 120, /* 75*/ 121, 122, /* 76*/ 123, 124, /* 77*/ 125, 126, /* 78*/ 127, 128, /* 79*/ 129, -100 , /* 80*/ 130, -8, /* 81*/ 131, 132, /* 82*/ 133, 134, /* 83*/ -120, 135, /* 84*/ 136, -31, /* 85*/ 137, 138, /* 86*/ -109, -234, /* 87*/ 139, 140, /* 88*/ 141, 142, /* 89*/ 143, 144, /* 90*/ -112, 145, /* 91*/ -19, 146, /* 92*/ 147, 148, /* 93*/ 149, -66, /* 94*/ 150, -145, /* 95*/ -13, -65, /* 96*/ 151, 152, /* 97*/ 153, 154, /* 98*/ -30, 155, /* 99*/ 156, 157, /* 100*/ -99, 158, /* 101*/ 159, 160, /* 102*/ 161, 162, /* 103*/ -23, 163, /* 104*/ -29, 164, /* 105*/ -11, 165, /* 106*/ 166, -115, /* 107*/ 167, 168, /* 108*/ 169, 170, /* 109*/ -16, 171, /* 110*/ -34, 172, /* 111*/ 173, -132, /* 112*/ 174, -108, /* 113*/ 175, -22, /* 114*/ 176, -9, /* 115*/ 177, -84, /* 116*/ -17, -37, /* 117*/ -28, 178, /* 118*/ 179, 180, /* 119*/ 181, 182, /* 120*/ 183, 184, /* 121*/ 185, 186, /* 122*/ 187, -104, /* 123*/ 188, -78, /* 124*/ 189, -61, /* 125*/ -79, -178, /* 126*/ -59, -134, /* 127*/ 190, -25, /* 128*/ -83, -18, /* 129*/ 191, -57, /* 130*/ -67, 192, /* 131*/ -98, 193, /* 132*/ -12, -68, /* 133*/ 194, 195, /* 134*/ -55, -128, /* 135*/ -24, -50, /* 136*/ -70, 196, /* 137*/ -94, -33, /* 138*/ 197, -129, /* 139*/ -74, 198, /* 140*/ -82, 199, /* 141*/ -56, -87, /* 142*/ -44, 200, /* 143*/ -248, 201, /* 144*/ -163, -81, /* 145*/ -52, -123, /* 146*/ 202, -113, /* 147*/ -48, -41, /* 148*/ -122, -40, /* 149*/ 203, -90, /* 150*/ -54, 204, /* 151*/ -86, -192, /* 152*/ 205, 206, /* 153*/ 207, -130, /* 154*/ -53, 208, /* 155*/ -133, -45, /* 156*/ 209, 210, /* 157*/ 211, -91, /* 158*/ 212, 213, /* 159*/ -106, -88, /* 160*/ 214, 215, /* 161*/ 216, 217, /* 162*/ 218, -49, /* 163*/ 219, 220, /* 164*/ 221, 222, /* 165*/ 223, 224, /* 166*/ 225, 226, /* 167*/ 227, -102, /* 168*/ -160, 228, /* 169*/ -46, 229, /* 170*/ -127, 230, /* 171*/ -103, 231, /* 172*/ 232, 233, /* 173*/ -60, 234, /* 174*/ 235, -76, /* 175*/ 236, -121, /* 176*/ 237, -73, /* 177*/ -149, 238, /* 178*/ 239, -107, /* 179*/ -35, 240, /* 180*/ -71, -27, /* 181*/ -69, 241, /* 182*/ -89, -77, /* 183*/ -62, -118, /* 184*/ -75, -85, /* 185*/ -72, -58, /* 186*/ -63, -80, /* 187*/ 242, -42, /* 188*/ -150, -157, /* 189*/ -139, -236, /* 190*/ -126, -243, /* 191*/ -142, -214, /* 192*/ -138, -206, /* 193*/ -240, -146, /* 194*/ -204, -147, /* 195*/ -152, -201, /* 196*/ -227, -207, /* 197*/ -154, -209, /* 198*/ -153, -254, /* 199*/ -176, -156, /* 200*/ -165, -210, /* 201*/ -172, -185, /* 202*/ -195, -170, /* 203*/ -232, -211, /* 204*/ -219, -239, /* 205*/ -200, -177, /* 206*/ -175, -212, /* 207*/ -244, -143, /* 208*/ -246, -171, /* 209*/ -203, -221, /* 210*/ -202, -181, /* 211*/ -173, -250, /* 212*/ -184, -164, /* 213*/ -193, -218, /* 214*/ -199, -220, /* 215*/ -190, -249, /* 216*/ -230, -217, /* 217*/ -169, -216, /* 218*/ -191, -197, /* 219*/ -47, 243, /* 220*/ 244, 245, /* 221*/ 246, 247, /* 222*/ -148, -159, /* 223*/ 248, 249, /* 224*/ -92, -93, /* 225*/ -96, -225, /* 226*/ -151, -95, /* 227*/ 250, 251, /* 228*/ -241, 252, /* 229*/ -161, -36, /* 230*/ 253, 254, /* 231*/ -135, -39, /* 232*/ -187, -124, /* 233*/ 255, -251, /* 234*/ -162, -238, /* 235*/ -242, -38, /* 236*/ -43, -125, /* 237*/ -215, -253, /* 238*/ -140, -208, /* 239*/ -137, -235, /* 240*/ -158, -237, /* 241*/ -136, -205, /* 242*/ -155, -141, /* 243*/ -228, -229, /* 244*/ -213, -168, /* 245*/ -224, -194, /* 246*/ -196, -226, /* 247*/ -183, -233, /* 248*/ -231, -167, /* 249*/ -174, -189, /* 250*/ -252, -166, /* 251*/ -198, -222, /* 252*/ -188, -179, /* 253*/ -223, -182, /* 254*/ -180, -186, /* 255*/ -245, -247, }; int Compression::CompressionTable[] = { 0x2, 0x000, 0x5, 0x01F, 0x6, 0x022, 0x7, 0x034, 0x7, 0x075, 0x6, 0x028, 0x6, 0x03B, 0x7, 0x032, 0x8, 0x0E0, 0x8, 0x062, 0x7, 0x056, 0x8, 0x079, 0x9, 0x19D, 0x8, 0x097, 0x6, 0x02A, 0x7, 0x057, 0x8, 0x071, 0x8, 0x05B, 0x9, 0x1CC, 0x8, 0x0A7, 0x7, 0x025, 0x7, 0x04F, 0x8, 0x066, 0x8, 0x07D, 0x9, 0x191, 0x9, 0x1CE, 0x7, 0x03F, 0x9, 0x090, 0x8, 0x059, 0x8, 0x07B, 0x8, 0x091, 0x8, 0x0C6, 0x6, 0x02D, 0x9, 0x186, 0x8, 0x06F, 0x9, 0x093, 0xA, 0x1CC, 0x8, 0x05A, 0xA, 0x1AE, 0xA, 0x1C0, 0x9, 0x148, 0x9, 0x14A, 0x9, 0x082, 0xA, 0x19F, 0x9, 0x171, 0x9, 0x120, 0x9, 0x0E7, 0xA, 0x1F3, 0x9, 0x14B, 0x9, 0x100, 0x9, 0x190, 0x6, 0x013, 0x9, 0x161, 0x9, 0x125, 0x9, 0x133, 0x9, 0x195, 0x9, 0x173, 0x9, 0x1CA, 0x9, 0x086, 0x9, 0x1E9, 0x9, 0x0DB, 0x9, 0x1EC, 0x9, 0x08B, 0x9, 0x085, 0x5, 0x00A, 0x8, 0x096, 0x8, 0x09C, 0x9, 0x1C3, 0x9, 0x19C, 0x9, 0x08F, 0x9, 0x18F, 0x9, 0x091, 0x9, 0x087, 0x9, 0x0C6, 0x9, 0x177, 0x9, 0x089, 0x9, 0x0D6, 0x9, 0x08C, 0x9, 0x1EE, 0x9, 0x1EB, 0x9, 0x084, 0x9, 0x164, 0x9, 0x175, 0x9, 0x1CD, 0x8, 0x05E, 0x9, 0x088, 0x9, 0x12B, 0x9, 0x172, 0x9, 0x10A, 0x9, 0x08D, 0x9, 0x13A, 0x9, 0x11C, 0xA, 0x1E1, 0xA, 0x1E0, 0x9, 0x187, 0xA, 0x1DC, 0xA, 0x1DF, 0x7, 0x074, 0x9, 0x19F, 0x8, 0x08D, 0x8, 0x0E4, 0x7, 0x079, 0x9, 0x0EA, 0x9, 0x0E1, 0x8, 0x040, 0x7, 0x041, 0x9, 0x10B, 0x9, 0x0B0, 0x8, 0x06A, 0x8, 0x0C1, 0x7, 0x071, 0x7, 0x078, 0x8, 0x0B1, 0x9, 0x14C, 0x7, 0x043, 0x8, 0x076, 0x7, 0x066, 0x7, 0x04D, 0x9, 0x08A, 0x6, 0x02F, 0x8, 0x0C9, 0x9, 0x0CE, 0x9, 0x149, 0x9, 0x160, 0xA, 0x1BA, 0xA, 0x19E, 0xA, 0x39F, 0x9, 0x0E5, 0x9, 0x194, 0x9, 0x184, 0x9, 0x126, 0x7, 0x030, 0x8, 0x06C, 0x9, 0x121, 0x9, 0x1E8, 0xA, 0x1C1, 0xA, 0x11D, 0xA, 0x163, 0xA, 0x385, 0xA, 0x3DB, 0xA, 0x17D, 0xA, 0x106, 0xA, 0x397, 0xA, 0x24E, 0x7, 0x02E, 0x8, 0x098, 0xA, 0x33C, 0xA, 0x32E, 0xA, 0x1E9, 0x9, 0x0BF, 0xA, 0x3DF, 0xA, 0x1DD, 0xA, 0x32D, 0xA, 0x2ED, 0xA, 0x30B, 0xA, 0x107, 0xA, 0x2E8, 0xA, 0x3DE, 0xA, 0x125, 0xA, 0x1E8, 0x9, 0x0E9, 0xA, 0x1CD, 0xA, 0x1B5, 0x9, 0x165, 0xA, 0x232, 0xA, 0x2E1, 0xB, 0x3AE, 0xB, 0x3C6, 0xB, 0x3E2, 0xA, 0x205, 0xA, 0x29A, 0xA, 0x248, 0xA, 0x2CD, 0xA, 0x23B, 0xB, 0x3C5, 0xA, 0x251, 0xA, 0x2E9, 0xA, 0x252, 0x9, 0x1EA, 0xB, 0x3A0, 0xB, 0x391, 0xA, 0x23C, 0xB, 0x392, 0xB, 0x3D5, 0xA, 0x233, 0xA, 0x2CC, 0xB, 0x390, 0xA, 0x1BB, 0xB, 0x3A1, 0xB, 0x3C4, 0xA, 0x211, 0xA, 0x203, 0x9, 0x12A, 0xA, 0x231, 0xB, 0x3E0, 0xA, 0x29B, 0xB, 0x3D7, 0xA, 0x202, 0xB, 0x3AD, 0xA, 0x213, 0xA, 0x253, 0xA, 0x32C, 0xA, 0x23D, 0xA, 0x23F, 0xA, 0x32F, 0xA, 0x11C, 0xA, 0x384, 0xA, 0x31C, 0xA, 0x17C, 0xA, 0x30A, 0xA, 0x2E0, 0xA, 0x276, 0xA, 0x250, 0xB, 0x3E3, 0xA, 0x396, 0xA, 0x18F, 0xA, 0x204, 0xA, 0x206, 0xA, 0x230, 0xA, 0x265, 0xA, 0x212, 0xA, 0x23E, 0xB, 0x3AC, 0xB, 0x393, 0xB, 0x3E1, 0xA, 0x1DE, 0xB, 0x3D6, 0xA, 0x31D, 0xB, 0x3E5, 0xB, 0x3E4, 0xA, 0x207, 0xB, 0x3C7, 0xA, 0x277, 0xB, 0x3D4, 0x8, 0x0C0, 0xA, 0x162, 0xA, 0x3DA, 0xA, 0x124, 0xA, 0x1B4, 0xA, 0x264, 0xA, 0x33D, 0xA, 0x1D1, 0xA, 0x1AF, 0xA, 0x39E, 0xA, 0x24F, 0xB, 0x373, 0xA, 0x249, 0xB, 0x372, 0x9, 0x167, 0xA, 0x210, 0xA, 0x23A, 0xA, 0x1B8, 0xB, 0x3AF, 0xA, 0x18E, 0xA, 0x2EC, 0x7, 0x062, 0x4, 0x00D }; int Compression::bit_num = 8, Compression::treepos = 0; int Compression::value, Compression::mask; int Compression::Compress( char *out_buff, const char *in_buff, int length ) { const unsigned char *input = (const unsigned char*)in_buff; unsigned char *output = (unsigned char *)out_buff; int holdCount = 0; int holdValue = 0; int packCount = 0; int packValue = 0; int byteValue = 0; int inputLength = length; int inputIndex = 0; int outputCount = 0; while ( inputIndex < inputLength ) { byteValue = input[inputIndex++] << 1; packCount = CompressionTable[byteValue]; packValue = CompressionTable[byteValue | 1]; holdValue <<= packCount; holdValue |= packValue; holdCount += packCount; while ( holdCount >= 8 ) { holdCount -= 8; output[outputCount++] = (unsigned char)(holdValue >> holdCount); } } packCount = CompressionTable[0x200]; packValue = CompressionTable[0x201]; holdValue <<= packCount; holdValue |= packValue; holdCount += packCount; while ( holdCount >= 8 ) { holdCount -= 8; output[outputCount++] = (unsigned char)(holdValue >> holdCount); } if ( holdCount > 0 ) output[outputCount++] = (unsigned char)(holdValue << (8 - holdCount)); return outputCount; } void Compression::Reset() { bit_num = 8; treepos = 0; } int Compression::Decompress( char *output, const char *input, int length ) { unsigned char * pdest = reinterpret_cast(output); const unsigned char * psrc = reinterpret_cast(input); int len = length; // len will decrease int dest_index = 0; while(true) { if(bit_num == 8) { // End of input. if(len == 0) return dest_index; len--; value = *psrc++; bit_num = 0; mask = 0x80; } if(value & mask) treepos = DecompressionTree[treepos * 2]; else treepos = DecompressionTree[treepos * 2 + 1]; mask >>= 1; // shift on reck bit_num++; if(treepos <= 0) // this is a leaf. { if(treepos == -256) // special flush character { bit_num = 8; // flush rest of byte treepos = 0; // start on tree top again continue; } pdest[dest_index++] = -treepos; // data is negative value treepos = 0; // start on tree top again } } return 0; } ================================================ FILE: Crypt/uo_huffman.h ================================================ #pragma once class Compression { public: static int Compress( char *out, const char *in, int inlen ); static int Decompress( char *out, const char *in, int inlen ); static void Reset(); protected: static int bit_num, treepos; static int value, mask; static int CompressionTable[]; static int DecompressionTree[]; }; ================================================ FILE: FastColoredTextBox/AutocompleteItem.cs ================================================ using System; using System.Drawing; using System.Drawing.Printing; namespace FastColoredTextBoxNS { /// /// Item of autocomplete menu /// public class AutocompleteItem { public string Text; public int ImageIndex = -1; public object Tag; string toolTipTitle; string toolTipText; string menuText; public AutocompleteMenu Parent { get; internal set; } public AutocompleteItem() { } public AutocompleteItem(string text) { Text = text; } public AutocompleteItem(string text, int imageIndex) : this(text) { this.ImageIndex = imageIndex; } public AutocompleteItem(string text, int imageIndex, string menuText) : this(text, imageIndex) { this.menuText = menuText; } public AutocompleteItem(string text, int imageIndex, string menuText, string toolTipTitle, string toolTipText) : this(text, imageIndex, menuText) { this.toolTipTitle = toolTipTitle; this.toolTipText = toolTipText; } /// /// Returns text for inserting into Textbox /// public virtual string GetTextForReplace() { return Text; } /// /// Compares fragment text with this item /// public virtual CompareResult Compare(string fragmentText) { if (Text.StartsWith(fragmentText, StringComparison.InvariantCultureIgnoreCase) && Text != fragmentText) return CompareResult.VisibleAndSelected; return CompareResult.Hidden; } /// /// Returns text for display into popup menu /// public override string ToString() { return menuText ?? Text; } /// /// This method is called after item inserted into text /// public virtual void OnSelected(AutocompleteMenu popupMenu, SelectedEventArgs e) { ; } /// /// Title for tooltip. /// /// Return null for disable tooltip for this item public virtual string ToolTipTitle { get { return toolTipTitle; } set { toolTipTitle = value; } } /// /// Tooltip text. /// /// For display tooltip text, ToolTipTitle must be not null public virtual string ToolTipText { get { return toolTipText; } set { toolTipText = value; } } /// /// Menu text. This text is displayed in the drop-down menu. /// public virtual string MenuText { get { return menuText; } set { menuText = value; } } /// /// Fore color of text of item /// public virtual Color ForeColor { get { return Color.Transparent; } set { throw new NotImplementedException("Override this property to change color"); } } /// /// Back color of item /// public virtual Color BackColor { get { return Color.Transparent; } set { throw new NotImplementedException("Override this property to change color"); } } } public enum CompareResult { /// /// Item do not appears /// Hidden, /// /// Item appears /// Visible, /// /// Item appears and will selected /// VisibleAndSelected } /// /// Autocomplete item for code snippets /// /// Snippet can contain special char ^ for caret position. public class SnippetAutocompleteItem : AutocompleteItem { public SnippetAutocompleteItem(string snippet) { Text = snippet.Replace("\r", ""); ToolTipTitle = "Code snippet:"; ToolTipText = Text; } public override string ToString() { return MenuText ?? Text.Replace("\n", " ").Replace("^", ""); } public override string GetTextForReplace() { return Text; } public override void OnSelected(AutocompleteMenu popupMenu, SelectedEventArgs e) { e.Tb.BeginUpdate(); e.Tb.Selection.BeginUpdate(); //remember places var p1 = popupMenu.Fragment.Start; var p2 = e.Tb.Selection.Start; //do auto indent if (e.Tb.AutoIndent) { for (int iLine = p1.iLine + 1; iLine <= p2.iLine; iLine++) { e.Tb.Selection.Start = new Place(0, iLine); e.Tb.DoAutoIndent(iLine); } } e.Tb.Selection.Start = p1; //move caret position right and find char ^ while (e.Tb.Selection.CharBeforeStart != '^') if (!e.Tb.Selection.GoRightThroughFolded()) break; //remove char ^ e.Tb.Selection.GoLeft(true); e.Tb.InsertText(""); // e.Tb.Selection.EndUpdate(); e.Tb.EndUpdate(); } /// /// Compares fragment text with this item /// public override CompareResult Compare(string fragmentText) { if (Text.StartsWith(fragmentText, StringComparison.InvariantCultureIgnoreCase) && Text != fragmentText) return CompareResult.Visible; return CompareResult.Hidden; } } /// /// This autocomplete item appears after dot /// public class MethodAutocompleteItem : AutocompleteItem { string firstPart; string lowercaseText; public MethodAutocompleteItem(string text) : base(text) { lowercaseText = Text.ToLower(); } public override CompareResult Compare(string fragmentText) { int i = fragmentText.LastIndexOf(' '); if (i < 0) return CompareResult.Hidden; string lastPart = fragmentText.Substring(i + 1); firstPart = fragmentText.Substring(0, i); if (lastPart == "") return CompareResult.Visible; if (Text.StartsWith(lastPart, StringComparison.InvariantCultureIgnoreCase)) return CompareResult.VisibleAndSelected; if (lowercaseText.Contains(lastPart.ToLower())) return CompareResult.Visible; return CompareResult.Hidden; } public override string GetTextForReplace() { return firstPart + " " + Text; } } /// /// This Item does not check correspondence to current text fragment. /// SuggestItem is intended for dynamic menus. /// public class SuggestItem : AutocompleteItem { public SuggestItem(string text, int imageIndex) : base(text, imageIndex) { } public override CompareResult Compare(string fragmentText) { return CompareResult.Visible; } } } ================================================ FILE: FastColoredTextBox/AutocompleteMenu.cs ================================================ using System; using System.Collections.Generic; using System.Windows.Forms; using System.Drawing; using System.ComponentModel; using System.Drawing.Drawing2D; using System.Text.RegularExpressions; namespace FastColoredTextBoxNS { /// /// Popup menu for autocomplete /// [Browsable(false)] public class AutocompleteMenu : UserControl, IDisposable { AutocompleteListView listView; public Range Fragment { get; internal set; } /// /// Regex pattern for serach fragment around caret /// public string SearchPattern { get; set; } /// /// Minimum fragment length for popup /// public int MinFragmentLength { get; set; } /// /// User selects item /// public event EventHandler Selecting; /// /// It fires after item inserting /// public event EventHandler Selected; /// /// Occurs when popup menu is opening /// public new event EventHandler Opening; /// /// Allow TAB for select menu item /// public bool AllowTabKey { get { return listView.AllowTabKey; } set { listView.AllowTabKey = value; } } /// /// Interval of menu appear (ms) /// public int AppearInterval { get { return listView.AppearInterval; } set { listView.AppearInterval = value; } } /// /// Sets the max tooltip window size /// public Size MaxTooltipSize { get { return listView.MaxToolTipSize; } set { listView.MaxToolTipSize = value; } } /// /// Tooltip will perm show and duration will be ignored /// public bool AlwaysShowTooltip { get { return listView.AlwaysShowTooltip; } set { listView.AlwaysShowTooltip = value; } } /// /// Back color of selected item /// [DefaultValue(typeof(Color), "Orange")] public Color SelectedColor { get { return listView.SelectedColor; } set { listView.SelectedColor = value; } } /// /// Border color of hovered item /// [DefaultValue(typeof(Color), "Red")] public Color HoveredColor { get { return listView.HoveredColor; } set { listView.HoveredColor = value; } } public AutocompleteMenu(FastColoredTextBox tb) { // create a new popup and add the list view to it Visible = false; BorderStyle = BorderStyle.FixedSingle; AutoSize = false; Margin = Padding.Empty; Padding = Padding.Empty; BackColor = Color.White; listView = new AutocompleteListView(tb); CalcSize(); listView.Parent = this; SearchPattern = @"[\w\.]"; MinFragmentLength = 2; } public new Font Font { get { return listView.Font; } set { listView.Font = value; } } new internal void OnOpening(CancelEventArgs args) { if (Opening != null) Opening(this, args); } public new void Close() { listView.toolTip.Hide(listView); Hide(); } internal void CalcSize() { Size = new System.Drawing.Size(listView.Size.Width + 4, listView.Size.Height + 4); } public virtual void OnSelecting() { listView.OnSelecting(); } public void SelectNext(int shift) { listView.SelectNext(shift); } internal void OnSelecting(SelectingEventArgs args) { if (Selecting != null) Selecting(this, args); } public void OnSelected(SelectedEventArgs args) { if (Selected != null) Selected(this, args); } public new AutocompleteListView Items { get { return listView; } } /// /// Shows popup menu immediately /// /// If True - MinFragmentLength will be ignored public void Show(bool forced) { Items.DoAutocomplete(forced); } /// /// Minimal size of menu /// public new Size MinimumSize { get { return Items.MinimumSize; } set { Items.MinimumSize = value; } } /// /// Image list of menu /// public new ImageList ImageList { get { return Items.ImageList; } set { Items.ImageList = value; } } /// /// Tooltip duration (ms) /// public int ToolTipDuration { get { return Items.ToolTipDuration; } set { Items.ToolTipDuration = value; } } /// /// Tooltip /// public ToolTip ToolTip { get { return Items.toolTip; } set { Items.toolTip = value; } } protected override void Dispose(bool disposing) { base.Dispose(disposing); if (listView != null && !listView.IsDisposed) listView.Dispose(); } } [System.ComponentModel.ToolboxItem(false)] public class AutocompleteListView : UserControl, IDisposable { public event EventHandler FocussedItemIndexChanged; internal List visibleItems; IEnumerable sourceItems = new List(); int focussedItemIndex = 0; int hoveredItemIndex = -1; private int ItemHeight { get { return Font.Height + 2; } } AutocompleteMenu Menu { get { return Parent as AutocompleteMenu; } } int oldItemCount = 0; FastColoredTextBox tb; internal ToolTip toolTip = new ToolTip(); System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); internal bool AllowTabKey { get; set; } public ImageList ImageList { get; set; } internal int AppearInterval { get { return timer.Interval; } set { timer.Interval = value; } } internal int ToolTipDuration { get; set; } internal Size MaxToolTipSize { get; set; } internal bool AlwaysShowTooltip { get { return toolTip.ShowAlways; } set { toolTip.ShowAlways = value; } } public Color SelectedColor { get; set; } public Color HoveredColor { get; set; } public int FocussedItemIndex { get { return focussedItemIndex; } set { if (focussedItemIndex != value) { focussedItemIndex = value; if (FocussedItemIndexChanged != null) FocussedItemIndexChanged(this, EventArgs.Empty); } } } public AutocompleteItem FocussedItem { get { if (FocussedItemIndex >= 0 && focussedItemIndex < visibleItems.Count) return visibleItems[focussedItemIndex]; return null; } set { FocussedItemIndex = visibleItems.IndexOf(value); } } internal AutocompleteListView(FastColoredTextBox tb) { SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true); base.Font = new Font(FontFamily.GenericSansSerif, 9); visibleItems = new List(); VerticalScroll.SmallChange = ItemHeight; MaximumSize = new Size(Size.Width, 180); toolTip.ShowAlways = false; AppearInterval = 500; timer.Tick += new EventHandler(timer_Tick); SelectedColor = Color.Orange; HoveredColor = Color.Red; ToolTipDuration = 3000; toolTip.Popup += ToolTip_Popup; MouseDown += new MouseEventHandler(ListMouseDown); this.tb = tb; tb.KeyDown += new KeyEventHandler(tb_KeyDown); tb.SelectionChanged += new EventHandler(tb_SelectionChanged); tb.KeyPressed += new KeyPressEventHandler(tb_KeyPressed); Form form = tb.FindForm(); if (form != null) { form.LocationChanged += delegate { SafetyClose(); }; form.ResizeBegin += delegate { SafetyClose(); }; form.FormClosing += delegate { SafetyClose(); }; form.LostFocus += delegate { SafetyClose(); }; } tb.LostFocus += (o, e) => { if (Menu != null && !Menu.IsDisposed) if (!Menu.Focused) SafetyClose(); }; tb.Scroll += delegate { SafetyClose(); }; this.VisibleChanged += (o, e) => { if (this.Visible) DoSelectedVisible(); }; } private void ListMouseDown(object sender, MouseEventArgs e) { DoAutocomplete(false); } private void ToolTip_Popup(object sender, PopupEventArgs e) { if (MaxToolTipSize.Height > 0 && MaxToolTipSize.Width > 0) e.ToolTipSize = MaxToolTipSize; } protected override void Dispose(bool disposing) { if (toolTip != null) { toolTip.Popup -= ToolTip_Popup; toolTip.Dispose(); } if (tb != null) { tb.KeyDown -= tb_KeyDown; tb.KeyPress -= tb_KeyPressed; tb.SelectionChanged -= tb_SelectionChanged; } if (timer != null) { timer.Stop(); timer.Tick -= timer_Tick; timer.Dispose(); } base.Dispose(disposing); } void SafetyClose() { if (Menu != null && !Menu.IsDisposed) Menu.Close(); } void tb_KeyPressed(object sender, KeyPressEventArgs e) { bool backspaceORdel = e.KeyChar == '\b' || e.KeyChar == 0xff; /* if (backspaceORdel) prevSelection = tb.Selection.Start;*/ if (Menu.Visible && !backspaceORdel) DoAutocomplete(false); else ResetTimer(timer); } void timer_Tick(object sender, EventArgs e) { timer.Stop(); DoAutocomplete(false); } void ResetTimer(System.Windows.Forms.Timer timer) { timer.Stop(); timer.Start(); } internal void DoAutocomplete() { DoAutocomplete(false); } internal void DoAutocomplete(bool forced) { if (!Menu.Enabled) { Menu.Close(); return; } visibleItems.Clear(); FocussedItemIndex = 0; VerticalScroll.Value = 0; //some magic for update scrolls AutoScrollMinSize -= new Size(1, 0); AutoScrollMinSize += new Size(1, 0); //get fragment around caret Range fragment = tb.Selection.GetFragment(Menu.SearchPattern); string text = fragment.Text; //calc screen point for popup menu Point point = tb.PlaceToPoint(fragment.End); point.Offset(2, tb.CharHeight); // if (forced || (text.Length >= Menu.MinFragmentLength && tb.Selection.IsEmpty /*pops up only if selected range is empty*/ && (tb.Selection.Start > fragment.Start || text.Length == 0 /*pops up only if caret is after first letter*/))) { Menu.Fragment = fragment; bool foundSelected = false; //build popup menu foreach (var item in sourceItems) { item.Parent = Menu; CompareResult res = item.Compare(text); if (res != CompareResult.Hidden) visibleItems.Add(item); if (res == CompareResult.VisibleAndSelected && !foundSelected) { foundSelected = true; FocussedItemIndex = visibleItems.Count - 1; } } if (foundSelected) { AdjustScroll(); } } //show popup menu if (Count > 0) { if (!Menu.Visible) { CancelEventArgs args = new CancelEventArgs(); Menu.OnOpening(args); if (!args.Cancel) { Menu.Location = point; Menu.Parent = tb; Menu.Show(); } } DoSelectedVisible(); Invalidate(); } else Menu.Close(); } void tb_SelectionChanged(object sender, EventArgs e) { /* FastColoredTextBox tb = sender as FastColoredTextBox; if (Math.Abs(prevSelection.iChar - tb.Selection.Start.iChar) > 1 || prevSelection.iLine != tb.Selection.Start.iLine) Menu.Close(); prevSelection = tb.Selection.Start;*/ if (Menu.Visible) { bool needClose = false; if (!tb.Selection.IsEmpty) needClose = true; else if (!Menu.Fragment.Contains(tb.Selection.Start)) { if (tb.Selection.Start.iLine == Menu.Fragment.End.iLine && tb.Selection.Start.iChar == Menu.Fragment.End.iChar + 1) { //user press key at end of fragment char c = tb.Selection.CharBeforeStart; if (!Regex.IsMatch(c.ToString(), Menu.SearchPattern)) //check char needClose = true; } else needClose = true; } if (needClose) Menu.Close(); } } void tb_KeyDown(object sender, KeyEventArgs e) { var tb = sender as FastColoredTextBox; if (Menu.Visible) if (ProcessKey(e.KeyCode, e.Modifiers)) e.Handled = true; if (!Menu.Visible) { if (tb.HotkeysMapping.ContainsKey(e.KeyData) && tb.HotkeysMapping[e.KeyData] == FCTBAction.AutocompleteMenu) { DoAutocomplete(); e.Handled = true; } else { if (e.KeyCode == Keys.Escape && timer.Enabled) timer.Stop(); } } } void AdjustScroll() { if (oldItemCount == visibleItems.Count) return; int needHeight = ItemHeight * visibleItems.Count + 1; Height = Math.Min(needHeight, MaximumSize.Height); Menu.CalcSize(); AutoScrollMinSize = new Size(0, needHeight); oldItemCount = visibleItems.Count; } protected override void OnPaint(PaintEventArgs e) { AdjustScroll(); var itemHeight = ItemHeight; int startI = VerticalScroll.Value / itemHeight - 1; int finishI = (VerticalScroll.Value + ClientSize.Height) / itemHeight + 1; startI = Math.Max(startI, 0); finishI = Math.Min(finishI, visibleItems.Count); int y = 0; int leftPadding = 18; for (int i = startI; i < finishI; i++) { y = i * itemHeight - VerticalScroll.Value; var item = visibleItems[i]; if (item.BackColor != Color.Transparent) using (var brush = new SolidBrush(item.BackColor)) e.Graphics.FillRectangle(brush, 1, y, ClientSize.Width - 1 - 1, itemHeight - 1); if (ImageList != null && visibleItems[i].ImageIndex >= 0) e.Graphics.DrawImage(ImageList.Images[item.ImageIndex], 1, y); if (i == FocussedItemIndex) using (var selectedBrush = new LinearGradientBrush(new Point(0, y - 3), new Point(0, y + itemHeight), Color.Transparent, SelectedColor)) using (var pen = new Pen(SelectedColor)) { e.Graphics.FillRectangle(selectedBrush, leftPadding, y, ClientSize.Width - 1 - leftPadding, itemHeight - 1); e.Graphics.DrawRectangle(pen, leftPadding, y, ClientSize.Width - 1 - leftPadding, itemHeight - 1); } if (i == hoveredItemIndex) using (var pen = new Pen(HoveredColor)) e.Graphics.DrawRectangle(pen, leftPadding, y, ClientSize.Width - 1 - leftPadding, itemHeight - 1); using (var brush = new SolidBrush(item.ForeColor != Color.Transparent ? item.ForeColor : ForeColor)) e.Graphics.DrawString(item.ToString(), Font, brush, leftPadding, y); } } public override Color ForeColor { get { return Color.Black; } set { } } protected override void OnScroll(ScrollEventArgs se) { base.OnScroll(se); Invalidate(); } protected override void OnMouseClick(MouseEventArgs e) { base.OnMouseClick(e); if (e.Button == System.Windows.Forms.MouseButtons.Left || Environment.OSVersion.Platform == PlatformID.Unix) { FocussedItemIndex = PointToItemIndex(e.Location); DoSelectedVisible(); Invalidate(); } } protected override void OnMouseDoubleClick(MouseEventArgs e) { base.OnMouseDoubleClick(e); FocussedItemIndex = PointToItemIndex(e.Location); Invalidate(); OnSelecting(); } internal virtual void OnSelecting() { if (FocussedItemIndex < 0 || FocussedItemIndex >= visibleItems.Count) return; tb.TextSource.Manager.BeginAutoUndoCommands(); try { AutocompleteItem item = FocussedItem; SelectingEventArgs args = new SelectingEventArgs() { Item = item, SelectedIndex = FocussedItemIndex }; Menu.OnSelecting(args); if (args.Cancel) { FocussedItemIndex = args.SelectedIndex; Invalidate(); return; } if (!args.Handled) { var fragment = Menu.Fragment; DoAutocomplete(item, fragment); } Menu.Close(); // SelectedEventArgs args2 = new SelectedEventArgs() { Item = item, Tb = Menu.Fragment.tb }; item.OnSelected(Menu, args2); Menu.OnSelected(args2); } finally { tb.TextSource.Manager.EndAutoUndoCommands(); } } private void DoAutocomplete(AutocompleteItem item, Range fragment) { string newText = fragment.Text; int last = newText.LastIndexOf('.'); if (last > 0) { newText = newText.Substring(0, last + 1); } else { newText = ""; } newText = newText + item; if (item.ToolTipText != null) { newText = newText + " "; } //replace text of fragment var tb = fragment.tb; tb.BeginAutoUndo(); tb.TextSource.Manager.ExecuteCommand(new SelectCommand(tb.TextSource)); if (tb.Selection.ColumnSelectionMode) { var start = tb.Selection.Start; var end = tb.Selection.End; start.iChar = fragment.Start.iChar; end.iChar = fragment.End.iChar; tb.Selection.Start = start; tb.Selection.End = end; } else { tb.Selection.Start = fragment.Start; tb.Selection.End = fragment.End; } tb.InsertText(newText); tb.TextSource.Manager.ExecuteCommand(new SelectCommand(tb.TextSource)); tb.EndAutoUndo(); tb.Focus(); } int PointToItemIndex(Point p) { return (p.Y + VerticalScroll.Value) / ItemHeight; } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { ProcessKey(keyData, Keys.None); return base.ProcessCmdKey(ref msg, keyData); } private bool ProcessKey(Keys keyData, Keys keyModifiers) { if (keyModifiers == Keys.None) switch (keyData) { case Keys.Down: SelectNext(+1); return true; case Keys.PageDown: SelectNext(+10); return true; case Keys.Up: SelectNext(-1); return true; case Keys.PageUp: SelectNext(-10); return true; case Keys.Enter: OnSelecting(); return true; case Keys.Tab: if (!AllowTabKey) break; OnSelecting(); return true; case Keys.Escape: Menu.Close(); return true; } return false; } public void SelectNext(int shift) { FocussedItemIndex = Math.Max(0, Math.Min(FocussedItemIndex + shift, visibleItems.Count - 1)); DoSelectedVisible(); Invalidate(); } private void DoSelectedVisible() { if (FocussedItem != null) SetToolTip(FocussedItem); var y = FocussedItemIndex * ItemHeight - VerticalScroll.Value; if (y < 0) VerticalScroll.Value = FocussedItemIndex * ItemHeight; if (y > ClientSize.Height - ItemHeight) VerticalScroll.Value = Math.Min(VerticalScroll.Maximum, FocussedItemIndex * ItemHeight - ClientSize.Height + ItemHeight); //some magic for update scrolls AutoScrollMinSize -= new Size(1, 0); AutoScrollMinSize += new Size(1, 0); } private void SetToolTip(AutocompleteItem autocompleteItem) { var title = autocompleteItem.ToolTipTitle; var text = autocompleteItem.ToolTipText; if (string.IsNullOrEmpty(title)) { toolTip.ToolTipTitle = null; toolTip.SetToolTip(this, null); return; } if (this.Parent != null) { IWin32Window window = this.Parent ?? this; /* Point location; if ((this.PointToScreen(this.Location).X + MaxToolTipSize.Width + 105) < Screen.FromControl(this.Parent).WorkingArea.Right) location = new Point(Right + 5, 0); else location = new Point(Left - 105 - MaximumSize.Width, 0);*/ var location = new Point(Right + 3 + Menu.Left, Menu.Top); if (string.IsNullOrEmpty(text)) toolTip.ToolTipTitle = null; else toolTip.ToolTipTitle = title; toolTip.Show(text, window, location.X, location.Y, ToolTipDuration); } } public int Count { get { return visibleItems.Count; } } public void SetAutocompleteItems(ICollection items) { List list = new List(items.Count); foreach (var item in items) list.Add(new AutocompleteItem(item)); SetAutocompleteItems(list); } public void SetAutocompleteItems(IEnumerable items) { sourceItems = items; } } public class SelectingEventArgs : EventArgs { public AutocompleteItem Item { get; internal set; } public bool Cancel { get; set; } public int SelectedIndex { get; set; } public bool Handled { get; set; } } public class SelectedEventArgs : EventArgs { public AutocompleteItem Item { get; internal set; } public FastColoredTextBox Tb { get; set; } } } ================================================ FILE: FastColoredTextBox/Bookmarks.cs ================================================ using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Drawing2D; using System.Text; namespace FastColoredTextBoxNS { /// /// Base class for bookmark collection /// public abstract class BaseBookmarks : ICollection, IDisposable { #region ICollection public abstract void Add(Bookmark item); public abstract void Clear(); public abstract bool Contains(Bookmark item); public abstract void CopyTo(Bookmark[] array, int arrayIndex); public abstract int Count { get; } public abstract bool IsReadOnly { get; } public abstract bool Remove(Bookmark item); public abstract IEnumerator GetEnumerator(); System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion #region IDisposable public abstract void Dispose(); #endregion #region Additional properties public abstract void Add(int lineIndex, string bookmarkName); public abstract void Add(int lineIndex); public abstract bool Contains(int lineIndex); public abstract bool Remove(int lineIndex); public abstract Bookmark GetBookmark(int i); #endregion } /// /// Collection of bookmarks /// public class Bookmarks : BaseBookmarks { protected FastColoredTextBox tb; protected List items = new List(); protected int counter; public Bookmarks(FastColoredTextBox tb) { this.tb = tb; tb.LineInserted += tb_LineInserted; tb.LineRemoved += tb_LineRemoved; } protected virtual void tb_LineRemoved(object sender, LineRemovedEventArgs e) { for (int i = 0; i < Count; i++) if (items[i].LineIndex >= e.Index) { if (items[i].LineIndex >= e.Index + e.Count) { items[i].LineIndex = items[i].LineIndex - e.Count; continue; } var was = e.Index <= 0; foreach (var b in items) if (b.LineIndex == e.Index - 1) was = true; if (was) { items.RemoveAt(i); i--; } else items[i].LineIndex = e.Index - 1; //if (items[i].LineIndex == e.Index + e.Count - 1) //{ // items[i].LineIndex = items[i].LineIndex - e.Count; // continue; //} // //items.RemoveAt(i); //i--; } } protected virtual void tb_LineInserted(object sender, LineInsertedEventArgs e) { for (int i = 0; i < Count; i++) if (items[i].LineIndex >= e.Index) { items[i].LineIndex = items[i].LineIndex + e.Count; } else if (items[i].LineIndex == e.Index - 1 && e.Count == 1) { if (tb[e.Index - 1].StartSpacesCount == tb[e.Index - 1].Count) items[i].LineIndex = items[i].LineIndex + e.Count; } } public override void Dispose() { tb.LineInserted -= tb_LineInserted; tb.LineRemoved -= tb_LineRemoved; } public override IEnumerator GetEnumerator() { foreach (var item in items) yield return item; } public override void Add(int lineIndex, string bookmarkName) { Add(new Bookmark(tb, bookmarkName ?? "Bookmark " + counter, lineIndex)); } public override void Add(int lineIndex) { Add(new Bookmark(tb, "Bookmark " + counter, lineIndex)); } public override void Clear() { items.Clear(); counter = 0; } public override void Add(Bookmark bookmark) { foreach (var bm in items) if (bm.LineIndex == bookmark.LineIndex) return; items.Add(bookmark); counter++; tb.Invalidate(); } public override bool Contains(Bookmark item) { return items.Contains(item); } public override bool Contains(int lineIndex) { foreach (var item in items) if (item.LineIndex == lineIndex) return true; return false; } public override void CopyTo(Bookmark[] array, int arrayIndex) { items.CopyTo(array, arrayIndex); } public override int Count { get { return items.Count; } } public override bool IsReadOnly { get { return false; } } public override bool Remove(Bookmark item) { tb.Invalidate(); return items.Remove(item); } /// /// Removes bookmark by line index /// public override bool Remove(int lineIndex) { bool was = false; for (int i = 0; i < Count; i++) if (items[i].LineIndex == lineIndex) { items.RemoveAt(i); i--; was = true; } tb.Invalidate(); return was; } /// /// Returns Bookmark by index. /// public override Bookmark GetBookmark(int i) { return items[i]; } } /// /// Bookmark of FastColoredTextbox /// public class Bookmark { public FastColoredTextBox TB { get; private set; } /// /// Name of bookmark /// public string Name { get; set; } /// /// Line index /// public int LineIndex { get; set; } /// /// Color of bookmark sign /// public Color Color { get; set; } /// /// Scroll textbox to the bookmark /// public virtual void DoVisible() { TB.Selection.Start = new Place(0, LineIndex); TB.DoRangeVisible(TB.Selection, true); TB.Invalidate(); } public Bookmark(FastColoredTextBox tb, string name, int lineIndex) { this.TB = tb; this.Name = name; this.LineIndex = lineIndex; Color = tb.BookmarkColor; } public virtual void Paint(Graphics gr, Rectangle lineRect) { var size = TB.CharHeight - 1; using (var brush = new LinearGradientBrush(new Rectangle(0, lineRect.Top, size, size), Color.White, Color, 45)) gr.FillEllipse(brush, 0, lineRect.Top, size, size); using (var pen = new Pen(Color)) gr.DrawEllipse(pen, 0, lineRect.Top, size, size); } } } ================================================ FILE: FastColoredTextBox/Char.cs ================================================ using System; namespace FastColoredTextBoxNS { /// /// Char and style /// public struct Char { /// /// Unicode character /// public char c; /// /// Style bit mask /// /// Bit 1 in position n means that this char will rendering by FastColoredTextBox.Styles[n] public StyleIndex style; public Char(char c) { this.c = c; style = StyleIndex.None; } } } ================================================ FILE: FastColoredTextBox/CommandManager.cs ================================================ using System.Collections.Generic; using System; namespace FastColoredTextBoxNS { public class CommandManager { readonly int maxHistoryLength = 200; LimitedStack history; Stack redoStack = new Stack(); public TextSource TextSource { get; private set; } public bool UndoRedoStackIsEnabled { get; set; } public event EventHandler RedoCompleted = delegate { }; public CommandManager(TextSource ts) { history = new LimitedStack(maxHistoryLength); TextSource = ts; UndoRedoStackIsEnabled = true; } public virtual void ExecuteCommand(Command cmd) { if (disabledCommands > 0) return; //multirange ? if (cmd.ts.CurrentTB.Selection.ColumnSelectionMode) if (cmd is UndoableCommand) //make wrapper cmd = new MultiRangeCommand((UndoableCommand) cmd); if (cmd is UndoableCommand) { //if range is ColumnRange, then create wrapper (cmd as UndoableCommand).autoUndo = autoUndoCommands > 0; history.Push(cmd as UndoableCommand); } try { cmd.Execute(); } catch (ArgumentOutOfRangeException) { //OnTextChanging cancels enter of the text if (cmd is UndoableCommand) history.Pop(); } // if (!UndoRedoStackIsEnabled) ClearHistory(); // redoStack.Clear(); // TextSource.CurrentTB.OnUndoRedoStateChanged(); } public void Undo() { if (history.Count > 0) { var cmd = history.Pop(); // BeginDisableCommands(); //prevent text changing into handlers try { cmd.Undo(); } finally { EndDisableCommands(); } // redoStack.Push(cmd); } //undo next autoUndo command if (history.Count > 0) { if (history.Peek().autoUndo) Undo(); } TextSource.CurrentTB.OnUndoRedoStateChanged(); } protected int disabledCommands = 0; private void EndDisableCommands() { disabledCommands--; } private void BeginDisableCommands() { disabledCommands++; } int autoUndoCommands = 0; public void EndAutoUndoCommands() { autoUndoCommands--; if (autoUndoCommands == 0) if (history.Count > 0) history.Peek().autoUndo = false; } public void BeginAutoUndoCommands() { autoUndoCommands++; } internal void ClearHistory() { history.Clear(); redoStack.Clear(); TextSource.CurrentTB.OnUndoRedoStateChanged(); } internal void Redo() { if (redoStack.Count == 0) return; UndoableCommand cmd; BeginDisableCommands(); //prevent text changing into handlers try { cmd = redoStack.Pop(); if (TextSource.CurrentTB.Selection.ColumnSelectionMode) TextSource.CurrentTB.Selection.ColumnSelectionMode = false; TextSource.CurrentTB.Selection.Start = cmd.sel.Start; TextSource.CurrentTB.Selection.End = cmd.sel.End; cmd.Execute(); history.Push(cmd); } finally { EndDisableCommands(); } //call event RedoCompleted(this, EventArgs.Empty); //redo command after autoUndoable command if (cmd.autoUndo) Redo(); TextSource.CurrentTB.OnUndoRedoStateChanged(); } public bool UndoEnabled { get { return history.Count > 0; } } public bool RedoEnabled { get { return redoStack.Count > 0; } } } public abstract class Command { public TextSource ts; public abstract void Execute(); } internal class RangeInfo { public Place Start { get; set; } public Place End { get; set; } public RangeInfo(Range r) { Start = r.Start; End = r.End; } internal int FromX { get { if (End.iLine < Start.iLine) return End.iChar; if (End.iLine > Start.iLine) return Start.iChar; return Math.Min(End.iChar, Start.iChar); } } } public abstract class UndoableCommand : Command { internal RangeInfo sel; internal RangeInfo lastSel; internal bool autoUndo; public UndoableCommand(TextSource ts) { this.ts = ts; sel = new RangeInfo(ts.CurrentTB.Selection); } public virtual void Undo() { OnTextChanged(true); } public override void Execute() { lastSel = new RangeInfo(ts.CurrentTB.Selection); OnTextChanged(false); } protected virtual void OnTextChanged(bool invert) { bool b = sel.Start.iLine < lastSel.Start.iLine; if (invert) { if (b) ts.OnTextChanged(sel.Start.iLine, sel.Start.iLine); else ts.OnTextChanged(sel.Start.iLine, lastSel.Start.iLine); } else { if (b) ts.OnTextChanged(sel.Start.iLine, lastSel.Start.iLine); else ts.OnTextChanged(lastSel.Start.iLine, lastSel.Start.iLine); } } public abstract UndoableCommand Clone(); } } ================================================ FILE: FastColoredTextBox/Commands.cs ================================================ using System; using System.Collections.Generic; namespace FastColoredTextBoxNS { /// /// Insert single char /// /// This operation includes also insertion of new line and removing char by backspace public class InsertCharCommand : UndoableCommand { public char c; char deletedChar = '\x0'; /// /// Constructor /// /// Underlaying textbox /// Inserting char public InsertCharCommand(TextSource ts, char c) : base(ts) { this.c = c; } /// /// Undo operation /// public override void Undo() { ts.OnTextChanging(); switch (c) { case '\n': MergeLines(sel.Start.iLine, ts); break; case '\r': break; case '\b': ts.CurrentTB.Selection.Start = lastSel.Start; char cc = '\x0'; if (deletedChar != '\x0') { ts.CurrentTB.ExpandBlock(ts.CurrentTB.Selection.Start.iLine); InsertChar(deletedChar, ref cc, ts); } break; case '\t': ts.CurrentTB.ExpandBlock(sel.Start.iLine); for (int i = sel.FromX; i < lastSel.FromX; i++) ts[sel.Start.iLine].RemoveAt(sel.Start.iChar); ts.CurrentTB.Selection.Start = sel.Start; break; default: ts.CurrentTB.ExpandBlock(sel.Start.iLine); ts[sel.Start.iLine].RemoveAt(sel.Start.iChar); ts.CurrentTB.Selection.Start = sel.Start; break; } ts.NeedRecalc(new TextSource.TextChangedEventArgs(sel.Start.iLine, sel.Start.iLine)); base.Undo(); } /// /// Execute operation /// public override void Execute() { ts.CurrentTB.ExpandBlock(ts.CurrentTB.Selection.Start.iLine); string s = c.ToString(); ts.OnTextChanging(ref s); if (s.Length == 1) c = s[0]; if (String.IsNullOrEmpty(s)) throw new ArgumentOutOfRangeException(); if (ts.Count == 0) InsertLine(ts); InsertChar(c, ref deletedChar, ts); ts.NeedRecalc(new TextSource.TextChangedEventArgs(ts.CurrentTB.Selection.Start.iLine, ts.CurrentTB.Selection.Start.iLine)); base.Execute(); } internal static void InsertChar(char c, ref char deletedChar, TextSource ts) { var tb = ts.CurrentTB; switch (c) { case '\n': if (!ts.CurrentTB.AllowInsertRemoveLines) throw new ArgumentOutOfRangeException("Cant insert this char in ColumnRange mode"); if (ts.Count == 0) InsertLine(ts); InsertLine(ts); break; case '\r': break; case '\b': //backspace if (tb.Selection.Start.iChar == 0 && tb.Selection.Start.iLine == 0) return; if (tb.Selection.Start.iChar == 0) { if (!ts.CurrentTB.AllowInsertRemoveLines) throw new ArgumentOutOfRangeException("Cant insert this char in ColumnRange mode"); if (tb.LineInfos[tb.Selection.Start.iLine - 1].VisibleState != VisibleState.Visible) tb.ExpandBlock(tb.Selection.Start.iLine - 1); deletedChar = '\n'; MergeLines(tb.Selection.Start.iLine - 1, ts); } else { deletedChar = ts[tb.Selection.Start.iLine][tb.Selection.Start.iChar - 1].c; ts[tb.Selection.Start.iLine].RemoveAt(tb.Selection.Start.iChar - 1); tb.Selection.Start = new Place(tb.Selection.Start.iChar - 1, tb.Selection.Start.iLine); } break; case '\t': int spaceCountNextTabStop = tb.TabLength - (tb.Selection.Start.iChar % tb.TabLength); if (spaceCountNextTabStop == 0) spaceCountNextTabStop = tb.TabLength; for (int i = 0; i < spaceCountNextTabStop; i++) ts[tb.Selection.Start.iLine].Insert(tb.Selection.Start.iChar, new Char(' ')); tb.Selection.Start = new Place(tb.Selection.Start.iChar + spaceCountNextTabStop, tb.Selection.Start.iLine); break; default: ts[tb.Selection.Start.iLine].Insert(tb.Selection.Start.iChar, new Char(c)); tb.Selection.Start = new Place(tb.Selection.Start.iChar + 1, tb.Selection.Start.iLine); break; } } internal static void InsertLine(TextSource ts) { var tb = ts.CurrentTB; if (!tb.Multiline && tb.LinesCount > 0) return; if (ts.Count == 0) ts.InsertLine(0, ts.CreateLine()); else BreakLines(tb.Selection.Start.iLine, tb.Selection.Start.iChar, ts); tb.Selection.Start = new Place(0, tb.Selection.Start.iLine + 1); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Merge lines i and i+1 /// internal static void MergeLines(int i, TextSource ts) { var tb = ts.CurrentTB; if (i + 1 >= ts.Count) return; tb.ExpandBlock(i); tb.ExpandBlock(i + 1); int pos = ts[i].Count; // /* if(ts[i].Count == 0) ts.RemoveLine(i); else*/ if (ts[i + 1].Count == 0) ts.RemoveLine(i + 1); else { ts[i].AddRange(ts[i + 1]); ts.RemoveLine(i + 1); } tb.Selection.Start = new Place(pos, i); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } internal static void BreakLines(int iLine, int pos, TextSource ts) { Line newLine = ts.CreateLine(); for (int i = pos; i < ts[iLine].Count; i++) newLine.Add(ts[iLine][i]); ts[iLine].RemoveRange(pos, ts[iLine].Count - pos); // ts.InsertLine(iLine + 1, newLine); } public override UndoableCommand Clone() { return new InsertCharCommand(ts, c); } } /// /// Insert text /// public class InsertTextCommand : UndoableCommand { public string InsertedText; /// /// Constructor /// /// Underlaying textbox /// Text for inserting public InsertTextCommand(TextSource ts, string insertedText) : base(ts) { this.InsertedText = insertedText; } /// /// Undo operation /// public override void Undo() { ts.CurrentTB.Selection.Start = sel.Start; ts.CurrentTB.Selection.End = lastSel.Start; ts.OnTextChanging(); ClearSelectedCommand.ClearSelected(ts); base.Undo(); } /// /// Execute operation /// public override void Execute() { ts.OnTextChanging(ref InsertedText); InsertText(InsertedText, ts); base.Execute(); } internal static void InsertText(string insertedText, TextSource ts) { var tb = ts.CurrentTB; try { tb.Selection.BeginUpdate(); char cc = '\x0'; if (ts.Count == 0) { InsertCharCommand.InsertLine(ts); tb.Selection.Start = Place.Empty; } tb.ExpandBlock(tb.Selection.Start.iLine); var len = insertedText.Length; for (int i = 0; i < len; i++) { var c = insertedText[i]; if (c == '\r' && (i >= len - 1 || insertedText[i + 1] != '\n')) InsertCharCommand.InsertChar('\n', ref cc, ts); else InsertCharCommand.InsertChar(c, ref cc, ts); } ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } finally { tb.Selection.EndUpdate(); } } public override UndoableCommand Clone() { return new InsertTextCommand(ts, InsertedText); } } /// /// Insert text into given ranges /// public class ReplaceTextCommand : UndoableCommand { string insertedText; List ranges; List prevText = new List(); /// /// Constructor /// /// Underlaying textbox /// List of ranges for replace /// Text for inserting public ReplaceTextCommand(TextSource ts, List ranges, string insertedText) : base(ts) { //sort ranges by place ranges.Sort((r1, r2) => { if (r1.Start.iLine == r2.Start.iLine) return r1.Start.iChar.CompareTo(r2.Start.iChar); return r1.Start.iLine.CompareTo(r2.Start.iLine); }); // this.ranges = ranges; this.insertedText = insertedText; lastSel = sel = new RangeInfo(ts.CurrentTB.Selection); } /// /// Undo operation /// public override void Undo() { var tb = ts.CurrentTB; ts.OnTextChanging(); tb.BeginUpdate(); tb.Selection.BeginUpdate(); for (int i = 0; i < ranges.Count; i++) { tb.Selection.Start = ranges[i].Start; for (int j = 0; j < insertedText.Length; j++) tb.Selection.GoRight(true); ClearSelected(ts); InsertTextCommand.InsertText(prevText[prevText.Count - i - 1], ts); } tb.Selection.EndUpdate(); tb.EndUpdate(); if (ranges.Count > 0) ts.OnTextChanged(ranges[0].Start.iLine, ranges[ranges.Count - 1].End.iLine); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; prevText.Clear(); ts.OnTextChanging(ref insertedText); tb.Selection.BeginUpdate(); tb.BeginUpdate(); for (int i = ranges.Count - 1; i >= 0; i--) { tb.Selection.Start = ranges[i].Start; tb.Selection.End = ranges[i].End; prevText.Add(tb.Selection.Text); ClearSelected(ts); if (insertedText != "") InsertTextCommand.InsertText(insertedText, ts); } if (ranges.Count > 0) ts.OnTextChanged(ranges[0].Start.iLine, ranges[ranges.Count - 1].End.iLine); tb.EndUpdate(); tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); lastSel = new RangeInfo(tb.Selection); } public override UndoableCommand Clone() { return new ReplaceTextCommand(ts, new List(ranges), insertedText); } internal static void ClearSelected(TextSource ts) { var tb = ts.CurrentTB; tb.Selection.Normalize(); Place start = tb.Selection.Start; Place end = tb.Selection.End; int fromLine = Math.Min(end.iLine, start.iLine); int toLine = Math.Max(end.iLine, start.iLine); int fromChar = tb.Selection.FromX; int toChar = tb.Selection.ToX; if (fromLine < 0) return; // if (fromLine == toLine) ts[fromLine].RemoveRange(fromChar, toChar - fromChar); else { ts[fromLine].RemoveRange(fromChar, ts[fromLine].Count - fromChar); ts[toLine].RemoveRange(0, toChar); ts.RemoveLine(fromLine + 1, toLine - fromLine - 1); InsertCharCommand.MergeLines(fromLine, ts); } } } /// /// Clear selected text /// public class ClearSelectedCommand : UndoableCommand { string deletedText; /// /// Construstor /// /// Underlaying textbox public ClearSelectedCommand(TextSource ts) : base(ts) { } /// /// Undo operation /// public override void Undo() { ts.CurrentTB.Selection.Start = new Place(sel.FromX, Math.Min(sel.Start.iLine, sel.End.iLine)); ts.OnTextChanging(); InsertTextCommand.InsertText(deletedText, ts); ts.OnTextChanged(sel.Start.iLine, sel.End.iLine); ts.CurrentTB.Selection.Start = sel.Start; ts.CurrentTB.Selection.End = sel.End; } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; string temp = null; ts.OnTextChanging(ref temp); if (temp == "") throw new ArgumentOutOfRangeException(); deletedText = tb.Selection.Text; ClearSelected(ts); lastSel = new RangeInfo(tb.Selection); ts.OnTextChanged(lastSel.Start.iLine, lastSel.Start.iLine); } internal static void ClearSelected(TextSource ts) { var tb = ts.CurrentTB; Place start = tb.Selection.Start; Place end = tb.Selection.End; int fromLine = Math.Min(end.iLine, start.iLine); int toLine = Math.Max(end.iLine, start.iLine); int fromChar = tb.Selection.FromX; int toChar = tb.Selection.ToX; if (fromLine < 0) return; // if (fromLine == toLine) ts[fromLine].RemoveRange(fromChar, toChar - fromChar); else { ts[fromLine].RemoveRange(fromChar, ts[fromLine].Count - fromChar); ts[toLine].RemoveRange(0, toChar); ts.RemoveLine(fromLine + 1, toLine - fromLine - 1); InsertCharCommand.MergeLines(fromLine, ts); } // tb.Selection.Start = new Place(fromChar, fromLine); // ts.NeedRecalc(new TextSource.TextChangedEventArgs(fromLine, toLine)); } public override UndoableCommand Clone() { return new ClearSelectedCommand(ts); } } /// /// Replaces text /// public class ReplaceMultipleTextCommand : UndoableCommand { List ranges; List prevText = new List(); public class ReplaceRange { public Range ReplacedRange { get; set; } public String ReplaceText { get; set; } } /// /// Constructor /// /// Underlaying textsource /// List of ranges for replace public ReplaceMultipleTextCommand(TextSource ts, List ranges) : base(ts) { //sort ranges by place ranges.Sort((r1, r2) => { if (r1.ReplacedRange.Start.iLine == r2.ReplacedRange.Start.iLine) return r1.ReplacedRange.Start.iChar.CompareTo(r2.ReplacedRange.Start.iChar); return r1.ReplacedRange.Start.iLine.CompareTo(r2.ReplacedRange.Start.iLine); }); // this.ranges = ranges; lastSel = sel = new RangeInfo(ts.CurrentTB.Selection); } /// /// Undo operation /// public override void Undo() { var tb = ts.CurrentTB; ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = 0; i < ranges.Count; i++) { tb.Selection.Start = ranges[i].ReplacedRange.Start; for (int j = 0; j < ranges[i].ReplaceText.Length; j++) tb.Selection.GoRight(true); ClearSelectedCommand.ClearSelected(ts); var prevTextIndex = ranges.Count - 1 - i; InsertTextCommand.InsertText(prevText[prevTextIndex], ts); ts.OnTextChanged(ranges[i].ReplacedRange.Start.iLine, ranges[i].ReplacedRange.Start.iLine); } tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; prevText.Clear(); ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = ranges.Count - 1; i >= 0; i--) { tb.Selection.Start = ranges[i].ReplacedRange.Start; tb.Selection.End = ranges[i].ReplacedRange.End; prevText.Add(tb.Selection.Text); ClearSelectedCommand.ClearSelected(ts); InsertTextCommand.InsertText(ranges[i].ReplaceText, ts); ts.OnTextChanged(ranges[i].ReplacedRange.Start.iLine, ranges[i].ReplacedRange.End.iLine); } tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); lastSel = new RangeInfo(tb.Selection); } public override UndoableCommand Clone() { return new ReplaceMultipleTextCommand(ts, new List(ranges)); } } /// /// Removes lines /// public class RemoveLinesCommand : UndoableCommand { List iLines; List prevText = new List(); /// /// Constructor /// /// Underlaying textbox /// List of ranges for replace /// Text for inserting public RemoveLinesCommand(TextSource ts, List iLines) : base(ts) { //sort iLines iLines.Sort(); // this.iLines = iLines; lastSel = sel = new RangeInfo(ts.CurrentTB.Selection); } /// /// Undo operation /// public override void Undo() { var tb = ts.CurrentTB; ts.OnTextChanging(); tb.Selection.BeginUpdate(); //tb.BeginUpdate(); for (int i = 0; i < iLines.Count; i++) { var iLine = iLines[i]; if (iLine < ts.Count) tb.Selection.Start = new Place(0, iLine); else tb.Selection.Start = new Place(ts[ts.Count - 1].Count, ts.Count - 1); InsertCharCommand.InsertLine(ts); tb.Selection.Start = new Place(0, iLine); var text = prevText[prevText.Count - i - 1]; InsertTextCommand.InsertText(text, ts); ts[iLine].IsChanged = true; if (iLine < ts.Count - 1) ts[iLine + 1].IsChanged = true; else ts[iLine - 1].IsChanged = true; if (text.Trim() != string.Empty) ts.OnTextChanged(iLine, iLine); } //tb.EndUpdate(); tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); } /// /// Execute operation /// public override void Execute() { var tb = ts.CurrentTB; prevText.Clear(); ts.OnTextChanging(); tb.Selection.BeginUpdate(); for (int i = iLines.Count - 1; i >= 0; i--) { var iLine = iLines[i]; prevText.Add(ts[iLine].Text); //backward ts.RemoveLine(iLine); //ts.OnTextChanged(ranges[i].Start.iLine, ranges[i].End.iLine); } tb.Selection.Start = new Place(0, 0); tb.Selection.EndUpdate(); ts.NeedRecalc(new TextSource.TextChangedEventArgs(0, 1)); lastSel = new RangeInfo(tb.Selection); } public override UndoableCommand Clone() { return new RemoveLinesCommand(ts, new List(iLines)); } } /// /// Wrapper for multirange commands /// public class MultiRangeCommand : UndoableCommand { private UndoableCommand cmd; private Range range; private List commandsByRanges = new List(); public MultiRangeCommand(UndoableCommand command) : base(command.ts) { this.cmd = command; range = ts.CurrentTB.Selection.Clone(); } public override void Execute() { commandsByRanges.Clear(); var prevSelection = range.Clone(); var iChar = -1; var iStartLine = prevSelection.Start.iLine; var iEndLine = prevSelection.End.iLine; ts.CurrentTB.Selection.ColumnSelectionMode = false; ts.CurrentTB.Selection.BeginUpdate(); ts.CurrentTB.BeginUpdate(); ts.CurrentTB.AllowInsertRemoveLines = false; try { if (cmd is InsertTextCommand) ExecuteInsertTextCommand(ref iChar, (cmd as InsertTextCommand).InsertedText); else if (cmd is InsertCharCommand && (cmd as InsertCharCommand).c != '\x0' && (cmd as InsertCharCommand).c != '\b') //if not DEL or BACKSPACE ExecuteInsertTextCommand(ref iChar, (cmd as InsertCharCommand).c.ToString()); else ExecuteCommand(ref iChar); } catch (ArgumentOutOfRangeException) { } finally { ts.CurrentTB.AllowInsertRemoveLines = true; ts.CurrentTB.EndUpdate(); ts.CurrentTB.Selection = range; if (iChar >= 0) { ts.CurrentTB.Selection.Start = new Place(iChar, iStartLine); ts.CurrentTB.Selection.End = new Place(iChar, iEndLine); } ts.CurrentTB.Selection.ColumnSelectionMode = true; ts.CurrentTB.Selection.EndUpdate(); } } private void ExecuteInsertTextCommand(ref int iChar, string text) { var lines = text.Split('\n'); var iLine = 0; foreach (var r in range.GetSubRanges(true)) { var line = ts.CurrentTB[r.Start.iLine]; var lineIsEmpty = r.End < r.Start && line.StartSpacesCount == line.Count; if (!lineIsEmpty) { var insertedText = lines[iLine % lines.Length]; if (r.End < r.Start && insertedText != "") { //add forwarding spaces insertedText = new string(' ', r.Start.iChar - r.End.iChar) + insertedText; r.Start = r.End; } ts.CurrentTB.Selection = r; var c = new InsertTextCommand(ts, insertedText); c.Execute(); if (ts.CurrentTB.Selection.End.iChar > iChar) iChar = ts.CurrentTB.Selection.End.iChar; commandsByRanges.Add(c); } iLine++; } } private void ExecuteCommand(ref int iChar) { foreach (var r in range.GetSubRanges(false)) { ts.CurrentTB.Selection = r; var c = cmd.Clone(); c.Execute(); if (ts.CurrentTB.Selection.End.iChar > iChar) iChar = ts.CurrentTB.Selection.End.iChar; commandsByRanges.Add(c); } } public override void Undo() { ts.CurrentTB.BeginUpdate(); ts.CurrentTB.Selection.BeginUpdate(); try { for (int i = commandsByRanges.Count - 1; i >= 0; i--) commandsByRanges[i].Undo(); } finally { ts.CurrentTB.Selection.EndUpdate(); ts.CurrentTB.EndUpdate(); } ts.CurrentTB.Selection = range.Clone(); ts.CurrentTB.OnTextChanged(range); ts.CurrentTB.OnSelectionChanged(); ts.CurrentTB.Selection.ColumnSelectionMode = true; } public override UndoableCommand Clone() { throw new NotImplementedException(); } } /// /// Remembers current selection and restore it after Undo /// public class SelectCommand : UndoableCommand { public SelectCommand(TextSource ts) : base(ts) { } public override void Execute() { //remember selection lastSel = new RangeInfo(ts.CurrentTB.Selection); } protected override void OnTextChanged(bool invert) { } public override void Undo() { //restore selection ts.CurrentTB.Selection = new Range(ts.CurrentTB, lastSel.Start, lastSel.End); } public override UndoableCommand Clone() { var result = new SelectCommand(ts); if (lastSel != null) result.lastSel = new RangeInfo(new Range(ts.CurrentTB, lastSel.Start, lastSel.End)); return result; } } } ================================================ FILE: FastColoredTextBox/DocumentMap.cs ================================================ using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Drawing.Drawing2D; using System.Text; using System.Windows.Forms; namespace FastColoredTextBoxNS { /// /// Shows document map of FCTB /// public class DocumentMap : Control { public EventHandler TargetChanged; FastColoredTextBox target; private float scale = 0.3f; private bool needRepaint = true; private Place startPlace = Place.Empty; private bool scrollbarVisible = true; [Description("Target FastColoredTextBox")] public FastColoredTextBox Target { get { return target; } set { if (target != null) UnSubscribe(target); target = value; if (value != null) { Subscribe(target); } OnTargetChanged(); } } /// /// Scale /// public float GetScale() { return scale; } /// /// Scale /// public void SetScale(float value) { scale = value; NeedRepaint(); } /// /// Scrollbar visibility /// [Description("Scrollbar visibility")] [DefaultValue(true)] public bool ScrollbarVisible { get { return scrollbarVisible; } set { scrollbarVisible = value; NeedRepaint(); } } public DocumentMap() { ForeColor = Color.Maroon; SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.ResizeRedraw, true); Application.Idle += Application_Idle; } void Application_Idle(object sender, EventArgs e) { if (needRepaint) Invalidate(); } protected virtual void OnTargetChanged() { NeedRepaint(); if (TargetChanged != null) TargetChanged(this, EventArgs.Empty); } protected virtual void UnSubscribe(FastColoredTextBox target) { target.Scroll -= new ScrollEventHandler(Target_Scroll); target.SelectionChangedDelayed -= new EventHandler(Target_SelectionChanged); target.VisibleRangeChanged -= new EventHandler(Target_VisibleRangeChanged); } protected virtual void Subscribe(FastColoredTextBox target) { target.Scroll += new ScrollEventHandler(Target_Scroll); target.SelectionChangedDelayed += new EventHandler(Target_SelectionChanged); target.VisibleRangeChanged += new EventHandler(Target_VisibleRangeChanged); } protected virtual void Target_VisibleRangeChanged(object sender, EventArgs e) { NeedRepaint(); } protected virtual void Target_SelectionChanged(object sender, EventArgs e) { NeedRepaint(); } protected virtual void Target_Scroll(object sender, ScrollEventArgs e) { NeedRepaint(); } protected override void OnResize(EventArgs e) { base.OnResize(e); NeedRepaint(); } public void NeedRepaint() { needRepaint = true; } protected override void OnPaint(PaintEventArgs e) { if (target == null) return; var zoom = this.GetScale() * 100 / target.Zoom; if (zoom <= float.Epsilon) return; //calc startPlace var r = target.VisibleRange; if (startPlace.iLine > r.Start.iLine) startPlace.iLine = r.Start.iLine; else { var endP = target.PlaceToPoint(r.End); endP.Offset(0, -(int) (ClientSize.Height / zoom) + target.CharHeight); var pp = target.PointToPlace(endP); if (pp.iLine > startPlace.iLine) startPlace.iLine = pp.iLine; } startPlace.iChar = 0; //calc scroll pos var linesCount = target.Lines.Count; var sp1 = (float) r.Start.iLine / linesCount; var sp2 = (float) r.End.iLine / linesCount; //scale graphics e.Graphics.ScaleTransform(zoom, zoom); //draw text var size = new SizeF(ClientSize.Width / zoom, ClientSize.Height / zoom); target.DrawText(e.Graphics, startPlace, size.ToSize()); //draw visible rect var p0 = target.PlaceToPoint(startPlace); var p1 = target.PlaceToPoint(r.Start); var p2 = target.PlaceToPoint(r.End); var y1 = p1.Y - p0.Y; var y2 = p2.Y + target.CharHeight - p0.Y; e.Graphics.SmoothingMode = SmoothingMode.HighQuality; using (var brush = new SolidBrush(Color.FromArgb(50, ForeColor))) using (var pen = new Pen(brush, 1 / zoom)) { var rect = new Rectangle(0, y1, (int) ((ClientSize.Width - 1) / zoom), y2 - y1); e.Graphics.FillRectangle(brush, rect); e.Graphics.DrawRectangle(pen, rect); } //draw scrollbar if (scrollbarVisible) { e.Graphics.ResetTransform(); e.Graphics.SmoothingMode = SmoothingMode.None; using (var brush = new SolidBrush(Color.FromArgb(200, ForeColor))) { var rect = new RectangleF(ClientSize.Width - 3, ClientSize.Height * sp1, 2, ClientSize.Height * (sp2 - sp1)); e.Graphics.FillRectangle(brush, rect); } } needRepaint = false; } protected override void OnMouseDown(MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) Scroll(e.Location); base.OnMouseDown(e); } protected override void OnMouseMove(MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) Scroll(e.Location); base.OnMouseMove(e); } private void Scroll(Point point) { if (target == null) return; var zoom = this.GetScale() * 100 / target.Zoom; if (zoom <= float.Epsilon) return; var p0 = target.PlaceToPoint(startPlace); p0 = new Point(0, p0.Y + (int) (point.Y / zoom)); var pp = target.PointToPlace(p0); target.DoRangeVisible(new Range(target, pp, pp), true); BeginInvoke((MethodInvoker) OnScroll); } private void OnScroll() { Refresh(); target.Refresh(); } protected override void Dispose(bool disposing) { if (disposing) { Application.Idle -= Application_Idle; if (target != null) UnSubscribe(target); } base.Dispose(disposing); } } } ================================================ FILE: FastColoredTextBox/EncodingDetector.cs ================================================ // Copyright Tao Klerks, 2010-2012, tao@klerks.biz // Licensed under the modified BSD license. using System; using System.IO; using System.Text; using System.Text.RegularExpressions; namespace FastColoredTextBoxNS { public static class EncodingDetector { const long _defaultHeuristicSampleSize = 0x10000; //completely arbitrary - inappropriate for high numbers of files / high speed requirements public static Encoding DetectTextFileEncoding(string InputFilename) { using (FileStream textfileStream = File.OpenRead(InputFilename)) { return DetectTextFileEncoding(textfileStream, _defaultHeuristicSampleSize); } } public static Encoding DetectTextFileEncoding(FileStream InputFileStream, long HeuristicSampleSize) { bool uselessBool = false; return DetectTextFileEncoding(InputFileStream, _defaultHeuristicSampleSize, out uselessBool); } public static Encoding DetectTextFileEncoding(FileStream InputFileStream, long HeuristicSampleSize, out bool HasBOM) { Encoding encodingFound = null; long originalPos = InputFileStream.Position; InputFileStream.Position = 0; //First read only what we need for BOM detection byte[] bomBytes = new byte[InputFileStream.Length > 4 ? 4 : InputFileStream.Length]; InputFileStream.Read(bomBytes, 0, bomBytes.Length); encodingFound = DetectBOMBytes(bomBytes); if (encodingFound != null) { InputFileStream.Position = originalPos; HasBOM = true; return encodingFound; } //BOM Detection failed, going for heuristics now. // create sample byte array and populate it byte[] sampleBytes = new byte[HeuristicSampleSize > InputFileStream.Length ? InputFileStream.Length : HeuristicSampleSize]; Array.Copy(bomBytes, sampleBytes, bomBytes.Length); if (InputFileStream.Length > bomBytes.Length) InputFileStream.Read(sampleBytes, bomBytes.Length, sampleBytes.Length - bomBytes.Length); InputFileStream.Position = originalPos; //test byte array content encodingFound = DetectUnicodeInByteSampleByHeuristics(sampleBytes); HasBOM = false; return encodingFound; } public static Encoding DetectBOMBytes(byte[] BOMBytes) { if (BOMBytes.Length < 2) return null; if (BOMBytes[0] == 0xff && BOMBytes[1] == 0xfe && (BOMBytes.Length < 4 || BOMBytes[2] != 0 || BOMBytes[3] != 0 ) ) return Encoding.Unicode; if (BOMBytes[0] == 0xfe && BOMBytes[1] == 0xff ) return Encoding.BigEndianUnicode; if (BOMBytes.Length < 3) return null; if (BOMBytes[0] == 0xef && BOMBytes[1] == 0xbb && BOMBytes[2] == 0xbf) return Encoding.UTF8; if (BOMBytes[0] == 0x2b && BOMBytes[1] == 0x2f && BOMBytes[2] == 0x76) return Encoding.UTF7; if (BOMBytes.Length < 4) return null; if (BOMBytes[0] == 0xff && BOMBytes[1] == 0xfe && BOMBytes[2] == 0 && BOMBytes[3] == 0) return Encoding.UTF32; if (BOMBytes[0] == 0 && BOMBytes[1] == 0 && BOMBytes[2] == 0xfe && BOMBytes[3] == 0xff) return Encoding.GetEncoding(12001); return null; } public static Encoding DetectUnicodeInByteSampleByHeuristics(byte[] SampleBytes) { long oddBinaryNullsInSample = 0; long evenBinaryNullsInSample = 0; long suspiciousUTF8SequenceCount = 0; long suspiciousUTF8BytesTotal = 0; long likelyUSASCIIBytesInSample = 0; //Cycle through, keeping count of binary null positions, possible UTF-8 // sequences from upper ranges of Windows-1252, and probable US-ASCII // character counts. long currentPos = 0; int skipUTF8Bytes = 0; while (currentPos < SampleBytes.Length) { //binary null distribution if (SampleBytes[currentPos] == 0) { if (currentPos % 2 == 0) evenBinaryNullsInSample++; else oddBinaryNullsInSample++; } //likely US-ASCII characters if (IsCommonUSASCIIByte(SampleBytes[currentPos])) likelyUSASCIIBytesInSample++; //suspicious sequences (look like UTF-8) if (skipUTF8Bytes == 0) { int lengthFound = DetectSuspiciousUTF8SequenceLength(SampleBytes, currentPos); if (lengthFound > 0) { suspiciousUTF8SequenceCount++; suspiciousUTF8BytesTotal += lengthFound; skipUTF8Bytes = lengthFound - 1; } } else { skipUTF8Bytes--; } currentPos++; } //1: UTF-16 LE - in english / european environments, this is usually characterized by a // high proportion of odd binary nulls (starting at 0), with (as this is text) a low // proportion of even binary nulls. // The thresholds here used (less than 20% nulls where you expect non-nulls, and more than // 60% nulls where you do expect nulls) are completely arbitrary. if (((evenBinaryNullsInSample * 2.0) / SampleBytes.Length) < 0.2 && ((oddBinaryNullsInSample * 2.0) / SampleBytes.Length) > 0.6 ) return Encoding.Unicode; //2: UTF-16 BE - in english / european environments, this is usually characterized by a // high proportion of even binary nulls (starting at 0), with (as this is text) a low // proportion of odd binary nulls. // The thresholds here used (less than 20% nulls where you expect non-nulls, and more than // 60% nulls where you do expect nulls) are completely arbitrary. if (((oddBinaryNullsInSample * 2.0) / SampleBytes.Length) < 0.2 && ((evenBinaryNullsInSample * 2.0) / SampleBytes.Length) > 0.6 ) return Encoding.BigEndianUnicode; //3: UTF-8 - Martin Dürst outlines a method for detecting whether something CAN be UTF-8 content // using regexp, in his w3c.org unicode FAQ entry: // http://www.w3.org/International/questions/qa-forms-utf-8 // adapted here for C#. string potentiallyMangledString = Encoding.ASCII.GetString(SampleBytes); Regex UTF8Validator = new Regex(@"\A(" + @"[\x09\x0A\x0D\x20-\x7E]" + @"|[\xC2-\xDF][\x80-\xBF]" + @"|\xE0[\xA0-\xBF][\x80-\xBF]" + @"|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}" + @"|\xED[\x80-\x9F][\x80-\xBF]" + @"|\xF0[\x90-\xBF][\x80-\xBF]{2}" + @"|[\xF1-\xF3][\x80-\xBF]{3}" + @"|\xF4[\x80-\x8F][\x80-\xBF]{2}" + @")*\z"); if (UTF8Validator.IsMatch(potentiallyMangledString)) { //Unfortunately, just the fact that it CAN be UTF-8 doesn't tell you much about probabilities. //If all the characters are in the 0-127 range, no harm done, most western charsets are same as UTF-8 in these ranges. //If some of the characters were in the upper range (western accented characters), however, they would likely be mangled to 2-byte by the UTF-8 encoding process. // So, we need to play stats. // The "Random" likelihood of any pair of randomly generated characters being one // of these "suspicious" character sequences is: // 128 / (256 * 256) = 0.2%. // // In western text data, that is SIGNIFICANTLY reduced - most text data stays in the <127 // character range, so we assume that more than 1 in 500,000 of these character // sequences indicates UTF-8. The number 500,000 is completely arbitrary - so sue me. // // We can only assume these character sequences will be rare if we ALSO assume that this // IS in fact western text - in which case the bulk of the UTF-8 encoded data (that is // not already suspicious sequences) should be plain US-ASCII bytes. This, I // arbitrarily decided, should be 80% (a random distribution, eg binary data, would yield // approx 40%, so the chances of hitting this threshold by accident in random data are // VERY low). if ((suspiciousUTF8SequenceCount * 500000.0 / SampleBytes.Length >= 1) //suspicious sequences && ( //all suspicious, so cannot evaluate proportion of US-Ascii SampleBytes.Length - suspiciousUTF8BytesTotal == 0 || likelyUSASCIIBytesInSample * 1.0 / (SampleBytes.Length - suspiciousUTF8BytesTotal) >= 0.8 ) ) return Encoding.UTF8; } return null; } private static bool IsCommonUSASCIIByte(byte testByte) { if (testByte == 0x0A //lf || testByte == 0x0D //cr || testByte == 0x09 //tab || (testByte >= 0x20 && testByte <= 0x2F) //common punctuation || (testByte >= 0x30 && testByte <= 0x39) //digits || (testByte >= 0x3A && testByte <= 0x40) //common punctuation || (testByte >= 0x41 && testByte <= 0x5A) //capital letters || (testByte >= 0x5B && testByte <= 0x60) //common punctuation || (testByte >= 0x61 && testByte <= 0x7A) //lowercase letters || (testByte >= 0x7B && testByte <= 0x7E) //common punctuation ) return true; else return false; } private static int DetectSuspiciousUTF8SequenceLength(byte[] SampleBytes, long currentPos) { int lengthFound = 0; if (SampleBytes.Length >= currentPos + 1 && SampleBytes[currentPos] == 0xC2 ) { if (SampleBytes[currentPos + 1] == 0x81 || SampleBytes[currentPos + 1] == 0x8D || SampleBytes[currentPos + 1] == 0x8F ) lengthFound = 2; else if (SampleBytes[currentPos + 1] == 0x90 || SampleBytes[currentPos + 1] == 0x9D ) lengthFound = 2; else if (SampleBytes[currentPos + 1] >= 0xA0 && SampleBytes[currentPos + 1] <= 0xBF ) lengthFound = 2; } else if (SampleBytes.Length >= currentPos + 1 && SampleBytes[currentPos] == 0xC3 ) { if (SampleBytes[currentPos + 1] >= 0x80 && SampleBytes[currentPos + 1] <= 0xBF ) lengthFound = 2; } else if (SampleBytes.Length >= currentPos + 1 && SampleBytes[currentPos] == 0xC5 ) { if (SampleBytes[currentPos + 1] == 0x92 || SampleBytes[currentPos + 1] == 0x93 ) lengthFound = 2; else if (SampleBytes[currentPos + 1] == 0xA0 || SampleBytes[currentPos + 1] == 0xA1 ) lengthFound = 2; else if (SampleBytes[currentPos + 1] == 0xB8 || SampleBytes[currentPos + 1] == 0xBD || SampleBytes[currentPos + 1] == 0xBE ) lengthFound = 2; } else if (SampleBytes.Length >= currentPos + 1 && SampleBytes[currentPos] == 0xC6 ) { if (SampleBytes[currentPos + 1] == 0x92) lengthFound = 2; } else if (SampleBytes.Length >= currentPos + 1 && SampleBytes[currentPos] == 0xCB ) { if (SampleBytes[currentPos + 1] == 0x86 || SampleBytes[currentPos + 1] == 0x9C ) lengthFound = 2; } else if (SampleBytes.Length >= currentPos + 2 && SampleBytes[currentPos] == 0xE2 ) { if (SampleBytes[currentPos + 1] == 0x80) { if (SampleBytes[currentPos + 2] == 0x93 || SampleBytes[currentPos + 2] == 0x94 ) lengthFound = 3; if (SampleBytes[currentPos + 2] == 0x98 || SampleBytes[currentPos + 2] == 0x99 || SampleBytes[currentPos + 2] == 0x9A ) lengthFound = 3; if (SampleBytes[currentPos + 2] == 0x9C || SampleBytes[currentPos + 2] == 0x9D || SampleBytes[currentPos + 2] == 0x9E ) lengthFound = 3; if (SampleBytes[currentPos + 2] == 0xA0 || SampleBytes[currentPos + 2] == 0xA1 || SampleBytes[currentPos + 2] == 0xA2 ) lengthFound = 3; if (SampleBytes[currentPos + 2] == 0xA6) lengthFound = 3; if (SampleBytes[currentPos + 2] == 0xB0) lengthFound = 3; if (SampleBytes[currentPos + 2] == 0xB9 || SampleBytes[currentPos + 2] == 0xBA ) lengthFound = 3; } else if (SampleBytes[currentPos + 1] == 0x82 && SampleBytes[currentPos + 2] == 0xAC ) lengthFound = 3; else if (SampleBytes[currentPos + 1] == 0x84 && SampleBytes[currentPos + 2] == 0xA2 ) lengthFound = 3; } return lengthFound; } } } ================================================ FILE: FastColoredTextBox/ExportToHTML.cs ================================================ using System.Text; using System.Drawing; using System.Collections.Generic; namespace FastColoredTextBoxNS { /// /// Exports colored text as HTML /// /// At this time only TextStyle renderer is supported. Other styles is not exported. public class ExportToHTML { public string LineNumbersCSS = ""; /// /// Use nbsp; instead space /// public bool UseNbsp { get; set; } /// /// Use nbsp; instead space in beginning of line /// public bool UseForwardNbsp { get; set; } /// /// Use original font /// public bool UseOriginalFont { get; set; } /// /// Use style tag instead style attribute /// public bool UseStyleTag { get; set; } /// /// Use 'br' tag instead of '\n' /// public bool UseBr { get; set; } /// /// Includes line numbers /// public bool IncludeLineNumbers { get; set; } FastColoredTextBox tb; public ExportToHTML() { UseNbsp = true; UseOriginalFont = true; UseStyleTag = true; UseBr = true; } public string GetHtml(FastColoredTextBox tb) { this.tb = tb; Range sel = new Range(tb); sel.SelectAll(); return GetHtml(sel); } public string GetHtml(Range r) { this.tb = r.tb; Dictionary styles = new Dictionary(); StringBuilder sb = new StringBuilder(); StringBuilder tempSB = new StringBuilder(); StyleIndex currentStyleId = StyleIndex.None; r.Normalize(); int currentLine = r.Start.iLine; styles[currentStyleId] = null; // if (UseOriginalFont) sb.AppendFormat("", r.tb.Font.Name, r.tb.Font.SizeInPoints, r.tb.CharHeight); // if (IncludeLineNumbers) tempSB.AppendFormat("{0} ", currentLine + 1); // bool hasNonSpace = false; foreach (Place p in r) { Char c = r.tb[p.iLine][p.iChar]; if (c.style != currentStyleId) { Flush(sb, tempSB, currentStyleId); currentStyleId = c.style; styles[currentStyleId] = null; } if (p.iLine != currentLine) { for (int i = currentLine; i < p.iLine; i++) { tempSB.Append(UseBr ? "
" : "\r\n"); if (IncludeLineNumbers) tempSB.AppendFormat("{0} ", i + 2); } currentLine = p.iLine; hasNonSpace = false; } switch (c.c) { case ' ': if ((hasNonSpace || !UseForwardNbsp) && !UseNbsp) goto default; tempSB.Append(" "); break; case '<': tempSB.Append("<"); break; case '>': tempSB.Append(">"); break; case '&': tempSB.Append("&"); break; default: hasNonSpace = true; tempSB.Append(c.c); break; } } Flush(sb, tempSB, currentStyleId); if (UseOriginalFont) sb.Append("
"); //build styles if (UseStyleTag) { tempSB.Length = 0; tempSB.Append(""); sb.Insert(0, tempSB.ToString()); } if (IncludeLineNumbers) sb.Insert(0, LineNumbersCSS); return sb.ToString(); } private string GetCss(StyleIndex styleIndex) { List