Jump to content

NOXv2: minimalism and cartridges.

Did someone say bad ideas? because someone should have when i started the plans for this thing...

 

just over 2 years ago i built "NOX", my bedside media center pc. it's since then seen an SSD upgrade, windows 10, and some minor tweaks.

now... about a month or so ago i decided that NOX had some shortcomings, and while the RGB was a funny joke it sort of grew old.

 

based on these shortcomings, and the ultimate bad idea of custom pc stuff, i set to work on NOXv2.

 

the mindblowing spec list:

- asrock QQ1900M (motherboard with soldered celeron from 2013)

- 8GB RAM i had laying around

- fanless MSI GT1030

- kingston A400 120GB SSD

- BeQuiet pure power 10 - 300W

- a fully custom case that's basicly a black box.

- some special sauce

 

The idea:

the concept i am going for is a truly minimalist computer, both in design and use. along the way i had the absolutely ridiculous idea to implement a system with "cartridges" (smart cards with i2c memory) to launch and close programs, much like the concept of olden day consoles.

 

i'm starting out this thread with a proof of concept build, at least proving that my absolutely RIDICULOUS idea will work:

deciding required volume, getting the panels made, test-fitting, a rough "draft" of the back panel, and some inside shots.

also, surprisingly, the airlfow setup in this box actually works: the psu fan is the only fan of the system, exhausting trough the vent of the power supply, pulling air in past the cpu heatsink, all the way around the GPU, trough the MSI logo in the GPU's bracket. This magically keeps the box cool enough, and is barely audible even in a quiet room.

Quote

uc?id=1wgcv9A9lwYzYhBGFvXvl1ECU_bS_DAYg

uc?id=1bi_OGRrsowPzQ-htHkaLHuxsWvel2tcy

uc?id=1i9xQ5Hu5hEHPU6mZUy4Mpc45ki5rX1mq

uc?id=1h55tyBZfsN3uE4IY_lfW-WE-7BM44Gyc

uc?id=15e_nf0xUEzW2sfJGM2eEE3_7jN2i-URT

 

The software:

if you made it all the way here, congrats. if you hadnt noticed already, i've planned to fudge some software together to make the cartridge idea work.

the first idea was to hook the card reader (arduino micro) into the on-board serial port, but that fell trough pretty fast, so the current "proof of concept" is just using the USB serial port.

the general idea of the cartridge system is as follows: each card has 256 bytes of storage, and contains two commands:

byte 0 to 128: the "start command" to be executed when the card is inserted

byte 128 to 256: the "exit command" to be executed when the card is removed

the idea is that the "start command" starts up a specific game, where possible with a specific savegame, and the "exit command" gets you back to the desktop after removing the card.

 

i've been debating to switch that to something that doesnt require software on the computer's end, using the arduino's "hello i'm a keyboard" options.

some other things i want to change are its current dependency on a modified library to work with the 256byte cards i'm using, and the currently hardcoded locations for the two commands.

either way, as it stands, the arduino code for the reader, in the mess that it is in at the moment:

Spoiler

#include <I2CEEPROM.h>

#define CHIP_ADDRESS 0x50 // Address of EEPROM chip
#define EEPROM_BYTES 256 // Number of bytes in EEPROM chip
#define CARDPIN 4
#define MODEPIN 5

I2CEEPROM i2c_eeprom(CHIP_ADDRESS); // Create I2C EEPROM instance
unsigned int current_address = 0;
byte textin = ' ';
int counter = 0;
int cardstate = 0; //0=no card, 1=card
byte exitcmd[128];

void setup(void)
{
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  pinMode(CARDPIN, INPUT);
  pinMode(MODEPIN, INPUT);
  for (int i = 0; i < 128; i++){
    exitcmd[i] = ' ';
  }
}

