Advertisement
Guest User

Untitled

a guest
Apr 20th, 2025
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 29.49 KB | Source Code | 0 0
  1. /*
  2.     A revised version of the lc3vmwin_memory
  3.  
  4.     .PLAN
  5.  
  6.     - Users can use keyboard to navigate through the memory area
  7.         - Cursor must use very distinct foreground and background color
  8.             - Is it possible to blink?
  9.         - Cursor has two indices: Start and End
  10.             - Everything between the two should have a rect border
  11.  
  12.         - CTRL / SHIFT / ALT + Arrow keys for navigation
  13.             - LEFT for moving the cursor back 1 byte
  14.             - RIGHT for moving the cursor forward 1 byte
  15.             - UP for moving the cursor back 16 byte
  16.             - DOWN for moving the cursor forward 16 byte
  17.  
  18.             - CTRL+LEFT for moving the cursor to the first byte of the current row
  19.             - CTRL+RIGHT for moving the cursor to the last byte of the current row
  20.  
  21.             - SHIFT+LEFT for MARKING the byte and the previous byte as SELECTED, and move the cursor to the previous byte
  22.             - SHIFT+RIGHT for MARKING the byte and the next byte as SELECTED, and move the cursor to the previous byte
  23.             - SHIFT+UP for MARKING the byte and its previous 16 bytes as SELECTED, and move the cursor back 16 bytes
  24.             - SHIFT+DOWN for MARKING the byte and its next 16 bytes as SELECTED, and move the cursor forward 16 bytes
  25.  
  26.             - ALT should be reserved for advanced usage, e.g. scripted cursor movements
  27.  
  28.         - PAGEUP same as the Page < button
  29.  
  30.         - PAGEDOWN same as the Page > button
  31.  
  32.         - HOME same as the Page << button
  33.  
  34.         - END same as the Page >> button
  35.  
  36.         - ENTER key for popping up the memory edit window
  37.  
  38.         - ESC to CANCEL all selections
  39.  
  40.         - CTRL + F1 ~ F12 to mark 12 locations, and F1 ~ F12 to jump to (shit man, this is awesome!)
  41.  
  42.         - CTRL + Z for undo and CTRL + Y for redo
  43.  
  44.         - CTRL + S for save as (for safety, do not save in position), CTRL + L for load file
  45.  
  46.     - A sidebar action window that displays a stack of user actions (for undoing)
  47.  
  48.     - A readonly toggle (this should make the UI slightly different)
  49. */
  50.  
  51. #include "memory_editor.hpp"
  52. #include <string>
  53. #include <sstream>
  54. #include <iomanip>
  55.  
  56.  
  57. MemoryEditor::MemoryEditor()
  58. {
  59.     buffer.reserve(0);
  60.     bufferSize = 0;
  61.     readOnly = true;
  62.     font = nullptr;
  63.     fontSize = 14;
  64.     initialWindowSize = {0, 0};
  65.     minWindowSize = {0, 0};
  66.     winPos = {0, 0};
  67.     cursorStartIndex = 0;
  68.     cursorEndIndex = 0;
  69.     initialAddress = 0;
  70.     minInitialAddress = 0;
  71.     maxInitialAddress = 0;
  72.     cursorStartBoundary = 0;
  73.     cursorEndBoundary = 0;
  74.     drawList = nullptr;
  75. }
  76.  
  77. MemoryEditor::MemoryEditor(uint8_t* memory, uint64_t memorySize, const ImGuiWindowConfig& config)
  78. {
  79.     IM_ASSERT(memory != nullptr);
  80.  
  81.     // TODO: Add a warning dialog if memorySize is over 1GB
  82.  
  83.     buffer = std::vector<charGlyph>(memorySize, {0, 255, 255, 255});
  84.  
  85.     bufferSize = memorySize;
  86.  
  87.     for (size_t i = 0; i < bufferSize; i++)
  88.     {
  89.         buffer[i] = {(*memory), 255, 255, 255};
  90.         memory++;
  91.     }
  92.  
  93.     readOnly            = config.readOnly;
  94.     fontSize            = config.fontSize;
  95.     initialWindowSize   = config.initialWindowSize;
  96.     minWindowSize       = config.minWindowSize;
  97.     winPos              = config.winPos;
  98.  
  99.     cursorStartIndex    = 0;
  100.     cursorEndIndex      = 0;
  101.     initialAddress      = 0;
  102.  
  103.     minInitialAddress   = 0;
  104.     maxInitialAddress   = CalculateMaxInitialAddress();
  105.     cursorStartBoundary = 0;
  106.     cursorEndBoundary   = bufferSize - 1;
  107.    
  108.     // FIXME: find a custom font that accepts visible space
  109.     ImGuiIO& io = ImGui::GetIO();
  110.     io.Fonts->AddFontFromFileTTF("./JetBrainsMono.ttf", fontSize, nullptr, io.Fonts->GetGlyphRangesDefault());
  111. }
  112.  
  113. void MemoryEditor::Draw()
  114. {
  115.     ImGui::GetStyle().WindowBorderSize = 2.0f;
  116.     // ImGui::SetNextWindowPos(winPos);
  117.     ImGui::SetNextWindowSizeConstraints(minWindowSize, initialWindowSize);
  118.  
  119.     /*
  120.         EXPLAIN:
  121.         I don't know for sure, but looks like we add all lines, rectangles into a draw list
  122.         https://github.com/WerWolv/ImHex/blob/00cf8ecb18b2024ba375c353ce9680d33512f65a/libs/ImGui/include/imgui_memory_editor.h#L212C9-L212C60
  123.     */
  124.    
  125.     drawList = ImGui::GetForegroundDrawList();
  126.  
  127.     if (ImGui::Begin("Memory Editor Window", nullptr, ImGuiWindowFlags_NoCollapse))
  128.     {
  129.         /*
  130.             EXPLAIN:
  131.             Render first row -> each row shows 16 bytes
  132.             Address    00 01 ... 0F ASCII
  133.         */
  134.  
  135.         ImGui::SetCursorPos({20.0f, 20.0f});
  136.  
  137.         ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 255, 0, 255));
  138.  
  139.         /*
  140.             EXPLAIN:
  141.             This is to get correct Selectable size for each glyph
  142.             (note that the length also includes the prefix space)
  143.         */
  144.         ImVec2 textSize = ImGui::CalcTextSize(" 00 ");
  145.  
  146.         ImGui::Text("Address ");
  147.         ImGui::SameLine();
  148.         ImGui::Text(" 00");
  149.         ImGui::SameLine();
  150.         ImGui::Text(" 01");
  151.         ImGui::SameLine();
  152.         ImGui::Text(" 02");
  153.         ImGui::SameLine();
  154.         ImGui::Text(" 03");
  155.         ImGui::SameLine();
  156.         ImGui::Text(" 04");
  157.         ImGui::SameLine();
  158.         ImGui::Text(" 05");
  159.         ImGui::SameLine();
  160.         ImGui::Text(" 06");
  161.         ImGui::SameLine();
  162.         ImGui::Text(" 07");
  163.         ImGui::SameLine();
  164.         ImGui::Text(" 08");
  165.         ImGui::SameLine();
  166.         ImGui::Text(" 09");
  167.         ImGui::SameLine();
  168.         ImGui::Text(" 0A");
  169.         ImGui::SameLine();
  170.         ImGui::Text(" 0B");
  171.         ImGui::SameLine();
  172.         ImGui::Text(" 0C");
  173.         ImGui::SameLine();
  174.         ImGui::Text(" 0D");
  175.         ImGui::SameLine();
  176.         ImGui::Text(" 0E");
  177.         ImGui::SameLine();
  178.         ImGui::Text(" 0F");
  179.         ImGui::SameLine();
  180.         /*
  181.             EXPLAIN:
  182.             Get the Cursor X to draw the last line of ASCII if bufferSize does not divide 16,
  183.             in that case we need to move Cursor X to the same X position
  184.         */
  185.         ImVec2 asciiPos = ImGui::GetCursorPos();
  186.         ImGui::Text("        ASCII     ");
  187.  
  188.         ImGui::PopStyleColor();
  189.         ImGui::SameLine();
  190.  
  191.         if (ImGui::Button("Page <"))
  192.         {
  193.             if (initialAddress <= 0x00200)
  194.             {
  195.                 initialAddress = 0x00000;
  196.             }
  197.             else
  198.             {
  199.                 initialAddress -= 0x200;
  200.             }
  201.         }
  202.  
  203.         ImGui::SameLine();
  204.         if (ImGui::Button("Page <<"))
  205.         {
  206.             initialAddress = 0x00000;
  207.         }
  208.  
  209.         // Render memory glyphs
  210.  
  211.         ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 255));
  212.  
  213.         std::stringstream ss;
  214.  
  215.         /*
  216.             EXPLAIN:
  217.             We need quite a few explanations for the drawing of the glyphs themselves
  218.  
  219.             1. We only render 512 InputText each frame (32 rows * 16 bytes per row)
  220.  
  221.             2. For each byte, we need to PushID(i) to avoid conflict IDs, and PopID() at the end
  222.         */
  223.  
  224.         for (size_t i = initialAddress; i < initialAddress + PAGE_ROWS * PAGE_COLUMNS; i++)
  225.         // for (size_t i = initialAddress; i < bufferSize; i++)
  226.         {
  227.             /*
  228.                 Starting from initialAddress, we only render a max of 512 bytes each frame -
  229.                 This is 16 bytes per row * 32 rows hardcoded
  230.             */
  231.            
  232.             // Break if i is over bufferSize - 1,
  233.             // because sometimes the last row doesn't always have PAGE_COLUMNS bytes
  234.             if (i > bufferSize - 1)
  235.             {
  236.                 break;
  237.             }
  238.  
  239.             // EXPLAIN: PushID() to avoid conflict IDs
  240.             ImGui::PushID(i);
  241.  
  242.             // Header
  243.             if (i % 16 == 0)
  244.             {
  245.                 ImGui::SetCursorPosX(20.0f);
  246.                 ss << "0x" << std::hex << std::setfill('0') << std::setw(5) << static_cast<int>(i) << " ";
  247.                 std::string header = ss.str();
  248.                 ImGui::Text("%s", header.c_str());
  249.                 ss.str("");
  250.                 ss.clear();
  251.                 ImGui::SameLine();
  252.             }
  253.  
  254.             /*
  255.                 EXPLAIN:
  256.                 This is how we render the rectangles for "selected" cells.
  257.                 Basically every cell within the cursor range should be "selected"
  258.                
  259.                 StartIndex is not necessarily <= EndIndex
  260.             */
  261.            
  262.             int64_t cursorMinIndex = (cursorStartIndex <= cursorEndIndex ? cursorStartIndex : cursorEndIndex);
  263.             int64_t cursorMaxIndex = (cursorStartIndex <= cursorEndIndex ? cursorEndIndex : cursorStartIndex);
  264.  
  265.             if (i >= cursorMinIndex && i <= cursorMaxIndex)
  266.             {
  267.                 ImGui::SameLine();
  268.                 ImVec2 cursorPosUpperLeft = ImGui::GetCursorScreenPos();
  269.                 ImVec2 cursorPosLowerLeft = ImVec2(cursorPosUpperLeft.x, cursorPosUpperLeft.y + textSize.y);
  270.                 ImVec2 cursorPosUpperRight = ImVec2(cursorPosUpperLeft.x + textSize.x, cursorPosUpperLeft.y);
  271.                 ImVec2 cursorPosLowerRight = ImVec2(cursorPosUpperLeft.x + textSize.x, cursorPosUpperLeft.y + textSize.y);
  272.                 /*
  273.                     TODO:
  274.                     Figure out a way to draw contiguous rectangle instead of a lot of small rectangles
  275.                     - We probably need to use cursorMinIndex and cursorMaxIndex
  276.                     - What if we need to draw multiple lines?
  277.                     - Actually, for the first cell of second row, how do we draw a cell without both left and right borders?
  278.                 */
  279.                 // drawList->AddRectFilled(cursorPosUpperLeft, cursorPosLowerRight, IM_COL32(125, 175, 175, 100));
  280.                 if ( i == cursorMinIndex)
  281.                 {
  282.                     drawList->AddLine(cursorPosUpperLeft, cursorPosLowerLeft, IM_COL32(255, 255, 255, 255));
  283.                 }
  284.                 drawList->AddLine(cursorPosUpperLeft, cursorPosUpperRight, IM_COL32(255, 255, 255, 255));
  285.                 drawList->AddLine(cursorPosLowerLeft, cursorPosLowerRight, IM_COL32(255, 255, 255, 255));
  286.                 if ( i == cursorMaxIndex)
  287.                 {
  288.                     drawList->AddLine(cursorPosUpperRight, cursorPosLowerRight, IM_COL32(255, 255, 255, 255));
  289.                 }
  290.  
  291.                 ImGui::SameLine();
  292.             }
  293.  
  294.             ss << ' ' << std::hex << std::setfill('0') << std::setw(2) << static_cast<int>(buffer[i].ch);
  295.             std::string byteHex = ss.str();
  296.            
  297.             // EXPLAIN: We use a light green color for rectangles so text should be yellow
  298.             if (i >= cursorMinIndex && i <= cursorMaxIndex)
  299.             {
  300.                 ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 0, 255));
  301.                 ImGui::Text("%s", byteHex.c_str());
  302.                 ImGui::PopStyleColor();
  303.             }
  304.             else
  305.             {
  306.                 ImGui::Text("%s", byteHex.c_str());
  307.             }
  308.             ss.str("");
  309.             ss.clear();
  310.  
  311.             ImGui::SameLine();
  312.  
  313.             /*
  314.                 EXPLAIN:
  315.                 If bufferSize cannot be divided by 16 (e.g. 60 bytes, so the last row is ),
  316.                 we need to move the cursor (as the cursor is not at the ASCII position)
  317.             */
  318.  
  319.             if ((i % 16 == 15) || (i == bufferSize - 1))
  320.             {
  321.                 // Do not use ImGui::GetCursorScreenPos(), otherwise it messes up when window moves
  322.                 ImVec2 moveToASCIIPos = ImVec2(asciiPos.x, ImGui::GetCursorPos().y);
  323.                 ImGui::SetCursorPos(moveToASCIIPos);
  324.                 ImGui::Text(" ");
  325.                 int64_t leftIndex = (i / 16) * 16;
  326.                 for (int64_t j = leftIndex; j <= i; j++)
  327.                 {
  328.                     ImGui::SameLine();
  329.                     char ascii = buffer[j].ch;
  330.                     // ss << (std::isprint(ascii) ? ascii : '.');
  331.                     ascii = (std::isprint(ascii) ? ascii : '.');
  332.  
  333.  
  334.                     if (j >= cursorMinIndex && j <= cursorMaxIndex)
  335.                     {
  336.                         ImVec2 cursorPosUpperLeft = ImGui::GetCursorScreenPos();
  337.                         ImVec2 asciiTextSize = ImGui::CalcTextSize(" 0");
  338.                         ImVec2 cursorPosLowerLeft = ImVec2(cursorPosUpperLeft.x, cursorPosUpperLeft.y + asciiTextSize.y);
  339.                         ImVec2 cursorPosUpperRight = ImVec2(cursorPosUpperLeft.x + asciiTextSize.x, cursorPosUpperLeft.y);
  340.                         ImVec2 cursorPosLowerRight = ImVec2(cursorPosUpperLeft.x + asciiTextSize.x, cursorPosUpperLeft.y + asciiTextSize.y);
  341.                         // Some sort of light blue rectangle
  342.                         // drawList->AddRectFilled(cursorPosUpperLeft, cursorPosLowerRight, IM_COL32(125, 175, 175, 100));
  343.                         if ( j == cursorMinIndex)
  344.                         {
  345.                             drawList->AddLine(cursorPosUpperLeft, cursorPosLowerLeft, IM_COL32(255, 255, 255, 255));
  346.                         }
  347.                         drawList->AddLine(cursorPosUpperLeft, cursorPosUpperRight, IM_COL32(255, 255, 255, 255));
  348.                         drawList->AddLine(cursorPosLowerLeft, cursorPosLowerRight, IM_COL32(255, 255, 255, 255));
  349.                         if ( j == cursorMaxIndex)
  350.                         {
  351.                             drawList->AddLine(cursorPosUpperRight, cursorPosLowerRight, IM_COL32(255, 255, 255, 255));
  352.                         }
  353.                         // drawList->AddLine(cursorPosUpperLeft, cursorPosLowerLeft, IM_COL32(255, 255, 255, 255));
  354.  
  355.                         ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 0, 255));
  356.                         // FIXME: Use a different font to display Unicode
  357.                         if (ascii == ' ')
  358.                         {
  359.                             ImGui::Text("\xE2\x90\xA3");
  360.                         }
  361.                         else
  362.                         {
  363.                             ImGui::Text("%c", ascii);
  364.                         }
  365.                         ImGui::PopStyleColor();
  366.                     }
  367.                     else
  368.                     {
  369.                         ImGui::Text("%c", ascii);
  370.                     }
  371.                 }
  372.                 // ImGui::SameLine();
  373.                 // std::string asciiString = ss.str();
  374.                 // ImGui::Text("%s", asciiString.c_str());
  375.                 ss.str("");
  376.                 ss.clear();
  377.             }
  378.  
  379.             ImGui::PopID();
  380.         }
  381.  
  382.         Input();
  383.  
  384.         ImGui::PopStyleColor();
  385.     }
  386.  
  387.     ImVec2 winPos = ImGui::GetWindowPos();
  388.     ImVec2 scrollbarMin = ImVec2(winPos.x + 848.0f, winPos.y + 44.0f);
  389.     ImVec2 scrollbarMax = ImVec2(winPos.x + 872.0f, winPos.y + 680.0f);
  390.  
  391.     DrawScrollBar(drawList, scrollbarMin, scrollbarMax);
  392.  
  393.     DrawScrollBarSlider(drawList, scrollbarMin, scrollbarMax);
  394.  
  395.     ImGui::Separator();
  396.  
  397.     if (ImGui::Button("Toggle Read-Only"))
  398.     {
  399.         readOnly = !readOnly;
  400.     }
  401.  
  402.     ImGui::SameLine();
  403.     if (readOnly)
  404.     {
  405.         ImGui::TextColored(ImVec4(255, 0, 0, 255), "READ-ONLY MODE");
  406.     }
  407.     else
  408.     {
  409.         ImGui::TextColored(ImVec4(0, 255, 0, 255), "WRITE-READ MODE");
  410.     }
  411.  
  412.     ImGui::End();
  413. }
  414.  
  415. void MemoryEditor::Input()
  416. {
  417.     /*
  418.         Keypresses:
  419.         Check this piece of code for reference:
  420.         https://github.com/WerWolv/ImHex/blob/00cf8ecb18b2024ba375c353ce9680d33512f65a/libs/ImGui/include/imgui_memory_editor.h#L260
  421.  
  422.         Move initialAddress if any of the selected area reaches off screen addresses
  423.     */
  424.  
  425.     if (ImGui::IsWindowFocused())
  426.     {
  427.         bool isCtrlDown = false;
  428.         bool isShiftDown = false;
  429.         if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl))
  430.         {
  431.             isCtrlDown = true;
  432.         }
  433.  
  434.         if (ImGui::IsKeyDown(ImGuiKey_LeftShift))
  435.         {
  436.             isShiftDown = true;
  437.         }
  438.  
  439.         if (ImGui::IsKeyPressed(ImGuiKey_RightArrow))
  440.         {
  441.             /*
  442.                 EXPLAIN:
  443.                 CTRL + SHIFT + RIGHT ARROW for contiguous selection towards the end of row
  444.                 No need to adjust initialAddress as we are not moving vertically
  445.             */
  446.             if (isCtrlDown && isShiftDown)
  447.             {
  448.                 cursorEndIndex = cursorEndIndex | 0x0F;
  449.             }
  450.             /*
  451.                 EXPLAIN:
  452.                 CTRL + RIGHT ARROW for jumping to end of row
  453.                 No need to adjust initialAddress as we are not moving vertically
  454.             */
  455.             else if (isCtrlDown)
  456.             {
  457.                 /* EXPLAIN: Check for last row that has less than 16 cells */
  458.                 if ((cursorStartIndex | 0x0F) > bufferSize - 1)
  459.                 {
  460.                     cursorStartIndex = bufferSize - 1;
  461.                 }
  462.                 else
  463.                 {
  464.                     cursorStartIndex = cursorStartIndex | 0x0F;
  465.                 }
  466.                 cursorEndIndex = cursorStartIndex;
  467.             }
  468.             /*
  469.                 EXPLAIN:
  470.                 SHIFT + RIGHT ARROW for contiguous selection with the next address
  471.             */
  472.             else if (isShiftDown)
  473.             {
  474.                 /*
  475.                     EXPLAIN:
  476.                     Reset cursorEndIndex in case there are cells already selected
  477.                 */
  478.                 // cursorEndIndex = cursorStartIndex;
  479.                 if (cursorEndIndex < bufferSize - 1)
  480.                 {
  481.                     cursorEndIndex++;
  482.                 }
  483.             }
  484.             /*
  485.                 EXPLAIN:
  486.                 RIGHT ARROW moves the cursor to the next cell
  487.                 We also adjust initialAddress if needed
  488.             */
  489.             else
  490.             {
  491.                 if (cursorStartIndex < bufferSize - 1)
  492.                 {
  493.                     cursorStartIndex += 1;
  494.                 }
  495.  
  496.                 if (cursorStartIndex >= (initialAddress + PAGE_ROWS * PAGE_COLUMNS))
  497.                 {
  498.                     initialAddress += PAGE_COLUMNS;
  499.                 }
  500.  
  501.                 // Always reset cursorEndIndex, just in case there are multiple selected cells
  502.                 cursorEndIndex = cursorStartIndex;
  503.             }
  504.         }
  505.  
  506.         if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow))
  507.         {
  508.             /*
  509.                 EXPLAIN:
  510.                 CTRL + SHIFT + LEFT ARROW for contiguous selection towards the end of row
  511.                 NOTE: always fix cursorStartIndex and move cursorEndIndex
  512.  
  513.                 No need to adjust initialAddress as we are not moving vertically
  514.             */
  515.             if (isCtrlDown && isShiftDown)
  516.             {
  517.                 cursorEndIndex &= (int64_t)0xFFFFFFFFFFFFFFF0;
  518.             }
  519.             /*
  520.                 EXPLAIN:
  521.                 CTRL + LEFT ARROW for jumping to the first cell of row
  522.  
  523.                 No need to adjust initialAddress as we are not moving vertically
  524.             */
  525.             else if (isCtrlDown)
  526.             {
  527.                 cursorStartIndex &= (int64_t)0xFFFFFFFFFFFFFFF0;
  528.                 cursorEndIndex = cursorStartIndex;
  529.             }
  530.             /*
  531.                 EXPLAIN:
  532.                 SHIFT + LEFT ARROW for contiguous selection with the previous address
  533.                 NOTE: always fix cursorStartIndex and move cursorEndIndex
  534.             */
  535.             else if (isShiftDown)
  536.             {
  537.                 if (cursorEndIndex > 0)
  538.                 {
  539.                     cursorEndIndex--;
  540.                 }
  541.             }
  542.             /*
  543.                 EXPLAIN:
  544.                 LEFT ARROW for moving the cursor to the previous address
  545.                 We also check screen boundaries (initialAddress -> initialAddress + 32 * 16 - 1)
  546.             */
  547.             else
  548.             {
  549.                 if (cursorStartIndex > 0)
  550.                 {
  551.                     cursorStartIndex -= 1;
  552.  
  553.                     if (cursorStartIndex < initialAddress)
  554.                     {
  555.                         initialAddress -= PAGE_COLUMNS;
  556.                     }
  557.                 }
  558.                 // Always reset cursorEndIndex, just in case there are multiple selected cells
  559.                 cursorEndIndex = cursorStartIndex;
  560.             }
  561.         }
  562.  
  563.         if (ImGui::IsKeyPressed(ImGuiKey_DownArrow))
  564.         {
  565.             if (isShiftDown)
  566.             {
  567.                 /*
  568.                     EXPLAIN:
  569.                     SHIFT + DOWN ARROW = adding the next 16 cells after cursorEndIndex into selection
  570.                     if cursorEndIndex <= cursorStartIndex, reset cursorEndIndex first
  571.                     if cursorEndIndex >= cursorStartIndex, no need to reset
  572.  
  573.                     We need to adjust initialAddress as we are now moving vertically --
  574.  
  575.                 */
  576.  
  577.                 if (cursorEndIndex + 16 <= bufferSize - 1)
  578.                 {
  579.                     cursorEndIndex += 16;
  580.                 }
  581.                 else
  582.                 {
  583.                     cursorEndIndex = bufferSize - 1;
  584.                 }
  585.  
  586.                 ResetInitialAddress(BREAKTHROUGH_ENDINDEX);
  587.             }
  588.             else if (isCtrlDown)
  589.             {
  590.                 /*
  591.                     EXPLAIN:
  592.                     Just turn a page if possible, otherwise take the last cell.
  593.                     This is for fast scrolling -> 512 bytes per keystroke
  594.                 */
  595.                 cursorStartIndex = initialAddress + PAGE_ROWS * PAGE_COLUMNS + (cursorStartIndex & 0x0F);
  596.                 if (cursorStartIndex > bufferSize - 1)
  597.                 {
  598.                     cursorStartIndex = bufferSize - 1;
  599.                 }
  600.                 cursorEndIndex = cursorStartIndex;
  601.                 ResetInitialAddress(BREAKTHROUGH_STARTINDEX);
  602.             }
  603.             else
  604.             {
  605.                 /*
  606.                     EXPLAIN:
  607.                     If possible, go down one row to the same column, otherwise go to the last cell
  608.                     Exception: if we are already at the last row, DON'T go to the last cell, it feels weird
  609.  
  610.                     We also need to adjust initialAddress if cursorStartIndex goes out of the bigger boundary
  611.                 */
  612.                 if ((cursorStartIndex | 0x0F) != ((bufferSize - 1) | 0x0F))
  613.                 {
  614.                     if (cursorStartIndex + 16 > bufferSize - 1)
  615.                     {
  616.                         cursorStartIndex = bufferSize - 1;
  617.                     }
  618.                     else
  619.                     {
  620.                         cursorStartIndex += 16;
  621.                     }
  622.                 }
  623.                 // Always reset cursorEndIndex, just in case there are multiple selected cells
  624.                 cursorEndIndex = cursorStartIndex;
  625.  
  626.                 // Reset initialAddress based on cursorStartIndex if out of window
  627.                 ResetInitialAddress(BREAKTHROUGH_STARTINDEX);
  628.             }
  629.         }
  630.  
  631.         if (ImGui::IsKeyPressed(ImGuiKey_UpArrow))
  632.         {
  633.             if (isShiftDown)
  634.             {
  635.                 /*
  636.                     EXPLAIN:
  637.                     SHIFT + UP ARROW = adding the previous 16 cells after cursorEndIndex into selection
  638.  
  639.                     We also need to move initialAddress up one row if needed
  640.                 */
  641.  
  642.                 if (cursorEndIndex - 16 >= 0)
  643.                 {
  644.                     cursorEndIndex -= 16;
  645.                 }
  646.                 else
  647.                 {
  648.                     cursorEndIndex = 0;
  649.                 }
  650.  
  651.                 ResetInitialAddress(BREAKTHROUGH_ENDINDEX);
  652.             }
  653.             else if (isCtrlDown)
  654.             {
  655.                 /*
  656.                     EXPLAIN:
  657.                     Just move up one page, or go to the first cell if that's not possible
  658.                 */
  659.                 cursorStartIndex = initialAddress - PAGE_ROWS * PAGE_COLUMNS + (cursorStartIndex & 0x0F);
  660.                 if (cursorStartIndex < 0)
  661.                 {
  662.                     cursorStartIndex = 0;
  663.                 }
  664.                 cursorEndIndex = cursorStartIndex;
  665.                 ResetInitialAddress(BREAKTHROUGH_STARTINDEX);
  666.             }
  667.             else
  668.             {
  669.                 /*
  670.                     EXPLAIN:
  671.                     If possible, go up one row to the same column, otherwise go to the first cell
  672.                     Exception: if we are already at the first row, DON'T go to the first cell, it feels weird
  673.  
  674.                     We also need to adjust initialAddress if cursorStartIndex goes out of the smaller boundary
  675.                 */
  676.                 if (cursorStartIndex > 0x0F)
  677.                 {
  678.                     if (cursorStartIndex < 16)
  679.                     {
  680.                         cursorStartIndex = 0;
  681.                     }
  682.                     else
  683.                     {
  684.                         cursorStartIndex -= 16;
  685.                     }
  686.  
  687.                     // if (cursorStartIndex < initialAddress)
  688.                     // {
  689.                     //     initialAddress -= PAGE_COLUMNS;
  690.                     // }
  691.                 }
  692.                 // Always reset cursorEndIndex, just in case there are multiple selected cells
  693.                 cursorEndIndex = cursorStartIndex;
  694.  
  695.                 // Reset initialAddress based on cursorStartIndex if out of window
  696.                 // When we reset when cursor is oving up to the previous screen, we want maximum
  697.                 // visibility for the user, so initialAddress should be PAGE_ROWS rows away
  698.                 // cursorStartIndex
  699.                 ResetInitialAddress(BREAKTHROUGH_STARTINDEX);
  700.             }
  701.         }
  702.  
  703.         if (!readOnly)
  704.         {
  705.             if (ImGui::IsKeyPressed(ImGuiKey_KeypadAdd))
  706.             {
  707.                 buffer[cursorStartIndex].ch += 1;
  708.             }
  709.  
  710.             if (ImGui::IsKeyPressed(ImGuiKey_KeypadSubtract))
  711.             {
  712.                 buffer[cursorStartIndex].ch -= 1;
  713.             }
  714.         }
  715.     }
  716. }
  717.  
  718. int64_t MemoryEditor::CalculateMaxInitialAddress()
  719. {
  720.     /*
  721.         EXPLAIN:
  722.         First, we go back to the first cell of the last row, because we already know bufferSize;
  723.         Next, we try to go up 31 (which is PAGE_ROWS - 1) rows;
  724.         And if it becomes negative, we just set it to 0 (whole buffer is less than one page)
  725.     */
  726.    
  727.     if (bufferSize <= PAGE_ROWS * PAGE_COLUMNS)
  728.     {
  729.         return 0;
  730.     }
  731.  
  732.     int64_t maxInitialAddress = (bufferSize - 1) & (int64_t)0xFFFFFFFFFFFFFFF0;
  733.     maxInitialAddress -= (PAGE_COLUMNS * (PAGE_ROWS - 1));
  734.  
  735.     if (maxInitialAddress < 0)
  736.     {
  737.         return 0;
  738.     }
  739.     else
  740.     {
  741.         return maxInitialAddress;
  742.     }
  743. }
  744.  
  745. void MemoryEditor::ResetInitialAddress(enum BREAKTHROUGH_TYPE bType)
  746. {
  747.     /*
  748.         Call this function AFTER you changed cursorStartIndex/cursorEndIndex
  749.     */
  750.  
  751.     if (bType == BREAKTHROUGH_STARTINDEX)
  752.     {
  753.         if (cursorStartIndex < initialAddress)
  754.         {
  755.             /*
  756.                 EXPLAIN:
  757.                 Now we are probably breaking UP (to a lower address page)
  758.             */
  759.            
  760.             initialAddress = (cursorStartIndex & (int64_t)0xFFFFFFFFFFFFFFF0) - (PAGE_ROWS - 1) * PAGE_COLUMNS;
  761.             if (initialAddress < 0)
  762.             {
  763.                 initialAddress = 0;
  764.             }
  765.         }
  766.         else if (cursorStartIndex >= initialAddress + PAGE_ROWS * PAGE_COLUMNS)
  767.         {
  768.             /*
  769.                 EXPLAIN:
  770.                 Now we are probably breaking DOWN (to a higher address page)
  771.             */
  772.  
  773.             initialAddress = cursorStartIndex & (int64_t)0xFFFFFFFFFFFFFFF0;
  774.             if (initialAddress > maxInitialAddress)
  775.             {
  776.                 initialAddress = maxInitialAddress;
  777.             }
  778.         }
  779.     }
  780.     else if (bType == BREAKTHROUGH_ENDINDEX)
  781.     {
  782.         if (cursorEndIndex < initialAddress)
  783.         {
  784.             // initialAddress -= PAGE_COLUMNS;
  785.             initialAddress = initialAddress - (PAGE_ROWS - 1) * PAGE_COLUMNS;
  786.             if (initialAddress < 0)
  787.             {
  788.                 initialAddress = 0;
  789.             }
  790.         }
  791.         else if (cursorEndIndex >= initialAddress + PAGE_ROWS * PAGE_COLUMNS)
  792.         {
  793.             initialAddress += PAGE_COLUMNS;
  794.         }
  795.     }
  796. }
  797.  
  798. void MemoryEditor::DrawScrollBar(ImDrawList* drawList, ImVec2 upperLeft, ImVec2 lowerRight)
  799. {
  800.     /*
  801.         EXPLAIN:
  802.         This function insert two items into the drawList (which MUST be passed from Draw())
  803.         - A solid color scrollbar as a rectangle
  804.         - A solid color scrollbar current-page indicator as a small rectangle on top of above
  805.     */
  806.  
  807.     if (drawList == nullptr)
  808.     {
  809.         fprintf(stderr, "drawList is NULL!\n");
  810.         return;
  811.     }
  812.  
  813.     // FIXME: Just a test to find the best upperLeft coordinates
  814.     drawList->AddRect(upperLeft, lowerRight, IM_COL32(150, 50, 50, 255));
  815. }
  816.  
  817. void MemoryEditor::DrawScrollBarSlider(ImDrawList* drawList, ImVec2 upperLeft, ImVec2 lowerRight)
  818. {
  819.     /*
  820.         EXPLAIN:
  821.         The size of the slider depends on size of buffer / size of one page (512 bytes)
  822.         But we probably want a minimum size of, say, 4 pixels?
  823.         Also, a maximum of, say, 64 pixels?
  824.     */
  825.     int64_t size = (int64_t)(lowerRight.y - upperLeft.y) * PAGE_ROWS * PAGE_COLUMNS / (bufferSize | 0x0F);
  826.  
  827.     if (size < MIN_SLIDER_SIZE)
  828.     {
  829.         size = MIN_SLIDER_SIZE;
  830.     }
  831.     // else if (size > MAX_SLIDER_SIZE)
  832.     // {
  833.     //     size = MAX_SLIDER_SIZE;
  834.     // }
  835.  
  836.     int64_t yOffset = (int64_t)(lowerRight.y - upperLeft.y) * initialAddress / (bufferSize | 0x0F);
  837.  
  838.     drawList->AddRectFilled(ImVec2(upperLeft.x, upperLeft.y + yOffset), ImVec2(lowerRight.x, upperLeft.y + size + yOffset), IM_COL32(125, 125, 125, 255));
  839. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement