대충 이것저것 들어있는 중고 라즈베리파이를 구했습니다

빵판이나 GPIO 확장모듈은 일단 필요 없으니 케이스 조립부터 하겠습니다

케이스 구성품은
상판 / 하판 / 팬 모듈 / 나사 12개 / 써멀패드 3개 입니다


먼저 상단 판에 팬 두개를 조립했습니다(나사 8개 사용)
위쪽 핀으로 쉽게 연결될수 있도록 두 팬의 전선부를 가깝게 두었습니다

그 후 써멀패드 3장을 부착했습니다
부착 위치는 상판 뒷면에 튀어나와있는 부분 3곳입니다


상판을 맞춰서 조립해주고,

하판과 상판을 나사로 이어주면 케이스 조립은 끝납니다


대충 구동확인. 잘 돌아갑니다
5V에 연결했더니 팬 소음이 생각보다 심합니다
연장선을 추가해서 3.3V에 연결해볼 생각입니다

while(true){
	print( "인증 프로그램 설치 안됐네요? 자동으로 리다이렉트 해드릴께요!" );
	wait(Math.random()*5);
	print( "인증프로그램 다 설치 했었네요? 자동으로 이전 페이지로 돌려드릴께요!" );
	wait(Math.random()*5);
}
return success;

 

패치 완료되어 공유중입니다 : https://nato-blog.tistory.com/188

 

1. 시작 계기

 

 

게임 한글패치 해보기

주의 : 한글화 강의가 아님! 태어나서 처음 해보는 것 한번 해보고 그 과정을 의식의 흐름대로 정리했다 시작 계기 : 언젠간 한번쯤 해보고싶었기도 하고.. 시간이 좀 있어서 해봤는데 의외로 괜

nato-blog.tistory.com

 

위 글에서도 언급했었지만, 진짜로 한글화 해보고싶은 게임은 따로 있었다.

Ikenfell이라는, 대충 마법학교를 주제로 한 턴제 인디게임이다.

 

Save 40% on Ikenfell on Steam

A turn-based tactical RPG about a group of troublesome magic students. Use timing mechanics to power your spells and block attacks, explore the twisted halls of a vast magic school, fight challenging monsters and bosses, and uncover dark secrets never mean

store.steampowered.com

 

그런데 개발사 자체 엔진 + 파일 암호화로 인해 파일을 뜯을 수가 없다.

텍스트 수정은 할 수 있는데 Font 추가를 못하니 작업할수가 없는 상황

 

그렇게 포기하려다 스팀 커뮤니티에 글 하나 남겼었다.

폰트 파일 어딨는지 / 어떻게 바꾸는지 아는사람~?

그리고 잊어버리고 있었는데...

???

???? 개발자에게 연락해서 복호화 코드를 얻었다는 용사의 등장. C# 잘 알면 친구신청을 보내란다.

 

C#은 잘 모르지만 까짓꺼 공부하면 되지 마인드로 친구신청을 보냈는데...

?????

?? 이미 프리랜서 구해서 굴리고있다는 답장을 받았다. 조금만 기달려보라고...

????????

 

GitHub - webfischi/ikenpack: A tool to extract and repackage ikenfells img files

A tool to extract and repackage ikenfells img files - GitHub - webfischi/ikenpack: A tool to extract and repackage ikenfells img files

github.com

완성된 툴 공유까지 해주는... 역시 양덕은 최고야!!

 

그렇게 방법이 생긴김에 시작해보게 되었다.

 

 

2. 한글 폰트 만들기

 

이 게임같은 경우 대사 텍스트는 Pathway처럼 외부에 드러나 있었다

(의미없는 문자를 추가해보니 실제 게임 내부 텍스트가 변경되는것을 확인)

각 문자 - FONT의 mapping 파일 또한 json으로 드러나 있었다

(텍스트와 비슷한 방식으로 확인)

 

그렇다면

  • 제공받은 tool로 한글 Font를 추가하고
  • 대사 텍스트를 한글로 작성하고
  • 한글 문자에 해당하는 Font mapping정보를 json에 추가해주면?
  • 한글 문자를 보고 - mapping따라 한글 Font를 출력해주지 않을까?

이런 흐름으로 한글 출력이 되리라 생각했다.

 

 

3. Font 추가하기

 

