<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>Furkan Mudanyalı&#39;s Personal Webpage</title>
<link>https://furkanmudanyali.com/</link>
<description>Furkan Mudanyalı&#39;s Personal Webpage</description>

<item>
<title>The Thinkpad X220 from the Dumpster</title>
<link>https://furkanmudanyali.com/blog/2026-01-07-x220/</link>
<guid>https://furkanmudanyali.com/blog/2026-01-07-x220/</guid>
<description><![CDATA[<div id="content"><h2>The Thinkpad X220 from the Dumpster</h2><p>
  Computer engineer finds a working Thinkpad X220 from the dumpster, immediately bricks it. Mechatronics engineer comes to the rescue, and shows why computer engineering
  isn't real engineering. Then proceeds to fail on the simple task of installing an OS.
</p>
<table>
  <caption>mfw</caption>
  <tr>
    <td>
      <img src="https://furkanmudanyali.com/blog/2026-01-07-x220/mfw.png" width="100%"/>
    </td>
  </tr>
</table>
<p>
  Back in fall of 2025, I went to Istanbul, and have done my <a href="/blog/2023-12-29-mitsumi">yearly dumpster dive</a> of Kadıköy Salı Pazarı.
  I haven't found much stuff there compared to before, due to the increasing popularity of the place. I also even experienced slight fear for my life
  because I asked some shady seller about laptops, and he thought I was laughing or something, told me why I was laughing. There was apparently something I was laughing about and he was curious.
  I told him I wasn't laughing and quickly left the place. I swear I felt like I was going to get shivved, but fortunately,
  I'm safe. Thank Allah. Even then, I managed to find some interesting stuff. A PS Move controller that doesn't hold a charge for $1.25, a copy of Crysis 2 for PC
  for the same price, an iPod touch 3rd gen in very good condition for $25, and a Thinkpad X220 for a whopping $6. If that isn't daylight robbery, then I don't
  know what is.
</p>
<img src="https://furkanmudanyali.com/blog/2026-01-07-x220/kadikoypazar.webp"/>
<p>
  Now I don't really care about the rest, but the Thinkpad really interested me. It was in terrible condition, had breaks all over its chassis, and was missing a battery.
  But fortunately it had some RAM, an hdd, and the screen was intact with no cracks. I had no way of testing it in Istanbul, and I also didn't have a charger. So when I came
  back to Bursa, I used the charging adapter from my other Thinkpad z60m which worked fine. Also apparently, this laptop throttles with a 65W charger if no battery is present, but mine
  was 90W. To no one's surprise, the laptop didn't turn on. I said its gotta be the RAM, I took it off, and reinserted it. This time it made 5 beeps. Then I took it out again,
  but this time I wiped the pins with my tshirt before plugging it back in, and then it came to life! There was a Windows 10 installation on it, with modded minecraft.
</p>
<img src="https://furkanmudanyali.com/blog/2026-01-07-x220/x220-alive.webp"/>
<p>
  I didn't do much with the Windows installation in it. I replaced the HDD with a spare 120G SSD I had, then I thoroughly cleaned it. I have decided to install OpenSUSE Tumbleweed. My dad
  used it for a week and there were no problems, the screen was fine, the speakers weren't blown, the ports worked fine etc. I have also noticed the included RAM was 4GB, so I wanted to
  upgrade it. From what I learned online, Lenovo said this laptop only supported up to 8GB RAM, but people reported that 16GB worked fine. So I bought 2x8GB DDR3 sticks and a battery. When they
  arrived, I installed them, and noticed it only showed 8GB of RAM available. This is where things start to go off the rail.
</p>
<p>
  From what I've read, prior to BIOS version 1.28, there was no throttling on RAM, but it would throttle down to 1333MHz in versions 1.29 and above. Mine was in version 1.20, and for whatever
  reason I thought this could be why the laptop didn't see the other 8GB RAM. So I found a patched latest BIOS that removes this limitation, and went off to flash it. But the problem is, the flashing
  program has to run on a Windows PE in order to work, and apparently Windows 10 didn't work quite right with it either. I didn't want to install Windows 8.1 on it just to flash a BIOS, so I found a
  live Windows PE recovery USB based on 8.1, and started flashing from there. During flashing, the screen went black, never to turn on again. I bricked the survivor within a week of obtaining it, hooray!
</p>
<p>
  So, the only way to recover my mess was to flash the BIOS chip directly using a programmer. <a href="https://sateallia.org" target="_blank">An embedded software developer friend of mine</a> suggested that I go get a CH341 programmer with a SOIC 8-pin clip,
  and so I have done. I also didn't want to simply flash the old BIOS, I wanted more... While it was shipping, I decided to flash coreboot on it. At that moment I was also running OpenSUSE Tumbleweed
  on my main PC, and the goddamn thing just wouldn't compile on it due to whatever reason. So I compiled it under a Void Linux VM. I used <a href="https://garcia.casa/posts/installing-coreboot-on-a-thinkpad-x220/" target="_blank">this guide</a>
  to do the configuration, and in general learn how I'm supposed to flash it. I also lobotomized the Intel Management Engine in the process!
</p>
<p>
  When the programmer arrived, I quickly disassembled the thinkpad and tried to attach the SOIC clip, and the keyword here is try. No matter what I did I simply couldn't get it
  to fit properly on the chip, and flashrom just didn't recognize it. To make sure that my computer wasn't the problem, I first tried to flash from my Debian 12 server, which didn't work.
  As a last resort, I took out the work PC running Windows 10 LTSC from my server cabinet,
  which I just used for SQL Server, then tried flashing it from there. Nope. Whatever flasher I tried didn't recognize it, I couldn't manage to fit the clip on the BIOS chip properly.
  I know im not the best when it comes to fine craftsmanship, but man am I that terrible? After hours of fiddling around with the connector I unsurprisingly broke the entire clip and just threw it
  in the trash. My ever generous friend called me a donkey and told me to just ship it to him, what would I do without him... 
</p>
<table>
  <caption>Wall of Shame</caption>
  <tr>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/shame1.jpeg"/></td>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/shame2.jpeg"/></td>
  </tr>
    <tr>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/shame3.jpeg"/></td>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/shame4.jpeg"/></td>
  </tr>
</table>
<p>
  I just packed the thinkpad in a box and shipped it off to him, and he received it several days later. As I pack things a lot better than him, he received it in <a href="https://furkanmudanyali.com/blog/2026-01-07-x220/onepiece.webp" target="_blank">one piece.</a>
  The last time he shipped me various PS1/PS2 accessories, there wasn't any single thing that wasn't rattling. During this time, he also bought his own CH341 programmer and SOIC clip.
  He just disassembled the laptop, put the clip in, and flashed the BIOS in mere minutes. That's it, it was that simple. He wouldn't shut up making fun of me but unbeknownst to him,
  this was also where his troubles began.
</p>
<img src="https://furkanmudanyali.com/blog/2026-01-07-x220/clipinplace.jpg"/>
<p>
  When it came down to installing an OS, I have decided on GhostBSD with Gershwin Desktop, as I was curious about how it performed. So I told my friend to install it. He downloaded the
  image and put it inside a USB stick, and it wouldn't work. The BIOS didn't recognize it as bootable media. He tried several times to no avail, it was a shit USB stick. So he tried another
  USB stick he got from a convention and was pretty attached to, but that also turned out to be a dud. He even tried an SD card with a USB adapter, nope. He also tried 2 different SATA USB adapters, no luck.
  He searched his entire house for anything,
  be it an HDD enclosure or another thumb drive, something that he can put an OS installation on. After like 5-6 hours, he finally found something, an M.2 enclosure. He used that from a Type-C hub
  and even then, because of the iffy cable he used, it gave him all sorts of headaches, but at least he somehow managed to install the damn thing. The first thing he did tomorrow was to buy a legit USB drive.
</p>
<table>
  <caption>finally</caption>
  <tr>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/poorcable.jpeg"/></td>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/ghostbsd.jpg"/></td>
  </tr>
</table>
<p>
  After all this, the laptop was finally back alive. I asked him how much RAM the OS reported, and as if the cruel fate was laughing its ass off, it reported 8GB RAM. He replaced one of the
  sticks with a 4GB one he had and wouldn't you know it, it said 12GB. All of this trouble was because of me misinterpreting a broken RAM. To be fair, my thought process was that I bought the
  RAM new and so it must've been working fine, and I would still try to flash coreboot later on and probably mess it up. At least that's the silver lining. As he was the one who resurrected this laptop,
  I asked him to give it a name. He immediately decided on Rikka. Rikka is now my first device that does not have a name from Girls' Frontline, Kantai Collection or Azur Lane.
</p>
<p>
  Now any other idiot that got up to here would leave it there, but not I. The SeaBIOS payload in the coreboot only supported legacy BIOS, and no UEFI. I want my UEFI. Actually I was
  gonna leave it there but my friend insisted on it and I figured why not. We also decided it should have a custom splash anyways, and I found another guide that
  <a href="https://kiljan.org/2024/04/07/replace-a-lenovo-thinkpad-x220s-bios-with-an-open-source-uefi-capable-firmware/" target="_blank">covered this exact thing.</a> We went through several
  iterations of a splash image featuring Rikka and inside jokes but ultimately I decided on a simpler one.
  I compiled the new BIOS with the splash and he flashed it from inside the OS this time, and it worked fine. But he had to reinstall the OS because the new BIOS only supported UEFI this time,
  more fun to him!
</p>
<table>
  <caption>Boot Splash Iterations</caption>
  <tr>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/splash1.jpeg"/></td>
    <td><img src="https://furkanmudanyali.com/blog/2026-01-07-x220/splash2.png"/></td>
  </tr>
</table>
<p>
  When he went to Aras Kargo to ship the laptop back to my company, the worker there, for some reason, refused to ship it to the company, and insisted on a company number or something. While he
  was waiting there for an hour, I had to call our own local Aras Kargo, and ask for our company number. The same number that shows up in their systems when they search for the company, unbelievable.
  The things he bear for me... After several days, I have finally got back my thinkpad, better than ever. Now it was time to play with it...
</p>
<img src="https://furkanmudanyali.com/blog/2026-01-07-x220/insurance.png"/>
<p>
  The first thing I have noticed was that I didn't quite like Gershwin Desktop in the state it was in. Perhaps it will get better in time, but I decided to give Elementary OS a try. The whole install process
  was smooth, took me 5 minutes to install it, unlike certain someone. I loved the UI, it was brilliant, but I didn't like the underlying Ubuntu system. When I tried to install firefox with apt, it instead
  decided to install it from snap, which pissed me off. So I moved on to CachyOS, since I am most familiar with Arch on desktops.
</p>
<img src="https://furkanmudanyali.com/blog/2026-01-07-x220/elementary.jpg"/>
<p>
  Again, CachyOS was also pretty easy to install, and I decided to use KDE. It also felt snappier. I installed steam to give proton a try in this old HD3000, obviously Vulkan and therefore
  DXVK is a no-go, but I can still run basic games on it just fine. I also finished <a href="https://store.steampowered.com/app/736570/The_Crooked_Man/" target="_blank">The Crooked Man</a> on it.
  So far, its a pretty nifty machine, and it is capable of 1080p YouTube playback without dropping frames, and that's probably the most intense thing I will ever do on it, besides Runescape, Runescape
  also ran better on CachyOS. I also bought another 8GB RAM and that one worked fine. Finally I had 16GB RAM.
</p>
<img src="https://furkanmudanyali.com/blog/2026-01-07-x220/crookedman.jpeg"/>
<p>
  Don't try this at home.
</p></div>]]></description>
<pubDate>Wed, 07 Jan 2026 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Headscale is Just Better</title>
<link>https://furkanmudanyali.com/blog/2026-01-01-headscale/</link>
<guid>https://furkanmudanyali.com/blog/2026-01-01-headscale/</guid>
<description><![CDATA[<div id="content"><h2>Headscale is Just Better</h2><p>
    For the longest time, I have been using Wireguard to connect all my devices together, with wg-quick it works sure,
    but its kind of a pain to register new devices to the network. First I have to generate a private and public key pair,
    then add that public key to my server's configuration and set its ip, then restart the interface, then assign the private
    key, ip address and dns to the client, and the server's public key as the peer with allowed ips set.
    If I want to tunnel the connection of the client through the server, I need to update the allowed ips parameter from 10.0.0.0/24
    to 0.0.0.0/0 and restart. It's actually a hassle to do all that.
</p>
<p>
    My friend was shilling me Tailscale for a while and I was telling him how I am too invested in Wireguard, how it will take me a long
    time to set it up all over again, and all the excuses I could find. But after a moment, I found some free time and finally caved in
    to his continuous praises. Setting it up wasn't too much of a problem, I just followed the guide which told me to download the .deb
    file and install it, which I did. The default configuration was pretty sane too, I only updated the server_url and base_domain. The extra
    thing I've done was to put it behind nginx reverse proxy, with following config:
</p>
<h3>/etc/nginx/sites-available/headscale</h3>
<code>
map $http_upgrade $connection_upgrade {
    default      upgrade;
    ''           close;
}

server {
    server_name SERVERNAME;

    listen 80;
    listen [::]:80;
    listen 443      ssl;
    listen [::]:443 ssl;
    http2 on;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $server_name;
        proxy_redirect http:// https://;
        proxy_buffering off;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;
    }

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}
</code>
<p>
    And that was that, I now was running a Headscale server, and all I had to do was just register my clients by installing the appropriate Tailscale
    software and connect to my server with: <code>tailscale up --login-server https://SERVER_URL</code>
    which will give me a link to visit on my browser, which will tell me a command to run on my server running Headscale, all pretty trivial things.
</p>
<p>
    The one major advantage that I have immediately noticed was that Tailscale uses DERP servers to connect clients directly to each other, so I don't
    have to use the server as a proxy to talk to other clients. This is a huge thing. You can set it up to use your own DERP server, but I wasn't too
    bothered to use public ones.
</p>
<code>
[furkan@klukai ~]$ tailscale ping sqlslave
pong from sqlslave (100.64.0.6) via DERP(fra) in 114ms
pong from sqlslave (100.64.0.6) via DERP(fra) in 111ms
pong from sqlslave (100.64.0.6) via DERP(fra) in 113ms
pong from sqlslave (100.64.0.6) via X.X.X.X:26299 in 22ms
</code>
<p>
    Now, you can use every client as an exit node, but it involves a bit more steps. First, you need to get the client to advertise itself as an exit node:
    <code>tailscale set --advertise-exit-node</code>
    Then, in your server, you need to allow this client to be used as an exit node. For this, you list the available nodes on your server:
    <code>headscale nodes list-routes</code>
    Find the ID of the client advertising itself as exit, then approve it:
    <code>headscale nodes approve-routes --identifier ID --routes 0.0.0.0/0</code>
    Thats it, now you can use this client as an exit node on any other client. How convenient is that?
</p>
<p>
    I had many clients to connect, and surprisingly Tailscale did not mess with the ongoing Wireguard setup that I had, even though it also uses Wireguard
    under the hood. So I just connected to my clients through Wireguard, set up Tailscale, ensured connectivity, then disabled Wireguard. It was a breeze.
    I'm not saying that Tailscale won't collide with Wireguard, but in my case it didn't, which I'm grateful for.
</p>
<p>
    I also have 2 test servers for my work, that I want some other people to be able to connect to, and only limited to those 2 machines. While searching around,
    I have found out that Tailscale supports ACL, now I don't know much about this but there was an example config in the documentation that fit my exact needs.
    I just had to set the policy file location in Headscale config, and put the configuration in that path.
</p>
<h3>/etc/headscale/acl.hujson</h3>
<code>
{
  "groups": {
    "group:dev": [
      "furkan@",
      "ekp@"
    ]
  },
  "acls": [
    {
      "action": "accept",
      "src": [
        "autogroup:member"
      ],
      "dst": [
        "autogroup:self:*",
        "autogroup:internet:*"
      ]
    },
    {
      "action": "accept",
      "src": [
        "group:dev"
      ],
      "dst": [
        "tag:dev:*"
      ]
    }
  ],
  "tagOwners": {
    "tag:dev": [
      "group:dev"
    ]
  }
}
</code>
<p>
    With this config, all I had to do was to add the tag "dev" to the 2 machines that I wanted to be accessible by the members of this group ekp and furkan:
</p>
<code>
tailscale up --advertise-tags=tag:dev --login-server https://SERVER_URL
</code>
<p>
    As far as I'm aware, you cannot advertise tags after having the connection up, so if you have the connection already, you have to take it down and back
    up again. 
</p>
<p>
    In one of my older blogs, I talked about <a href="https://furkanmudanyali.com/blog/2024-01-17-portfw/">port forwarding with Wireguard and UFW</a>, for a
    minecraft server. This time, I wanted to do something similar, but wanted to try something different. I thought about using an Nginx reverse proxy, and
    have found out that Nginx supports streams, all I had to do was install the extension libnginx-mod-stream, or at least that's what its called under Debian.
    The stream directive is different than the http, and you need to reference it the same way as http in under /etc/nginx/nginx.conf.
</p>
<h3>/etc/nginx/nginx.conf</h3>
<code>
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
}

