Interface update and code improvements, translation

This commit is contained in:
Yuri Torlopov 2023-01-29 21:47:56 +03:00
parent b9fb337b77
commit a9e291c819
21 changed files with 631 additions and 216 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

BIN
DecodedCode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

After

Width:  |  Height:  |  Size: 206 KiB

BIN
ExampleCode.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

After

Width:  |  Height:  |  Size: 205 KiB

View file

@ -1,10 +1,48 @@
# JDarkroom # JDarkroom
This is a simple, completely offline reimplementation of Darkroom website<br> ### ENG
that existed a while ago, but now it is offline. Using JDarkroom you can retrieve<br> This is a simple, completely offline reimplementation of the Darkroom website that existed<br>
locker code in Akuda bar by dropping a save file here or by entering an Internet code.<br> a while ago, but now it is offline. Using JDarkroom you can retrieve locker code in Akuda bar.<br>
There are two ways how you can do that:
- By dropping a save file here (slot[0-4].sav)
- Enter an Internet code that you can find at the bottom of the screen when you are about to save your game.<br>
Example of an Internet code: ```fCe\w9!iBXJ1ijn&```<br>
Both Russian and English game versions are supported. Both Russian and English game versions are supported.
***A note***<br>
This app would not exist, if it was not this super helpful article: https://9214.github.io/13
Why did I make this if there are online websites that are much more similar to the original Darkroom?<br>
As the author of this article said, these can go offline at any time, but with this tool you don't have to<br>
worry about that, because this app will just be stored on your pc as a single file.
### РУС
Это простая, не требующая интернет-соединения программа, которая повторяет функционал сайта Darkroom,<br>
который существовал раньше, однако, теперь он больше не доступен. Используя JDarkroom вы можете получить код от шкафа в баре Акуда.<br>
Сделать это можно двумя способами:
- Перенести в окно программы файл сохранения (slot[0-4].sav)
- Ввести специальный Интернет-код, который вы можете найти внизу вашего экрана при сохранении игры.<br>
Пример Интернет-кода: ```fCe\w9!iBXJ1ijn&```<br>
Поддерживаются Английская и Русская версии игры.
***Заметка***<br>
Это приложение не существовало бы, если бы не эта суперполезная статья: https://9214.github.io/13
Зачем я создал это приложение, ведь уже существуют сайты, которые очень похожи на оригинальный Darkroom?<br>
Как сказал автор этой статьи, эти веб-сайты, так же как и оригинальный, могут быть отключены в любое время,<br>
но с этим приложением вам не стоит переживать, ведь оно просто будет находится у вас на компьютере в виде одного файла.
### Screenshots ### Screenshots
#### You can drop save file here:
![Drop Save Here](DropSave.png) ![Drop Save Here](DropSave.png)
![Code Field](CodeField.png)
#### An example:
![Example Code](ExampleCode.png)
#### Code that resulted from ```fCe\w9!iBXJ1ijn&```:
![Decoded Code](DecodedCode.png)
#### If you enter wrong code:
![Incorrect Code Entered](IncorrectCode.png) ![Incorrect Code Entered](IncorrectCode.png)

8
build.bat Normal file
View file

@ -0,0 +1,8 @@
@ECHO OFF
set pkg=me\theentropyshard\jdarkroom
mkdir out
javac -encoding utf8 -classpath src\%pkg% -d out -sourcepath . src\%pkg%\*.java
cd out
jar cfe0 JDarkroom.jar me.theentropyshard.jdarkroom.Main %pkg%\*.class -C ..\resources .
move JDarkroom.jar ..\
cd ..\

BIN
resources/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 KiB

BIN
resources/mdisk.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 853 B

4
resources/readme.txt Normal file
View file

@ -0,0 +1,4 @@
background.png taken from the Internet, resized and darkened
mdisk.ico taken from https://github.com/9214/daruma, I sent an email asking for
this icon, at the moment of writing this, author still not answered, but the license is
WTFPL, so I think it is OK to take this icon.

2
run-en.bat Normal file
View file