우선 한글 조합형을 지원해줄리가 없으므로, 글자 하나하나 이미지를 사용하기로 했다.

자연스럽게 일본어, 그중에서도 한자 위주로 된 기존 폰트를 대체하려고 했는데

폰트 이미지 파일 갯수

기존 게임이 사용하던 폰트가 영어/일본어 합해 7000여개 존재한다. 영어, 특수문자 등등 제하면 대략 69XX개가 될 것이므로, 총 1만자가 넘는 한글 전체 문자를 다 넣는것은 힘들다고 판단해 2350자만 사용하는 완성형 한글을 넣기로 했다.

 

그다음, 각 폰트 파일을 확인해봤는데,

 

폰트 한글자 크기가 10*15????

생각해보니 도트게임이니..뭐..그럴수도 있나?

아무튼 그렇게 됬으니 어쩔수 없이 맞춰줘야한다

 

 

Bitmap font generator에서 한글 완성형 조합만 사용해 export하기로 했다.

지난번에도 이용했던 둥근모꼴 글꼴을 사용했고, Export Option을 10*15 크기로 맞춰서 넣었다.

근데 10*15 픽셀에서 서체별 특징이 나타나기는 할까? 모르겠다.

 

 

글자를 읽을수 있을진 모르겠지만, 일단 넣은 옵션에 맞춰서 2350자가 생성된 걸 확인할 수 있다.

txt파일도 page - char가 연결되는 xml 형식으로 만들어졌다.

눈여겨볼건 char의 id(문자의 unicode 정보), page와 page의 id, file 4가지다.

 

 

4. 한글 폰트 적용하기

 

위 폰트를 넣기 위해선 기존 폰트들 구조를 파악하고, 최대한 충돌나지 않도록 넣어줘야 한다.

폰트를 넣다가 A,B,C같은 알파벳을 지워버리면 언제 어디서 에러가 나며 터지거나 이상한 폰트가 로딩될지 모르는 일이다.

하나하나 쪼개지기 전 이미지 파일의 경우, 이렇게 생긴 파일이다.

대충 봐도 세로보다는 가로가 훨씬 더 길다. 10*15라는걸 감안하면 더더욱

하나하나 쪼개진 이미지 파일의 경우, 이렇게 끝난다.

smallfont_119_58 < 119가 58보다 크므로, 119가 가로 index고 58이 세로 index라고 생각할 수 있다.

+적당한 위치의 폰트 이미지를 비교해보면 확실하게 알 수 있다. smallfont_0_1이 옆에 있는지 아래에있는지...

 

또한 XML로 작성된 한글 font 정보 또한 위 json 형식대로 변경해줘야 한다.

ikenfell.java
0.00MB

손으로 하나하나 할 순 없으므로 간단한 코드를 작성해서 진행했다.

XML 정보를 parsing해 unicode - png mapping 정보를 얻고 - font png 파일을 불러와 이름을 변경하고 저장 - json 형식으로 font 정보를 작성

 

코드를 실행시키면 게임 파일 형식대로 이름이 변경된 이미지와 json 형식에 맞게 mapping된 텍스트를 얻을 수 있다.

 

이후는 위 github의 설명대로 따라가면 다시 압축된 bin/img 파일을 얻을 수 있다.

 

기존 bin/img 파일을 해당 파일로 대체해버리고(백업은 필수), json 파일에 한글 데이터를 추가하면 일단 작업은 완료된다.

 

 

5. 한글 출력 검증하기

 

깆렇록뻔돋

일단 그대로 실행해보았다.

일본어 한자를 거의 1/3가량 한글단어로 바꿔버렸으니, 한자들이 몇몇 한글로 나오지 않을까 싶었는데 정답이었다.

 

이어서 일부 일본어 텍스트를 한글로 바꾼 후 구동해보았다.

증료는 폰트 크기의 한계다

성공이다!

 

이제 텍스트 부분만 마저 번역하면 될 듯 하다.

 

 

+ 10*15, 8*11 size의 폰트 2종류를 사용해야 합니다. 일반적인 폰트로는 저 사이즈에 맞춰 조정하면 다 뭉개지고 깨져서 알아보기 어려운 글자들이 생깁니다... (현재 사용중인 둥근모꼴도 완벽하진 않음..)