http {
    ......
}

stream {
	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/streams-enabled/*;
}
</code>
<p>
    Then I just set up a 25565 TCP and 19132 UDP proxy pass under /etc/nginx/streams-available, which I symlinked to /etc/nginx/streams-enabled:
</p>
<h3>/etc/nginx/streams-enabled/minecraft</h3>
<code>
server {
        listen 25565;
        proxy_pass oasis:25565;
        proxy_timeout 1h;
}
server {
        listen 19132 udp;
        proxy_pass oasis:19132;
        proxy_timeout 1h;
}
</code>
<p>
    The reason for both Java and Bedrock edition addresses is because I am running my server with GeyserMC, which allows Bedrock clients to connect
    to the Java server. It's a really handy thing.
</p></div>]]></description>
<pubDate>Thu, 01 Jan 2026 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>No More Meta, No More Social Media</title>
<link>https://furkanmudanyali.com/blog/2025-12-31-socialmedia/</link>
<guid>https://furkanmudanyali.com/blog/2025-12-31-socialmedia/</guid>
<description><![CDATA[<div id="content"><h2>No More Meta, No More Social Media</h2><p>
    For a good while, I have been pretty unsatisfied with the current status of social media. Instagram is full of
    mindless content and advertising, Twitter has more bots than actual users, Facebook has been a piece of shit since
    time immemorial... I have been keeping an account on these platforms just for the sake of it, thinking maybe it'll
    do some good for friends, relatives and acquaintances, if they ever decide to contact me and whatnot.
</p>
<p>
    The whole concept of social media has derailed anyways, you see people posting their private moments and highlights for the
    public to see, "influencers" trying to farm engagement by doing whatever stupid shit they can think of so that they could make
    some easy bucks, countless of scam businesses somehow making up into my feed with their bullshit products, huge echo chambers
    powered by bots repeating nonsensical stuff to push some sort of agenda, its a circus.
</p>
<p>
    In a quiet afternoon, while checking my WhatsApp messages, I noticed this thing called Meta AI and it somehow
    ended up in all the groups that I was a member of. It had a huge disclaimer with it saying how its secure and how it cannot
    read messages and yada yada which tripped all my breakers, why is there a stupid chat bot in my MESSAGING app???? What is the
    purpose of forcing a stupid LLM between me and the people I am talking to? With this being the final straw, I just deleted my account
    and left the platform for good. Then I moved on to other Meta products, deleting them one by one. I had quit Twitter before this ordeal
    already. I do actually use chat bots, don't get me wrong. I just don't want them being forcefully shoved down my throat.
</p>
<p>
    Now the only messaging platform I use is Signal, and otherwise people can still contact me by my phone, or email. I do not plan on
    continuing to bear with the bullshit of these platforms just so that someone may have just a tiny little bit of more convenience when they try
    to contact me, and if this enough of a reason for them to stop talking with me, then whose loss is it?
</p>
<p>
    While in the process of cleansing myself from these platforms, I had a thought. Why should I stop there? I was using my Apple account as my
    mail provider, and Apple wasn't exactly troubled with the amount of spam I get in my inbox, their spam filter is laughable if it even exists.
    Some time ago, half accidentally and half as a joke, I sent a mail to my friend who was using an icloud.com email, as @THREELETTERAGENCY.gov as the sender.
    iCloud, upon receiving this stupid mail that lacks DKIM, DMARC, SPF record, advertising itself as THREELETTERAGENCY.gov, said yo this looks legit,
    and put it under his inbox. I have been using my icloud account since 2012, and it has been in countless breaches due to me being a young idiot and
    using my mail in every sketchy website, from promises of free hosting to dubious fanmade pokemon games. So with no filtering on sight, I am getting like
    15 spam mails daily. While some do get a chuckle out of me by offering a free electric toothbrush, it gets tiring very quickly.
</p>
<p>
    So, I decided to retire this cursed iCloud account, and actually pay for a reputable mail provider. I am not gonna advertise the provider that I use for the moment
    as I am benchmarking their service, but I effectively migrated to it, using different mail relays for every service that require my email address, to make
    sure that my original address does not get involved in any sort of breach this time.
</p>
<p>
    Also a funny thing, I recently discovered that some crawler was hogging my bandwidth for a good while, and what do I see? A Meta crawler abusing my gitea instance.
    ZUCKKKKKKKK.
</p>
<img src="https://furkanmudanyali.com/blog/2025-12-31-socialmedia/metacrawler.png"></div>]]></description>
<pubDate>Wed, 31 Dec 2025 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>No More Defaults: Auto-Selecting Japanese Subtitles &amp; Stereo, with help of AI</title>
<link>https://furkanmudanyali.com/blog/2025-08-09-mpvlua/</link>
<guid>https://furkanmudanyali.com/blog/2025-08-09-mpvlua/</guid>
<description><![CDATA[<div id="content"><h2>No More Defaults: Auto-Selecting Japanese Subtitles & Stereo, with help of AI</h2><p>
    One of the ways I study Japanese is <a href="https://learnjapanese.moe/guide/#15-what-even-is-immersion" target="_blank">the immersion method</a>.
    What it basically is that you consume a content in target language without any aid, and try
    understanding it. It may sound ridiculous but thats literally how I, along with many people I know,
    learned English in the first place! The easiest way to do it is watching anime with Japanese subtitles,
    or no subtitles at all. The problem is though, as far as I know, MPV does not provide a way to do this
    at the time of writing. I can only nudge it to give me Japanese subtitles if they're available, else
    it just defaults to whatever subtitle there is. While trying to troubleshoot this with ChatGPT, it offered
    me to write a plugin for MPV to handle this, I figured why not. It gave me a short script and told me to
    put it in my mpv config, so I did. And to my surprise, it actually worked!
</p>
<h3>~/.config/mpv/scripts/subtitle.lua</h3>
<code>
local function choose_audio_and_subs()
    local tracks = mp.get_property_native("track-list") or {}

    local sid = nil
    for _, t in ipairs(tracks) do
        if t.type == "sub" and t.lang == "ja" then
            sid = t.id
            break
        end
    end
    if sid then
        mp.msg.info("select-tracks: selecting JA subtitle #"..sid)
        mp.set_property_number("sid", sid)
    else
        mp.msg.info("select-tracks: no JA subs, disabling")
        mp.set_property("sub", "no")
    end
end

mp.register_event("file-loaded", function()
        local tracks = mp.get_property_native("track-list") or {}
        for _, t in ipairs(tracks) do
            if t.type == "sub" and t.lang == "ja" then
                mp.set_property_number("sid", t.id)
                return
            end
        end
        mp.set_property("sub", "no")
end)
</code>
<p>
    After seeing the potential, I figured I should also ask one of the pet peeves I have;
    I do not have and do not plan to have any surround setup. I am strictly stereo only. I
    want stereo audio. But the thing is, video files often have several audio channel tracks
    for a given language, and they almost always default to the one with most channels. This
    causes the media player to downmix the audio, resulting in a subpar experience compared to
    the stereo track. I can't hear them people speaking! So I asked ChatGPT about it, and after some
    trial and error, it also managed to do it, incredible!
</p>
<h3>~/.config/mpv/scripts/stereo.lua</h3>
<code>
-- mpv script: if the selected audio track for a given language is multichannel, switch to its stereo counterpart with detailed logging
local mp = require("mp")

-- Get the native track list
local function get_tracks()
    return mp.get_property_native("track-list") or {}
end

-- Find an audio track by id
local function find_audio_track_by_id(id)
    for _, t in ipairs(get_tracks()) do
        if t.id == id and t.type == "audio" then
            return t
        end
    end
    return nil
end

-- Attempt to switch to stereo for the same language
local function switch_to_stereo_if_needed()
    local tracks = get_tracks()

    -- Get current audio track id
    local aid_prop = mp.get_property("aid")
    local current_id = tonumber(aid_prop)
    if not current_id then
        return
    end

    local sel = find_audio_track_by_id(current_id)
    if not sel then
        return
    end

    local ch = sel["audio-channels"] or 0

    -- Only act on multichannel
    if ch <= 2 then
        return
    end

    -- Find stereo counterpart
    for _, t in ipairs(tracks) do
        local tchan = t["audio-channels"] or 0
        if t.type == "audio"
        and t.lang == sel.lang
        and tchan == 2
        and t.id ~= current_id then
            mp.commandv("set", "aid", tostring(t.id))
            return
        end
    end
end

-- Hook into MPV events
mp.observe_property("aid", "string", function(name, value)
    switch_to_stereo_if_needed()
end)
mp.register_event("file-loaded", switch_to_stereo_if_needed)
</code>
<p>
You can also see it in action when I launch MPV from command line.
</p>
<h4>Switched to stereo audio and japanese subtitle:</h4>
<img id="smallimg" src="https://furkanmudanyali.com/blog/2025-08-09-mpvlua/meshi.webp" alt="stereo audio + japanese subtitles"/>
<h4>Switched to stereo audio and disabled subtitles:</h4>
<img id="smallimg" src="https://furkanmudanyali.com/blog/2025-08-09-mpvlua/geass.webp" alt="stereo audio + no subtitles"/>
<p>
    The takeaway is that the AI helped me avoid digging through MPV documentation
    to write these simple scripts, so I'm grateful for that. It feels like a slippery
    slope though, as if you depend on it too much, you will be at a loss when it doesn't
    work. My embedded software developer friend abused Claude for a week and got rate limited, now he's
    struggling to write any code.
</p></div>]]></description>
<pubDate>Sat, 09 Aug 2025 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Port Forwarding with Wireguard and UFW</title>
<link>https://furkanmudanyali.com/blog/2024-01-17-portfw/</link>
<guid>https://furkanmudanyali.com/blog/2024-01-17-portfw/</guid>
<description><![CDATA[<div id="content"><h2>Port Forwarding with Wireguard and UFW</h2><p>
    Let's say you got a cheap VPS just hosting minimal stuff, like a Git or a HTTP server, and you also
    got a rather powerful home server that is capable of hosting demanding software, like a modded minecraft
    server. If you wanted to open the stuff you were hosting on your home server to the internet, chances are
    you probably wouldn't be able to do it without paying your ISP money for static IP. Nowadays, ISPs are utilizing
    CGNAT to save on public IPs, and that prevents us from just porwarding our ports freely. As we already have a VPS
    with its own dedicated IP in this case, what we can do is set up a VPN and forward requests going into our VPS on
    a dedicated port to our home server using NAT.
</p>
<img alt="diagram" src="https://furkanmudanyali.com/blog/2024-01-17-portfw/diagram.png"/>

<p>
    In this schema, the VPS will be our Wireguard "host", and the home server will be our "client".
    For the case of Minecraft, we would want to forward incoming connections over port 25565 on our host's public facing interface,
    which in my case "eth0", to the client, over the VPN interface "wg0" using DNAT, and route the response back again using SNAT.
</p>

<p>
    First and foremost, we need to enable IPv4 forwarding on our VPS so that we do not scratch our heads later on thinking why its not working.
</p>
<code>
sudo sysctl -w net.ipv4.ip_forward=1
</code>
<p>
    To make this setting persistent, we need to add "net.ipv4.ip_forward=1" without quotes to /etc/sysctl.conf
</p>

<p>
    For configuring, I will be using "wg-quick" as it makes things easier for us. Our host configuration will look something like this:
</p>

<h2>Host: /etc/wireguard/wg0.conf</h2>
<code>
[Interface]
Address = 10.0.0.1/32
ListenPort = {Wireguard Port}
PrivateKey = {Super Secret Key}
PostUp = ufw route allow in on wg0 out on eth0
PostUp = ufw route allow proto tcp from any port 25565 to 10.0.0.2 port 25565
PostUp = iptables -t nat -A PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.0.0.2:25565
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PreDown = ufw route delete allow in on wg0 out on eth0
PreDown = ufw route delete allow proto tcp from any port 25565 to 10.0.0.2 port 25565
PreDown = iptables -t nat -D PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.0.0.2:25565
PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = {Not So Secret Key}
AllowedIPs = 10.0.0.2/32
</code>
<p>
    This might look like a handful, but let's break it down to see how simple it actually is.
</p>

<code>
Address = 10.0.0.1/32
ListenPort = {Wireguard Port}
PrivateKey = {Super Secret Key}
</code>
<p>
    These are just standard things, we set the host's internal IP address to 10.0.0.1, and set the private key and listening port.
</p>

<code>
PostUp = ufw route allow in on wg0 out on eth0
</code>
<p>
    This adds a rule on our firewall to allow traffic coming from our VPN interface wg0 to our public facing interface eth0, its
    really easy to understand.
</p>

<code>
PostUp = ufw route allow proto tcp from any port 25565 to 10.0.0.2 port 25565
</code>
<p>
    This adds a rule on our firewall again to allow forwarding incoming connections over port 25565 to our client's internal ip 10.0.0.2.
    Pretty verbose.
</p>

<code>
PostUp = iptables -t nat -A PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.0.0.2:25565
</code>
<p>
    This is kind of a handful, lets break it down further.<br/>
    <br/>
    <b>iptables:</b> the tool we use to configure the forwarding<br/>
    <b>-t nat:</b> specify that we want to work on NAT table<br/>
    <b>-A PREROUTING:</b> append a rule to the PREROUTING chain of the table<br/>
    <b>-p tcp:</b> Specifying tcp protocol to use<br/>
    <b>--dport 25565:</b> Specifying the destination port<br/>
    <b>-j DNAT:</b> Specifying the target of the rule, which is DNAT (Destination NAT)<br/>
    <b>--to-destination:</b> The new destination address that will be rewritten<br/>
    <br/>
    So what we are basically doing here is rewriting package addresses over port 25565 on the PREROUTING chain.
</p>

<code>
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
</code>
<p>
    When I previously said that we will be using SNAT, I kind of lied. Ideally we would want to rewrite all the responses
    coming from our client, so what we are doing here is we are masquerading packages coming from our client to our public facing
    interface eth0 as if they were originating from our host. Let's also break this down.<br/>
    <br/>
    <b>iptables:</b> the tool we use to configure the forwarding<br/>
    <b>-t nat:</b> specify that we want to work on NAT table<br/>
    <b>-A POSTROUTING:</b> append a rule to the POSTROUTING chain of the table<br/>
    <b>-o eth0:</b> Specify the output interface<br/>
    <b>-j MASQUERADE:</b> Specify the target of the rule.
</p>

<code>
PreDown = ufw route delete allow in on wg0 out on eth0
PreDown = ufw route delete allow proto tcp from any port 25565 to 10.0.0.2 port 25565
PreDown = iptables -t nat -D PREROUTING -p tcp --dport 25565 -j DNAT --to-destination 10.0.0.2:25565
PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
</code>
<p>
    This is pretty basic, we are just cleaning after ourselves when the wireguard interface is down.
</p>

<code>
[Peer]
PublicKey = {Not So Secret Key}
AllowedIPs = 10.0.0.2/32
</code>
<p>
    This is just the client portion, we just specify its public key and give it the address 10.0.0.2.
</p>

<p>
    So with the configuration out of the way, we can just simply turn wireguard on using wg-quick, and then move on to our client configuration,
    which is much simpler yet.
</p>
<code>
wg-quick up wg0
OR
systemctl enable --now wg-quick@wg0
</code>

<h2>Client: /etc/wireguard/wg0.conf</h2>
<code>
[Interface]
Address = 10.0.0.2/32
PrivateKey = {Do Not Share Anywhere}
Table = 74
PostUp = ip rule add pref 1453 from 10.0.0.2 lookup 74
PostDown = ip rule del pref 1453

[Peer]
PublicKey = {Public Key of the Host}
AllowedIPs = 0.0.0.0/0
Endpoint = x.x.x.x:{Wireguard Port}
PersistentKeepalive = 25
</code>
<p>
    As you can see, its much more simpler, but we are still doing something related to routing. Let's break it down.
</p>

<code>
[Interface]
Address = 10.0.0.2/32
PrivateKey = {Do Not Share Anywhere}
</code>
<p>
    Again, pretty standard stuff. We just set our IP address to 10.0.0.2 and set our private key. We don't really have to
    specify port.
</p>

<code>
Table = 74
PostUp = ip rule add pref 1453 from 10.0.0.2 lookup 74
</code>
<p>
    So this part is interesting (not really). All we are really saying is we set a table with some arbitrary value, and say that
    we want traffic originating from IP address 10.0.0.2 to use routing table 74 with a preference of an arbitrary value 1453. Which
    translates to saying that we want to route our responses back to the wg0 interface which our requests are originating from. Let's break down
    the code.<br/>
    <br/>
    <b>ip:</b> tool to configure routing<br/>
    <b>rule add:</b> we want to add a rule<br/>
    <b>pref 1453:</b> arbitrary preference value for prioritization<br/>
    <b>from 10.0.0.2:</b> the source IP address which we rewritten in the host<br/>
    <b>lookup 74:</b> specify the routing table to use for packets
</p>

<code>
PostDown = ip rule del pref 1453
</code>
<p>
    Just cleaning after ourselves again, when the interface goes down.
</p>

<code>
[Peer]
PublicKey = {Public Key of the Host}
AllowedIPs = 0.0.0.0/0
Endpoint = x.x.x.x:{Wireguard Port}
PersistentKeepalive = 25
</code>
<p>
    The host portion of our configuration. We set its public key, and say that we allow all the IP addresses coming from host. We also specify
    the endpoint and listening port of the host. Then we set a keepalive value of 25 to not drop connections.
</p>

<p>
    So, why did we go for the trouble of setting up additional routing on our client configuration? Because in my case, I do not want all my traffic
    to be routed to wg0. I just want the connections coming from wg0 to be routed to wg0. When you set AllowedIPs to 0.0.0.0/0, wg-quick automatically
    sets routing table so that all the connection is routed through wg0. If we then turn it off by saying Table = off, then we can listen to incoming connections
    sure, but we have no way to respond back since the packets do not know where to go without proper routing. You might also notice that I do not have
    any UFW configurations on my client, and that is because my home server is not open to public normally, so there really is not much reason to use it.
</p>
<img alt="demo" src="https://furkanmudanyali.com/blog/2024-01-17-portfw/demo.png"/></div>]]></description>
<pubDate>Wed, 17 Jan 2024 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>PS/2 Modding, Cleaning and Fixing a Mitsumi KPQ-E99ZC Keyboard</title>
<link>https://furkanmudanyali.com/blog/2023-12-29-mitsumi/</link>
<guid>https://furkanmudanyali.com/blog/2023-12-29-mitsumi/</guid>
<description><![CDATA[<div id="content"><h2>PS/2 Modding, Cleaning and Fixing a Mitsumi KPQ-E99ZC Keyboard</h2><p>
    Last month I went to Istanbul and visited Kadikoy Sali Pazari in Friday. I found lots of awesome things
    for little to no price. I got this
    <a href="https://deskthority.net/wiki/Mitsumi_KPQ-E99ZC" target="_blank">Mitsumi KPQ-E99ZC</a>
    and an IBM KB-8926 for 5 TL each. I also bought a Sony SDM-HX93 monitor with 2 VGA and 1 DVI inputs for 100 TL. I
    also bought some other various wireless keyboards, some bits and pieces and so on, overall it was a really good
    haul.
</p>
<img alt="haul" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/haul.jpg" />
<p>
    The problem was that the keyboard had an AT connector so I couldn't test it when I got back home. Only today
    I managed to find a junk keyboard and salvage its PS/2 cable so that I could replace the Mitsumi's cable
    with this one. AT and PS/2 are pin compatible, its just that only the connectors are different. I took apart
    the keyboard and deduced the pins using my multimeter.
</p>
<img alt="pinout" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/pinout.jpg" />
<img alt="soldered" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/soldered.jpg" />
<p>
    Not my best soldering job since I didn't have any flux but its sturdy. I plugged it in and it worked, I tested
    it for a bit and noticed some keys weren't registering correctly. I figured with some cleaning I could fix it, so I
    started taking out the keys.
</p>
<img alt="PS/2 Cable" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/ps2cable.jpg" />
<p>
    The switches on this keyboard consist of a rubber foot that presses on the membrane and a rubber sleeve to give the
    key springiness. Only the space key has 2 additional springs. With this design the keys feel soft and tacky,
    but the downside is its a pain in the bum to put it back after you take it apart. You can learn more about this
    switch type from here:
    <a href="https://deskthority.net/wiki/Mitsumi_KPQ_Type" target="_blank">Deskthority Mitsumi KPQ Type</a>
</p>
<img alt="switch" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/switch.jpg" />
<p>
    I painstakingly took out all the keys and admired its filth. Then I took apart the keyboard again to wash its
    chassis with my trusty toothbrush and some soap. I also had to take out the rubber feet from their holes, so it
    was lots of fun having to put them back in one by one for all the keys...
</p>
<img alt="decapped" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/decapped.jpg" />
<img alt="cleaned" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/cleaned.jpg" />
<p>
    During the process I managed to break one of the feet due to my carelessness. I managed to fix it by applying some
    superglue and sanding down the dried excess glue. As a result it became like new again and didn't give me any
    problems thankfully.
</p>
<img alt="stubfix" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/stubfix.jpg" />
<p>
    After drying everything, I put back everything just enough to test the keys and some keys were still having a hard
    time pressing. I figured it shouldn't be the membrane sheet but rather the conductiveness of the rubber feet,
    because I cleaned and inspected the sheet and seen no problems with it. So I thought about a wild idea. I grinded
    some pencil lead and mixed it together with cheap glue, then I smeared it onto the faulty feet. Surprisingly this
    actually worked somewhat but I had to repeat the process around 3 times to get them all working properly again,
    because the glue wouldn't stick and would peel off easily, it was hard to get it right. I wish I had some sort of
    conductive paint or something similar as that would have saved me hours.
</p>
<img alt="glue" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/glue.jpg" />
<p>
    Overall this entire cleaning ordeal took me hours thanks to faulty rubber feet and I managed to get from 72 to 74
    attack on my OSRS Ironman account in the meanwhile, AFKing at the Nightmare Zone. I actually really like how this
    keyboard and the IBM I got feels. The IBM leans more on the thocky side. Lately mechanical keyboards
    started giving me headaches, so I'm really appreciating these rubber dome keyboards. maybe I'm getting old...
</p>
<img alt="finish" src="https://furkanmudanyali.com/blog/2023-12-29-mitsumi/finish.jpg" /></div>]]></description>
<pubDate>Fri, 29 Dec 2023 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Asagiri</title>
<link>https://furkanmudanyali.com/projects/2023-10-18-asagiri/</link>
<guid>https://furkanmudanyali.com/projects/2023-10-18-asagiri/</guid>
<description><![CDATA[<div id="content"><h2>Asagiri</h2><a href="https://homdworks.org/asagiri/asagiri" target=_blank>Gitea Page</a><br/><br/>

<h4>Components</h4>
<ul>
    <li>yukari (kernel)</li>
    <li>hanekawa (libc)</li>
    <li>shinobu (bootloader)</li>
</ul>

<h3>Build</h3>
<h4>Dependencies</h4>
<ul>
    <li>a decent compiler such as gcc or clang</li>
    <li>QEMU</li>
    <li>mtools</li>
    <li>make</li>
</ul>
<h4>Dependencies for compiling toolchain</h4>
<ul>
    <li>wget</li>
</ul>

<h3>Credits</h3>
<ul>
    <li>Furkan Mudanyalı</li>
    <li><a href="https://sateallia.org/" target=_blank>Sateallia</a></li>
</ul>

<h3>License</h3>
<p>
This project is licensed under GPLv3.0 or later.
</p></div>]]></description>
<pubDate>Wed, 18 Oct 2023 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Integer Scaling in Runelite on Monitors With Fractional Scaling under Windows</title>
<link>https://furkanmudanyali.com/blog/2023-06-28-runelitescaling/</link>
<guid>https://furkanmudanyali.com/blog/2023-06-28-runelitescaling/</guid>
<description><![CDATA[<div id="content"><h2>Integer Scaling in Runelite on Monitors With Fractional Scaling under Windows</h2><p>
    My second monitor has a scaling of 125% and the Old School Runescape client Runelite is blurry
    on it. This is common with applications that do not use the new WinUI and the solution across
    the programs is the same. Locate the .exe, right click, properties, compatibility, change
    high DPI settings and enable high DPI scaling override.
</p>
<img alt="properties" src="https://furkanmudanyali.com/blog/2023-06-28-runelitescaling/properties.png" id="smallimg"/>

<p>
    When you set this up and launch the game again, you'll notice things are different, worse even.
    Now instead of things looking blurry, they're looking garbled! That's because Runelite still
    gets the overall scaling from the OS and applies that to everything using nearest neighbor algorithm.
</p>
<img alt="garbled" src="https://furkanmudanyali.com/blog/2023-06-28-runelitescaling/garbled.png"/>

<p>
    To fix this, you have to close the client and then search for "RuneLite (configure)" under Windows search,
    or launch the executable with the flag --configure. Then in that window, you need to set the scale value
    to 1.0 and press save.
</p>
<img alt="configure" src="https://furkanmudanyali.com/blog/2023-06-28-runelitescaling/configure.png" id="smallimg"/>

<p>
    Next time you open the game, you'll notice the things look exactly like they should, the integer scaling
    option in the Stretched Mode plugin will work just fine. If youre using the GPU or 117 HD plugin
    (which you should if your system can support it), do not forget to change the UI scaling mode to nearest
    neighbor, else everything will look blurry.
</p>
<img alt="fixed" src="https://furkanmudanyali.com/blog/2023-06-28-runelitescaling/fixed.png"/></div>]]></description>
<pubDate>Wed, 28 Jun 2023 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>A Brief Overlook into Audio and Video Codecs and Ways to Improve Them Using Filters</title>
<link>https://furkanmudanyali.com/blog/2022-10-27-codecs/</link>
<guid>https://furkanmudanyali.com/blog/2022-10-27-codecs/</guid>
<description><![CDATA[<div id="content"><h2>A Brief Overlook into Audio and Video Codecs and Ways to Improve Them Using Filters</h2><p>
Today, in 27 October 2022, I gave a presentation about audio and video codecs for my Smart Multimedia Systems course
given by Dr. Sibel Malkoş in Kadir Has University. I talked about containers, lossy and lossless codecs and some filters
with examples to back them up. You can see the full presentation video that I got permission to share below, along with
the examples I used.
</p>
<a href="https://furkanmudanyali.com/blog/2022-10-27-codecs/Furkan_Mudanyali_A_Brief_Overlook_Into_Audio_Video_Codecs.pdf">Link to the PowerPoint presentation (PDF)</a><br/>
<a href="https://furkanmudanyali.com/blog/2022-10-27-codecs/Furkan_Mudanyali_A_Brief_Overlook_Into_Audio_Video_Codecs.pptx">Link to the PowerPoint presentation (PPTX)</a>
<br/><br/>
<div class="iframe_wrapper">
<iframe width="100%" height="100%" src="https://www.youtube.com/embed/HXeqY7_4sXw" title="YouTube video player"
frameborder="0" allowfullscreen></iframe>
</div>
<h3>The Script I Used to Capture Footage</h3>
<a href="https://furkanmudanyali.com/blog/2022-10-27-codecs/capture.sh">Link to the script</a>
<code>
#!/bin/bash

# Device config
VIDEO_DEVICE=/dev/video2
VIDEO_CAPABILITIES="video/x-raw, format=YUY2, framerate=30000/1001, width=640, height=480, pixel-format-ratio=11/10"
AUDIO_DEVICE=hw:4,0
AUDIO_CAPABILITIES="audio/x-raw, rate=48000, channels=2"
TV_NORM=NTSC
TV_INPUT=1
FILENAME=$(date +"%Y%m%d_%H_%M_%S.mkv")

# Stream config
QUEUE="queue max-size-buffers=0 max-size-time=0 max-size-bytes=0"
DO_TIMESTAMP="do-timestamp=false"

# Encoding config
# Deinterlacing options:
# mode: 0 => send_frame / 1 => send_field
# parity: 0 => top field first / 1 => bottom field first
# deint: 0 => all frames / 1 only marked interlaced
DEINTERLACE="yadif=mode=1:parity=0:deint=0"
SCALE="scale=iw*2:-1:flags=experimental+full_chroma_int+full_chroma_inp+accurate_rnd"
CROP="crop=iw-0.06*iw:ih-0.06*ih"

# Set input for device
v4l2-ctl --device=$VIDEO_DEVICE -i $TV_INPUT

# Basically GStreamer will be the input for FFplay which will show the preview
# GStreamer will have 2 sinks, 1 for recording raw footage to file and the other
# a pipe for ffplay.
ffplay -i <(
gst-launch-1.0 -q \
  v4l2src device="$VIDEO_DEVICE" $DO_TIMESTAMP norm="$TV_NORM" \
  ! $VIDEO_CAPABILITIES \
  ! $QUEUE \
  ! tee name=videoTee \
  alsasrc device="$AUDIO_DEVICE" $DO_TIMESTAMP \
  ! $AUDIO_CAPABILITIES \
  ! $QUEUE \
  ! tee name=audioTee \
  matroskamux name=pipeMux \
  ! $QUEUE \
  ! fdsink fd=1 \
  matroskamux name=fileMux \
  ! $QUEUE \
              ! filesink location="$FILENAME" \
  audioTee. ! $QUEUE ! pipeMux. \
  audioTee. ! $QUEUE ! fileMux. \
  videoTee. ! $QUEUE ! pipeMux. \
  videoTee. ! $QUEUE ! fileMux.
) \
  -hide_banner \
  -fflags nobuffer \
  -flags +ildct+ilme \
  -analyzeduration 0 \
  -probesize 32 \
  -rtbufsize 128M \
  -threads 8 \
  -sync ext
</code>
<h3>Ratchet: Deadlocked (PS2) Footage with Weave Deinterlacing</h3>
<video controls>
<source src="https://furkanmudanyali.com/blog/2022-10-27-codecs/original.mp4" type="video/mp4">
</video>
<h3>Ratchet: Deadlocked (PS2) Footage with YADIF Line Doubling</h3>
<code>
ffmpeg –i input.mkv -vf "yadif=mode=0:parity=0:deint=0" yadif.mp4
</code>
<video controls>
<source src="https://furkanmudanyali.com/blog/2022-10-27-codecs/yadif.mp4" type="video/mp4">
</video>
<h3>Ratchet: Deadlocked (PS2) Footage with BWDIF Line Doubling</h3>
<code>
ffmpeg –i input.mkv -vf "bwdif=mode=0:parity=0:deint=0" bwdif.mp4
</code>
<video controls>
<source src="https://furkanmudanyali.com/blog/2022-10-27-codecs/bwdif.mp4" type="video/mp4">
</video>
<h3>Big Buck Bunny passed through Minterpolate filter with basic blending</h3>
<code>
ffmpeg -i input.avi -vf "minterpolate=fps=48:mi_mode=blend" minterpolate_basic.mp4
</code>
<video controls>
<source src="https://furkanmudanyali.com/blog/2022-10-27-codecs/minterpolate_basic.mp4" type="video/mp4">
</video>
<h3>Big Buck Bunny passed through Minterpolate filter with all the available options</h3>
<code>
ffmpeg -i input.avi -vf "minterpolate=fps=48:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1" minterpolate_advanced.mp4
</code>
<video controls>
<source src="https://furkanmudanyali.com/blog/2022-10-27-codecs/minterpolate_advanced.mp4" type="video/mp4">
</video> 
<p>
Big thanks to the Blender Foundation for the Big Buck Bunny short film!<br/>
(c) copyright 2008, Blender Foundation / <a href="https://www.bigbuckbunny.org/" target="_blank">www.bigbuckbunny.org</a><br/>
Licensed under <a href="https://creativecommons.org/licenses/by/3.0/" target="_blank">CC BY 3.0</a>
</p></div>]]></description>
<pubDate>Thu, 27 Oct 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Comparison Between S-Video and Composite</title>
<link>https://furkanmudanyali.com/blog/2022-08-22-comparison/</link>
<guid>https://furkanmudanyali.com/blog/2022-08-22-comparison/</guid>
<description><![CDATA[<div id="content"><h2>Comparison Between S-Video and Composite</h2><p>
  Recently I got an S-Video cable for my PS2 and it looks absolutely beautiful compared to composite. So I decided to take a few screenshots from
  several games and put them here for comparison. I hope you enjoy it.
</p>
<table>
  <caption>Castlevania - Curse of Darkness</caption>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp1.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid1.png"/></td>
  </tr>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp2.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid2.png"/></td>
  </tr>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp3.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid3.png"/></td>
  </tr>
</table>
<br/><br/>
<table>
  <caption>Dragon Quest VIII - Journey of the Cursed King</caption>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp4.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid4.png"/></td>
  </tr>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp5.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid5.png"/></td>
  </tr>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp6.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid6.png"/></td>
  </tr>
</table>
<br/><br/>
<table>
  <caption>Ratchet & Clank - Up Your Arsenal</caption>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp7.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid7.png"/></td>
  </tr>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp8.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid8.png"/></td>
  </tr>
  <tr>
    <td><img alt="composite" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/comp9.png"/></td>
    <td><img alt="svideo" src="https://furkanmudanyali.com/blog/2022-08-22-comparison/svid9.png"/></td>
  </tr>
</table></div>]]></description>
<pubDate>Mon, 22 Aug 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Rescuing a Semi-bricked Japanese Wii</title>
<link>https://furkanmudanyali.com/blog/2022-08-21-wiirescue/</link>
<guid>https://furkanmudanyali.com/blog/2022-08-21-wiirescue/</guid>
<description><![CDATA[<div id="content"><h2>Rescuing a Semi-bricked Japanese Wii</h2><p>
Recently I got a second-hand Wii in its original packaging with all the peripherals that was imported from Japan for 700TL,
which is equivalent to $38 at the time being.
I also bought a seperate EU adapter for 100TL in case the adapter bundled with it was something like 100V input.
When I got it, everything was in the package,
the device was in pristine condition and the adapter was indeed 100V.
I hooked it up and wandered around, it seemed to be working. But when I went
to the settings to change the language to English, well I couldn't. Because it prompted me a web page saying
"You tried to access the address marc:JP/JP..." something. I kind of got baffled and looked up for the reason.
Turns out, the device was semi-bricked, and the reason it happens is because of system menu / console region mismatch.
</p>

<p>
I think this Wii was modchipped to unlock the region restrictions, and Wii games update the system automatically
if the system has a lower firmware. So what I think happened is someone put a PAL region disc with a high enough firmware
that would normally get blocked if it weren't for the modchip, then the game started installing an European firmware which
semi-bricked the console. I checked if that was the case and looked for the save files in the Wii. There were saves from couple of games
that came out prior to 2007 but so far no indication that was the case. Out on a whim, I pressed the eject button.
Turns out, my thought process was correct and a Wii Sports Resort DVD came out of the tray of
the console. The reason why I didn't notice a DVD was inside the console was because it was inserted in reverse and the Wii couldn't
read it. This DVD is important later on.
</p>

<p>
So, what can I do at this moment? There is no Homebrew Channel, I don't know the system version and if its 4.3, I don't
know its MAC address. Things seem grim, the 2 information that I need, locked away to eternity in the broken system settings...
<br/>
It was actually not that hard to pinpoint these informations. First, I transferred one of the save files to the SD card from the Wii storage
and moved it to my PC to extract the MAC address. The MAC address of Wii is saved on the 0xF128 offset of the save file, so you can use your favorite hex
editor such as HxD to get it. If you're on a Unix machine, you can use hexdump to get your MAC address even easier with the following command:
</p>
<code>
hexdump data.bin -s 0xF128 -n 6 -C
</code>
<p>
I covered my MAC address from the
pictures so that you don't accidentally use my address for LetterBomb and wonder why it doesn't work.
</p>

<img alt="Mac Address in HxD" src="https://furkanmudanyali.com/blog/2022-08-21-wiirescue/HxDMAC.png"/>
<img alt="Mac Address in hexdump" src="https://furkanmudanyali.com/blog/2022-08-21-wiirescue/hexdumpMAC.png"/>

<p>
So, why did I need the MAC address again? So that if my system version was 4.3 I could use it for the LetterBomb exploit. Moving on, all that was left to do
was determining the system version. We have some clues thanks to the Wii Sports Resort DVD we took out of the console. First, it is
a PAL region game, along with every other game in Turkey, so the region must be E. If the game that broke this Wii was really Wii
Sports Resort, then it came out in June 25, 2009, which means the firmware should be no lower than 4.0. So I'm looking at 4 possibilities
here: 4.0E, 4.1E, 4.2E and 4.3E. For 4.0E and 4.1E I tried Bannerbomb v1 which didn't work. Then I skipped 4.2E and tried LetterBomb
for 4.3E using the MAC address I extracted, but it also didn't work. It just froze the Wii when I opened the message. Lastly, I tried
Bannerbomb v2 for 4.2E, and to my surprise, it actually worked! Now I could install the Homebrew Channel! Or, not.
</p>

<p>
The Hackmii Installer v1.2 I loaded with the exploit did not like my hardware, and said "This installer can NOT continue." I looked around
and found out that it happens on older soft-mods like "cIOScorp" or "DarkCorp" that break the system files. In our case though, we just have
a region mismatched firmware. It might also not be a modchip and be one of those soft-mods that corrupt the system files that unlocked the
region, but not really important. All I know is that I need to fix this. Fortunately for me, there is a great modding tool called "ModMii" which
can be found at <a href="https://modmii.github.io" target="_blank">modmii.github.io</a>
that creates various setups along with their guides for every use case. The option we want to use is
"HackMii Solutions" which aims to solve this exact issue. So I select it and select 4.2 for my firmware, then it asks for a location to put the
files in, and generates a guide for me to follow while downloading the files in the background. The guide is actually so well made and it also
includes videos along with step-by-step instructions. I followed this guide and successfully installed the Homebrew Channel,
along with BootMii in the boot2. That's good and all but it still didn't fix our problem though, which was a region mismatch. So for that, I started
to take a backup of the NAND using BootMii before the next step.
</p>

<img alt="BootMii NAND dump" src="https://furkanmudanyali.com/blog/2022-08-21-wiirescue/BootMiiNAND.png"/>

<p>
Now what I did was a complete region change. I wanted to revert this console back to its Japanese firmware, so once again I used the great tool
"ModMii" to generate the guide and files for me using the "Region Change" option. I followed the guide and successfully managed to save this Wii
beyond its former glory. But there is one slight issue, my Japanese is bad and I cannot read any kanji. There is also no way to change the language
to English on Japanese firmware as far as I know. Back to ModMii I go and follow the guide to change the region to USA. I use NTSC because I prefer higher
refresh rate over resolution. 50Hz vs 60Hz difference is pretty noticeable to me whereas I cannot really differentiate much between 720x480 and 720x576.
For your other needs, you can explore the other options on ModMii like USB loader setup and such.
</p>

<img alt="Wii Settings in Japanese" src="https://furkanmudanyali.com/blog/2022-08-21-wiirescue/JapaneseSettings.png"/>

<p>
Along with region change, I also installed the recommended cIOS'es and set up USB Loader GX with a 16GB flash drive I had laying around. Most of the time
the backups wouldn't load and give me a black screen though, and the reason for that is probably because the USB flash drive is 3.0 and it either pulls higher amps but
the USB2.0 on the Wii cannot provide enough or there is just some incompatibility. People online also recommend against using flash drives and they recommend USB hard drives. Unfortunately I had
no USB hard drive, so I just used a 128GB SD card I found around. Also USB Loader GX cannot load backups through SD card, and it gives a crash when automatically
loaded through Priiloader at system boot, so I switched to Wiiflow Lite. I think Wiiflow looks better and it boots up so fast with Priiloader. If you
ever find a semi-bricked Wii, I hope this post helps you bring it back to life.
</p>

<img alt="WiiFlow" src="https://furkanmudanyali.com/blog/2022-08-21-wiirescue/WiiFlow.png"/></div>]]></description>
<pubDate>Sun, 21 Aug 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Capturing Composite Video and How YouTube Deals With Them</title>
<link>https://furkanmudanyali.com/blog/2022-08-11-composite/</link>
<guid>https://furkanmudanyali.com/blog/2022-08-11-composite/</guid>
<description><![CDATA[<div id="content"><h2>Capturing Composite Video and How YouTube Deals With Them</h2><p>
  I think composite video signal is not as bad as people make it out to be. With correct configuration,
  it looks lovely even on LCD displays, it has it's own charm. I think two of the main reasons why its
  perceived as bad is because the modern TVs that can take in composite signal upscale it horribly and apply some sort of
  filter like hq2x or similar, making
  it look like vaseline smeared window, and because of YouTube dropping the bitrate of 480p video to abysmal
  levels and chopping framerates.
</p>
<p>
  Lately I was playing my PS2 through composite attached to my LCD TV, thinking the video could look a lot better.
  Then I found a USB device with composite dongles hanging from it. Turns out, my dad bought a capture card years
  ago. It is an AverMedia C039. Thinking I can use it to get better video from composite, I tried it out on my computer,
  but no matter what I did, I wasn't getting a satisfying image. FFmpeg piped to FFplay had delay issues, MPV wasn't any better,
  VLC and Media Player Classic kept freezing... After a while, I discovered AmaRecTV and tried to use it as a last resort.
  To my surprise, even with no configuration, the video looked miles better. There was also no noticable delay and audio was in sync
  with the video. For comparison:<br/>
  <br/>- <a href="https://www.youtube.com/watch?v=fLyccTXyw9w" target="_blank">Output from Media Player Classic</a>
  <br/>- <a href="https://www.youtube.com/watch?v=LiwHYQtccWo" target="_blank">Output from AmaRecTV</a>
  <br/><br/>
  I know it is not a fair comparison as the video from AmaRecTV is actually upscaled to 720p, but more on that later.
  Apart from the blockier and choppier video on the MPC output, the colors are also washed out. So, I decided I would use
  AmaRecTV.
</p>
<p>
  Now, when I first tried to upload my capture from MPC to the youtube in 480p, I immediately noticed the quality was a lot worse
  than the original video. This is due to the fact that YouTube drops the bitrate on 480p way too much. The framerate also wasn't
  fluid at all. You can see the comparison in the below picture. To mitigate this issue, I had to upscale the video to 720p, for that
  I just used bicubic scaling, nothing special.
</p>
<img alt="blocky video" src="https://furkanmudanyali.com/blog/2022-08-11-composite/blocky.png"/>
<p>
  After thinking I was done with it, I captured another footage, but this time upscaling it to 720p. When I was done, I uploaded it to
  YouTube and noticed something different. The colors were way darker. This is apparently due to the way YouTube handles colorspace in
  either MP4 container or H264 codec, I'm not sure which. The original video was in BT709 full range (0-255) colorspace but YouTube cropped(?)
  the colorspace to limited range (16-255), trashing the darker levels. You can see the comparison between the YouTube video and the original video
  in the below picture.
</p>
<img alt="dark video" src="https://furkanmudanyali.com/blog/2022-08-11-composite/darker.png"/>
<p>
  So now, I display the capture on AmaRecTV using the options:<br/>
  <br/>- w=720, h=480, fps=29.97, fcc=YUY2, bit=16
  <br/>- 4:3 AR, Top Field First, Role-playing Deinterlacing, Scan Line Doubler disabled
  <br/>- t=20, b=18, l=0, r=0 cropping, underscan
  <br/><br/>
  And record the window of AmaRecTV on OBS with these options:<br/>
  <br/>Video:
  <br/>- Base Resolution: 640x480
  <br/>- Output Resolution: 960x720
  <br/>- Downscale Filter: Bicubic
  <br/><br/>Output / Recording:
  <br/>- Type: Custom Output (FFmpeg)
  <br/>- Container Format: MP4 // You might want to use MKV, up for preference
  <br/>- Video Bitrate: 5000 Kbps
  <br/>- Video Encoder: nvenc_h264 // For Nvidia cards, use libx264 if you don't have one.
  <br/>- Audio Bitrate: 320 Kbps // You might want to increate the bitrate
  <br/>- Audio Encoder: libopus // You might want to use ALAC for lossless
  <br/><br/>Advanced:
  <br/>- Color Space: 601
  <br/>- Color Range: Partial
</p>
<p>
  The reason for OBS is because the recording ability of AmaRecTV is not suited for my use-case. It is more for recording the video uncompressed.
  I have tried it using x264 codec but it simply didn't fit my needs. Also, as the resolution of the device is 720x480, there are black borders
  on the sides of the 640x480 content, so I need to crop those black bars and OBS does that for me in real-time. AmaRecTV also can create a virtual
  device for capturing in OBS but apparently it only works on 32-bit OBS, and mine is 64-bit. So I just capture the window.
</p>
<p>
  I recently started recording a playthrough of Champions of Norrath in my YouTube channel, so you can check it out to see my
  recording method in action:<br/><br/>
  <a href="https://www.youtube.com/playlist?list=PLEH5jA8L98d2ULNbV-IHrAEoglvOJeIgv" target="_blank">[PS2] Champions of Norrath Playthrough</a>
</p></div>]]></description>
<pubDate>Thu, 11 Aug 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>rockOS</title>
<link>https://furkanmudanyali.com/projects/2022-07-30-rockos/</link>
<guid>https://furkanmudanyali.com/projects/2022-07-30-rockos/</guid>
<description><![CDATA[<div id="content"><h2>rockOS</h2><a href="https://homdworks.org/FMudanyali/rockOS" target=_blank>Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/rockOS" target=_blank>GitHub Mirror</a><br/><br/>

<h4>Currently implemented</h4>
<ul>
    <li>GDT</li>
    <li>IDT</li>
    <li>PIC initialization</li>
    <li>PIT timer</li>
    <li>Keyboard driver</li>
    <li>Paging and Kheap</li>
</ul>

<h3>Build</h3>
<h4>Dependencies</h4>
<ul>
    <li>a decent compiler such as gcc or clang</li>
    <li>grub2 (if under GNU/Linux)</li>
    <li>xorriso</li>
    <li>make</li>
    <li>tar</li>
</ul>
<h4>Dependencies for compiling toolchain</h4>
<ul>
    <li>wget/curl</li>
</ul>

<p>
If it is your first time building, you need to compile the toolchain first.
For that, you just need to run build-toolchain.sh, which will download binutils,
gcc, grub if youre on macOS, and then compile them to $HOME/opt/cross-i686 by default
which can be changed from the prompt.
</p>

<p>
After you compiled the toolchain and added $PREFIX/bin to your PATH as instructed by the script,
you can run qemu.sh to compile and open the OS on QEMU or iso.sh to just generate the ISO.
</p>

<h3>Credits</h3>
<ul>
    <li>Furkan Mudanyalı</li>
    <li>Uko Kokņevičs</li>
</ul>

<h3>Acknowledgements</h3>
<ul>
    <li>osdev.org community for huge documentation on OS Development</li>
    <li>musl for libc implementation</li>
    <li>James Molloy for his paging and kheap tutorial</li>
    <li>Free Software Foundation for multiboot specification</li>
</ul>

<h3>License</h3>
<p>
This project is licensed under GPLv3.0 or later.
</p></div>]]></description>
<pubDate>Sat, 30 Jul 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>My Music Collection and How I Record my LPs</title>
<link>https://furkanmudanyali.com/blog/2022-07-12-longplay/</link>
<guid>https://furkanmudanyali.com/blog/2022-07-12-longplay/</guid>
<description><![CDATA[<div id="content"><h2>My Music Collection and How I Record my LPs</h2><p>
  My dad is a music collector and he started his collection in the year 1985. To this day,
  we are expanding the collection by visiting second-hand music shops in Kadikoy, Istanbul,
  buying from Facebook groups and importing from Germany. Today, the LP records we have
  easily exceed 2000. The collection consists of, but not limited to 80s soul, funk and
  disco music. We also have same albums with different presses. My dad actively documents
  the records that do not exist in Discogs database such as
  <a href="https://www.discogs.com/release/23799395" target=_blank>this</a> and
  <a href="https://www.discogs.com/release/23334551" target=_blank>this</a>.
</p>
<img alt="vinyls" src="https://furkanmudanyali.com/blog/2022-07-12-longplay/col1.jpg"/>
<img alt="vinyls cont." src="https://furkanmudanyali.com/blog/2022-07-12-longplay/col2.jpg"/>
<p>
  Sometimes, we record tracks from LPs for fun using a USB Webcam, a USB phono preamp and a
  computer. The LP plays either on our Pro-Ject Primary E with Ortofon OM 5E cartridge or Dual
  621 with Audio-Technica VM530EN cartridge, which is connected to the Rega Fono Mini A2D
  preamplifier, which is then connected to the computer used for playing media. As the preamplifier
  can output to both USB and analog at the same time, I can listen to the playing music from my amplifier
  while recording at the same time. For recording, I just use OBS (Open Broadcaster Software).
</p>
<p>
  Here, I provided an example of the recording of Goo Goo Wah Wah by Wah Wah Watson,
  for details about this album, you can visit the <a href="https://www.discogs.com/release/1290888" target=_blank>Discogs page</a>.
</p>
<video controls>
  <source src="https://furkanmudanyali.com/blog/2022-07-12-longplay/rec.mp4" type="video/mp4">
</video>
<img alt="rec" src="https://furkanmudanyali.com/blog/2022-07-12-longplay/rec1.jpg"/>
<img alt="rec cont." src="https://furkanmudanyali.com/blog/2022-07-12-longplay/rec2.jpg"/></div>]]></description>
<pubDate>Tue, 12 Jul 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Best Design Project of 2022 Kadir Has Computer Engineering Bachelor&#39;s Projects</title>
<link>https://furkanmudanyali.com/blog/2022-06-20-mgd/</link>
<guid>https://furkanmudanyali.com/blog/2022-06-20-mgd/</guid>
<description><![CDATA[<div id="content"><h2>Best Design Project of 2022 Kadir Has Computer Engineering Bachelor's Projects</h2><p>
  My bachelor's project titled "Automated Diagnosis of MGD (Meibomian Gland Dysfunction)" supervised by
  <a href="https://scholar.google.com/citations?user=pZJvWL8AAAAJ" target=_blank>Assoc. Prof. Tamer DAĞ</a>
  has been awarded the "Best Design Project" among 22 projects in
  <a href="https://cpe.khas.edu.tr/" target=_blank>Computer Engineering Department of Kadir Has University</a>.
</p>
<p>
  As the name of this project implies, the outcome of this project was an application to rapidly diagnose
  <a href="https://en.wikipedia.org/wiki/Meibomian_gland_dysfunction" target=_blank>MGD</a>. Due to my serious
  health issues, I had to start to this project alone, without a team, one semester late.
  But despite these shortcomings, I still managed to get the "Best Design Project" award in my department.
</p>
<img alt="award" src="https://furkanmudanyali.com/blog/2022-06-20-mgd/mgd.webp"/></div>]]></description>
<pubDate>Mon, 20 Jun 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>Combining KDE with i3</title>
<link>https://furkanmudanyali.com/blog/2022-05-28-i3kde/</link>
<guid>https://furkanmudanyali.com/blog/2022-05-28-i3kde/</guid>
<description><![CDATA[<div id="content"><h2>Combining KDE with i3</h2><p>
  KDE has been my desktop environment on GNU/Linux distributions for several years as I think it is the
  most feature-complete among other DEs. Previously I was using either XFCE, or just i3. As I have switched
  to a multi-monitor environment, the main monitor being horizontal and the side monitor being vertical, the
  need for tiling was more than ever. In vanilla KDE, you can only divide the screen to two using Super + arrow
  keys, but since I can easily fit 3 windows on my vertical monitor, I needed more than that. As a solution,
  I opted to replace the KDE window manager with i3, but as I went down the road, many other things also needed
  replacement. As a baseline, <a href="https://github.com/heckelson/i3-and-kde-plasma" target=_blank>this repository</a>
  helped me a lot.
</p>
<p>
  First, you need to install i3 or i3-gaps.
  Then, you need to create a user service to start i3 before plasma workspace, to do that, create this file:
</p>
<code>
$HOME/.config/systemd/user/plasma-i3.service

[Unit]
Description=Launch Plasma with i3
Before=plasma-workspace.target

[Service]
ExecStart=/usr/bin/i3
Restart=on-failure

[Install]
WantedBy=plasma-workspace.target
</code>
<p>
  Then, mask plasma-kwin_x11.target and enable the newly created service like this:
</p>
<code>
systemctl mask plasma-kwin_x11.target --user
systemctl enable plasma-i3 --user
</code>
<p>
  To fix layout issues, you have to make several windows floating and kill the plasma desktop
  as it covers the entire desktop, preventing you to do anything.
</p>
<code>
# Plasma compatibility improvements
for_window [window_role="pop-up"] floating enable
for_window [window_role="task_dialog"] floating enable

for_window [class="yakuake"] floating enable
for_window [class="systemsettings"] floating enable
for_window [class="plasmashell"] floating enable;
for_window [class="Plasma"] floating enable; border none
for_window [title="plasma-desktop"] floating enable; border none
for_window [title="win7"] floating enable; border none
for_window [class="krunner"] floating enable; border none
for_window [class="Kmix"] floating enable; border none
for_window [class="Klipper"] floating enable; border none
for_window [class="Plasmoidviewer"] floating enable; border none
for_window [class="(?i)*nextcloud*"] floating disable
for_window [class="plasmashell" window_type="notification"] border none, move right 700px, move down 450px
no_focus [class="plasmashell" window_type="notification"]

for_window [title="Desktop — Plasma"] kill; floating enable; border none
</code>
<p>
  If you use a single monitor, you can just use feh to set your wallpaper, but if you use multiple monitors,
  I recommend using nitrogen instead. Also if your monitors have different layout, I recommend you to run
  xrandr before running nitrogen like this in your i3 config:
</p>
<code>
exec --no-startup-id xrandr --output DVI-I-1 --rotate right --pos 1920x0 && xrandr --output HDMI-0 --primary --pos 0x677
exec --no-startup-id nitrogen --restore
</code>
<p>
  Since by replacing the KDE window manager we are also removing compositing, you might want to use a replacement
  compositor such as picom. If you compile it from source, you can enable experimental backends that enable you
  to use blur. You can run it like this in your i3 config:
</p>
<code>
exec --no-startup-id picom --experimental-backends -b
</code>
<p>
  You also might want to autostart kwallet.
</p>
<code>
exec --no-startup-id /usr/lib/pam_kwallet_init
</code>
<p>
  If you use a single monitor, the default KDE notifications will appear fine, but if you use multiple monitors,
  sometimes you might see them in the middle of your screen, which is really annoying. As a workaround, you might
  want to replace it with dunst instead.
</p>
<code>
exec --no-startup-id dunst
</code>
<p>
  These are the workarounds for broken stuff when you replace the KDE window manager, but if you want, you can go
  further by replacing the KDE taskbar with the one from i3 and start your applications using either dmenu or rofi
  but at that point you might just want to start creating your environment from scratch instead of piggybacking KDE.
</p>
<img alt="KDE with i3" src="https://furkanmudanyali.com/blog/2022-05-28-i3kde/i3kde.jpg"/></div>]]></description>
<pubDate>Sat, 28 May 2022 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>DanmakuProject</title>
<link>https://furkanmudanyali.com/projects/2021-12-22-danmakuproject/</link>
<guid>https://furkanmudanyali.com/projects/2021-12-22-danmakuproject/</guid>
<description><![CDATA[<div id="content"><h2>DanmakuProject</h2><a href="https://homdworks.org/FMudanyali/DanmakuProject" target=_blank>Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/DanmakuProject" target=_blank>GitHub Mirror</a><br/><br/>
<p>
  This is made for a school project. It uses SDL2 under Java using <a href="https://github.com/java-native-access/jna" target=_blank>JNA library</a>.
  The main bindings are from <a href="https://github.com/shinedeveloper/libsdl4j" target=_blank>libjsdl</a> which is an attempt to create an interface for
  SDL2 using JNA but it lacks documentation and demonstration (still a great project).
  Some SDL2_mixer bindings are also created using JNA, which is not part of the libjsdl. (see Audio.java)
</p>
<p>
  I hope this proves to be a good demonstration for using libjsdl and JNA.
</p>
<h4>Credits</h4>
<ul>
  <li>Furkan Mudanyalı (Code, planning, execution etc.)</li>
  <li>graphLoom, (Music, assets)</li>
</ul>
<h4>Further Credits</h4>
<ul>
  <li>libjsdl contributors (especially shinedeveloper for a loot of bindings)</li>
  <li>SDL2 contributors</li>
  <li>SDL2_mixer contributors</li>
</ul></div>]]></description>
<pubDate>Wed, 22 Dec 2021 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>ES2GEARS-Vita</title>
<link>https://furkanmudanyali.com/projects/2020-11-02-gears/</link>
<guid>https://furkanmudanyali.com/projects/2020-11-02-gears/</guid>
<description><![CDATA[<div id="content"><h2>ES2GEARS-Vita</h2><a href="https://homdworks.org/FMudanyali/ES2GEARS-Vita" target=_blank>Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/ES2GEARS-Vita" target=_blank>GitHub Mirror</a><br/><br/>
<video controls>
  <source src="https://furkanmudanyali.com/projects/2020-11-02-gears/es2gears.mp4" type="video/mp4">
</video>
<p>
  This gears demo is ported to PS Vita using <a href="https://furkanmudanyali.com/projects/2020-10-30-freeglut">FreeGLUT-Vita</a>.
</p>
<p>The original code can be found <a href="https://github.com/ehsan/gl-samples/blob/master/01-es2sdlgears/es2gears.c" target=_blank>here.</a></p>
<h4>Compilation Instructions</h4>
<code>
Install DolceSDK.
Install Pigs-In-A-Blanket.
Install FreeGLUT-Vita.
mkdir build
cd build
cmake ..
make -j8
</code>
<h4>Credits</h4>
<ul>
  <li>Furkan Mudanyali</li>
  <li>dots-tb</li>
  <li>SonicMastr</li>
  <li>Team CBPS</li>
</ul>
<h4>Original Authors</h4>
<ul>
  <li>Brian Paul</li>
  <li>Kristian Høgsberg</li>
  <li>Alexandros Frantzis</li>
</ul></div>]]></description>
<pubDate>Mon, 02 Nov 2020 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>FreeGLUT-Vita</title>
<link>https://furkanmudanyali.com/projects/2020-10-30-freeglut/</link>
<guid>https://furkanmudanyali.com/projects/2020-10-30-freeglut/</guid>
<description><![CDATA[<div id="content"><h2>FreeGLUT-Vita</h2><a href="https://homdworks.org/FMudanyali/FreeGLUT-Vita" target=_blank>Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/FreeGLUT-Vita" target=_blank>GitHub Mirror</a><br/><br/>
<p>
    FreeGLUT library ported to PS Vita using <a href="https://github.com/SonicMastr/Pigs-In-A-Blanket" target=_blank>Pigs-In-A-Blanket</a>.
</p>
<h4>Compile for DolceSDK</h4>
<code>
Install Pigs-In-A-Blanket.
mkdir build
cd build
cmake .. -DVITA=1
make -j8
make install
</code>
<h4>Credits</h4>
<ul>
  <li>FreeGLUT contributors</li>
  <li>Furkan Mudanyali</li>
  <li>dots-tb</li>
  <li>SonicMastr</li>
  <li>Graphene</li>
  <li>Team CBPS</li>
</ul></div>]]></description>
<pubDate>Fri, 30 Oct 2020 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>CreepTea-Vita</title>
<link>https://furkanmudanyali.com/projects/2020-10-24-creeptea/</link>
<guid>https://furkanmudanyali.com/projects/2020-10-24-creeptea/</guid>
<description><![CDATA[<div id="content"><h2>CreepTea-Vita</h2><a href="https://homdworks.org/FMudanyali/CreepTea-Vita" target=_blank>Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/CreepTea-Vita" target=_blank>GitHub Mirror</a><br/><br/>
<img alt="creeptea" src="https://furkanmudanyali.com/projects/2020-10-24-creeptea/creeptea1.gif"/>
<p>
  My first application port to PS Vita.<br/>
  Original code can be found <a href="https://dl.openhandhelds.org/cgi-bin/gp2x.cgi?0,0,0,0,38,1699" target=_blank>here</a>.
</p>
<h4>Compilation Instructions</h4>
<code>
Install DolceSDK.
mkdir build
cd build
cmake ..
make -j8
</code>
<h4>Credits</h4>
<ul>
  <li>Furkan Mudanyali</li>
  <li>dots-tb</li>
  <li>Graphene</li>
  <li>SonicMastr</li>
  <li>CreepNT</li>
  <li>teakhanirons</li>
  <li>Team CBPS</li>
</ul>
<h4>Original Authors</h4>
<ul>
  <li>Optimus</li>
  <li>apomakros</li>
</ul></div>]]></description>
<pubDate>Sat, 24 Oct 2020 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>TrophyShot</title>
<link>https://furkanmudanyali.com/projects/2020-06-05-trophyshot/</link>
<guid>https://furkanmudanyali.com/projects/2020-06-05-trophyshot/</guid>
<description><![CDATA[<div id="content"><h2>TrophyShot</h2><a href="https://homdworks.org/FMudanyali/TrophyShot">Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/TrophyShot">GitHub Mirror</a><br/><br/>
<p>
  Just like in PS4, it takes a screenshot whenever you unlock a trophy in PS Vita.
  It is best used with <a href="https://github.com/dots-tb/reScreeny" target=_blank>reScreeny</a>.
</p>
<h4>How To Install</h4>
<p>
  In config.txt inside tai folder,<br/>
  TrophyShot.suprx goes under *main like this:
</p>
<code>
*main
ur0:tai/TrophyShot.suprx
</code>
<p>After modifying the config, reboot.</p>
<h4>Credits</h4>
<ul>
  <li>Furkan Mudanyali</li>
  <li>Graphene</li>
  <li>dots-tb</li>
  <li>Princess of Sleeping</li>
  <li>teakhanirons</li>
  <li>SilicaandPina</li>
  <li>yasen</li>
  <li>cuevavirus</li>
  <li>Nkekev</li>
  <li>Team CBPS</li>
</ul></div>]]></description>
<pubDate>Fri, 05 Jun 2020 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
<item>
<title>BetterTrackPlug</title>
<link>https://furkanmudanyali.com/projects/2020-03-25-bettertrackplug/</link>
<guid>https://furkanmudanyali.com/projects/2020-03-25-bettertrackplug/</guid>
<description><![CDATA[<div id="content"><h2>BetterTrackPlug</h2><a href="https://homdworks.org/FMudanyali/BetterTrackPlug" target=_blank>Gitea Page</a><br/><br/>
<a href="https://github.com/FMudanyali/BetterTrackPlug" target=_blank>GitHub Mirror</a><br/><br/>
<img alt="trackplug" src="https://furkanmudanyali.com/projects/2020-03-25-bettertrackplug/trackplug.gif"/>
<p>
  A plugin that keeps track of how long you played your games, and an app to show you the values.
</p>
<h4>Differences between regular TrackPlug</h4>
<ul>
  <li>As it's rewritten as a kernel plugin, the files that record the playtime won't ever get corrupted.</li>
  <li>It only writes to file when you close/launch or suspend/resume the games so it doesn't murder your memory card, unlike the old one.</li>
  <li>Complete support for Adrenaline and Adrenaline Bubbles.</li>
  <li>You can add apps/games you selected to a blacklist (located at ux0:/data/TrackPlug/blacklist.txt)</li>
  <li>The app works at 60FPS instead of 30FPS.</li>
</ul>
<h4>How To Install</h4>
<p>Just put BetterTrackPlug.skprx under *KERNEL in config.txt</p>
<code>
*KERNEL
ur0:tai/BetterTrackPlug.skprx
</code>
<p>install the vpk. Reboot.</p>
<h4>How To Use</h4>
<ul>
  <li>UP/DOWN to navigate.</li>
  <li>Triangle to prompt clearing playtime of the selected record, asks if you want to blacklist if you do clear the playtime.</li>
</ul>
<h4>Credits</h4>
<ul>
  <li>Furkan Mudanyali</li>
  <li>dots-tb</li>
  <li>cuevavirus</li>
  <li>teakhanirons</li>
  <li>Team CBPS</li>
  <li>Rinnegatamante</li>
  <li>ecamci</li>
  <li>chinseng85</li>
</ul></div>]]></description>
<pubDate>Wed, 25 Mar 2020 00:00:00 -0000</pubDate>
<author>Furkan Mudanyalı</author>
</item>
</channel>
</rss>