1
+ /*
2
+ * This file is part of Universal-Updater
3
+ * Copyright (C) 2019-2021 Universal-Team
4
+ * Copyright (C) 2025 Alvin Wong
5
+ *
6
+ * This program is free software: you can redistribute it and/or modify
7
+ * it under the terms of the GNU General Public License as published by
8
+ * the Free Software Foundation, either version 3 of the License, or
9
+ * (at your option) any later version.
10
+ *
11
+ * This program is distributed in the hope that it will be useful,
12
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ * GNU General Public License for more details.
15
+ *
16
+ * You should have received a copy of the GNU General Public License
17
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ *
19
+ * Additional Terms 7.b and 7.c of GPLv3 apply to this file:
20
+ * * Requiring preservation of specified reasonable legal notices or
21
+ * author attributions in that material or in the Appropriate Legal
22
+ * Notices displayed by works containing it.
23
+ * * Prohibiting misrepresentation of the origin of that material,
24
+ * or requiring that modified versions of such material be marked in
25
+ * reasonable ways as different from the original version.
26
+ */
27
+
28
+ #include " animation.hpp"
29
+ #include " common.hpp"
30
+ #include " overlay.hpp"
31
+ #include " qrcodegen.h"
32
+
33
+ extern bool touching (touchPosition touch, Structs::ButtonPos button);
34
+ static const Structs::ButtonPos backButton{ 4 , 0 , 24 , 24 };
35
+
36
+ static std::vector<std::string> wrapUrlLines (std::string_view url) {
37
+ // Crudely line wrap the URL:
38
+ std::vector<std::string> wrappedUrlLines;
39
+ std::string line;
40
+ size_t lastWrap = 0 ;
41
+ for (size_t i = 0 ; i < url.size (); i++) {
42
+ char c = url[i];
43
+ line.push_back (c);
44
+
45
+ if (i - lastWrap < 30 ) continue ;
46
+
47
+ bool wrap = false ;
48
+ switch (c) {
49
+ case ' -' :
50
+ case ' /' :
51
+ case ' _' :
52
+ case ' ?' :
53
+ case ' =' :
54
+ case ' &' :
55
+ case ' ' :
56
+ wrap = true ;
57
+ break ;
58
+ default :
59
+ if (i - lastWrap >= 40 ) wrap = true ;
60
+ break ;
61
+ }
62
+
63
+ if (wrap) {
64
+ if (wrappedUrlLines.size () >= 9 ) {
65
+ line.append (" ..." );
66
+ break ;
67
+ }
68
+ wrappedUrlLines.emplace_back (std::move (line));
69
+ lastWrap = i;
70
+ }
71
+ }
72
+ if (!line.empty ()) wrappedUrlLines.emplace_back (std::move (line));
73
+ return wrappedUrlLines;
74
+ }
75
+
76
+ /* Show a QR Code URL. */
77
+ void Overlays::ShowQrCodeUrl (const std::string &title, const std::string &url) {
78
+
79
+ std::unique_ptr<uint8_t []> qrcode = std::make_unique<uint8_t []>(qrcodegen_BUFFER_LEN_MAX);
80
+ std::unique_ptr<uint8_t []> tempBuffer = std::make_unique<uint8_t []>(qrcodegen_BUFFER_LEN_MAX);
81
+ bool ok = qrcodegen_encodeText (url.c_str (), tempBuffer.get (), qrcode.get (), qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true );
82
+ if (!ok) {
83
+ Msg::DisplayMsg (Lang::get (" QR_CODE_GEN_FAILED" ));
84
+ return ;
85
+ }
86
+
87
+ int size = qrcodegen_getSize (qrcode.get ());
88
+ if (size <= 0 || size > 256 ) {
89
+ Msg::DisplayMsg (Lang::get (" QR_CODE_GEN_FAILED" ));
90
+ return ;
91
+ }
92
+
93
+ C3D_Tex tex;
94
+
95
+ C3D_TexInit (&tex, 256 , 256 , GPU_TEXCOLOR::GPU_A8);
96
+ C3D_TexSetFilter (&tex, GPU_NEAREST, GPU_NEAREST);
97
+ C3D_TexSetWrap (&tex, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
98
+
99
+ for (u32 x = 0 ; x < size; x++) {
100
+ for (u32 y = 0 ; y < size; y++) {
101
+ const u32 dstPos = ((((y >> 3 ) * (256 >> 3 ) + (x >> 3 )) << 6 ) +
102
+ ((x & 1 ) | ((y & 1 ) << 1 ) | ((x & 2 ) << 1 ) | ((y & 2 ) << 2 ) |
103
+ ((x & 4 ) << 2 ) | ((y & 4 ) << 3 )));
104
+
105
+ ((uint8_t *)tex.data )[dstPos] = qrcodegen_getModule (qrcode.get (), x, y) ? 255 : 0 ;
106
+ }
107
+ }
108
+
109
+ constexpr int border = 4 ;
110
+ int scale = 240 / (size + border * 2 );
111
+ int drawSize = size * scale;
112
+ int drawX = (400 - drawSize) / 2 ;
113
+ int drawY = (240 - drawSize) / 2 ;
114
+ int drawBorder = border * scale;
115
+
116
+ C2D_Image qrImage;
117
+ Tex3DS_SubTexture subtex{(u16 )drawSize, (u16 )drawSize, 0 .0f , 1 .0f , size / 256 .f , 1 .0f - (size / 256 .f )};
118
+ qrImage.tex = &tex;
119
+ qrImage.subtex = &subtex;
120
+ C2D_Tint tint{
121
+ .color = BLACK,
122
+ .blend = 1 .f ,
123
+ };
124
+ C2D_ImageTint imageTint{
125
+ .corners = {tint, tint, tint, tint},
126
+ };
127
+
128
+ std::vector<std::string> wrappedUrlLines = wrapUrlLines (url);
129
+
130
+ bool doOut = false ;
131
+
132
+ while (!doOut) {
133
+ Gui::clearTextBufs ();
134
+ C3D_FrameBegin (C3D_FRAME_SYNCDRAW);
135
+ C2D_TargetClear (Top, TRANSPARENT);
136
+ C2D_TargetClear (Bottom, TRANSPARENT);
137
+
138
+ GFX::DrawTop ();
139
+
140
+ C2D_DrawRectSolid (drawX - drawBorder, drawY - drawBorder, 0 .75f , drawSize + drawBorder * 2 , drawSize + drawBorder * 2 , WHITE);
141
+ C2D_DrawImageAt (qrImage, drawX, drawY, 1 .f , &imageTint);
142
+
143
+ Animation::QueueEntryDone ();
144
+ GFX::DrawBottom ();
145
+ Gui::Draw_Rect (0 , 0 , 320 , 25 , UIThemes->BarColor ());
146
+ Gui::Draw_Rect (0 , 25 , 320 , 1 , UIThemes->BarOutline ());
147
+ GFX::DrawIcon (sprites_arrow_idx, backButton.x , backButton.y , UIThemes->TextColor ());
148
+ Gui::DrawStringCentered (0 , 2 , 0.6 , UIThemes->TextColor (), title, 310 , 0 , font);
149
+
150
+ int y = 34 ;
151
+ for (const std::string &urlLine : wrappedUrlLines) {
152
+ Gui::DrawStringCentered (0 , y, 0 .6f , UIThemes->TextColor (), urlLine, 312 , 0 , font);
153
+ y += 20 ;
154
+ if (y >= 240 ) break ;
155
+ }
156
+
157
+ C3D_FrameEnd (0 );
158
+
159
+ hidScanInput ();
160
+ touchPosition touch;
161
+ hidTouchRead (&touch);
162
+ Animation::HandleQueueEntryDone ();
163
+ if ((hidKeysDown () & KEY_START) || (hidKeysDown () & KEY_B) || (hidKeysDown () & KEY_A) || (hidKeysDown () & KEY_TOUCH && touching (touch, backButton))) doOut = true ;
164
+ }
165
+
166
+ C3D_TexDelete (&tex);
167
+ }
0 commit comments