void loop()
{
  if (digitalRead(MODEPIN)){
    //programmer mode
    if (!(digitalRead(CARDPIN))){
      //if card is available
      //start commmand
      digitalWrite(13,HIGH);
      while(!(Serial.available())){
       delay(1000);
      }
      digitalWrite(13,LOW);
      counter = 0;
      do{
        textin = char(Serial.read());
        i2c_eeprom.write(counter, textin);
        counter++;
        delay(1);
      } while (textin != '\n');
      while (counter < 128){
        i2c_eeprom.write(counter, ' ');
        counter++;
      }
      //end of start command
      //exit command
      digitalWrite(13,HIGH);
      while(!(Serial.available())){
        delay(1000);
      }
      digitalWrite(13,LOW);
      counter = 128;
      do{
        textin = char(Serial.read());
        i2c_eeprom.write(counter, textin);
        counter++;
        delay(1);
      } while (textin != '\n');
      while (counter < 256){
        i2c_eeprom.write(counter, ' ');
        counter++;
      }
      for (int i=0; i < 10; i++){
        digitalWrite(13,HIGH);
        delay(500);
        digitalWrite(13,LOW);
        delay(500);
      }
      //end of exit command
    }
  } else {
    //chipkartridge mode
    delay(1000);
    if ((cardstate == 1) && digitalRead(CARDPIN)){
      //if card was in, and now it no longer is
      cardstate = 0;
      counter = 0;
      textin = ' ';
      while (counter < 128 && textin != '\n'){
        textin = char(exitcmd[counter]);
        Serial.print(char(textin));
        counter++;
      }
      counter = 0;
    }
    if ((cardstate == 0) && !digitalRead(CARDPIN)){
      //if card was not in, and now it is
      cardstate = 1;
      counter = 0;
      textin = ' ';
      while (counter < 128 && textin != '\n'){
        textin = char(i2c_eeprom.read(counter));
        Serial.print(char(textin));
        counter++;
      }
      counter = 0;
      textin = ' ';
      while (counter < 128 && textin != '\n'){
        textin = char(i2c_eeprom.read(counter+128));
        exitcmd[counter] = char(textin);
        counter++;
      }
      counter = 0;
    }
  }
}

 

besides the arduino code, i'm planning on using some off the shelf options for vareous functions:

- chatty for twitch streams (the cpu absolutely hates the twitch website, for reasons i havent quite figured out)

- steam for games, obviously

- retroarch for all emulator stuff

- kodi for all media center stuff

- maybe some windows 10 apps, perhaps?

 

 

The To-Do list:

- finish the cutouts on the back panel, so that usb devices actually fit.

- decide on a place for the card reader to go (ideas are welcome..)

- glue together the panels of the case that dont need to go apart

- actually put a power button on the system

- finish the card reader software (arduino stuff)

- figure out how to best make the card reader interface with the system

- possibly reducing some of the cable bulk on the power supply

Link to comment
Share on other sites

Link to post
Share on other sites

the card reader is taking shape:

uc?id=1QyVWWvfnzg5-VFc_uYTE0_6opyx4r_VE

 

my 3D printer is hating life right now, but at least the 3 components i needed to print came out decently enough.

 

managed to fit the arduino into a small enough enclosure by skipping the micro USB port and soldering leads straight onto the contacts on the back. testing functionality will have to wait until glue is dry.

Link to comment
Share on other sites

Link to post
Share on other sites

10 hours ago, Ske7ch23 said:

You sir, are mad. 

But I love it. Its like a modern nostalgia build! 

i am, in fact, completely sane :D

 

(below image at the max resolution my phone can do for dramatic effect, with a scale provided)

Spoiler

uc?id=1h9Uf1MEbxDZH0KhVdPZ_DAZOEL0kjARN

 

 

Link to comment
Share on other sites

Link to post
Share on other sites

4 minutes ago, James Evens said:

I guess you hate me now but there is space for strain relief inside the 3d-print. :D 

Anyway you could replace pin 13 with LED_BUILTIN which is predefined for most boards. There are more minor tweaks like defining uint16_t instead of int to avoid trouble when changing target since a int isn't always the same or making card state a byte or Boolean.

there's a LOT of inefficiency in the code, i usually tend to not bother with optimizing until i actually end up running into issues caused by bloated code.

 

and as for pin 13.. that's actually debug code which i forgot i left in, good you mention ?

Link to comment
Share on other sites

Link to post
Share on other sites

ignore the tape, and the two random bluescreens.. ITS WORKING:

uc?id=1IAnWdVIeV8cTAFvcOlcuJcIPSwQj5N04

 

it'll connect into the back of the desktop with a mini DIN-8 connector.

then inside it hooks onto a USB 2.0 header, and onto the power button header.

 

in essence, this is your entire front I/O, except it's on a 1 meter braided cable.

it was 30 bucks for a box of braiding, but DAMN this is some nice, dense, cat-proof braiding.