@ -0,0 +1,2 @@
@ECHO OFF
java -jar JDarkroom.jar en-US

2
run-ru.bat Normal file
View file

@ -0,0 +1,2 @@
@ECHO OFF
java -jar JDarkroom.jar ru-RU

3
src/META-INF/MANIFEST.MF Normal file
View file

@ -0,0 +1,3 @@
Manifest-Version: 1.0
Main-Class: me.theentropyshard.jdarkroom.Main

View file

@ -0,0 +1,97 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom;
public enum Decoder {
;
public static String decodeInternetCode(String code) {
byte[] bytes = new byte[16];
for(int i = 0; i < 16; i++) {
bytes[i] = DecodingData.CODE_INDEX_TABLE.get(Character.toString(code.charAt(i)));
}
for(int i = 6; i > 0; i--) {
int byte1 = 2 * i + i / 6;
int bi1 = i % 6;
int byte2 = 15;
int bi2 = i - 1;
Utils.swapBits(byte1, byte2, bi1, bi2, bytes);
}
String bin = Integer.toBinaryString(bytes[bytes.length - 1]);
int index = Integer.parseInt(bin.substring(0, 3), 2);
byte firstRoundKey = DecodingData.KEY[index];
byte secondRoundKey = DecodingData.KEY[DecodingData.KEY.length - 1 - index];
for(int i = 30; i > 0; i--) {
int m = (i * firstRoundKey + 45) % 90;
int n = (i * secondRoundKey + 45) % 90;
int p = m % 6;
int q = n % 6;
int byte1 = (m - p) / 6;
int bi1 = p;
int byte2 = (n - q) / 6;
int bi2 = q;
Utils.swapBits(byte1, byte2, bi1, bi2, bytes);
}
byte thirdRoundKey = DecodingData.KEY[bytes[bytes.length - 1] & 0b111];
for(int i = 40; i > 0; i--) {
int m = i * thirdRoundKey % 90;
int n = m % 6;
int byteInd = (m - n) / 6;
int bitInd = n;
bytes[byteInd] ^= (1 << bitInd);
}
return Decoder.readInternetCode(bytes);
}
private static String readInternetCode(byte[] bytes) {
int intCode = 0;
intCode |= (bytes[8] & (0x1E000000 >> 25)) << 25;
intCode |= (bytes[9] & (0x01000000 >> 19)) << 19;
intCode |= (bytes[9] & (0x00001F00 >> 8)) << 8;
intCode |= (bytes[10] & (0x001F0000 >> 14)) << 14;
intCode |= (bytes[10] & (0x0000000C >> 2)) << 2;
intCode |= (bytes[11] & (0x00000003 << 4)) >> 4;
int l1 = Integer.parseInt(Integer.toHexString((intCode >> 24) & 0xFF), 10);
int d1 = Integer.parseInt(Integer.toHexString((intCode >> 16) & 0xFF), 16);
int l2 = Integer.parseInt(Integer.toHexString((intCode >> 8) & 0xFF), 16);
int d2 = Integer.parseInt(Integer.toHexString((intCode >> 0) & 0xFF), 16);
String[] letters1 = DecodingData.SAVE_INDEX_TABLE.get(l1 + 1).split("\\s");
String[] letters2 = DecodingData.SAVE_INDEX_TABLE.get(l2 + 1).split("\\s");
String russianCode = letters1[1] + d1 + letters2[1] + d2;
String englishCode = letters1[0] + d1 + letters2[0] + d2;
String russianLabel = I18N.getString("codeLabelRu");
String englishLabel = I18N.getString("codeLabelEn");
String andText = I18N.getString("andText");
return String.format("%s: %s %s %s: %s", russianLabel, russianCode, andText, englishLabel, englishCode);
}
}

View file