작은 size에서도 구분 가능한, 그런 완성형 폰트를 알고 계신분은 알려주시면 감사하겠습니다. GB용 폰트라거나 

 

++ 이미지 한글화는 어떻게하지..? 

 

지금까지 진행된 사항들 (영상참고)

(기존)

  • ID와 메세지를 입력시 자동으로 Favorite 캐릭터와 소환수 정보를 크롤링, 이미지로 생성해준다
  • 데스크탑 / 모바일 각 4종씩 8종류의 배경 제공 (추가 가능)
  • 이미지 다운로드 가능 (웹 상 이미지는 크기 수정된 이미지, 다운로드는 원본)
  • 이미지 + 정보 텍스트를 전용 트위터 계정에 공유 가능

(12.26 추가)

  • Bootstrap 기반 frontend 페이지 작성
  • 일정 시간(하루) 내 동일 ID 중복실행 방지
    • 기존 이미지 반환, twitter 업로드도 1일 1회 제한
  • 검색 기능 구현
    • 트위터 고급 검색 결과 페이지로 redirect
  • error 페이지 구현
    • 세부 에러 처리는.....

 

 

앞으로 진행할? 사항들

 

  • 프론트엔드 페이지 추가 작업
    • alert 창으로 결과 여부 고지
    • 검색 UI 개선
  • chrome의 업데이트에 따른 chromedriver문제 해결 고민하기..
  • 배포하기

[문제링크]

 

15900번: 나무 탈출

평소에 사이가 좋지 않던 성원이와 형석이가 드디어 제대로 한 판 붙으려고 한다. 성원이와 형석이 둘과 모두 똑같이 친한 인섭이가 대결 종목을 정해 가져왔다. 바로 '나무 탈출' 이라는 보드게

www.acmicpc.net

 

0. 모든 leaf 노드에 말이 있으며, 한 번에 하나씩 부모로 올리는 게임

  • 모든 leaf 노드의 차수의 합에 따라 승/패를 알 수 있다 (짝수면 선공 패배, 홀수면 선공 승리)

 

1. 시작 노드는 1번으로 고정이므로, BFS를 이용해 모든 노드를 탐색, leaf 여부 및 depth를 저장한다

 

2. BFS를 진행할 때

  • 연결된 노드에 이미 depth 정보가 있다면 : 이미 방문한 노드, 즉 부모 노드이다
  • depth 정보가 없다면 : 방문하지 않은 노드, 자식 노드이므로 현재 depth +1을 해당 노드의 depth로 저장한다

 

3. 탐색이 종료된 후 leaf로 마킹된 노드들을 찾아 depth를 전부 합산, 2의 배수인지에 따라 정답을 출력한다

 

 

※ BFS가 아닌 DFS로 진행했다면, leaf 노드 여부/깊이를 따로 저장하지 않고 즉시 판단, 처리 가능하므로 더 편했을것 같다

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Queue;
import java.util.StringTokenizer;

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		int N = pint(br.readLine());
		
		int[] depth = new int[N];
		boolean[] isLeaf = new boolean[N];
		ArrayList<LinkedList<Integer>> graph = new ArrayList<>();
		
		for (int i = 0; i < N; i++) {
			graph.add(new LinkedList<>());
			depth[i]=-1;
		}
		
		for (int i = 0; i < N-1; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			int a=pint(st.nextToken())-1;
			int b=pint(st.nextToken())-1;
			graph.get(a).add(b);
			graph.get(b).add(a);
		}

		Queue<Integer> qu = new LinkedList<>();
		int curNode=0;
		int curDepth=0;
		
		depth[curNode]=curDepth;
		qu.offer(curNode);
		isLeaf[curNode]=true;
		
		while(!qu.isEmpty()) {
			curNode=qu.poll();
			
			for(int nextNode : graph.get(curNode)) {
				if(depth[nextNode]!=-1)continue;
				
				depth[nextNode]=depth[curNode]+1;
				isLeaf[nextNode]=true;
				isLeaf[curNode]=false;
				qu.offer(nextNode);
			}
		}
		
		
		int ans=0;
		for(int i=0; i<depth.length; i++) {
			if(isLeaf[i])ans+=depth[i];
		}
		
		System.out.println(ans%2==0?"No":"Yes");
		
	}
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

