import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.awt.Color; import java.util.regex.Pattern; import javax.imageio.ImageIO; import java.awt.Graphics2D; import java.awt.image.BufferedImage; public class ArcadeReader { /* * Important data we would like to know for all games: * Score * Rank (Probably implementation-specific) * Note accuracy [List] * Marvelous * Perfect * Great * Good * Bad * OK * Miss * Difficulty * Song Name / Title * Percentage (Can be EX Score, a percentage accuracy or survival percent) * Max Combo * Other (Not used by the auto detecter, used for storing misc. data.) * * Notes about Readers: * Love Live * - Does not use: Marvelous, OK * Project Diva * - Does not use: Marvelous, OK * - Fail condition is MISSxTAKE * DDR * - Does not use: Max Combo (Cannot calculate in combo carryover mode), Bad * - Fail condition is E rank. * Pop'n Music * - Does not use: Marvelous, Bad, OK * - Stores number of bars filled in Percentage. * - Fail condition is <17 in the meter (max is 24) * Sound Voltex * - Sound Voltex uses the note accuracy list slots as follows: * Early: * Error - Marvelous * Near - Perfect * Critical - Great * S-Critical - Good * Critical - Bad * Near - OK * Error - Miss * - Sound Voltex stores EX score in Percentage. * - Max Combo is Max Chain * - Sound Voltex will store additional data about accuracy of note types as such: * {"chip":{"scritical":0,"critical":0,"near":0,"error":0},"long":{"scritical":0,"error":0},"vol":{"scritical":0,"error":0},"gauge":,"gauge_pct":100} * //Also storing what type of clear gauge was met and the % of the gauge. * - Fail condition is <70% normal gauge. * IIDX * - Not going to support right now. * */ public static void retrieveData(Path img) { new LoveLiveReader().interpretBoxes(img); } } class ColorRange{ int r_h,r_l; int g_h,g_l; int b_h,b_l; ColorRange(int r_l,int r_h,int g_l,int g_h,int b_l,int b_h) { this.r_l=r_l; this.r_h=r_h; this.g_l=g_l; this.g_h=g_h; this.b_l=b_l; this.b_h=b_h; } boolean colorInRange(Color col) { return col.getRed()>=r_l&&col.getRed()<=r_h&&col.getGreen()>=g_l&&col.getGreen()<=g_h&&col.getBlue()>=b_l&&col.getBlue()<=b_h; } } class Box{ int x,y,w,h; boolean ja_required; final static int BOX_THRESHOLD=8; //How many pixels outside the specified region the score can be. Box(int x,int y,int w, int h) { this.x=x; this.y=y; this.w=w; this.h=h; } boolean insideBox(int x,int y) { return this.x-BOX_THRESHOLD<=x&&this.x+this.w+BOX_THRESHOLD>=x&&this.y-BOX_THRESHOLD<=y&&this.y+this.h+BOX_THRESHOLD>=y; } } class LoveLiveReader extends Reader{ final static int REGION_PADDING = 32; LoveLiveReader(){ readRegions.add(new Box(713,401,232,50)); //score[0] readRegions.add(new Box(613,290,65,36)); //rank[1] readRegions.add(new Box(509,604,190,54)); //notes[2] readRegions.add(new Box(509,680,190,54)); //notes[3] readRegions.add(new Box(509,760,190,54)); //notes[4] readRegions.add(new Box(509,840,190,54)); //notes[5] readRegions.add(new Box(509,920,190,54)); //notes[6] readRegions.add(new Box(26,374,265,36)); //difficulty[7] readRegions.add(new Box(277,165,572,40)); //title[8] readRegions.add(new Box(716,502,226,45)); //pct[9] readRegions.add(new Box(782,452,158,50)); //maxcombo[10] } void seek(int[]arr,int i,ColorRange SEEKCOLOR,Color FINALCOLOR,int width) { arr[i]=FINALCOLOR.getRGB(); for (int x=-1;x<=1;x++) { for (int y=-1;y<=1;y++) { if (SEEKCOLOR.colorInRange(new Color(arr[i+x+y*width]))) { seek(arr,i+x+y*width,SEEKCOLOR,FINALCOLOR,width); } } } } void ColorFilter(int[] arr,int region,int width) { final int TRANSPARENT = new Color(0,0,0,0).getRGB(); switch (region) { case 0:{ final ColorRange TARGETCOLOR = new ColorRange(240,255,130,150,0,10); final ColorRange SEEKINGCOLOR = new ColorRange(140,255,110,255,0,200); final Color FINALCOLOR = Color.MAGENTA; for (int i=0;imaxWidth) { maxWidth=readRegions.get(i).w; } } try { BufferedImage originalImg = ImageIO.read(img.toFile()); BufferedImage cutImg = new BufferedImage(maxWidth,regionHeights,BufferedImage.TYPE_INT_ARGB); Graphics2D g = cutImg.createGraphics(); int currentHeight=0; for (int i=0;i readRegions = new ArrayList<>(); String readAllBoxes(Path img) { try { Process p = Runtime.getRuntime().exec(new String[]{"python3","runocr.py","ja",img.toAbsolutePath().toString()}); while (p.isAlive()); InputStreamReader result = new InputStreamReader(p.getInputStream()); StringBuilder sb = new StringBuilder(); while (result.ready()) { sb.append((char)result.read()); } result.close(); sb.append("\n"); p = Runtime.getRuntime().exec(new String[]{"python3","runocr.py","en",img.toAbsolutePath().toString()}); while (p.isAlive()); result = new InputStreamReader(p.getInputStream()); while (result.ready()) { sb.append((char)result.read()); } return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return ""; } void trimAllData(String[] data) { StringBuilder sb = new StringBuilder(); for (int i=0;i