Link to comment
Share on other sites

Link to post
Share on other sites

glued the case together today, and spent the rest of the day pretty much programming...

its a simple demo.. but it's a demo, and its working: card in: cmd opens, card out: cmd closes

uc?id=1l_584rI_tjTJtR9FYcSTPpOP9UbFpQVk

 

code as it stands, taken out the "looping" debug print seen in the demo as well.

i've had to find an alternative to arduino's "keyboard" library because it doesnt play nice with systems that dont have qwerty layout.

i've also done away with the modified i2c library, and changed the static command locations on the card, so the 128 byte split is now gone.

Spoiler

#include <Wire.h>
#include <KeyboardAzertyFr.h>

#define CHIP_ADDRESS 0x50 // Address of EEPROM chip
#define EEPROM_BYTES 256 // Number of bytes in EEPROM chip
#define CARDPIN 4 //switch to detect if a card is present
#define MODEPIN 5 //switch to toggle modes

unsigned int current_address = 0;
byte textin = ' ';
int counter = 0;
int counter2 = 0;
boolean cardstate = false; //card state: false = no card, true = card
boolean mode = false; // mode selection: false = kbd operation, true = serial operation
byte exitcmd[256];

void cardWrite(unsigned int address, byte data) //function to write a single byte to the card, at a specified address
{
  Wire.beginTransmission(CHIP_ADDRESS);
  Wire.write((int)(address)); // write addr
  Wire.write(data); // Write byte
  Wire.endTransmission();
  delay(5); // Writing in I2C EEPROM takes ~5ms (even if I2C writing already done)
}

byte cardRead(unsigned int address) //function to read a single byte from the card, at a specified address
{
  byte read_data = 0xFF; //
  Wire.beginTransmission(CHIP_ADDRESS);
  Wire.write((int)(address));   // write addr
  Wire.endTransmission();
  Wire.requestFrom(CHIP_ADDRESS, 1); // Request 1 byte from device
  if (Wire.available()) {
    read_data = Wire.read();
  }
  return read_data;
}


void setup()
{
  Wire.begin(); //start i2c system
  KeyboardAzertyFr.begin(); //start keyboard
  Serial.begin(9600); // start serial connection
  pinMode(CARDPIN, INPUT); // setup card detection switch
  pinMode(MODEPIN, INPUT); //setup mode switch
  for (int i = 0; i < 256; i++){
    exitcmd[i] = ' '; //prepare variable, not necessary but good practisce
  }
  delay(10000);
  mode = digitalRead(MODEPIN);
  cardstate = digitalRead(CARDPIN);
}

void loop()
{
  mode = digitalRead(MODEPIN);
  if (mode && Serial.available())//if in serial mode and input is available
  {
    while (Serial.available())//flush the buffer
    {
      Serial.read();
    }
    Serial.write("Entering programming mode...\n");
    Serial.write("enter starting command\n");
    while(!(Serial.available()))//wait for command
    {
      delay(1000);
    }
    counter = 0;
    do{ //read characters until receiving newxline and write to the card starting at address 0
      textin = char(Serial.read());
      cardWrite(counter, textin);
      counter++;
      delay(1);
    } while ((textin != '\n') && (counter < 256));
    Serial.write("enter exit command\n");
    while(!(Serial.available())){//wait for command
      delay(1000);
    }
    do{ //read characters until receiving newxline and write to the card starting behind the starting code's newline
      textin = char(Serial.read());
      cardWrite(counter, textin);
      counter++;
      delay(1);
    } while ((textin != '\n') && (counter < 256));
    while (counter < 256){//fill the rest of the card with spaces
      cardWrite(counter, ' ');
      counter++;
    }
  }
  if (digitalRead(CARDPIN) != cardstate) //if the card state changed
  {
    if (cardstate == false) //if card was removed
    {
      textin = ' ';
      counter = 0;
      if (mode == false) //if kbd mode
      {
        //send KBD init signal
        KeyboardAzertyFr.press(131);
        delay(5);
        KeyboardAzertyFr.press('r');
        delay(5);
        KeyboardAzertyFr.releaseAll();
        delay(500);//delay to wait for window to open
      }
      while (counter < 256 && textin != '\n'){//until receiving newline
        textin = char(exitcmd[counter]);//read character from array
        if (mode) //if serial mode
        {
          Serial.print(char(textin));//write character to terminal
        }
        else //if kbd mode
        {
          KeyboardAzertyFr.write(char(textin));//send keyboard signal
        }
        counter++;
      }
      counter = 0;
      cardstate = digitalRead(CARDPIN);
    }
    else //if card was insterted
    {
      //data loading routine
      counter = 0;
      textin = ' ';
      if (mode == false) //if kbd mode
      {
        //send KBD init signal
        KeyboardAzertyFr.press(131);
        delay(5);
        KeyboardAzertyFr.press('r');
        delay(5);
        KeyboardAzertyFr.releaseAll();
        delay(500);//delay to wait for window to open
      }
      while ((counter < 256) && (textin != '\n'))//until receiving newline: read start command
      {
        textin = char(cardRead(counter));//read one character from card
        if (mode) //if serial mode
        {
          Serial.print(char(textin));//write character to terminal
        }
        else //if kbd mode
        {
          KeyboardAzertyFr.write(char(textin));//send keyboard signal
        }
        counter++;
      }
      textin = ' ';
      counter2 = 0;
      while (counter < 256 && textin != '\n')//until receiving newline: read exit command
      {
        textin = char(cardRead(counter));//read one character from card
        exitcmd[counter2] = char(textin);//write character to array
        counter++; //increment counters
        counter2++;
      }
      counter = 0;
      cardstate = digitalRead(CARDPIN); //update cardstate variable
    }
  }
  delay(500);
}

 