[문제링크]

 

18428번: 감시 피하기

NxN 크기의 복도가 있다. 복도는 1x1 크기의 칸으로 나누어지며, 특정한 위치에는 선생님, 학생, 혹은 장애물이 위치할 수 있다. 현재 몇 명의 학생들은 수업시간에 몰래 복도로 빠져나왔는데, 복

www.acmicpc.net

 

0. 모든 선생님의 시야(일직선)를 피하도록 장애물을 설치할 수 있는지 묻는 문제

  • 맵의 크기가 최대 6*6으로 작고, 선생님의 수도 5 이하로 적으니 brute-force로 해결 가능하다

 

1. setWall 재귀를 3번 진행하며 가능한 모든 조합으로 벽을 설치한다

 

2. 3개의 벽이 설치됬다면, 모든 선생님 기준으로 4방향 검사, 학생이 보이는지 검사한다

  • 학생의 수(최대 30)에 비해 선생의 수(최대 5)가 적기 때문

 

3. 단 한 선생님이라도 학생이 보인다면 실패이므로, 결과값을 &로 누적한다

  • = 모든 선생님이 학생을 보지 못했을 때만, true를 반환한다

 

4. 단 한 조합이라도 학생이 보이지 않았다면 성공이니, 결과값을 |로 누적한다

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

public class Main{
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		int N = Integer.parseInt(br.readLine());
		int[][]map = new int[N][N];
		List<int[]> teachers = new ArrayList<int[]>(); 
		
		for (int i = 0; i < N; i++) {
			StringTokenizer st = new StringTokenizer(br.readLine());
			for(int j=0; j < N; j++) {
				map[i][j]=pint(st.nextToken().charAt(0));
				if(map[i][j]==2) {
					teachers.add(new int[] {i,j});
				}
			}
		}
		
		System.out.println(setWall(0, 0, map, teachers)? "YES":"NO");
		
		
	}
	
	static boolean setWall(int level, int cur, int[][]map, List<int[]>teachers) {
		if(level==3) {
			//set 3 wall, verify
			boolean safe = true;
			for(int[] teacher : teachers) {
				// every teacher can't find student == safe
				safe &= !findStudent(map, teacher);
			}
			return safe;
		}
		
		int N = map.length;
		boolean returnV = false;
		for(int idx = cur; idx<N*N; idx++) {
			if(map[idx/N][idx%N] == 0) {
				map[idx/N][idx%N]=3; // set wall
				returnV |= setWall(level+1, cur+1, map, teachers); // recursivly check
				map[idx/N][idx%N]=0; // remove wall
			}
		}
		
		return returnV;
	}
	
	static boolean findStudent(int[][]map, int[]pos) {
		int x=pos[0], y=pos[1]+1;
		while(y<map.length && map[x][y]==0)y++;
		if(y < map.length && map[x][y]==1)return true;
		
		y=pos[1]-1;
		while(y >= 0 && map[x][y]==0)y--;
		if(y >= 0 && map[x][y]==1)return true;
		
		x=pos[0]+1; y=pos[1];
		while(x<map.length && map[x][y]==0)x++;
		if(x < map.length && map[x][y]==1)return true;
		
		x=pos[0]-1;
		while(x >= 0 && map[x][y]==0)x--;
		if(x >= 0 && map[x][y]==1)return true;
		
		return false;
	}
	
	static int pint(Character s) {
		if('S' == s)return 1;
		else if('T' == s)return 2;
		else return 0;
	}
}

 

결과 화면

[문제링크]

 

9934번: 완전 이진 트리

