From e00f0902db7874e83b3a275bbe5050731018d0dc Mon Sep 17 00:00:00 2001 From: Michael Theall Date: Fri, 10 Apr 2020 00:46:46 -0500 Subject: [PATCH] Switch status icons --- Makefile.switch | 2 +- source/3ds/platform.cpp | 8 +- source/log.cpp | 12 +- source/switch/init.c | 2 + source/switch/platform.cpp | 307 ++++++++++++++++++++++++++-------- switch/gfx/airplane_icon.png | Bin 0 -> 4951 bytes switch/gfx/battery_icon.png | Bin 0 -> 3973 bytes switch/gfx/charging_icon.png | Bin 0 -> 4394 bytes switch/gfx/eth_icon.png | Bin 0 -> 520 bytes switch/gfx/eth_none_icon.png | Bin 0 -> 570 bytes switch/gfx/wifi1_icon.png | Bin 0 -> 3621 bytes switch/gfx/wifi2_icon.png | Bin 0 -> 4879 bytes switch/gfx/wifi3_icon.png | Bin 0 -> 6287 bytes switch/gfx/wifi_none_icon.png | Bin 0 -> 5744 bytes 14 files changed, 251 insertions(+), 80 deletions(-) create mode 100644 switch/gfx/airplane_icon.png create mode 100644 switch/gfx/battery_icon.png create mode 100644 switch/gfx/charging_icon.png create mode 100644 switch/gfx/eth_icon.png create mode 100644 switch/gfx/eth_none_icon.png create mode 100644 switch/gfx/wifi1_icon.png create mode 100644 switch/gfx/wifi2_icon.png create mode 100644 switch/gfx/wifi3_icon.png create mode 100644 switch/gfx/wifi_none_icon.png diff --git a/Makefile.switch b/Makefile.switch index f71801e..11ecc94 100644 --- a/Makefile.switch +++ b/Makefile.switch @@ -209,7 +209,7 @@ $(BUILD)/%.rgba: $(GRAPHICS)/%.png | $(BUILD) @convert $< $@ $(ROMFS_GFX): $(ROMFS)/%.rgba.zst: $(BUILD)/%.rgba - @zstd $< -o $@ --ultra -22 + @zstd -f $< -o $@ --ultra -22 $(ROMFS_SHADERS)/%_vsh.dksh: %_vsh.glsl @echo {vert} $(notdir $<) diff --git a/source/3ds/platform.cpp b/source/3ds/platform.cpp index a50cfe1..46a3b58 100644 --- a/source/3ds/platform.cpp +++ b/source/3ds/platform.cpp @@ -228,7 +228,8 @@ void drawStatus () auto const uv2 = ImVec2 (battery->right, battery->bottom); // draw battery icon - ImGui::GetForegroundDrawList ()->AddImage (&s_gfxTexture, p1, p2, uv1, uv2); + ImGui::GetForegroundDrawList ()->AddImage ( + &s_gfxTexture, p1, p2, uv1, uv2, ImGui::GetColorU32 (ImGuiCol_Text)); // get wifi strength auto const wifiStrength = osGetWifiStrength (); @@ -247,14 +248,15 @@ void drawStatus () auto const uv4 = ImVec2 (wifi->right, wifi->bottom); // draw wifi icon - ImGui::GetForegroundDrawList ()->AddImage (&s_gfxTexture, p3, p4, uv3, uv4); + ImGui::GetForegroundDrawList ()->AddImage ( + &s_gfxTexture, p3, p4, uv3, uv4, ImGui::GetColorU32 (ImGuiCol_Text)); // draw current timestamp char buffer[64]; auto const now = std::time (nullptr); std::strftime (buffer, sizeof (buffer), "%H:%M:%S", std::localtime (&now)); ImGui::GetForegroundDrawList ()->AddText ( - ImVec2 (p3.x - 65.0f, style.FramePadding.y), 0xFFFFFFFF, buffer); + ImVec2 (p3.x - 65.0f, style.FramePadding.y), ImGui::GetColorU32 (ImGuiCol_Text), buffer); } } diff --git a/source/log.cpp b/source/log.cpp index 47dcc01..de8f17a 100644 --- a/source/log.cpp +++ b/source/log.cpp @@ -81,12 +81,12 @@ void drawLog () s_messages.erase (begin, end); } - static ImVec4 const s_colors[] = { - [DEBUG] = ImVec4 (1.0f, 1.0f, 0.4f, 1.0f), // yellow - [INFO] = ImVec4 (1.0f, 1.0f, 1.0f, 1.0f), // white - [ERROR] = ImVec4 (1.0f, 0.4f, 0.4f, 1.0f), // red - [COMMAND] = ImVec4 (0.4f, 1.0f, 0.4f, 1.0f), // green - [RESPONSE] = ImVec4 (0.4f, 1.0f, 1.0f, 1.0f), // cyan + ImVec4 const s_colors[] = { + [DEBUG] = ImVec4 (1.0f, 1.0f, 0.4f, 1.0f), // yellow + [INFO] = ImGui::GetStyleColorVec4 (ImGuiCol_Text), // normal + [ERROR] = ImVec4 (1.0f, 0.4f, 0.4f, 1.0f), // red + [COMMAND] = ImVec4 (0.4f, 1.0f, 0.4f, 1.0f), // green + [RESPONSE] = ImVec4 (0.4f, 1.0f, 1.0f, 1.0f), // cyan }; for (auto const &message : s_messages) diff --git a/source/switch/init.c b/source/switch/init.c index e681da7..5a02d34 100644 --- a/source/switch/init.c +++ b/source/switch/init.c @@ -56,6 +56,7 @@ void userAppInit () romfsInit (); plInitialize (); + psmInitialize (); nifmInitialize (NifmServiceType_User); if (R_FAILED (socketInitialize (&s_socketInitConfig))) @@ -78,6 +79,7 @@ void userAppExit () socketExit (); nifmExit (); + psmExit (); plExit (); romfsExit (); appletUnlockExit (); diff --git a/source/switch/platform.cpp b/source/switch/platform.cpp index 55e719a..9c08501 100644 --- a/source/switch/platform.cpp +++ b/source/switch/platform.cpp @@ -38,19 +38,42 @@ #include #include #include +#include #include namespace { +/// \brief Texture index +enum TextureIndex +{ + DEKO3D_LOGO = 1, + BATTERY_ICON, + CHARGING_ICON, + ETH_NONE_ICON, + ETH_ICON, + AIRPLANE_ICON, + WIFI_NONE_ICON, + WIFI1_ICON, + WIFI2_ICON, + WIFI3_ICON, + + MAX_TEXTURE, +}; + /// \brief deko3d logo width constexpr auto LOGO_WIDTH = 500; /// \brief deko3d logo height constexpr auto LOGO_HEIGHT = 493; +/// \brief icon width +constexpr auto ICON_WIDTH = 24; +/// \brief icon height +constexpr auto ICON_HEIGHT = 24; + /// \brief Maximum number of samplers constexpr auto MAX_SAMPLERS = 2; /// \brief Maximum number of images -constexpr auto MAX_IMAGES = 2; +constexpr auto MAX_IMAGES = MAX_TEXTURE; /// \brief Number of framebuffers constexpr auto FB_NUM = 2u; @@ -163,6 +186,7 @@ void rebuildSwapchain (unsigned const width_, unsigned const height_) s_swapchain = dk::SwapchainMaker{s_device, nwindowGetDefault (), swapchainImages}.create (); } +/// \brief Initialize deko3d void deko3dInit () { // create deko3d device @@ -217,97 +241,132 @@ void deko3dInit () cmdBuf.clear (); } -void loadLogo () +/// \brief Load textures +void loadTextures () { - // read the deko3d logo - auto const path = "romfs:/deko3d.rgba.zst"; - - struct stat st; - if (stat (path, &st) != 0) + struct TextureInfo { - std::fprintf (stderr, "stat(%s): %s\n", path, std::strerror (errno)); - std::abort (); - } + TextureInfo (char const *const path_, unsigned const width_, unsigned const height_) + : path (path_), width (width_), height (height_) + { + } - fs::File fp; - if (!fp.open (path)) - { - std::fprintf (stderr, "open(%s): %s\n", path, std::strerror (errno)); - std::abort (); - } + char const *const path; + unsigned width; + unsigned height; + }; - std::vector buffer (st.st_size); - if (!fp.readAll (buffer.data (), st.st_size)) - { - std::fprintf (stderr, "read(%s): %s\n", path, std::strerror (errno)); - std::abort (); - } + TextureInfo textureInfos[] = {TextureInfo ("romfs:/deko3d.rgba.zst", LOGO_WIDTH, LOGO_HEIGHT), + TextureInfo ("romfs:/battery_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/charging_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/eth_none_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/eth_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/airplane_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/wifi_none_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/wifi1_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/wifi2_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT), + TextureInfo ("romfs:/wifi3_icon.rgba.zst", ICON_WIDTH, ICON_HEIGHT)}; - fp.close (); - - auto const size = ZSTD_getFrameContentSize (buffer.data (), st.st_size); - if (ZSTD_isError (size)) - { - std::fprintf (stderr, "ZSTD_getFrameContentSize: %s\n", ZSTD_getErrorName (size)); - std::abort (); - } - - // create memblock for transfer + // create memblock for transfer (large enough for the largest source file) dk::UniqueMemBlock memBlock = - dk::MemBlockMaker{s_device, imgui::deko3d::align (size, DK_MEMBLOCK_ALIGNMENT)} + dk::MemBlockMaker{s_device, imgui::deko3d::align (1048576, DK_MEMBLOCK_ALIGNMENT)} .setFlags (DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached) .create (); - // decompress into transfer memblock - auto const decoded = ZSTD_decompress (memBlock.getCpuAddr (), size, buffer.data (), st.st_size); - if (ZSTD_isError (decoded)) - { - std::fprintf (stderr, "ZSTD_decompress: %s\n", ZSTD_getErrorName (decoded)); - std::abort (); - } - - // initialize deko3d logo texture image layout - dk::ImageLayout layout; - dk::ImageLayoutMaker{s_device} - .setFlags (0) - .setFormat (DkImageFormat_RGBA8_Unorm) - .setDimensions (LOGO_WIDTH, LOGO_HEIGHT) - .initialize (layout); - - auto const logoAlign = layout.getAlignment (); - auto const logoSize = layout.getSize (); - - // create image memblock + // create image memblock (large enough to hold all the images) s_imageMemBlock = dk::MemBlockMaker{s_device, - imgui::deko3d::align (logoSize, std::max (logoAlign, DK_MEMBLOCK_ALIGNMENT))} + imgui::deko3d::align (1048576 + (MAX_TEXTURE - 2) * 4096, DK_MEMBLOCK_ALIGNMENT)} .setFlags (DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image) .create (); + auto &cmdBuf = s_cmdBuf[0]; + + unsigned imageIndex = 1; + unsigned imageOffset = 0; + for (auto const &textureInfo : textureInfos) + { + struct stat st; + if (::stat (textureInfo.path, &st) != 0) + { + std::fprintf (stderr, "stat(%s): %s\n", textureInfo.path, std::strerror (errno)); + std::abort (); + } + + fs::File fp; + if (!fp.open (textureInfo.path)) + { + std::fprintf (stderr, "open(%s): %s\n", textureInfo.path, std::strerror (errno)); + std::abort (); + } + + // read file into memory + std::vector buffer (st.st_size); + if (!fp.readAll (buffer.data (), buffer.size ())) + { + std::fprintf (stderr, "read(%s): %s\n", textureInfo.path, std::strerror (errno)); + std::abort (); + } + + // get uncompressed size + auto const size = ZSTD_getFrameContentSize (buffer.data (), buffer.size ()); + if (ZSTD_isError (size)) + { + std::fprintf (stderr, "ZSTD_getFrameContentSize: %s\n", ZSTD_getErrorName (size)); + std::abort (); + } + assert (size <= memBlock.getSize ()); + + // wait for previous transfer to complete + s_queue.waitIdle (); + + // decompress into transfer memblock + auto const decoded = + ZSTD_decompress (memBlock.getCpuAddr (), size, buffer.data (), buffer.size ()); + if (ZSTD_isError (decoded)) + { + std::fprintf (stderr, "ZSTD_decompress: %s\n", ZSTD_getErrorName (decoded)); + std::abort (); + } + + // initialize texture image layout + dk::ImageLayout layout; + dk::ImageLayoutMaker{s_device} + .setFlags (0) + .setFormat (DkImageFormat_RGBA8_Unorm) + .setDimensions (textureInfo.width, textureInfo.height) + .initialize (layout); + + // calculate image offset + imageOffset = imgui::deko3d::align (imageOffset, layout.getAlignment ()); + assert (imageOffset < s_imageMemBlock.getSize ()); + assert (s_imageMemBlock.getSize () - imageOffset >= layout.getSize ()); + + // initialize image descriptor + dk::Image image; + image.initialize (layout, s_imageMemBlock, imageOffset); + s_imageDescriptors[imageIndex++].initialize (image); + + // copy texture to image + dk::ImageView imageView (image); + cmdBuf.copyBufferToImage ({memBlock.getGpuAddr ()}, + imageView, + {0, 0, 0, textureInfo.width, textureInfo.height, 1}); + s_queue.submitCommands (cmdBuf.finishList ()); + + imageOffset += imgui::deko3d::align (layout.getSize (), layout.getAlignment ()); + } + // initialize sampler descriptor s_samplerDescriptors[1].initialize ( dk::Sampler{} .setFilter (DkFilter_Linear, DkFilter_Linear) .setWrapMode (DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge, DkWrapMode_ClampToEdge)); - // initialize deko3d logo texture image descriptor - dk::Image logoTexture; - logoTexture.initialize (layout, s_imageMemBlock, 0); - s_imageDescriptors[1].initialize (logoTexture); - - auto &cmdBuf = s_cmdBuf[0]; - - // copy deko3d logo texture to image view - dk::ImageView imageView{logoTexture}; - cmdBuf.copyBufferToImage ( - {memBlock.getGpuAddr ()}, imageView, {0, 0, 0, LOGO_WIDTH, LOGO_HEIGHT, 1}); - - // submit commands to transfer deko3d logo texture - s_queue.submitCommands (cmdBuf.finishList ()); - // wait for commands to complete before releasing memblocks s_queue.waitIdle (); } +/// \brief Deinitialize deko3d void deko3dExit () { // clean up all of the deko3d objects @@ -326,6 +385,112 @@ void deko3dExit () s_depthMemBlock = nullptr; s_device = nullptr; } + +/// \brief Draw time status +void drawTimeStatus () +{ + auto const &io = ImGui::GetIO (); + auto const &style = ImGui::GetStyle (); + + // draw current timestamp + char buffer[64]; + auto const now = std::time (nullptr); + std::strftime (buffer, sizeof (buffer), "%H:%M:%S", std::localtime (&now)); + ImGui::GetForegroundDrawList ()->AddText ( + ImVec2 (io.DisplaySize.x - 240.0f, style.FramePadding.y), + ImGui::GetColorU32 (ImGuiCol_Text), + buffer); +} + +/// \brief Draw network status +void drawNetworkStatus () +{ + TextureIndex netIcon = AIRPLANE_ICON; + + NifmInternetConnectionType type; + std::uint32_t wifiStrength; + NifmInternetConnectionStatus status; + if (R_SUCCEEDED (nifmGetInternetConnectionStatus (&type, &wifiStrength, &status))) + { + if (type == NifmInternetConnectionType_Ethernet) + { + if (status == NifmInternetConnectionStatus_Connected) + netIcon = ETH_ICON; + else + netIcon = ETH_NONE_ICON; + } + else + { + if (wifiStrength >= 3) + netIcon = WIFI3_ICON; + else if (wifiStrength == 2) + netIcon = WIFI3_ICON; + else if (wifiStrength == 1) + netIcon = WIFI3_ICON; + else if (wifiStrength == 0) + netIcon = WIFI_NONE_ICON; + } + } + + auto const &io = ImGui::GetIO (); + auto const &style = ImGui::GetStyle (); + + auto const x1 = io.DisplaySize.x - ICON_WIDTH - 100.0f; + auto const x2 = x1 + ICON_WIDTH; + auto const y1 = style.FramePadding.y; + auto const y2 = y1 + ICON_HEIGHT; + + ImGui::GetForegroundDrawList ()->AddImage ( + imgui::deko3d::makeTextureID (dkMakeTextureHandle (netIcon, 1)), + ImVec2 (x1, y1), + ImVec2 (x2, y2), + ImVec2 (0, 0), + ImVec2 (1, 1), + ImGui::GetColorU32 (ImGuiCol_Text)); +} + +/// \brief Draw power status +void drawPowerStatus () +{ + std::uint32_t batteryCharge = 0; + psmGetBatteryChargePercentage (&batteryCharge); + + ChargerType charger = ChargerType_None; + psmGetChargerType (&charger); + + TextureIndex powerIcon = BATTERY_ICON; + if (charger != ChargerType_None) + powerIcon = CHARGING_ICON; + + auto const &io = ImGui::GetIO (); + auto const &style = ImGui::GetStyle (); + + auto const x1 = io.DisplaySize.x - ICON_WIDTH - style.FramePadding.x; + auto const x2 = x1 + ICON_WIDTH; + auto const y1 = style.FramePadding.y; + auto const y2 = y1 + ICON_HEIGHT; + + ImGui::GetForegroundDrawList ()->AddImage ( + imgui::deko3d::makeTextureID (dkMakeTextureHandle (powerIcon, 1)), + ImVec2 (x1, y1), + ImVec2 (x2, y2), + ImVec2 (0, 0), + ImVec2 (1, 1), + ImGui::GetColorU32 (ImGuiCol_Text)); + + char buffer[16]; + std::sprintf (buffer, "%u%%", batteryCharge); + ImGui::GetForegroundDrawList ()->AddText ( + ImVec2 (x1 - 60.0f, y1), ImGui::GetColorU32 (ImGuiCol_Text), buffer); +} + +/// \brief Draw status +void drawStatus () +{ + drawTimeStatus (); + drawNetworkStatus (); + drawPowerStatus (); +} } bool platform::init () @@ -338,7 +503,7 @@ bool platform::init () return false; deko3dInit (); - loadLogo (); + loadTextures (); imgui::deko3d::init (s_device, s_queue, s_cmdBuf[0], @@ -390,6 +555,8 @@ bool platform::loop () ImVec2 (x1, y1), ImVec2 (x2, y2)); + drawStatus (); + return true; } diff --git a/switch/gfx/airplane_icon.png b/switch/gfx/airplane_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d26f9cd30f92911d5f1a3cbb7840dae848df394e GIT binary patch literal 4951 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 literal 0 HcmV?d00001 diff --git a/switch/gfx/battery_icon.png b/switch/gfx/battery_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..07efbec09e55076e2b0902e6901d74c33730a9fb GIT binary patch literal 3973 zcmeHKX;f236uvJDSqNnbC?Xm_lz_@fLNFMmh(I9Fu%lp)g&<+6Ac#@Gg`l9+94d7| zty%yAnfboC@4aL; z`+B?UFin^Mpd_B-nyuX@r=qsm5)XeF!0KrLX&C@L zs4J}*U?mTrB@95k8(@6GzSDklQGgWgC1ds*U-j%aB)p@spYJ2=lt{& zu{lkVcC>U4Rgw3Cd*z1rWhvdhXCvd9T6*tgEm%~aw;s}}ems&R(}isZuGJLzR9??H z#M@}yr{~R+h%yZ>SzJV-gB8KjNU78Y=xAMJ7zHro5u1RG@JSdM$f$1_G82qCfKeiU z;%k2`^0^2@j3sb#wiAi%>_>1Th7EBe{2^{k&_Xk7jqr^|aKlK08uf^=uYGrdEGmQC z@H+e<=y^({60{IO6J78IZ~?lO7EOyvr_pE(2A#<=)M06BvnCj@bqyz(a3)VOF*P-_ zw6!v`m@&oFlq<5H!Q%^sLXOpJM+d=NTRWkEm;_@m7%Xj;v5tVJk`EZ4D<*KCQl-21O8#iTsv{}7v`;MHQyLNxNr=ak_ z!9$0OjvW1}?0ET!lc&Bu{msS7s!NxzRDXNxc1`UMck1reH~#df>F3A4G(Tz4yl89h z=zQ7L-9zXibPkn4WpC-yN4iK93YkJ9bYY|*R zxn#FfW8oP&4)gBo_V>wfxEwtI%lzNKLPv00TG!7`zT>ZXu(7yY=CAD$rS5oE8|Rnf znIWmZcXh=s$5&kPnF2KzMq%VDPSLI1Rwp=}M%g&Hd4=*8-p1j0Qs4Fl-CpKp910GW z?B-(J0yT`ZMLnv{sW&M43FU>w>5%iE|)srFrECCROlOt0FR!n!bZ<1(XTOIC(g zgd`-*Vacz@dp>NNM>X_&!?Z(v`mZ^?@QKIa>2#}pwfbD>_euMi#6)b#Uawovgm?3H zHWp7PE?gB>6_haRbe5bJzX*EYQFo`mMLoMKy46^9L*#>I~-m5EBsZ>b)P@!8KW?^ z3uE`f*b_1KUKo2j{^M^1eitOUy?fDL)~QOjU}c0{sSqzoRG&wdu*g2D!G th8M;}3`EDLOtnPO3B%EV_()}vJW&B7&Uex*S9E>`kh*!h7S9jc@H?N6J)Hmm literal 0 HcmV?d00001 diff --git a/switch/gfx/charging_icon.png b/switch/gfx/charging_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..69542dd0d06121848039d7a0a49523e2dd342589 GIT binary patch literal 4394 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 NWxC!ZYvtxc{{;)ysE7an literal 0 HcmV?d00001 diff --git a/switch/gfx/eth_icon.png b/switch/gfx/eth_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9a95c2465c8bcb4f6a6801139087672f412a474a GIT binary patch literal 520 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^+ z;1LNlQD8O*GnRDD@dgUAmw5WRvOi(r6c#qByD07o6#DJy;uvCa`syUzAQnS`*8MpT zB|Bppmvrr9oan(aPbPK8L5~!hG}XMryw4aq41WY{owTYw;UvFwLf4@WA-_uZzk9Ou z;s3)^8g=?i1tffZa?bCtcZ-R9H%0L3bFcNw&o$e9t*?I`4-eY*!xZ%aU({B}k!Jt~= z8c~vxSdwa$T$Bo=7>o>z40H`lbPWwc3=FIcEv-y}TvICpgKz6Rg-|r)=BH$)RpQpr T{`uoNpaup{S3j3^P6NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VX;-`;;_Kaj^+ z;1LNlQD8O*GnRDD@dgUAmw5WRvOi(r6c#qByD09;z`&^M>Eak-ar*2eMZY5f0&V;A z%p5lfx$850VN&7c>O5*XCxMkIQf3lM^X=sx9Bc;}tC*`8T%4VnY9vG+I=|VrTe?is z!tLzrJ^5;%|1$FN4ud!ZwX3DKw3)l>9xtY9f*t6Q~ zT5jQ*eY=wrU!7QBrrnUeduPSN<>zzb-(Ovv?~rMh(fFh%*=%Ln3E2tt@s=yux%;EP zWmIfoh-)~}C877R*YdlM59glJr)|Z2H2Uq%d0uRuQOD@+Zs=tAyKkA47N7fI{ufGQKL_Y+ErD}<5 zL`h0wNvc(HQ7VvPFfuSQ&^0j8H8cn@Ft9STv@!*9O|1+JzOC~VLeY?$pOTqYiCaVa T=a1`v8W=oX{an^LB{Ts5kS)%4 literal 0 HcmV?d00001 diff --git a/switch/gfx/wifi1_icon.png b/switch/gfx/wifi1_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..31e1df2d0566e84f06b16f9699f16f56b95a71d2 GIT binary patch literal 3621 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 E58w7a9RL6T literal 0 HcmV?d00001 diff --git a/switch/gfx/wifi2_icon.png b/switch/gfx/wifi2_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c4df395eba57dae0b258c5a5ad5e0ea9b5a2adc7 GIT binary patch literal 4879 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 literal 0 HcmV?d00001 diff --git a/switch/gfx/wifi3_icon.png b/switch/gfx/wifi3_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..57fee15d8fe5a8abc3cbb8d1c797d78a44b44b1b GIT binary patch literal 6287 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 literal 0 HcmV?d00001