@ -1,3 +1,20 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom; package me.theentropyshard.jdarkroom;
import java.util.Collections; import java.util.Collections;
@ -68,6 +85,7 @@ public final class DecodingData {
*/ */
public static final Map<String, Byte> CODE_INDEX_TABLE = Collections.unmodifiableMap( public static final Map<String, Byte> CODE_INDEX_TABLE = Collections.unmodifiableMap(
new HashMap<String, Byte>() {{ new HashMap<String, Byte>() {{
// @formatter:off
this.put("A", (byte) 0); this.put("А", (byte) 0); this.put("A", (byte) 0); this.put("А", (byte) 0);
this.put("B", (byte) 1); this.put("Б", (byte) 1); this.put("B", (byte) 1); this.put("Б", (byte) 1);
this.put("C", (byte) 2); this.put("Ц", (byte) 2); this.put("C", (byte) 2); this.put("Ц", (byte) 2);
@ -132,6 +150,7 @@ public final class DecodingData {
this.put("9", (byte) 61); // this.put("K", (byte) 10); this.put("9", (byte) 61); // this.put("K", (byte) 10);
this.put("?", (byte) 62); // this.put("K", (byte) 10); this.put("?", (byte) 62); // this.put("K", (byte) 10);
this.put("!", (byte) 63); // this.put("K", (byte) 10); this.put("!", (byte) 63); // this.put("K", (byte) 10);
// @formatter:on
}} }}
); );
} }

View file

@ -0,0 +1,64 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public enum I18N {
;
public static final Locale LOCALE = Locale.getDefault();
public static final String LANGUAGE = I18N.LOCALE.getLanguage().toLowerCase(Locale.ROOT);
private static final Map<String, String> TRANSLATION = new HashMap<>();
static {
I18N.TRANSLATION.put("en.dropFileHere", "<html>Drop save file here! (slot[0-4].sav)<br>Or click to enter Internet code!</html>");
I18N.TRANSLATION.put("ru.dropFileHere", "<html>Переместите файл сохранения сюда! (slot[0-4].sav)<br>Или нажмите, чтобы ввести Интернет-код!</html>");
I18N.TRANSLATION.put("en.enterCodeHere", "Enter Internet code here");
I18N.TRANSLATION.put("ru.enterCodeHere", "Введите сюда Интернет-код");
I18N.TRANSLATION.put("en.submit", "Submit");
I18N.TRANSLATION.put("ru.submit", "Отправить");
I18N.TRANSLATION.put("en.incorrectCodeLength", "Internet code length must be 16 chars!");
I18N.TRANSLATION.put("ru.incorrectCodeLength", "Длина Интернет-кода должна быть 16 символов!");
I18N.TRANSLATION.put("en.badFile", "Bad file given!");
I18N.TRANSLATION.put("ru.badFile", "Перемещен плохой файл!");
I18N.TRANSLATION.put("ru.andText", "и");
I18N.TRANSLATION.put("en.andText", "and");
I18N.TRANSLATION.put("ru.codeLabelRu", "Русский код");
I18N.TRANSLATION.put("ru.codeLabelEn", "Английский код");
I18N.TRANSLATION.put("en.codeLabelRu", "Russian code");
I18N.TRANSLATION.put("en.codeLabelEn", "English code");
}
public static String getString(String key) {
if(key.indexOf(".") == 2) {
return I18N.TRANSLATION.get(key);
}
return I18N.TRANSLATION.get(I18N.LANGUAGE + "." + key);
}
}

View file