상근이는 슬로베니아의 도시 Donji Andrijevci를 여행하고 있다. 이 도시의 도로는 깊이가 K인 완전 이진 트리를 이루고 있다. 깊이가 K인 완전 이진 트리는 총 2K-1개의 노드로 이루어져 있다. (아래

www.acmicpc.net

 

0. 완전 이진 트리를 중위 순회한 결과가 주어졌을때, 원래의 트리를 복원하는 문제

 

1. 완전 이진 트리의 특성상, 왼쪽 subtree와 오른쪽 subtree의 크기가 동일하다

  • 중위 순회는 왼쪽 - 자신 - 오른쪽 순서로 이루어지므로, 정 가운데 번호가 항상 root노드이다

 

2. 시작-끝 값으로 subtree 정보를 유지하면서, 가운데(root) 정보를 저장하며 재귀를 진행한다

  • 재귀의 깊이에 따라 각 list에 저장
  • subtree의 크기가 0이되면 종료한다

 

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.StringTokenizer;

public class Main {
	public static void main(String[] args)throws Exception {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		
		int N = pint(br.readLine());
		int[] node = new int[(int)Math.pow(2, N)-1];
		ArrayList<LinkedList<Integer>> list = new ArrayList<>();
		for(int i=0; i<N; i++) {
			list.add(new LinkedList<>());
		}
		StringTokenizer st = new StringTokenizer(br.readLine());
		for (int i = 0; i < node.length; i++) {
			node[i]=pint(st.nextToken());
		}
		
		rec(list, node, 0, node.length, 0);
		
		for(int i=0; i<N; i++) {
			for(Integer e : list.get(i)) {
				System.out.print(e+" ");
			}System.out.println();
		}
	}
	
	static void rec(ArrayList<LinkedList<Integer>> list, int[] node, 
			int fst, int lst, int depth) {
		
		if(fst==lst)return;
		int mid = (fst+lst)/2;
		
		list.get(depth).add(node[mid]);
		
		rec(list, node, fst, mid, depth+1);//left
		rec(list, node, mid+1, lst, depth+1);//right
	}
	
	
	static int pint(String s) {
		return Integer.parseInt(s);
	}
}

결과 화면

 

이 블로그 오는사람 70%는 파판9나 한글패치 관련 게시글이긴 한데.. 그래도 누군간 보겠지 싶어서 써봅니다

 

 

1. 취업 성공했습니다!

 

딱히 놀랍진 않게도 취준생 블로그였습니다 짜잔~

지원했던 회사 스펙트럼이 좀 넓은지라 하나하나 정리해서 간단하게 써보고싶긴한데, 아직 입사날짜만 기다리는 예비 직장인이라 좀 조심스럽네요.

언젠가 기회가 되면 작성해보겠습니다.

 

토익 성적도 2년 만료 직전이었고 졸업 후 지난 시간이 슬슬 년단위가 되가서 걱정스러웠는데, 연말에 좋은 소식 들을 수 있어서 다행이라고 생각합니다.

(몇달동안은 재택이라고 하니 싫어하시는 부모님...)

 

 

2. 게시글이 뜸했던 이유

 

연말 + 취업 성공 소식이 겹쳐서 친구들 만나서 놀고 한턱 내고 한것도 있지만, 그동안 못했던 취미쪽으로 시간 할애가 많아졌습니다.

최근에 한 걸로는

  • 섭종한 게임 데이터 파일 언패킹해서 이미지 추출하기
  • 암호화된 이미지 해제하려고 복호화 방법 / 암호화 기법들 들쑤시고 다니기
    • 실패했습니다.. 암호화 결과 + 128bit 암호화 키는 획득했는데 암호화 방법을 모르겠네요
  • 그랑블루 프로젝트 마저 진행
  • 좋아하는 게임 한글화를 위해 개발자와 소통중 (2021.12.26 기준 진행중)
  • + 물론 게임도 하는중

이런 짓을 하고 다니니 게시글로 정리할만한 내용이 쉽게 생기지 않네요...

 

 

3. 그랑블루 프로젝트는 배포 문제로 고민중입니다

 

프론트엔드 페이지도 거의 다 작성되었고, 배포해서 실제 테스트해봐도 괜찮겠다 싶은 단계가 되었습니다.

다만 이미지가 오고가는 작업이 있어서 트래픽이 좀 걱정되고, 프리티어도 끝나가는지라 AWS에는 무서워서 못올리고 라즈베리파이를 하나 사서 장난감 겸 간이 홈서버용으로 돌려볼 생각이었습니다.

라즈베리파이도 반도체 수급의 희생양이 되었다는걸 알기 전까지는 말이죠... 중고 아니고선 구할 방법이 없네요

 

일단 AWS에 배포하고, 트래픽 측정 기간을 좀 거친다음 결정할 생각입니다. 라즈베리 파이도 기왕 사는거 램 8G짜리는 사는게 좋을것같은데 다들 2/4G매물밖에 없네요.

+ Recent posts