new todo: implementing a toggle for the "kbd mode init signal" allowing for exiting programs by giving the program specific inputs.

for example, using the first byte as an indicator for which mode to use:

0 = use kbd init

1 = skip kbd init

2 = send alt-f4

3 = send a number of escape presses (loads of games actually exit this way..)

etc.

Link to comment
Share on other sites

Link to post
Share on other sites

34 minutes ago, PacketMan said:

Damn, looking good.

It would be really nice to see github wiki pages on these projects.

Keep it up ?

if it ends up usable enough, i'll probably write a "how to make this work" guide on the forum.

first order of business on that side is to get things to a point you cant end up in unexpected hold-ups anymore.

 

oh, and ofcourse.. i gotta figure out a way to stop the glue on the reader to stop coming off :D

Link to comment
Share on other sites

Link to post
Share on other sites

  • 2 weeks later...

so.. between about 20 different hardware failures on the card reader, other priorities, and distractions.. i got things to work with a game:

uc?id=1qV3G-viaJ3r6ZUo5X4fULaRYqRluIOcN

Link to comment
Share on other sites

Link to post
Share on other sites

so.. more issues go on..

 

i've gotten to the point i've taken some broken keyboard library off the arduino forums, forked it, and fixed it.

 

to work on stuff on my main rig, and mostly to not have to program cards with a bedside pc, i've made a "devkit":

12UtWIJITaV9XWP5cnO_vMaNio-qAJjig?authus

 

i'm gonna try to finish up the propper card reader this weekend, the case of the pc itself still needs some finishing touches as well.

Link to comment
Share on other sites

Link to post
Share on other sites

  • 2 weeks later...

78cfd252c9.png

with some delays because life is bullshit.. front I/O and card reader box is finished.

 

hoping to finish off the case this weeked...

Link to comment
Share on other sites

Link to post
Share on other sites

  • 2 weeks later...

so.. i managed to finish off the case today, with some delay..

 

first off, sanding the edges round:

uc?id=1XoSB4-DrfT2ngSkbaK7s72Xsl3QHy34x

 

then, finishing off with some paint, and feet:

uc?id=1j6EpDYBiHlayFgkiygiWYV2apQrUJz8e

 

gotta wait for stuff to dry now, pretty up the hardware inside, and then deal with software.

Link to comment
Share on other sites

Link to post
Share on other sites

Some more late night programming.. up to version 3, things being a lot more user friendly now:

Spoiler