@ -1,8 +1,21 @@
package me.theentropyshard.jdarkroom; /* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import java.io.File; package me.theentropyshard.jdarkroom;
import java.io.IOException;
import java.io.RandomAccessFile;
public class JDarkroom { public class JDarkroom {
public JDarkroom() { public JDarkroom() {
@ -14,134 +27,6 @@ public class JDarkroom {
new View(); new View();
} }
public String findInSave(File file) {
if(file == null || file.isDirectory()) {
return null;
}
StringBuilder russianCode = new StringBuilder();
StringBuilder englishCode = new StringBuilder();
try(RandomAccessFile raf = new RandomAccessFile(file, "r")) {
if(raf.length() <= 0) {
return null;
}
raf.seek(DecodingData.CODE_OFFSET);
for(int i = 0; i < 16; i++) {
byte b = raf.readByte();
if(i % 4 == 0) {
String[] letters = DecodingData.SAVE_INDEX_TABLE.get((int) b).split("\\s");
russianCode.append(letters[1]);
englishCode.append(letters[0]);
}
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
return russianCode + " or " + englishCode;
}
public String decodeInternetCode(String code) {
if(code == null || code.length() != 16) {
return null;
}
byte[] bytes = new byte[16];
for(int i = 0; i < 16; i++) {
bytes[i] = DecodingData.CODE_INDEX_TABLE.get(Character.toString(code.charAt(i)));
}
for(int i = 6; i > 0; i--) {
int byte1 = 2 * i + i / 6;
int bi1 = i % 6;
int byte2 = 15;
int bi2 = i - 1;
JDarkroom.swapBits(byte1, byte2, bi1, bi2, bytes);
}
String bin = Integer.toBinaryString(bytes[bytes.length - 1]);
int index = Integer.parseInt(bin.substring(0, 3), 2);
byte firstRoundKey = DecodingData.KEY[index];
byte secondRoundKey = DecodingData.KEY[DecodingData.KEY.length - 1 - index];
for(int i = 30; i > 0; i--) {
int m = (i * firstRoundKey + 45) % 90;
int n = (i * secondRoundKey + 45) % 90;
int p = m % 6;
int q = n % 6;
int byte1 = (m - p) / 6;
int bi1 = p;
int byte2 = (n - q) / 6;
int bi2 = q;
JDarkroom.swapBits(byte1, byte2, bi1, bi2, bytes);
}
byte thirdRoundKey = DecodingData.KEY[bytes[bytes.length - 1] & 0b111];
for(int i = 40; i > 0; i--) {
int m = i * thirdRoundKey % 90;
int n = m % 6;
int byteInd = (m - n) / 6;
int bitInd = n;
bytes[byteInd] ^= (1 << bitInd);
}
int intCode = 0;
intCode |= (bytes[8] & (0x1E000000 >> 25)) << 25;
intCode |= (bytes[9] & (0x01000000 >> 19)) << 19;
intCode |= (bytes[9] & (0x00001F00 >> 8)) << 8;
intCode |= (bytes[10] & (0x001F0000 >> 14)) << 14;
intCode |= (bytes[10] & (0x0000000C >> 2)) << 2;
intCode |= (bytes[11] & (0x00000003 << 4)) >> 4;
int l1 = Integer.parseInt(Integer.toHexString((intCode >> 24) & 0xFF), 10);
int d1 = Integer.parseInt(Integer.toHexString((intCode >> 16) & 0xFF), 16);
int l2 = Integer.parseInt(Integer.toHexString((intCode >> 8) & 0xFF), 16);
int d2 = Integer.parseInt(Integer.toHexString((intCode >> 0) & 0xFF), 16);
String[] letters1 = DecodingData.SAVE_INDEX_TABLE.get(l1 + 1).split("\\s");
String[] letters2 = DecodingData.SAVE_INDEX_TABLE.get(l2 + 1).split("\\s");
String russianCode = letters1[1] + d1 + letters2[1] + d2;
String englishCode = letters1[0] + d1 + letters2[0] + d2;
return russianCode + " or " + englishCode;
}
/**
* Swaps two different bits in two numbers
*
* @param byteInd1 Index of first byte
* @param byteInd2 Index of second byte
* @param bi1 0-based bit from first number, indexed from right
* @param bi2 0-based bit from second number, indexed from right
* @param bytes Byte array
*/
public static void swapBits(int byteInd1, int byteInd2, int bi1, int bi2, byte... bytes) {
int bit1 = (bytes[byteInd1] >> bi1) & 1;
int bit2 = (bytes[byteInd2] >> bi2) & 1;
if(bit1 == 0) {
bytes[byteInd2] &= ~(1 << bi2);
} else {
bytes[byteInd2] |= 1 << bi2;
}
if(bit2 == 0) {
bytes[byteInd1] &= ~(1 << bi1);
} else {
bytes[byteInd1] |= 1 << bi1;
}
}
private static JDarkroom instance; private static JDarkroom instance;
public static JDarkroom getInstance() { public static JDarkroom getInstance() {

View file

@ -1,11 +1,47 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom; package me.theentropyshard.jdarkroom;
import javax.swing.*; import javax.swing.*;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
JDialog.setDefaultLookAndFeelDecorated(true); Pattern localePattern = Pattern.compile("([a-z]{2})-([A-Z]{2})$");
JFrame.setDefaultLookAndFeelDecorated(true); if(args.length > 0) {
Matcher matcher = localePattern.matcher(args[0]);
if(matcher.matches()) {
try {
String[] langAndCountry = args[0].split("-");
Locale.setDefault(new Locale(langAndCountry[0], langAndCountry[1]));
} catch (Exception se) {
se.printStackTrace();
}
}
}
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ignored) {
}
new JDarkroom(); new JDarkroom();
} }

