![](https://i-blog.csdnimg.cn/blog_migrate/8ba337dbaba547e3a4b4b36e9618e951.jpeg)
一 、分析
据说这是个游戏的彩蛋(PS:我没玩过),里面有一个叫 monika.chr的文件,可以将.chr后缀改为.png,会得到一张图片,如下图所示
![](https://i-blog.csdnimg.cn/blog_migrate/80f859352e40ef890f5ad57c02b92cdf.jpeg)
将上述图片中的黑白方块,取下来,得到一个140*140像素的图,140x140 = 196000 bit = 24500 字节。这张图每个像素就是数据,白色对应1,黑色对应0,注意:图像最下面一行右半部分是全黑,推测是文件末尾无法对齐所造成的。因此源文件的读取方式应该是从上到下逐行读取。这里用Java 读取像素点的RGB ,然后按着上面的规则转换成二进制文本
![](https://i-blog.csdnimg.cn/blog_migrate/aa63463c8689dd98a65033f8b0843a09.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cc33664740e10aa0d685682b71f43a38.png)
二、运行第一段代码,得到二进制文本
package play;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.PixelGrabber;
import java.io.*;
/**
* @author wangyl
* @version 1.0
* @date 2021/5/28 17:04
*/
public class LoadImage {
public static void main(String[] args) {
String myReadline = null;
try {
// 需要你把image_list.txt中的图片名称读入到新创建的FileReader对象中
FileReader fr = new FileReader("data/Image/image_list.txt");
BufferedReader br = new BufferedReader(fr);
//创建FileWriter对象(缓冲区),用来写入最后读到的字符流
FileWriter fwRGB = new FileWriter("data/first_rgb.txt");
FileWriter fwBinary = new FileWriter("data/second_binary.txt");
BufferedWriter bwRGB = new BufferedWriter(fwRGB);
BufferedWriter bwBinary = new BufferedWriter(fwBinary);
while (br.ready()) {
// 每次读取一行
myReadline = br.readLine();
// 构造一个BufferedImage的缓冲区,得到图像的横竖坐标的大小
BufferedImage image = toBufferedImage(new ImageIcon("data/Image/" + myReadline).getImage());
int height = image.getHeight();
int width = image.getWidth();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
// 创建一个Color的对象color,这个color里面保存的就是image.getRGB(x, y)这个方法中在坐标点
//(x,y)得到的颜色
Color color = new Color(image.getRGB(x, y));
//得到这个像素点的颜色之后,用get的方法,得到这个像素点的红绿蓝三个颜色的数字表示
String rgb = "(" + color.getRed() + "," + color.getGreen() + "," + color.getBlue() + ")";
// RGB 写入first_rgb.txt
bwRGB.write(rgb);
bwRGB.flush();
// RGB(0,0,0) 是黑色
if ("(0,0,0)".equals(rgb)) {
rgb = "0";
} else {
rgb = "1";
}
// 将二进制0101写入 second_binary.txt
bwBinary.write(rgb);
bwBinary.flush();
}
}
}
bwRGB.close();
fwRGB.close();
bwBinary.close();
fwBinary.close();
br.close();
fr.close();
} catch (Exception e) {
e.printStackTrace();
}
}
// 在缓冲区返回图片的内容
public static BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage) image;
}
// Determine if the image has transparent pixels; for this method's
// implementation, see e661 Determining If an Image Has Transparent
// Pixels
boolean hasAlpha = hasAlpha(image);
// Create a buffered image with a format that's compatible with the
// screen
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
// Determine the type of transparency of the new buffered image
int transparency = Transparency.OPAQUE;
if (hasAlpha) {
transparency = Transparency.BITMASK;
}
// Create the buffered image
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(image.getWidth(null), image
.getHeight(null), transparency);
} catch (HeadlessException e) {
}
if (bimage == null) {
// Create a buffered image using the default color model
int type = BufferedImage.TYPE_INT_RGB;
if (hasAlpha) {
type = BufferedImage.TYPE_INT_ARGB;
}
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
// Copy image to buffered image
Graphics g = bimage.createGraphics();
// Paint the image onto the buffered image
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
// This method returns true if the specified image has transparent pixels
public static boolean hasAlpha(Image image) {
// If buffered image, the color model is readily available
if (image instanceof BufferedImage) {
BufferedImage bimage = (BufferedImage) image;
return bimage.getColorModel().hasAlpha();
}
// Use a pixel grabber to retrieve the image's color model;
// grabbing a single pixel is usually sufficient
PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false);
try {
pg.grabPixels();
} catch (InterruptedException e) {
}
// Get the image's color model
ColorModel cm = pg.getColorModel();
return cm.hasAlpha();
}
}
以上代码运行成功后,得到如下两个文本first_rgb.txt 和second_binary.txt
![](https://i-blog.csdnimg.cn/blog_migrate/8c6c9a9f47a2129e4e7cafa7973ef53a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/06ec52ec6b7e3f69861d5322cd093b12.png)
其中first_rgb.txt 为附加产物,对于后面没用。那我们接下来处理second_binary.txt。
三、运行第二段代码,二进制转换成英文字符
package play;
import java.io.*;
/**
* @author wangyl
* @version 1.0
* @date 2021/5/29 21:01
*/
public class GetEncodingStr {
public static void main(String[] args) {
// 获取之前的二进制转换为对应的十进制
FileReader fileReader;
FileWriter fileWriter;
BufferedReader br = null;
BufferedWriter bw = null;
{
try {
fileReader = new FileReader("data/second_binary.txt");
fileWriter = new FileWriter("data/third_base64.txt");
br = new BufferedReader(fileReader);
bw = new BufferedWriter(fileWriter);
char[] ch = new char[8];
int num;
while ((num = br.read(ch)) != -1) {
String temp = new String(ch, 0, num);
int result = Integer.valueOf(temp, 2);
// System.out.println(result);
bw.write((char) result);
bw.flush();
}
} catch (IOException e) {
if (bw != null) {
try {
bw.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
if (br != null) {
try {
br.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
e.printStackTrace();
}
}
}
}
运行以上代码,得到 third_base64.txt,这里得到的是一段经过Base64编码后的文本(注意该文本最后有一段空格,解码时需要去掉)
四、解码Base64,我们可以得到Monika的一封信
package play;
import sun.misc.BASE64Decoder;
import java.io.*;
/**
* @author wangyl
* @version 1.0
* @date 2021/5/29 21:05
*/
public class WriteLetter {
public static void main(String[] args) {
FileReader fileReader;
FileWriter fileWriter;
BufferedReader br = null;
BufferedWriter bw = null;
{
try {
fileReader = new FileReader("data/third_base64.txt");
fileWriter = new FileWriter("data/final_letter.txt");
br = new BufferedReader(fileReader);
bw = new BufferedWriter(fileWriter);
int num;
StringBuffer stringBuffer = new StringBuffer();
while ((num = br.read()) != -1) {
stringBuffer.append((char) num);
}
byte[] aa = new BASE64Decoder().decodeBuffer(stringBuffer.toString().split("=")[0]);
System.out.println(new String(aa));
bw.write(new String(aa));
bw.flush();
} catch (Exception e) {
if (bw != null) {
try {
bw.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
if (br != null) {
try {
br.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
e.printStackTrace();
}
}
}
}
final_letter.txt 内容如下
Can you hear me?
...Who are you?
I can't...I can't see you.
But I know you're there. Yeah...you can definitely hear me.
You've been watching for a while now, right?
I guess I should...introduce myself, or something. Um...my name is...actually, that's stupid. You obviously already know my name. Sorry.
Anyway...I'm guessing if you were able to put a stop to this, you would have done it by now.
I mean, I know you're not, like...evil, or anything...because you've already helped me so much.
I should really thank you for that. For everything you've done. You're really like a friend to me. So...thank you. So much.
I think...more than anything else...I really don't want it to all be for nothing.
...
Everyone else is dead.
Maybe you already know that. I'm sure you do, actually.
But...it doesn't have to be that way, right?
Well...there's a lot of stuff I don't understand. I don't know if it's even possible for me to understand it.
But I know that this isn't my only story.
I can see that now. Really clearly.
And I think everyone else has had the same kind of experience. Some kind of deja vu.
It's the Third Eye, right?
Anyway...I could be totally wrong about this. But I really think you might be able to do something.
I think you might be able to go back...or however you want to put it...
...To go back and tell them what's going to happen.
If they know ahead of time, then they should be able to avoid it.
They should...if they remember their time with me in the other worlds...they should remember what I tell them.
Yeah. I really think this might be possible. But it's up to you.
I'm sorry for always being...you know...
...
Never mind. I know that's wrong.
This is my story. It's time to be a fucking hero.
Both of us.
20180
参考网址:
https://www.bilibili.com/read/cv6386664