//################################################################
//# Arduino Chipkartridge V3 for arduino micro                   #
//# Using 256 byte chipcards to launch and close programs        #
//# System requirements:                                         #
//# - Control-R to bring up a "run" prompt.                      #
//#   Works in windows 10, most likely can be done in linux too. #
//# - A keyboard layout with matching keyboard library.          #
//#   Arduino uses QWERTY int'l by default, this is developed on #
//#   a belgian AZERTY keyboard, with custom keyboard library.   #
//#                                                              #
//# The reader has two modes:                                    #
//# - Serial mode:                                               #
//#   Used for debugging, and for programming cards.             #
//#   Outputs the commands to the terminal.                      #
//# - KBD mode:                                                  #
//#   Normal operation for the card reader.                      #
//#   Calling the "run" prompt and executing commands.           #
//#                                                              #
//# Programming cards is done from Arduino IDE's serial monitor. #
//# - Sending an empty command calls the programming routine.    #
//# - Type startup command into the textbox, and press send.     #
//# - Type exit command into the textbox, and press send.        #
//################################################################
#include <Wire.h> //library for communicating with i2c cards
#include <KeyboardBE.h> //modified keyboard library for belgian keyboard layout

#define CHIP_ADDRESS 0x50 // Address of EEPROM chip
#define EEPROM_BYTES 256 // Number of bytes in EEPROM chip
#define CARDPIN 4 //input to detect if a card is present - HIGH = card present | LOW = no card present
#define MODEPIN 5 //input to toggle modes -- HIGH = serial mode | LOW = kbd mode

unsigned int current_address = 0; //control variable
byte textin = ' ';
int counter = 0; //global counter 1
int counter2 = 0;//global counter 2
boolean cardstate = false; //card state: false = no card, true = card
boolean mode = false; // mode selection: false = kbd operation, true = serial operation
byte exitcmd[EEPROM_BYTES]; //string to hold the command executed on card removal

//################################################################
//# Card writing and reading routines.                           #
//# If using cards > 256 byte, this needs this needs to be       #
//# changed to use two address bytes.                            #
//################################################################
void cardWrite(unsigned int address, byte data) //function to write a single byte to the card, at a specified address
{
  Wire.beginTransmission(CHIP_ADDRESS);
  Wire.write((int)(address)); // write addr
  Wire.write(data); // Write byte
  Wire.endTransmission();
  delay(5); // Writing in I2C EEPROM takes ~5ms (even if I2C writing already done)
}

byte cardRead(unsigned int address) //function to read a single byte from the card, at a specified address
{
  byte read_data = 0xFF; //
  Wire.beginTransmission(CHIP_ADDRESS);
  Wire.write((int)(address));   // write addr
  Wire.endTransmission();
  Wire.requestFrom(CHIP_ADDRESS, 1); // Request 1 byte from device
  if (Wire.available()) {
    read_data = Wire.read();
  }
  return read_data;
}

//################################################################
//# The card programming routine.                                #
//# This routine activates when serial input is detected.        #
//# Exit out the routine by removing the card.                   #
//# If no card is inserted, the routine exits on activation.     #
//################################################################
void programmingRoutine()
{
  while (Serial.available()){Serial.read();}//flush the buffer
  Serial.write("Entering programming mode...\n");
  Serial.write("enter starting command\n");
  while(cardstate && (!(Serial.available())))//wait for command, exits if card removed
  {
    delay(1000);//sleep 1 second
    cardstate = digitalRead(CARDPIN);//update card state
  }
  if(cardstate)//if card present, start writing
  {
    counter = 0;//reset global counter
    do{ //read characters until receiving newxline and write to the card starting at address 0
      textin = char(Serial.read());
      cardWrite(counter, textin);
      counter++;
      delay(1);
    } while ((textin != '\n') && (counter < EEPROM_BYTES));
    Serial.write("enter exit command\n");
  }
  while(cardstate && (!(Serial.available())))//wait for command, exits if card removed
  {
    delay(1000);//sleep 1 second
    cardstate = digitalRead(CARDPIN);//update card state
  }
  if(cardstate)//if card present, start writing
  {
    do{ //read characters until receiving newxline and write to the card starting behind the starting code's newline
      textin = char(Serial.read());
      cardWrite(counter, textin);
      counter++;
      delay(1);
    } while ((textin != '\n') && (counter < EEPROM_BYTES));
    Serial.write("Card programmed successfully, exiting...\n");
  }
  while (counter < EEPROM_BYTES)//fill the rest of the card with spaces
  {
    cardWrite(counter, ' ');
    counter++;
  }
  if(!(cardstate)) Serial.write("No card present, exiting..");//no card present == error out programming attempt
  Serial.write("Done!");

}