View file

@ -0,0 +1,59 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public enum SaveFileReader {
;
public static String findInSave(File file) {
if(file == null || file.isDirectory()) {
return null;
}
StringBuilder russianCode = new StringBuilder();
StringBuilder englishCode = new StringBuilder();
try(RandomAccessFile raf = new RandomAccessFile(file, "r")) {
if(raf.length() <= 0) {
return null;
}
raf.seek(DecodingData.CODE_OFFSET);
for(int i = 0; i < 16; i++) {
byte b = raf.readByte();
if(i % 4 == 0) {
String[] letters = DecodingData.SAVE_INDEX_TABLE.get((int) b).split("\\s");
russianCode.append(letters[1]);
englishCode.append(letters[0]);
}
}
} catch (IOException e) {
e.printStackTrace();
return null;
}
String russianLabel = I18N.getString("codeLabelRu");
String englishLabel = I18N.getString("codeLabelEn");
String andText = I18N.getString("andText");
return String.format("%s: %s %s %s: %s", russianLabel, russianCode, andText, englishLabel, englishCode);
}
}

View file

@ -0,0 +1,73 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
public enum Utils {
;
private static final Map<String, BufferedImage> IMAGE_CACHE = new HashMap<>();
public static BufferedImage loadImage(String path) {
if(Utils.IMAGE_CACHE.containsKey(path)) {
return Utils.IMAGE_CACHE.get(path);
}
try {
BufferedImage bufferedImage = ImageIO.read(Objects.requireNonNull(Utils.class.getResourceAsStream(path)));
Utils.IMAGE_CACHE.put(path, bufferedImage);
return bufferedImage;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* Swaps two different bits in two numbers
*
* @param byteInd1 Index of first byte
* @param byteInd2 Index of second byte
* @param bi1 0-based bit from first number, indexed from right
* @param bi2 0-based bit from second number, indexed from right
* @param bytes Byte array
*/
public static void swapBits(int byteInd1, int byteInd2, int bi1, int bi2, byte... bytes) {
int bit1 = (bytes[byteInd1] >> bi1) & 1;
int bit2 = (bytes[byteInd2] >> bi2) & 1;
if(bit1 == 0) {
bytes[byteInd2] &= ~(1 << bi2);
} else {
bytes[byteInd2] |= 1 << bi2;
}
if(bit2 == 0) {
bytes[byteInd1] &= ~(1 << bi1);
} else {
bytes[byteInd1] |= 1 << bi1;
}
}
}

View file

@ -1,3 +1,20 @@
/* JDarkroom. Reimplementation of Darkroom website.
* Copyright (C) 2023 TheEntropyShard
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package me.theentropyshard.jdarkroom; package me.theentropyshard.jdarkroom;
import javax.swing.*; import javax.swing.*;
@ -8,107 +25,215 @@ import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
public final class View extends JPanel { public final class View extends JPanel {
private static final String YOUR_CODE_IS = "Your code is: "; private static final Color TRANSPARENT_COLOR = new Color(255, 255, 255, 96);
private static final float FONT_SIZE = 20.0F; private static final Color TRANSPARENT_COLOR_DARKER = View.TRANSPARENT_COLOR.darker().darker().darker();
private final JTextField codeField; private static final BufferedImage BACKGROUND = Utils.loadImage("/background.png");
private static final int WIDTH = View.BACKGROUND.getWidth();
private static final int HEIGHT = View.BACKGROUND.getHeight();
private static final Font FONT = new Font("Arial", Font.BOLD, 14);
private final JLabel resultLabel;
@SuppressWarnings("unchecked")
public View() { public View() {
this.setPreferredSize(new Dimension(800, 600)); this.setPreferredSize(new Dimension(View.WIDTH, View.HEIGHT));
CardLayout cardLayout = new CardLayout(); CardLayout cardLayout = new CardLayout(30, 50);
this.setLayout(cardLayout); this.setLayout(cardLayout);
JLabel codeLabel = new JLabel(View.YOUR_CODE_IS) {{ this.resultLabel = new JLabel() {{
this.setHorizontalAlignment(JLabel.CENTER); this.setHorizontalAlignment(JLabel.CENTER);
this.setFont(this.getFont().deriveFont(View.FONT_SIZE)); this.setFont(View.FONT);
this.setFocusable(true);
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
cardLayout.show(View.this, "DropLabel");
}
});
}}; }};
this.add(codeLabel, "CodeLabel");
JLabel dropLabel = new JLabel("Drop save file here, or click to enter Internet Code!", JLabel.CENTER) {{ JPanel dropFilePanel = new JPanel() {
this.setDropTarget(new DropTarget() { {
@Override this.setOpaque(false);
public synchronized void drop(DropTargetDropEvent e) { this.setLayout(new GridBagLayout());
try { GridBagConstraints c = new GridBagConstraints();
e.acceptDrop(DnDConstants.ACTION_COPY); c.fill = GridBagConstraints.BOTH;
List<File> droppedFiles = (List<File>) e.getTransferable().getTransferData(DataFlavor.javaFileListFlavor); c.anchor = GridBagConstraints.CENTER;
String code = JDarkroom.getInstance().findInSave(droppedFiles.get(0)); this.addMouseListener(new MouseAdapter() {
if(code == null) { @Override
codeLabel.setText("Incorrect Internet Code entered!"); public void mousePressed(MouseEvent e) {
codeLabel.setForeground(Color.RED); super.mousePressed(e);
codeField.setText(""); cardLayout.show(View.this, "enterCodePanel");
} else { }
codeLabel.setForeground(Color.DARK_GRAY); });
codeLabel.setText(View.YOUR_CODE_IS + code); this.add(new JPanel() {
} {
cardLayout.show(View.this, "CodeLabel"); this.setOpaque(false);
e.dropComplete(true); this.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
} catch (Exception ex) { this.add(new JLabel(I18N.getString("dropFileHere")) {{
ex.printStackTrace(); this.setFont(View.FONT);
this.setHorizontalAlignment(JLabel.CENTER);
}});
} }
}
});
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
cardLayout.show(View.this, "InputPanel");
}
});
this.setFocusable(true);
this.setFont(this.getFont().deriveFont(View.FONT_SIZE));
}};
this.add(dropLabel, "DropLabel");
JPanel panel = new JPanel(new GridBagLayout()); @Override
GridBagConstraints c = new GridBagConstraints(); protected void paintComponent(Graphics g) {
c.fill = GridBagConstraints.BOTH; super.paintComponent(g);
c.anchor = GridBagConstraints.CENTER; g.setColor(View.TRANSPARENT_COLOR_DARKER);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
}, c);
this.setDropTarget(new DropTarget() {
@Override
public synchronized void drop(DropTargetDropEvent dtde) {
dtde.acceptDrop(DnDConstants.ACTION_COPY);
try {
List<File> droppedFiles = (List<File>)
dtde.getTransferable().getTransferData(DataFlavor.javaFileListFlavor);
String code = SaveFileReader.findInSave(droppedFiles.get(0));
if(code == null) {
resultLabel.setText(I18N.getString("badFile"));
} else {
resultLabel.setText(code);
}
cardLayout.show(View.this, "resultPanel");
} catch (Exception e) {
e.printStackTrace();
dtde.dropComplete(false);
}
dtde.dropComplete(true);
}
});
}
this.codeField = new JTextField() {{ @Override
this.setPreferredSize(new Dimension(250, 50)); protected void paintComponent(Graphics g) {
this.setFont(this.getFont().deriveFont(View.FONT_SIZE)); super.paintComponent(g);
}}; g.setColor(View.TRANSPARENT_COLOR);
panel.add(this.codeField, c); g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
};
JButton button = new JButton("Enter") {{ JPanel enterCodePanel = new JPanel() {
this.setPreferredSize(new Dimension(100, 50)); {
this.setFont(this.getFont().deriveFont(View.FONT_SIZE)); this.setOpaque(false);
this.addActionListener(e -> { this.setLayout(new BorderLayout());
String code = JDarkroom.getInstance().decodeInternetCode(codeField.getText()); this.add(new JPanel() {{
if(code == null) { this.setOpaque(false);
codeLabel.setText("Incorrect Internet Code entered!"); this.setLayout(new GridBagLayout());
codeLabel.setForeground(Color.RED); GridBagConstraints c = new GridBagConstraints();
codeField.setText(""); c.fill = GridBagConstraints.BOTH;
} else { c.anchor = GridBagConstraints.CENTER;
codeLabel.setForeground(Color.DARK_GRAY); JTextField textField = new JTextField() {{
codeLabel.setText(View.YOUR_CODE_IS + code); this.setFont(View.FONT);
} this.setPreferredSize(new Dimension(220, 30));
cardLayout.show(View.this, "CodeLabel"); }};
}); this.add(new JPanel() {
}}; {
this.setOpaque(false);
this.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
this.setLayout(new GridLayout(2, 1));
this.add(new JPanel() {{
this.setLayout(new BorderLayout());
this.setOpaque(false);
this.add(new JLabel(I18N.getString("enterCodeHere")) {{
this.setHorizontalAlignment(JLabel.CENTER);
this.setFont(View.FONT);
}});
}});
this.add(new JPanel() {{
this.setOpaque(false);
this.add(textField);
this.add(new JButton(I18N.getString("submit")) {{
this.setFont(View.FONT);
this.setPreferredSize(new Dimension(120, 30));
this.addActionListener(e -> {
String text = textField.getText();
textField.setText("");
if(text.length() != 16) {
resultLabel.setText(I18N.getString("incorrectCodeLength"));
} else {
resultLabel.setText(Decoder.decodeInternetCode(text));
}
cardLayout.show(View.this, "resultPanel");
});
}});
}});
}
panel.add(button, c); @Override
this.add(panel, "InputPanel"); protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(View.TRANSPARENT_COLOR_DARKER);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
}, c);
}});
}
cardLayout.show(this, "DropLabel"); @Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(View.TRANSPARENT_COLOR);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
};
JPanel resultPanel = new JPanel() {
{
this.setOpaque(false);
this.setLayout(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.CENTER;
this.add(new JPanel() {
{
this.setOpaque(false);
this.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 1));
this.add(resultLabel);
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(View.TRANSPARENT_COLOR_DARKER);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
}, c);
this.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
cardLayout.show(View.this, "dropFilePanel");
}
});
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(View.TRANSPARENT_COLOR);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
}
};
this.add(dropFilePanel, "dropFilePanel");
this.add(enterCodePanel, "enterCodePanel");
this.add(resultPanel, "resultPanel");
cardLayout.show(this, "dropFilePanel");
JFrame frame = new JFrame("JDarkroom"); JFrame frame = new JFrame("JDarkroom");
frame.add(this); frame.add(this, BorderLayout.CENTER);
frame.pack(); frame.pack();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setIconImage(Utils.loadImage("/mdisk.png"));
frame.setLocationRelativeTo(null); frame.setLocationRelativeTo(null);
frame.setVisible(true); frame.setVisible(true);
} }
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(View.BACKGROUND, 0, 0, null);
}
} }