//################################################################
//# A routine to decide how the "run" prompt is launched.        #
//################################################################
void kbdInit()//subroutine to send kbd init to computer (opens "run" prompt)
{
  KeyboardBE.press(131);//windows key
  delay(5);
  KeyboardBE.press('r');//windows + r = "run" prompt
  delay(5);
  KeyboardBE.releaseAll();//release both keys
  delay(500);//delay to wait for window to open 
}

//################################################################
//# Card inserted routine, called when a card is inserted.       #
//# - Reads and executes the startup command.                    #
//# - Reads and stores the exit command.                         #
//################################################################
void cardInsertedRoutine()
{
  counter = 0;//reset global counter
  textin = ' ';//clear leftover newline from variable
  if (mode == false)//if kbd mode
  {
    kbdInit();//open "run" prompt
    while (counter < EEPROM_BYTES && textin != '\n')//until receiving newline
    {
      textin = char(cardRead(counter));//read one character from card
      KeyboardBE.write(char(textin));//read character from array, send to keyboard
      counter++;
    }
  }
  else//if serial mode
  {
    while (counter < EEPROM_BYTES && textin != '\n')//until receiving newline
    {
      textin = char(cardRead(counter));//read one character from card
      Serial.print(char(textin));//read character from array, send to terminal
      counter++;
    }
  }
  textin = ' ';//clear newline
  counter2 = 0;//reset global counter 2
  while (counter < EEPROM_BYTES && textin != '\n')//until receiving newline: read exit command
  {
    textin = char(cardRead(counter));//read one character from card
    exitcmd[counter2] = char(textin);//write character to array
    counter++; //increment counters
    counter2++;
  }
}
//################################################################
//# Card removed routine, called when the card is removed.       #
//# - Executes the lasst stored exit command.                    #
//################################################################
void cardRemovedRoutine()
{
  textin = ' ';//clear leftover newline from char variable
  counter = 0;//reset global counter
  if (mode == false)//if kbd mode
  {
    kbdInit();//open "run" prompt
    while (counter < EEPROM_BYTES && textin != '\n')//until receiving newline
    {
      textin = char(exitcmd[counter]);//read character from array
      KeyboardBE.write(char(textin));//read character from array, send to keyboard
      counter++;
    }
  }
  else//if serial mode
  {
    while (counter < EEPROM_BYTES && textin != '\n')//until receiving newline
    {
      textin = char(exitcmd[counter]);//read character from array
      Serial.print(char(textin));//read character from array, send to terminal
      counter++;
    }
  }
}

void setup()
{
  Wire.begin(); //start i2c system
  KeyboardBE.begin(); //start keyboard
  Serial.begin(9600); // start serial connection, used for programming cards and debug output
  pinMode(CARDPIN, INPUT); // setup card detection switch
  pinMode(MODEPIN, INPUT); //setup mode switch
  for (int i = 0; i < EEPROM_BYTES; i++){
    exitcmd[i] = ' '; //prepare variable, not necessary but good practisce, also means serial mode outputs cleaner.
  }
  cardstate = digitalRead(CARDPIN);//initialize card state
}

void loop()
{
  mode = digitalRead(MODEPIN); // update mode
  if (mode && Serial.available())//if in serial mode and input is available
  {
    programmingRoutine(); //run the programming subroutine
  }
  
  if (digitalRead(CARDPIN) != cardstate) //if the card state changed
  {
    if (cardstate == false) //if card was removed
    {
      cardRemovedRoutine();
    }
    else //if card was insterted
    {
      cardInsertedRoutine();
    }
    cardstate = digitalRead(CARDPIN); //update cardstate variable
  }
  delay(500);//wait for a change at a reasonable speed
}

 

Changes:

- Lots of prettying up, dividing things into functions, more documentation, etc.

- Programming routine now gets interrupted when the card is removed, allowing for a way to exit.

Link to comment
Share on other sites

Link to post
Share on other sites

an update on the software side of things:

- an attempt to configure retroarch has resulted in a spontanious uninstallation, i'm now looking elsewhere for emulation.

- civ v's horrible new launcher appareantly broke steam big picture compatibility, and as a result gets really weird with a steam controller...

- steam overlay softlocks steam controller in anno 1404

- watching twitch livestreams works flawlessly

- windows defender is still an arse, when you least expect it, and need the lost performance the most.

Link to comment
Share on other sites

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×